diff -ur rdesktop-1.6.0-orig/channels.c rdesktop-1.6.0/channels.c
--- rdesktop-1.6.0-orig/channels.c	2007-01-08 07:47:05.000000000 +0300
+++ rdesktop-1.6.0/channels.c	2008-12-05 18:58:00.000000000 +0300
@@ -19,6 +19,9 @@
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
 #include "rdesktop.h"
 
 #define MAX_CHANNELS			6
@@ -44,7 +47,7 @@
 */
 
 VCHANNEL *
-channel_register(char *name, uint32 flags, void (*callback) (STREAM))
+channel_register(char *name, uint32 flags, void (*callback) (STREAM,char*))
 {
 	VCHANNEL *channel;
 
@@ -159,7 +162,7 @@
 	if ((flags & CHANNEL_FLAG_FIRST) && (flags & CHANNEL_FLAG_LAST))
 	{
 		/* single fragment - pass straight up */
-		channel->process(s);
+		channel->process(s, channel->name);
 	}
 	else
 	{
@@ -183,7 +186,109 @@
 		{
 			in->end = in->p;
 			in->p = in->data;
-			channel->process(in);
+			channel->process(in, channel->name);
 		}
 	}
 }
+
+/* Generic callback for delivering data to third party add-ins */
+void addin_callback(STREAM s, char *name)
+{
+	pid_t pid;
+	int pipe_read;
+	int pipe_write;
+	uint32 blocksize;
+
+	blocksize = s->end - s->p;
+
+	lookup_addin(name, &pid, &pipe_read, &pipe_write);
+	if (pid > 0) {
+		/* Prepend the block with the block size so the
+                   add-in can identify blocks */
+		write(pipe_write, &blocksize, sizeof(uint32));
+		write(pipe_write, s->p, blocksize);
+	}
+}
+
+void addin_add_fds(int *n, fd_set *rfds, fd_set *wfds, struct timeval *tv)
+{
+	extern ADDIN_DATA addin_data[];
+	extern int addin_count;
+	int i;
+
+	for (i = 0; i < addin_count; i++) {
+		/* Take in account only live addins. */
+		if (addin_data[i].pid > 0) {
+			int addin_fd = addin_data[i].pipe_read;
+
+			FD_SET(addin_fd, rfds);
+			if (*n < addin_fd)
+				*n = addin_fd;
+		}
+	}
+}
+
+/* Process outgoing addin data */
+void addin_check_fds(fd_set *rfds, fd_set *wfds)
+{
+	extern ADDIN_DATA addin_data[];
+	extern int addin_count;
+	int i;
+	unsigned int block_len;
+	ssize_t bytes_read;
+	STREAM s;
+
+	for (i = 0; i < addin_count; i++) {
+		int addin_fd = addin_data[i].pipe_read;
+
+		if (!FD_ISSET(addin_fd, rfds)) {
+			continue;
+		}
+
+		bytes_read = read(addin_fd, &block_len, sizeof(block_len));
+		if (bytes_read <= 0) {
+			addin_check_terminated(&addin_data[i]);
+			continue;
+		}
+
+		if (block_len > 0xffff) {
+			warning("received too much data (%d) from addin %s\n",
+				block_len, addin_data[i].name);
+			addin_check_terminated(&addin_data[i]);
+			continue;
+		}
+
+		if (block_len > addin_data[i].buf_len) {
+			addin_data[i].buf = (unsigned char *)xrealloc(
+							addin_data[i].buf,
+							block_len);
+			addin_data[i].buf_len = block_len;
+		}
+
+		bytes_read = read(addin_fd, addin_data[i].buf, block_len);
+		if (bytes_read > 0) {
+			s = channel_init(addin_data[i].vchannel, bytes_read);
+			memcpy(s->p, addin_data[i].buf, bytes_read);
+			s->p += bytes_read;
+			s->end = s->p;
+
+			channel_send(s, addin_data[i].vchannel);
+		}
+	}
+}
+
+void addin_check_terminated(ADDIN_DATA *addin)
+{
+	int status = 0;
+
+	pid_t result = waitpid(addin->pid, &status, WNOHANG);
+	if (result <= 0)
+		return;
+
+	if (WIFEXITED(status) || WIFSIGNALED(status)) {
+		warning("Addin %s is suddenly terminated\n", addin->name);
+		/* Mark this addin as terminated to exclude
+		   it from processing. */
+		addin->pid = 0;
+	}
+}
diff -ur rdesktop-1.6.0-orig/cliprdr.c rdesktop-1.6.0/cliprdr.c
--- rdesktop-1.6.0-orig/cliprdr.c	2007-01-08 07:47:05.000000000 +0300
+++ rdesktop-1.6.0/cliprdr.c	2008-12-05 18:42:40.000000000 +0300
@@ -111,7 +111,7 @@
 }
 
 static void
