Friday, 3 August 2012

Selecting and copying all tables in a Word doc to Excel (Mac)

The examples I found online didn't support Mac or the functionality I was trying to get to.
So, I have an 80 page word document with tables littered about with requirements,to copy these tables into Word, create a new Macro and copy this code into the new subroutine.
I've made some alterations to other examples online in order to get this working with the older version of VBA that comes with Mac Office 2011.
  Dim C As Variant
  Dim R As Long
  Dim Tbl As Table
  Dim xlApp As Object
  Dim xlWkb As Object
  Dim xlWks As Object
  
  'Set the starting cell on the worksheet
  C = "A"
  R = 1

  'Attach to the open workbook
  On Error Resume Next
  Set xlApp = GetObject(, "Excel.Application")
  If xlApp Is Nothing Then _
  Set xlApp = CreateObject("Excel.Application")
    
  Set xlWkb = xlApp.ActiveWorkbook
    
  'Select the worksheet to copy the tables to
  Set xlWks = xlApp.Worksheets("Sheet1")
  For Each Tbl In ActiveDocument.Content.Tables
   Tbl.Range.FormattedText.Copy
     xlWks.Cells(R, C).Select
     xlWks.Paste
     ' Comment the following line out to remove the gap between pastes
     R = R + Tbl.Rows.Count + 1
   Next Tbl

Monday, 15 June 2009

Great place to stay in Northumberland

If you are looking to go to Hadrians Wall, either to housesteads or looking for some climbing, there are some great self-catering cottages nearby http://www.commonhousefarm.com/

Tuesday, 26 May 2009

Hosters Guide to Microsoft Licensing..

Authenticated vs. Unauthenticated
There is a simple distinction between authenticated and unauthenticated, if a server is authenticated, that means it is using Active Directory. Active Directory is the system of centralised user control in Windows. You do not need Active Directory to run windows, but certain systems require AD, these will be covered in this sheet. The most common are:
• Windows Clustering
• Microsoft Exchange
• Microsoft Office Sharepoint Server (Not Sharepoint Services 3.0)
• A cluster of terminal servers
• A domain controller
The following words indicate this is an authenticated environment:
• “In a domain”
• “Active Directory”
• “Not in a workgroup”
• “On AD”
The customer might say the following to indicate this is unauthenticated:
• “In a workgroup environment”

SAL or CAL

• A SAL is a ‘Subscriber access license’; it is the SPLA version of the CAL.
• 1 Windows Standard SAL will grant a user access to all authenticated servers in a hosted platform, for example:
This solution has 5 users
It also has 3 servers (Terminal Server, Domain Controller and Exchange Server) which are all authenticated
The licensing required for this solution is:

5x Windows Standard SAL
5x Terminal Server SAL
5x Exchange SAL

Those SAL’s grant those 5 users access to all 3 servers, regardless of number of processors.
If you added another Domain Controller for example, you would not need extra SALs.

Exchange
Exchange is a system allowing Calendaring, Email, Shared folders. Exchange has the following requirements:
1. You must have an Exchange SAL for each user using Exchange. There is no ‘unlimited’ license.
2. Every Exchange Server MUST be authenticated, Exchange requires Active Directory to operate
3. Because it requires AD, you need both an Exchange SAL AND a Windows SAL.
4. 1 Exchange SAL licenses every Exchange server in that setup.
This example has 10 users, 3 Exchange Servers and 1 Domain controller, you need the following licenses:
• 10x Exchange SAL (Ask customer which version)
• 10x Windows STD SAL

Windows Server 2008 Web Edition
In web edition you can do everything the full version allows, except:
• It must be web facing
• It cannot be a domain controller
• It cannot be a DNS server
• It cannot run Hyper-V
• It cannot be a terminal server

Terminal Services
1. Terminal Services does NOT need to be on an authenticated license
2. It is recommended that for more than 1 terminal server, you use Active Directory.