-cliprdr_process(STREAM s)
+cliprdr_process(STREAM s, char *name)
 {
 	uint16 type, status;
 	uint32 length, format;
Only in rdesktop-1.6.0/doc: .svn
Only in rdesktop-1.6.0/keymaps: .svn
diff -ur rdesktop-1.6.0-orig/lspci.c rdesktop-1.6.0/lspci.c
--- rdesktop-1.6.0-orig/lspci.c	2007-01-08 07:47:05.000000000 +0300
+++ rdesktop-1.6.0/lspci.c	2008-12-05 18:42:40.000000000 +0300
@@ -128,7 +128,7 @@
 
 /* Process new data from the virtual channel */
 static void
-lspci_process(STREAM s)
+lspci_process(STREAM s, char *name)
 {
 	unsigned int pkglen;
 	static char *rest = NULL;
diff -ur rdesktop-1.6.0-orig/proto.h rdesktop-1.6.0/proto.h
--- rdesktop-1.6.0-orig/proto.h	2008-04-02 15:13:22.000000000 +0400
+++ rdesktop-1.6.0/proto.h	2008-12-05 18:58:00.000000000 +0300
@@ -45,7 +45,7 @@
 RD_HCURSOR cache_get_cursor(uint16 cache_idx);
 void cache_put_cursor(uint16 cache_idx, RD_HCURSOR cursor);
 /* channels.c */
-VCHANNEL *channel_register(char *name, uint32 flags, void (*callback) (STREAM));
+VCHANNEL *channel_register(char *name, uint32 flags, void (*callback) (STREAM,char *));
 STREAM channel_init(VCHANNEL * channel, uint32 length);
 void channel_send(STREAM s, VCHANNEL * channel);
 void channel_process(STREAM s, uint16 mcs_channel);
@@ -314,6 +314,21 @@
 void scard_lock(int lock);
 void scard_unlock(int lock);
 
+/* External addins */
+void init_external_addin(char * addin_name, char * addin_path, char * args, ADDIN_DATA * addin_data);
+
+/* Generic callback for delivering data to third party add-ins */
+void addin_callback(STREAM s, char *name);
+
+/* Find an external add-in registration by virtual channel name */
+void lookup_addin(char *name, pid_t * pid, int * pipe_read, int * pipe_write);
+
+void addin_add_fds(int *n, fd_set *rfds, fd_set *wfds, struct timeval *tv);
+
+void addin_check_fds(fd_set *rfds, fd_set *wfds);
+
+void addin_check_terminated(ADDIN_DATA *addin);
+
 /* *INDENT-OFF* */
 #ifdef __cplusplus
 }
diff -ur rdesktop-1.6.0-orig/rdesktop.c rdesktop-1.6.0/rdesktop.c
--- rdesktop-1.6.0-orig/rdesktop.c	2008-04-05 09:22:26.000000000 +0400
+++ rdesktop-1.6.0/rdesktop.c	2008-12-05 18:58:00.000000000 +0300
@@ -28,6 +28,7 @@
 #include <sys/times.h>		/* times */
 #include <ctype.h>		/* toupper */
 #include <errno.h>
+#include <signal.h>		/* kill */
 #include "rdesktop.h"
 
 #ifdef HAVE_LOCALE_H
@@ -115,6 +116,9 @@
 extern uint32 g_num_devices;
 extern char *g_rdpdr_clientname;
 
+ADDIN_DATA addin_data[MAX_ADDINS];
+int addin_count = 0;
+
 #ifdef RDP2VNC
 extern int rfb_port;
 extern int defer_time;
@@ -208,6 +212,9 @@
 	fprintf(stderr,
 		"                   \"AKS\"              -> Device vendor name                 \n");
 #endif
+	fprintf(stderr,
+                "         '-r addin:<channelname>:</path/to/executable>[:arg1[:arg2:]...]': enable third\n");
+        fprintf(stderr, "                      party virtual channel add-in.\n");
 	fprintf(stderr, "   -0: attach to console\n");
 	fprintf(stderr, "   -4: use RDP version 4\n");
 	fprintf(stderr, "   -5: use RDP version 5 (default)\n");
@@ -417,6 +424,8 @@
 	struct passwd *pw;
 	uint32 flags, ext_disc_reason = 0;
 	char *p;
+	char *addin_name;
+	char *addin_path;
 	int c;
 	char *locale = NULL;
 	int username_option = 0;
@@ -672,8 +681,58 @@
 				break;
 
 			case 'r':
+				if (str_startswith(optarg, "addin"))
+				{
+					if (addin_count >= MAX_ADDINS)
+					{
+						error("Add-ins data table full, increase MAX_ADDINS\n");
+						return 1;
+					}
+
+					optarg += 5;
 
-				if (str_startswith(optarg, "sound"))
+					if (*optarg == ':')
+                                        {
+                                                addin_name = optarg + 1;
+                                                if (*addin_name != '\0')
+                                                {
+                                                        addin_path = next_arg(addin_name, ':');
+                                                }
+                                                else
+                                                {
+                                                        addin_path = 0;
+                                                }
+                                                if (addin_path != 0)
+                                                {
+                                                        p = next_arg(addin_path, ':');
+                                                }
+                                                if (*addin_name != '\0' && addin_path != 0 && *addin_path != '\0')
+                                                {
+                                                        init_external_addin(addin_name, addin_path,
+                                                                        p, &addin_data[addin_count]);
+                                                        if (addin_data[addin_count].pid != 0)
+                                                        {
+                                                                addin_count++;
+                                                        }
+                                                        else
+                                                        {
+                                                                error("Failed to initialise add-in [%s]\n", addin_name);
+                                                                return 1;
+                                                        }
+                                                }
+                                                else
+                                                {
+                                                        usage(argv[0]);
+                                                        return(1);
+                                                }
+                                        }
+                                        else
+                                        {
+                                                usage(argv[0]);
+                                                return(1);
+                                        }
+                                }
+				else if (str_startswith(optarg, "sound"))
 				{
 					optarg += 5;
 
@@ -982,6 +1041,17 @@
 	cache_save_state();
 	ui_deinit();
 
+	/* Send a SIGUSR1 to all addins to close and sleep for a couple of secs
+	   to give them a chance to stop */
+	for (c = 0; c < addin_count; c++)
+	{
+		if (addin_data[c].pid != 0)
+		{
+			kill(addin_data[c].pid,SIGUSR1);
+			sleep(2);
+		}
+	}
+
 	if (ext_disc_reason >= 2)
 		print_disconnect_reason(ext_disc_reason);
 
@@ -1625,3 +1695,105 @@
 		return False;
 	return True;
 }
+
+/* Initialise external addin */
+void init_external_addin(char * addin_name, char * addin_path, char * args, ADDIN_DATA * addin_data)
+{
+	char *p;
+	char *current_arg;
+	char * argv[256];
+	char argv_buffer[256][256];
+	int i;
+	int readpipe[2],writepipe[2];
+	pid_t child;
+
+	/* Initialise addin structure */
+	memset(addin_data, 0, sizeof(ADDIN_DATA));
+	/* Go through the list of args, adding each to argv */
+	argv[0] = addin_path;
+	i = 1;
+	p=current_arg=args;
+	while (current_arg != 0 && current_arg[0] != '\0')
+	{
+		p=next_arg(p, ':');;
+		if (p != 0 && *p != '\0')
+			*(p - 1) = '\0';
+		strcpy(argv_buffer[i], current_arg);
+		argv[i]=argv_buffer[i];
+		i++;
+		current_arg=p;
+	}
+	argv[i] = NULL;
+
+
+	/* Create pipes */
+	if (pipe(readpipe) < 0 || pipe(writepipe) < 0)
+	{
+		perror("pipes for addin");
+		return;
+	}
+
+	/* Fork process */
+	if ((child = fork()) < 0)
+	{
+		perror("fork for addin");
+		return;
+	}
+
+	/* Child */
+	if (child == 0)
+	{
+		/* Set stdin and stdout of child to relevant pipe ends */
+		dup2(writepipe[0],0);
+		dup2(readpipe[1],1);
+
+		/* Close all fds as they are not needed now */
+		close(readpipe[0]);
+		close(readpipe[1]);
+		close(writepipe[0]);
+		close(writepipe[1]);
+		execvp((char *)argv[0], (char **)argv);
+		perror("Error executing child");
+		_exit(128);
+	}
+	else
+	{
+		strcpy(addin_data->name, addin_name);
+		/* Close child end fd's */
+		close(readpipe[1]);
+		close(writepipe[0]);
+		addin_data->pipe_read=readpipe[0];
+		addin_data->pipe_write=writepipe[1];
+		addin_data->vchannel=channel_register(addin_name,
+						CHANNEL_OPTION_INITIALIZED |
+						CHANNEL_OPTION_ENCRYPT_RDP |	
+						CHANNEL_OPTION_COMPRESS_RDP,
+						addin_callback);
+		if (!addin_data->vchannel)
+		{
+			perror("Channel register failed");
+			return;
+		}
+		else
+			addin_data->pid=child;
+			
+	}
+
+}
+
+/* Find an external add-in registration by virtual channel name */
+void lookup_addin(char *name, pid_t * pid, int * pipe_read, int * pipe_write)
+{
+	int i;
+
+	*pid = 0;
+
+	for (i = 0; i < addin_count; i++) {
+		if (!strcmp(name,addin_data[i].name)) {
+			*pid=addin_data[i].pid;
+			*pipe_read=addin_data[i].pipe_read;
+			*pipe_write=addin_data[i].pipe_write;
+			break;
+		}
+	}
+}
diff -ur rdesktop-1.6.0-orig/rdesktop.h rdesktop-1.6.0/rdesktop.h
--- rdesktop-1.6.0-orig/rdesktop.h	2008-04-05 08:54:10.000000000 +0400
+++ rdesktop-1.6.0/rdesktop.h	2008-12-05 18:42:40.000000000 +0300
@@ -41,6 +41,8 @@
 
 #define VERSION "1.6.0"
 
+#define MAX_ADDINS 20
+
 #ifdef WITH_DEBUG
 #define DEBUG(args)	printf args;
 #else
diff -ur rdesktop-1.6.0-orig/rdpdr.c rdesktop-1.6.0/rdpdr.c
--- rdesktop-1.6.0-orig/rdpdr.c	2008-02-14 14:37:17.000000000 +0300
+++ rdesktop-1.6.0/rdpdr.c	2008-12-05 18:42:40.000000000 +0300
@@ -781,7 +781,7 @@
 }
 
 static void