SQL Server
SQL Server does not need to run on a domain, unless it is in a cluster.
http://www.microsoft.com/sqlserver/2008/en/us/editions-compare.aspx

Clustering
1. Clustering requires Active Directory to run
2. You only need to license the active SQL Server license, say you have 2 SQL Servers in a cluster, you do not need 2 SQL Licenses.
3. You do need to license the Windows License on both nodes.
4. Do NOT run a cluster node as a domain controller as well, or EVER allow a customer to do this. We will not support a solution with this setup.

This solution powers a web site, I have left the web servers out for simplicity.
The Customer does not know how many users will be accessing the website because this is a public site, there are 2 Servers in a cluster providing SQL.
You will need the following licenses:
• 1x Windows Std Proc (Domain Controller)
• 2x Windows Enterprise Proc (Cluster Nodes)
• 1x SQL Server (Standard or Enterprise) License

Domain Controllers
A domain controller is the server that contains a database on the users in the system, if this server fails, any software requiring logins in a domain will fail.
If any of the servers in a solution require Active Directory, there must be a domain controller in the solution. It is important to remember that if the solution only has 1 domain controller; this becomes a single point of failure, so if possible provide 2 domain controllers, especially if the customer is requesting NO single point of failure.
A Domain Controller needs at least 512MB RAM, so it can be a small spec machine.
If you are trying to keep the costs low, you can make both domain controllers virtual machines, speak to any of the Solutions Architects on this.
Virtualisation
For Virtual Guests (ie: the Virtual Machines, not the physical servers) once you license the host, there are a certain number of included licenses for Windows,

Standard Authenticated (1 Web or Standard VM)
Enterprise Authenticated (4 Web, Standard or Enterprise VM's)
Datacenter Unauthenticated (Unlimited Unauthenticated Web,Standard,Enterprise or Datacenter VMs)

Also for SQL Server, there are benefits, if you license the host with Enterprise Edition SQL, any Virtual Machines on that host do not need SQL Licenses.

Friday, 3 April 2009

Quick C# Twitter API

Quick little class I knocked together to do 2 basic functions in twitter but can easily do more. Add this to a cmdlet in powershell or command line app to integrate with anything else!!

///
/// Twitter.cs
/// A simple C# class with 2 basic methods, add away with other methods-
/// http://apiwiki.twitter.com/REST+API+Documentation#statuses/update
///

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Collections.Specialized;
using System.Web;

namespace Net.Ant.Twitter
{
public class Twitter
{
protected string _username;
protected string _password;

/// <summary>
/// Creates a new instance of the Twitter API class
/// </summary>
/// <param name="username">Your Twitter username</param>
/// <param name="password">Your Twitter password</param>
public Twitter(string username, string password)
{
_username = username;
_password = password;
}

#region Twitter API functions

/// <summary>
/// Sends a private message
/// </summary>
/// <param name="user">User to message</param>
/// <param name="text">Message text</param>
public void SendMessage(string user, string text)
{
NameValueCollection col = new NameValueCollection();
col.Add("user",user);
col.Add("text",text);
HttpPOSTRequest("http://twitter.com/direct_messages/new.xml", col);
}

/// <summary>
/// Tweet/Update!
/// </summary>
/// <param name="message">Message text</param>
public void Tweet(string message)
{
NameValueCollection col = new NameValueCollection();
col.Add("message", message);
HttpPOSTRequest("http://twitter.com/statuses/update.xml", col);
}

#endregion

#region Internal functions for HTTP requests

/// <summary>
/// Make a call to the twitter website, will used the username/pass in the class
/// </summary>
/// <param name="url">The URL to request</param>
protected string HttpPOSTRequest(string url,NameValueCollection post_parameters )
{
HttpWebRequest req = (HttpWebRequest) WebRequest.Create(url);
req.Credentials = new NetworkCredential(_username, _password);
req.PreAuthenticate = true;

string postData = PostEncodeArray(post_parameters);

req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = postData.Length;
using(Stream writeStream = req.GetRequestStream())
{
UTF8Encoding encoding = new UTF8Encoding();
byte[] bytes = encoding.GetBytes(postData);
writeStream.Write(bytes, 0, bytes.Length);
}

// make request
string result=string.Empty;
using (HttpWebResponse response = (HttpWebResponse) req.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader readStream = new StreamReader (responseStream, Encoding.UTF8))
{
result = readStream.ReadToEnd();
}
}
}
return result;
}

/// <summary>
/// Posts the supplied data to specified url.
/// </summary>
/// <returns>a string containing the result of the post.</returns>
protected string PostEncodeArray(NameValueCollection m_values)
{
StringBuilder parameters=new StringBuilder();

for (int i=0;i < m_values.Count;i++)
{
if (parameters.Length != 0) parameters.Append("&");
parameters.Append(m_values.AllKeys[i] + "=" + HttpUtility.HtmlEncode(m_values.Get(i)));
}
return parameters.ToString();
}
#endregion
}
}

Wednesday, 4 March 2009

IPQ Tutorial

Before I start the tutorial, a couple of notes:
Only attempt this if you are familiar with:
* C/C++
* IP Tables
* IP Networking

And I really don't recommend doing this on a live server! If, once you've coded your funky new firewall it drops packets and you can't get SSH access to kill it, then you'll see why.

Right, now here's an interesting library which comes with IP Route 2 for linux. Using this library you can tell your IP stack to put packets in a queue, you can then write an app to read through the packets and either drop or accept them (even modify them if you read further into it). The great thing is, this is all in user space so there's none of that messy kernel re-compiling.

Why? Well I'll give you an example.
Say you find IP tables limiting for example, because you can't tell it to allow packets to port 666 only when there's a full moon. Bad example, infact as you can get your firewall todo pretty much anything you can possibly think of its quite an interesting concept. I ended up using this todo pattern recognition on the packet size and source address to detect dDos attacks and it worked pretty well. As its all in user space you can load config files and make a fully blown firewall.

Now to continue, you're going to need all the usual tools suchas gcc and you can write this in either C or C++.

You're also going to need the libraries and IP route 2 installed.

Once you've installed the devel libraries for netfilter and networking etc. You will need to check that you have both the iptable_filter and ip_queue modules loaded:

# lsmod | grep iptable_filter
# lsmod | grep ip_queue

If not, then load them!
# modprobe iptable_filter
# modprobe ip_queue

Right now, you can use the action 'QUEUE' in iptables, for example to queue any tcp packets:
# iptables -A OUTPUT -p tcp -j QUEUE

Note that these packets will sit in the queue until the application runs, so as I mentioned, don't do this unless you're on a terminal.

Now for the code.

Include some standard libraries, which you will probably need

#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>


Right now we include some standard socket libraries

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


Include the netfilter library, make sure this is installed

#include <linux/netfilter.h>


Now we include the ip queue library, note if you're compiling in c++ then tell the compiler to pretend its C like so:

extern "C" { // compile in c
#include <libipq.h>
}


Or for those just keeping it in C:

#include <libipq.h>