-rdpdr_process(STREAM s)
+rdpdr_process(STREAM s, char *name)
 {
 	uint32 handle;
 	uint8 *magic;
diff -ur rdesktop-1.6.0-orig/rdpsnd.c rdesktop-1.6.0/rdpsnd.c
--- rdesktop-1.6.0-orig/rdpsnd.c	2008-02-14 14:37:17.000000000 +0300
+++ rdesktop-1.6.0/rdpsnd.c	2008-12-05 18:42:40.000000000 +0300
@@ -544,7 +544,7 @@
 }
 
 static void
-rdpsnd_process(STREAM s)
+rdpsnd_process(STREAM s, char *name)
 {
 	uint16 len;
 
@@ -611,7 +611,7 @@
 }
 
 static void
-rdpsnddbg_process(STREAM s)
+rdpsnddbg_process(STREAM s, char *name)
 {
 	unsigned int pkglen;
 	static char *rest = NULL;
diff -ur rdesktop-1.6.0-orig/seamless.c rdesktop-1.6.0/seamless.c
--- rdesktop-1.6.0-orig/seamless.c	2008-04-02 15:13:22.000000000 +0400
+++ rdesktop-1.6.0/seamless.c	2008-12-05 18:42:40.000000000 +0300
@@ -370,7 +370,7 @@
 
 
 static void
-seamless_process(STREAM s)
+seamless_process(STREAM s, char *name)
 {
 	unsigned int pkglen;
 	static char *rest = NULL;
Only in rdesktop-1.6.0: .svn
diff -ur rdesktop-1.6.0-orig/types.h rdesktop-1.6.0/types.h
--- rdesktop-1.6.0-orig/types.h	2007-01-08 07:47:06.000000000 +0300
+++ rdesktop-1.6.0/types.h	2008-12-05 18:42:40.000000000 +0300
@@ -125,7 +125,7 @@
 	char name[8];
 	uint32 flags;
 	struct stream in;
-	void (*process) (STREAM);
+	void (*process) (STREAM,char *);
 }
 VCHANNEL;
 
@@ -265,4 +265,15 @@
 }
 FILEINFO;
 
+typedef struct _ADDIN_DATA
+{
+	char name[255];
+	unsigned char *buf;
+	unsigned int buf_len;
+	pid_t pid;
+	int pipe_read;
+	int pipe_write;
+	VCHANNEL *vchannel;
+} ADDIN_DATA;
+
 typedef RD_BOOL(*str_handle_lines_t) (const char *line, void *data);
diff -ur rdesktop-1.6.0-orig/xwin.c rdesktop-1.6.0/xwin.c
--- rdesktop-1.6.0-orig/xwin.c	2008-05-11 10:14:38.000000000 +0400
+++ rdesktop-1.6.0/xwin.c	2008-12-05 18:42:40.000000000 +0300
@@ -2620,6 +2620,9 @@
 		rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
 		seamless_select_timeout(&tv);
 
+		/* add addins handles */
+		addin_add_fds(&n, &rfds, &wfds, &tv);
+
 		n++;
 
 		switch (select(n, &rfds, &wfds, NULL, &tv))
@@ -2642,6 +2645,8 @@
 		rdpsnd_check_fds(&rfds, &wfds);
 #endif
 
+		addin_check_fds(&rfds, &wfds);
+
 		rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False);
 
 		if (FD_ISSET(rdp_socket, &rfds))