Now we include the structs for ip, tcp and udp packets (obviously icmp and others are available as well.
If you want to know what the structs are then read the header files which should give you a good pointer.

#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>


Define the buffer size for reading off the queue

#define BUFSIZE 2048



using namespace std;


Here's a clean function which makes sure the handle is destroyed incase we need to quit, pass the handle to the function to die

static void die(struct ipq_handle *h)
{
ipq_perror("passer");
ipq_destroy_handle(h);
exit(1);
}


Now here's another function which you might find helpful for easily converting between an int and a readable IP. Pass it an unsigned 32 and it returns the string.


char * inttoip ( __u32 ip ) {
in_addr src_addr;
src_addr.s_addr = (ip);
return inet_ntoa(src_addr);
}



int main() {

// packet counter
unsigned short int packet_count = 0;

int status;
unsigned char buf[BUFSIZE];

struct ipq_handle *h;

cout << "Creating IP Queue handle..\n";
h = ipq_create_handle(0, PF_INET);

if (!h)
die(h);

cout << "Handle created, setting mode to " << IPQ_COPY_PACKET << " .\n";
status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE);
if (status < 0)
die(h);
cout << "IP Queue ready, listening for all queued packets. \n";
do{
status = ipq_read(h, buf, BUFSIZE, 0);
if (status < 0)
die(h);
if (ipq_message_type(buf) == NLMSG_ERROR ){
fprintf(stderr, "Received error message %d\n", ipq_get_msgerr(buf));
cout <<"make sure you are root and ip_queue module is loaded.\n";
exit(1);
}
else if (ipq_message_type(buf) == IPQM_PACKET) {

ipq_packet_msg_t *m = ipq_get_packet(buf);

struct iphdr *ip = (struct iphdr*) m->payload;



Now for quick reference, here's the struct for iphdr:
__u8 tos;
__u16 tot_len;
__u16 id;
__u16 frag_off;
__u8 ttl;
__u8 protocol;
__u16 check;
__u32 saddr;
__u32 daddr;
And if, for example, the protocol was TCP, then you can typecast the packet to a tcp struct like so:

struct tcphdr *tcp = (struct tcphdr*) (m->payload + (4 * ip->ihl));


Once we have that we can do:

if ( tcp->dport == '666' && IS_FULL_MOON && USER_IS_WEARING_SOCKS)
status = ipq_set_verdict(h, m->packet_id,NF_ACCEPT, 0, NULL);
else
status = ipq_set_verdict(h, m->packet_id,NF_DROP, 0, NULL);
}
else{
fprintf(stderr, "Unknown message type :\n");
}

} while (1);

ipq_destroy_handle(h);
return 0;
}

Monday, 9 June 2008

Easier in Windows?

So, maybe I've been slated by my Linux colleagues, but I wanted to see how simple that last example was in Linux.

My assumptions point towards a C (maybe even C++ if I'm lucky) compiled .so library with some handy headers which differ massively from version to version of apache. But the IIS module was a bit toooo easy, so I'm up for the challenge.

OK, so the last example wasn't an actual 'module', which I will demonstrate later, but a mere handler. So maybe I could quickly knock up a handler for CGI grabbing a Perl library for ImageMagick, but that again wouldn't be the same kind of performance shown by the .NET library I demonstrated in the last example.

Instead, this module will have:

* A proper HTTPD module, using the API
* Using the http://www.imagemagick.org/script/magick-core.php ImageMagick C API
* A comparable speed to the IIS module (probably much faster)

After a bit of surfing, we have a contender: http://threebit.net/tutorials/apache2_modules/tut1/tutorial1.html

So, we already had Apache 2.2 installed on this test (RHEL 5.1) machine, get rid of that:


[root@cpetest ~]# rpm -e httpd mod_perl mod_ssl mod_python php webalizer httpd-manual



Now, following the tutorial (roughly) from the beginning,


wget http://apache.rmplc.co.uk/httpd/httpd-2.0.63.tar.gz
tar xzf httpd-2.0.63.tar.gz
mkdir threebit-tutorials
cd threebit-tutorials/
export TUTORIAL_HOME=`pwd`
wget http://threebit.net/tutorials/tutorials.tar.gz
tar xzf tutorials.tar.gz
cd ~/httpd-2.0.63
./configure --prefix=$TUTORIAL_HOME/apache2 --enable-so
make && make install
cd ../threebit-tutorials/apache2
ls conf/



So here we have a seemingly working apache compile, put quite horribly in the root home directory (my fault for taking this tutorial a bit too literally)


[root@cpetest apache2]# bin/apachectl start
[root@cpetest apache2]# lsof -i tcp:80
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
httpd 32010 root 3u IPv6 763802 TCP *:http (LISTEN)
httpd 32011 nobody 3u IPv6 763802 TCP *:http (LISTEN)
httpd 32012 nobody 3u IPv6 763802 TCP *:http (LISTEN)
httpd 32013 nobody 3u IPv6 763802 TCP *:http (LISTEN)
httpd 32014 nobody 3u IPv6 763802 TCP *:http (LISTEN)
httpd 32015 nobody 3u IPv6 763802 TCP *:http (LISTEN)



So, yes it does work.

Right, so now we compile a quick test, looking down the tutorial and picking out my key points,
1. Edit a C file, put in a struct called 'module' to make it look like a natural part of the code, place in a callback (usual C module-ness) then fill in the guts of the callback.
2. Compile the code into a shared library which should build into the correct place (the configure, make scripts that the example has provided ensure that)
3. Change the httpd config to see this new super module that currently does nothing.

Looking at the callback, it takes a pointer to a request_rec struct, which after some simple digging, has these fields:
http://www.temme.net/sander/api/httpd/structrequest__rec.html

So, straightfoward enough, changing the first line in the callback to check everything works as it should,


// Send a message to stderr (apache redirects this to the error log)
fprintf(stderr,"apache2_mod_tut1: A request was made to %s.\n",r->the_request);



So, continuing down the tutorial, lets try and build:


[root@cpetest tut1]# autoconf
configure.in:5: error: possibly undefined macro: AM_INIT_AUTOMAKE
If this token and others are legitimate, please use m4_pattern_allow.
See the Autoconf documentation.
configure.in:9: error: possibly undefined macro: AM_PROG_LIBTOOL
[root@cpetest tut1]#



Oh, something about libtool, crap, is it installed at least?


[root@cpetest tut1]# libtool --help
Usage: libtool [OPTION]... [MODE-ARG]...



Yep, so more searching, blah blah deprecated blah shows something about aclocal, so I run


[root@cpetest tut1]# aclocal -I /usr/share/aclocal/



And then autoconf works, great. We have a configure script to build something to tell the compiler to compile, straightforward enough.


automake -a
[root@cpetest tut1]# automake -a
configure.in: installing `./install-sh'
configure.in: installing `./missing'
configure.in:9: installing `./config.guess'
configure.in:9: installing `./config.sub'
Makefile.am: installing `./depcomp'
configure.in:9: required file `./ltmain.sh' not found



Ok, missing shell script which is standard in most builds, find it somewhere,


find / | grep ltmain.sh
cp /usr/share/libtool/ltmain.sh .
automake -a



Nothing, hoorah that fixed that step.


./configure --with-apache=$TUTORIAL_HOME/apache2
make
$TUTORIAL_HOME/apache2/bin/apxs -i -a -n tut1 libmodtut1.la
vi /root/threebit-tutorials/apache2/conf/httpd.conf



That shows that it did (as the tutorial said, load the module into the config)
Then, after that, apache started but threw a 403, having the apache root as inside the 'root' user home directory didn't help. Changing the default to /var/www/, touching a index.html inside that directory and ahoy, we have a 200.


[root@cpetest apache2]# wget http://localhost:21000/index.html
[root@cpetest apache2]# tail -n 1 logs/error_log
apache2_mod_tut1: A request was made to GET /index.html HTTP/1.0.



So, yes! It works! One quick test,


[root@cpetest apache2]# wget http://localhost:21000/index.html?flip=x
apache2_mod_tut1: A request was made to GET /index.html?flip=x HTTP/1.0.



Right, now onto the actual coding, looking at that original callback method, we have the last line returning 'DECLINED', looking at httpd.h, there is another return 'DONE' which I assume makes it stop processing modules, which is what we want for this image filter.

YES, I realise that ideally the module should be a handler, but I don't really have time for that at the moment.

So, we change the callback to see if this is a request we are interested in, otherwise let apache get on with its business.

Next, we install ImageMagick,


wget ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz
tar xzf ImageMagick.tar.gz
cd ImageMagick-6.4.1/
./configure
make && make install



Now, we edit Makefile.am so that it knows to look in the right place for the ImageMagick Libs,


[root@cpetest tut1]# cat Makefile.am
## This is the shared library to be built
lib_LTLIBRARIES = libmodtut1.la

## Define the source file for the module
libmodtut1_la_SOURCES = mod_tut1.c

## Define that an include directory is required.
INCLUDES = -I@apache_dir@/include -I/usr/local/include/ImageMagick

So now the module code looks like this:

[root@cpetest tut1]# cat mod_tut1.c
#include "httpd.h"
#include "http_config.h"
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <wand/MagickWand.h>

static int mod_tut1_method_handler (request_rec *r)
{
int is_image = 0;

// decide the content type, is it a jpeg?
is_image = strcmp ( r->content_type, "image/jpeg" ) ;

if (!is_image) // Get on with the show
return DECLINED;
else { // Now we take over
// image magick gubbins.
MagickBooleanType status;

// setup our wand
MagickWand *magick_wand;
// Something about initialisation
MagickWandGenesis();
magick_wand=NewMagickWand();
// Super, read the file that apache would have done otherwise
status=MagickReadImage(magick_wand,r->filename);
// Check it worked
if (status == MagickFalse)
fprintf ( stderr , "error reading image" ) ;
else {
// Great. Flip the image!
MagickFlipImage ( magick_wand ) ;

status=MagickWriteImages(magick_wand,stdout,MagickTrue);
if (status == MagickFalse)
fprintf( stderr , "error writing to output" ) ;
magick_wand=DestroyMagickWand(magick_wand);
MagickWandTerminus();

}
return DONE; // return the end of response
}
}

static void mod_tut1_register_hooks (apr_pool_t *p)
{
ap_hook_handler(mod_tut1_method_handler, NULL, NULL, APR_HOOK_LAST);
}

module AP_MODULE_DECLARE_DATA tut1_module =
{
// Only one callback function is provided. Real
// modules will need to declare callback functions for
// server/directory configuration, configuration merging
// and other tasks.
STANDARD20_MODULE_STUFF,
NULL,
NULL,
NULL,
NULL,
NULL,
mod_tut1_register_hooks, /* callback for registering hooks */
};




[root@cpetest tut1]# /root/threebit-tutorials/apache2/bin/apachectl stop
Syntax error on line 232 of /root/threebit-tutorials/apache2/conf/httpd.conf:
Cannot load /root/threebit-tutorials/apache2/modules/libmodtut1.so into server: /root/threebit-tutorials/apache2/modules/libmodtut1.so: undefined symbol: NewMagickWand



That would mean that Apache doesn't load the imagemagick libraries itself (no suprise there), so apache has the 'LoadFile' option, by copying the
libMagickCore.so and libMagickWand.so files into the lib folder for Apache then having:


LoadFile lib/libMagickCore.so
LoadFile lib/libMagickWand.so



Before we load the new funky module

Then, it does manage to start!

Surfing for the same Winter.jpg I used in the other test, it gives the image the right way up, shame. But the logs show this:


sh: html2ps: command not found



So I installed that (no idea why)

Then we had this issue:


mod_tut1.c unknown 48 no decode delegate for this image format `/var/www/Winter.jpg'




convert -list format


Showed that ImageMagick wasn't even installed with JPEG support!! So I've quickly changed all this to work for BMP format.

Then we have the error 'error writing to output', which means my assumption of stdout being the output was a bit obvious!

Summary:

Well, aside from my naivety of libtool, automake and autoconf, the apache module API is very similar to IIS, you have the same sort of input (a callback is pretty much the same as a interface implement), the same sort of information (the correct info passed as a parameter) and some tools to compile and load the module as you need. All in all, both seem easy enough to setup, next time I'll develop something useful.

Friday, 6 June 2008

First IIS 7 module

Hi,

To see what I can do with the example they gave here:

http://blogs.iis.net/rickjames/archive/2007/02/26/iis-7-hello-world-module-walkthrough.aspx

I want to see if I can create an IIS 7 module for JPEG files, to allow image flipping, then maybe some other things later on, eg:
http://mywebsite.com/picture.jpg?flip=true

What I will need to establish (I'm guessing) is:
* How we can read the GET string into the module
* How we can send the headers and mime types back in the response
* How to establish what directory to stream the image from
* How to load the .NET image libraries


Then download a starter pack for IIS 7 modules (should give me a head start)
http://www.iis.net/downloads/default.aspx?tabid=34&g=6&i=1302

This contains a vsi file, which (assuming you have Visual Studio installed), adds a template for a IIS 7 module.

Opening up the template it explains where to start:

A managed module class derives from the System.Web.IHttpHandler interface, and implements the following methods:

Init. This method is called when the module instance is initialized. It typically performs any instance-level initialization (such as reading configuration), and registers one or more of the module's methods for the desired request processing events.
Dispose. This method is called when the module instance is disposed. It typically does no work, unless the module needs to clean up any un-managed resources, or wants to do an early cleanup of its state before it is garbage collected.
ProcessRequest:
IsReusable:
Now for the development work

1. Create a project using the IIS7 Module template
2. Change the namespace and class names
3. Add a project reference for System. Drawing so that we have some imaging tools to play with.


And the code:

Firstly, setup our includes.


using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

// Extra libs for this module
using System.IO;
using System.Drawing;

Start our class, implementing the IHttpHandler Interface

namespace ashaw.modules.iis7
{
public class AntsIIS7ImageFilter : IHttpHandler
{
#region IHttpHandler implementation - key
public AntsIIS7ImageFilter() { }
public String ModuleName { get { return "AntsII7ImageFilter"; } }
public void Init(HttpApplication application) { }
public void Dispose() { }
public bool IsReusable { get { return false; } }
#endregion

#region the actual request processing.
public void ProcessRequest (HttpContext context )
{
// Get the physical path that the image will be stored in
string path = context.Request.PhysicalPath;

// Get the file path
string filePath = context.Request.FilePath;

if (!File.Exists(path))
{
context.Response.AppendToLog("Can't find file:"+path);
context.Response.StatusCode = 404; // file does not exist
return;
}
try
{
// Setup the headers so that it knows its an image
context.Response.Headers["Content-Type"] = "image/jpeg";

// Create an image class for use!
Image myImage = new Bitmap(path);

// Check for flips
if (!String.IsNullOrEmpty(context.Request.Params["flip"]))
{
// Choose what kind of flip to do.
switch (context.Request.Params["flip"].ToLower())
{
case "x":
myImage.RotateFlip(RotateFlipType.RotateNoneFlipX);
break;
case "xy":
myImage.RotateFlip(RotateFlipType.RotateNoneFlipXY);
break;
case "y":
myImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
break;
}
}

// write the image to the output stream.
myImage.Save( context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg ) ;

}
catch (Exception ex)
{
// Write a message in the IIS logs
context.Response.AppendToLog(ex.Message);

// Return an error to the client
context.Response.StatusCode = 500;
context.Response.Write(ex.Message + ex.StackTrace );
}
}

#endregion


Next step: Build the solution, which generates a DLL, then setup your web.config to look similar to this:


<configuration>
<system.webserver>
<handlers>
<add name="ants" path="*.jpg" verb="*" type="ashaw.modules.iis7.AntsIIS7ImageFilter" resourcetype="Unspecified" requireaccess="Script" precondition="integratedMode">
</add>
</handlers>
</system.webserver>
<configuration>


Create a /bin directory in your web application and copy across a jpg file to test with.

Then try:
yourapp.com/mypicture.jpg

















yourapp.com/mypicture.jpg?flip=x

















yourapp.com/mypicture.jpg?flip=xy

















Then give yourself a pat on the back!

It's not hard to see how to change this to work for png images, or detect what kind you're requesting, just change the image type for the Image.Save () and the Content-Type for the MIME header.