changeset 8487:c3ffec7fab94

[gaim-migrate @ 9222] Apparently if you don't cvs add a directory before you cvs add files in that directory, cvs just puts the files in the napster directory. ... Seems logical enough to me. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Tue, 23 Mar 2004 03:45:13 +0000
parents 538f4d0faf1d
children c6dd51ece325
files src/protocols/napster/mdns.c src/protocols/napster/mdns.h src/protocols/napster/rendezvous.c src/protocols/rendezvous/mdns.c src/protocols/rendezvous/mdns.h src/protocols/rendezvous/rendezvous.c
diffstat 6 files changed, 1215 insertions(+), 1215 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/napster/mdns.c	Tue Mar 23 03:39:06 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,541 +0,0 @@
-/**
- * @file mdns.c Multicast DNS connection code used by rendezvous.
- *
- * gaim
- *
- * Gaim is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-/*
- * If you want to understand this, read RFC1035 and 
- * draft-cheshire-dnsext-multicastdns.txt
- */
-
-/*
- * XXX - THIS DOESN'T DO BOUNDS CHECKING!!!  DON'T USE IT ON AN UNTRUSTED
- * NETWORK UNTIL IT DOES!!!  THERE ARE POSSIBLE REMOTE ACCESS VIA BUFFER
- * OVERFLOW SECURITY HOLES!!!
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include <glib.h>
-
-#include "debug.h"
-
-#include "mdns.h"
-#include "util.h"
-
-int
-mdns_establish_socket()
-{
-	int fd = -1;
-	struct sockaddr_in addr;
-	struct ip_mreq mreq;
-	unsigned char loop;
-	unsigned char ttl;
-	int reuseaddr;
-
-	gaim_debug_info("mdns", "Establishing multicast socket\n");
-
-	/* What's the difference between AF_INET and PF_INET? */
-	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-		gaim_debug_error("mdns", "Unable to create socket: %s\n", strerror(errno));
-		return -1;
-	}
-
-	/* Make the socket non-blocking (although it shouldn't matter) */
-	fcntl(fd, F_SETFL, O_NONBLOCK);
-
-	/* Bind the socket to a local IP and port */
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(5353);
-	addr.sin_addr.s_addr = INADDR_ANY;
-	if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
-		gaim_debug_error("mdns", "Unable to bind socket to interface.\n");
-		close(fd);
-		return -1;
-	}
-
-	/* Ensure loopback is enabled (it should be enabled by default, by let's be sure) */
-	loop = 1;
-	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(unsigned char)) == -1) {
-		gaim_debug_error("mdns", "Error calling setsockopt for IP_MULTICAST_LOOP\n");
-	}
-
-	/* Set TTL to 255--required by mDNS */
-	ttl = 255;
-	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(unsigned char)) == -1) {
-		gaim_debug_error("mdns", "Error calling setsockopt for IP_MULTICAST_TTL\n");
-		close(fd);
-		return -1;
-	}
-
-	/* Join the .local multicast group */
-	mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.251");
-	mreq.imr_interface.s_addr = htonl(INADDR_ANY);
-	if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) {
-		gaim_debug_error("mdns", "Error calling setsockopt for IP_ADD_MEMBERSHIP\n");
-		close(fd);
-		return -1;
-	}
-
-	/* Make the local IP re-usable */
-	reuseaddr = 1;
-	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1) {
-		gaim_debug_error("mdns", "Error calling setsockopt for SO_REUSEADDR: %s\n", strerror(errno));
-	}
-
-	return fd;
-}
-
-int
-mdns_query(int fd, const char *domain)
-{
-	struct sockaddr_in addr;
-	unsigned int querylen;
-	unsigned char *query;
-	char *b, *c;
-	int i, n;
-
-	if (strlen(domain) > 255) {
-		return -EINVAL;
-	}
-
-	/*
-	 * Build the outgoing query packet.  It is made of the header with a
-	 * query made up of the given domain.  The header is 12 bytes.
-	 */
-	querylen = 12 + strlen(domain) + 2 + 4;
-	if (!(query = (unsigned char *)g_malloc(querylen))) {
-		return -ENOMEM;
-	}
-
-	/* The header section */
-	util_put32(&query[0], 0); /* The first 32 bits of the header are all 0's in mDNS */
-	util_put16(&query[4], 1); /* QDCOUNT */
-	util_put16(&query[6], 0); /* ANCOUNT */
-	util_put16(&query[8], 0); /* NSCOUNT */
-	util_put16(&query[10], 0); /* ARCOUNT */
-
-	/* The question section */
-	i = 12; /* Destination in query */
-	b = (char *)domain;
-	while ((c = strchr(b, '.'))) {
-		i += util_put8(&query[i], c - b); /* Length of domain-name segment */
-		memcpy(&query[i], b, c - b); /* Domain-name segment */
-		i += c - b; /* Increment the destination pointer */
-		b = c + 1;
-	}
-	i += util_put8(&query[i], strlen(b)); /* Length of domain-name segment */
-	strcpy(&query[i], b); /* Domain-name segment */
-	i += strlen(b) + 1; /* Increment the destination pointer */
-	i += util_put16(&query[i], 0x000c); /* QTYPE */
-	i += util_put16(&query[i], 0x8001); /* QCLASS */
-
-	/* Actually send the DNS query */
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(5353);
-	addr.sin_addr.s_addr = inet_addr("224.0.0.251");
-	n = sendto(fd, query, querylen, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
-	g_free(query);
-
-	if (n == -1) {
-		gaim_debug_error("mdns", "Error sending packet: %d\n", errno);
-		return -1;
-	} else if (n != querylen) {
-		gaim_debug_error("mdns", "Only sent %d of %d bytes of query.\n", n, querylen);
-		return -1;
-	}
-
-	return 0;
-}
-
-/*
- * XXX - Needs bounds checking!
- *
- * Read in a domain name from the given buffer starting at the given
- * offset.  This handles using domain name compression to jump around
- * the data buffer, if needed.
- *
- * @return A null-terminated string representation of the domain name.
- *         This should be g_free'd when no longer needed.
- */
-static gchar *
-mdns_read_name(const char *data, int datalen, int dataoffset)
-{
-	GString *ret = g_string_new("");
-	unsigned char tmp;
-
-	while ((tmp = util_get8(&data[dataoffset])) != 0) {
-		dataoffset++;
-
-		if ((tmp & 0xc0) == 0) { /* First two bits are 00 */
-			if (*ret->str)
-				g_string_append_c(ret, '.');
-			g_string_append_len(ret, &data[dataoffset], tmp);
-			dataoffset += tmp;
-
-		} else if ((tmp & 0x40) == 0) { /* First two bits are 10 */
-			/* Reserved for future use */
-
-		} else if ((tmp & 0x80) == 1) { /* First two bits are 01 */
-			/* Reserved for future use */
-
-		} else { /* First two bits are 11 */
-			/* Jump to another position in the data */
-			dataoffset = util_get8(&data[dataoffset]);
-
-		}
-	}
-
-	return g_string_free(ret, FALSE);
-}
-
-/*
- * XXX - Needs bounds checking!
- *
- * Determine how many bytes long a portion of the domain name is
- * at the given offset.  This does NOT jump around the data array
- * in the case of domain name compression.
- *
- * @return The length of the portion of the domain name.
- */
-static int
-mdns_read_name_len(const char *data, int datalen, int dataoffset)
-{
-	int startoffset = dataoffset;
-	unsigned char tmp;
-
-	while ((tmp = util_get8(&data[dataoffset++])) != 0) {
-
-		if ((tmp & 0xc0) == 0) { /* First two bits are 00 */
-			dataoffset += tmp;
-
-		} else if ((tmp & 0x40) == 0) { /* First two bits are 10 */
-			/* Reserved for future use */
-
-		} else if ((tmp & 0x80) == 1) { /* First two bits are 01 */
-			/* Reserved for future use */
-
-		} else { /* First two bits are 11 */
-			/* End of this portion of the domain name */
-			dataoffset++;
-			break;
-
-		}
-	}
-
-	return dataoffset - startoffset;
-}
-
-/*
- * XXX - Needs bounds checking!
- *
- */
-static Question *
-mdns_read_questions(int numquestions, const char *data, int datalen, int *offset)
-{
-	Question *ret;
-	int i;
-
-	ret = (Question *)g_malloc0(numquestions * sizeof(Question));
-	for (i = 0; i < numquestions; i++) {
-		ret[i].name = mdns_read_name(data, 0, *offset);
-		*offset += mdns_read_name_len(data, 0, *offset);
-		ret[i].type = util_get16(&data[*offset]); /* QTYPE */
-		*offset += 2;
-		ret[i].class = util_get16(&data[*offset]); /* QCLASS */
-		*offset += 2;
-	}
-
-	return ret;
-}
-
-/*
- * Read in a chunk of data, probably a buddy icon.
- *
- */
-static unsigned char *
-mdns_read_rr_rdata_null(const char *data, int datalen, int offset, unsigned short rdlength)
-{
-	unsigned char *ret = NULL;
-
-	if (offset + rdlength > datalen)
-		return NULL;
-
-	ret = (unsigned char *)g_malloc(rdlength);
-	memcpy(ret, &data[offset], rdlength);
-
-	return ret;
-}
-
-/*
- * XXX - Needs bounds checking!
- *
- */
-static char *
-mdns_read_rr_rdata_ptr(const char *data, int datalen, int offset)
-{
-	char *ret = NULL;
-
-	ret = mdns_read_name(data, datalen, offset);
-
-	return ret;
-}
-
-/*
- *
- *
- */
-static GHashTable *
-mdns_read_rr_rdata_txt(const char *data, int datalen, int offset, unsigned short rdlength)
-{
-	GHashTable *ret = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	int endoffset = offset + rdlength;
-	unsigned char tmp;
-	char buf[256], *key, *value;
-
-	while (offset < endoffset) {
-		/* Read in the length of the next name/value pair */
-		tmp = util_get8(&data[offset]);
-		offset++;
-
-		/* Ensure packet is valid */
-		if (offset + tmp > endoffset)
-			break;
-
-		/* Read in the next name/value pair */
-		strncpy(buf, &data[offset], tmp);
-		offset += tmp;
-
-		if (buf[0] == '=') {
-			/* Name/value pairs beginning with = are silently ignored */
-			continue;
-		}
-
-		/* The value is a substring of buf, starting just after the = */
-		buf[tmp] = '\0';
-		value = strchr(buf, '=');
-		if (value != NULL) {
-			value[0] = '\0';
-			value++;
-		}
-
-		/* Make the key all lowercase */
-		key = g_utf8_strdown(buf, -1);
-		if (!g_hash_table_lookup(ret, key))
-			g_hash_table_insert(ret, key, g_strdup(value));
-		else
-			g_free(key);
-	}
-
-	return ret;
-}
-
-/*
- * XXX - Needs bounds checking!
- *
- */
-static ResourceRecord *
-mdns_read_rr(int numrecords, const char *data, int datalen, int *offset)
-{
-	ResourceRecord *ret;
-	int i;
-
-	ret = (ResourceRecord *)g_malloc0(numrecords * sizeof(ResourceRecord));
-	for (i = 0; i < numrecords; i++) {
-		ret[i].name = mdns_read_name(data, 0, *offset); /* NAME */
-		*offset += mdns_read_name_len(data, 0, *offset);
-		ret[i].type = util_get16(&data[*offset]); /* TYPE */
-		*offset += 2;
-		ret[i].class = util_get16(&data[*offset]); /* CLASS */
-		*offset += 2;
-		ret[i].ttl = util_get32(&data[*offset]); /* TTL */
-		*offset += 4;
-		ret[i].rdlength = util_get16(&data[*offset]); /* RDLENGTH */
-		*offset += 2;
-
-		/* RDATA */
-		switch (ret[i].type) {
-			case RENDEZVOUS_RRTYPE_NULL:
-				ret[i].rdata = mdns_read_rr_rdata_null(data, datalen, *offset, ret[i].rdlength);
-			break;
-
-			case RENDEZVOUS_RRTYPE_PTR:
-				ret[i].rdata = mdns_read_rr_rdata_ptr(data, datalen, *offset);
-			break;
-
-			case RENDEZVOUS_RRTYPE_TXT:
-				ret[i].rdata = mdns_read_rr_rdata_txt(data, datalen, *offset, ret[i].rdlength);
-			break;
-
-			default:
-				ret[i].rdata = NULL;
-			break;
-		}
-		*offset += ret[i].rdlength;
-	}
-
-	return ret;
-}
-
-/*
- * XXX - Needs bounds checking!
- *
- */
-DNSPacket *
-mdns_read(int fd)
-{
-	DNSPacket *ret = NULL;
-	int i; /* Current position in datagram */
-	//char data[512];
-	char data[10096];
-	int datalen;
-	struct sockaddr_in addr;
-	socklen_t addrlen;
-
-	/* Read in an mDNS packet */
-	addrlen = sizeof(struct sockaddr_in);
-	if ((datalen = recvfrom(fd, data, sizeof(data), 0, (struct sockaddr *)&addr, &addrlen)) == -1) {
-		gaim_debug_error("mdns", "Error reading packet: %d\n", errno);
-		return NULL;
-	}
-
-	ret = (DNSPacket *)g_malloc0(sizeof(DNSPacket));
-
-	/* Parse the incoming packet, starting from 0 */
-	i = 0;
-
-	/* The header section */
-	ret->header.id = util_get16(&data[i]); /* ID */
-	i += 2;
-
-	/* For the flags, some bits must be 0 and some must be 1, the rest are ignored */
-	ret->header.flags = util_get16(&data[i]); /* Flags (QR, OPCODE, AA, TC, RD, RA, Z, AD, CD, and RCODE */
-	i += 2;
-	if ((ret->header.flags & 0x8000) == 0) {
-		/* QR should be 1 */
-		g_free(ret);
-		return NULL;
-	}
-	if ((ret->header.flags & 0x7800) != 0) {
-		/* OPCODE should be all 0's */
-		g_free(ret);
-		return NULL;
-	}
-
-	/* Read in the number of other things in the packet */
-	ret->header.numquestions = util_get16(&data[i]);
-	i += 2;
-	ret->header.numanswers = util_get16(&data[i]);
-	i += 2;
-	ret->header.numauthority = util_get16(&data[i]);
-	i += 2;
-	ret->header.numadditional = util_get16(&data[i]);
-	i += 2;
-
-	/* Read in all the questions */
-	ret->questions = mdns_read_questions(ret->header.numquestions, data, datalen, &i);
-
-	/* Read in all resource records */
-	ret->answers = mdns_read_rr(ret->header.numanswers, data, datalen, &i);
-
-	/* Read in all authority records */
-	ret->authority = mdns_read_rr(ret->header.numauthority, data, datalen, &i);
-
-	/* Read in all additional records */
-	ret->additional = mdns_read_rr(ret->header.numadditional, data, datalen, &i);
-
-	/* We should be at the end of the packet */
-	if (i != datalen) {
-		gaim_debug_error("mdns", "Finished parsing before end of DNS packet!  Only parsed %d of %d bytes.", i, datalen);
-		g_free(ret);
-		return NULL;
-	}
-
-	return ret;
-}
-
-/**
- * Free the rdata associated with a given resource record.
- */
-static void
-mdns_free_rr_rdata(unsigned short type, void *rdata)
-{
-	switch (type) {
-			case RENDEZVOUS_RRTYPE_NULL:
-			case RENDEZVOUS_RRTYPE_PTR:
-				g_free(rdata);
-			break;
-
-			case RENDEZVOUS_RRTYPE_TXT:
-				g_hash_table_destroy(rdata);
-			break;
-	}
-}
-
-/**
- * Free a given question
- */
-static void
-mdns_free_q(Question *q)
-{
-	g_free(q->name);
-}
-
-/**
- * Free a given resource record.
- */
-static void
-mdns_free_rr(ResourceRecord *rr)
-{
-	g_free(rr->name);
-	mdns_free_rr_rdata(rr->type, rr->rdata);
-}
-
-void
-mdns_free(DNSPacket *dns)
-{
-	int i;
-
-	for (i = 0; i < dns->header.numquestions; i++)
-		mdns_free_q(&dns->questions[i]);
-	for (i = 0; i < dns->header.numanswers; i++)
-		mdns_free_rr(&dns->answers[i]);
-	for (i = 0; i < dns->header.numauthority; i++)
-		mdns_free_rr(&dns->authority[i]);
-	for (i = 0; i < dns->header.numadditional; i++)
-		mdns_free_rr(&dns->additional[i]);
-
-	g_free(dns->questions);
-	g_free(dns->answers);
-	g_free(dns->authority);
-	g_free(dns->additional);
-	g_free(dns);
-}
--- a/src/protocols/napster/mdns.h	Tue Mar 23 03:39:06 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,144 +0,0 @@
-/**
- * @file mdns.h Multicast DNS connection code used by rendezvous.
- *
- * gaim
- *
- * Gaim is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#ifndef _MDNS_H_
-#define _MDNS_H_
-
-#include <errno.h>
-#include <string.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include "debug.h"
-
-/*
- * Some #define's stolen from libfaim.  Used to put
- * binary data (bytes, shorts and ints) into an array.
- */
-#define util_put8(buf, data) ((*(buf) = (unsigned char)(data)&0xff),1)
-#define util_put16(buf, data) ( \
-		(*(buf) = (unsigned char)((data)>>8)&0xff), \
-		(*((buf)+1) = (unsigned char)(data)&0xff),  \
-		2)
-#define util_put32(buf, data) ( \
-		(*((buf)) = (unsigned char)((data)>>24)&0xff), \
-		(*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
-		(*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
-		(*((buf)+3) = (unsigned char)(data)&0xff), \
-		4)
-#define util_get8(buf) ((*(buf))&0xff)
-#define util_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
-#define util_get32(buf) ((((*(buf))<<24)&0xff000000) + \
-		(((*((buf)+1))<<16)&0x00ff0000) + \
-		(((*((buf)+2))<< 8)&0x0000ff00) + \
-		(((*((buf)+3)    )&0x000000ff)))
-
-/*
- * Merriam-Webster's
- */
-#define RENDEZVOUS_RRTYPE_A		1
-#define RENDEZVOUS_RRTYPE_NS	2
-#define RENDEZVOUS_RRTYPE_CNAME	5
-#define RENDEZVOUS_RRTYPE_NULL	10
-#define RENDEZVOUS_RRTYPE_PTR	12
-#define RENDEZVOUS_RRTYPE_TXT	16
-
-/*
- * Express for Men's
- */
-typedef struct _Header {
-	unsigned short id;
-	unsigned short flags;
-	unsigned short numquestions;
-	unsigned short numanswers;
-	unsigned short numauthority;
-	unsigned short numadditional;
-} Header;
-
-typedef struct _Question {
-	gchar *name;
-	unsigned short type;
-	unsigned short class;
-} Question;
-
-typedef struct ResourceRecord {
-	gchar *name;
-	unsigned short type;
-	unsigned short class;
-	int ttl;
-	unsigned short rdlength;
-	void *rdata;
-} ResourceRecord;
-
-typedef struct _DNSPacket {
-	Header header;
-	Question *questions;
-	ResourceRecord *answers;
-	ResourceRecord *authority;
-	ResourceRecord *additional;
-} DNSPacket;
-
-/*
- * Bring in 'Da Noise, Bring in 'Da Functions
- */
-
-/**
- * Create a multicast socket that can be used for sending and 
- * receiving multicast DNS packets.  The socket joins the
- * link-local multicast group (224.0.0.251).
- *
- * @return The file descriptor of the new socket, or -1 if
- *         there was an error establishing the socket.
- */
-int mdns_establish_socket();
-
-/**
- * Send a multicast DNS query for the given domain across the given
- * socket.
- *
- * @param fd The file descriptor of a pre-established socket to
- *        be used for sending the outgoing mDNS query.
- * @param domain This is the domain name you wish to query.  It should 
- *        be of the format "_presence._tcp.local" for example.
- * @return 0 if sucessful.
- */
-int mdns_query(int fd, const char *domain);
-
-/**
- *
- *
- */
-DNSPacket *mdns_read(int fd);
-
-/**
- * Free a DNSPacket structure.
- *
- * @param dns The DNSPacket that you want to free.
- */
-void mdns_free(DNSPacket *dns);
-
-#endif /* _MDNS_H_ */
--- a/src/protocols/napster/rendezvous.c	Tue Mar 23 03:39:06 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,530 +0,0 @@
-/*
- * gaim - Rendezvous Protocol Plugin
- *
- * Gaim is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-#include <glib.h>
-#include <glib/gprintf.h>
-
-#include "internal.h"
-
-#include "account.h"
-#include "accountopt.h"
-#include "blist.h"
-#include "conversation.h"
-#include "debug.h"
-#include "prpl.h"
-
-#include "mdns.h"
-#include "util.h"
-
-#define RENDEZVOUS_CONNECT_STEPS 2
-
-typedef struct _RendezvousData {
-	int fd;
-	GHashTable *buddies;
-} RendezvousData;
-
-typedef struct _RendezvousBuddy {
-	gchar *firstandlast;
-	gchar *aim;
-	int p2pjport;
-	int status;
-	int idle;
-	gchar *msg;
-} RendezvousBuddy;
-
-#define UC_IDLE 2
-
-/****************************/
-/* Utility Functions        */
-/****************************/
-static void rendezvous_buddy_free(gpointer data)
-{
-	RendezvousBuddy *rb = data;
-
-	g_free(rb->firstandlast);
-	g_free(rb->msg);
-	g_free(rb);
-}
-
-/*
- * Extract the "user@host" name from a full presence domain
- * of the form "user@host._presence._tcp.local"
- *
- * @return If the domain is NOT a _presence._tcp.local domain
- *         then return NULL.  Otherwise return a newly allocated
- *         null-terminated string containing the "user@host" for
- *         the given domain.  This string should be g_free'd
- *         when no longer needed.
- */
-static gchar *rendezvous_extract_name(gchar *domain)
-{
-	gchar *ret, *suffix;
-
-	if (!g_str_has_suffix(domain, "._presence._tcp.local"))
-		return NULL;
-
-	suffix = strstr(domain, "._presence._tcp.local");
-	ret = g_strndup(domain, suffix - domain);
-
-	return ret;
-}
-
-/****************************/
-/* Buddy List Functions     */
-/****************************/
-static void rendezvous_addtolocal(GaimConnection *gc, const char *name, const char *domain)
-{
-	GaimAccount *account = gaim_connection_get_account(gc);
-	GaimBuddy *b;
-	GaimGroup *g;
-
-	g = gaim_find_group(domain);
-	if (g == NULL) {
-		g = gaim_group_new(domain);
-		gaim_blist_add_group(g, NULL);
-	}
-
-	b = gaim_find_buddy_in_group(account, name, g);
-	if (b != NULL)
-		return;
-
-	b = gaim_buddy_new(account, name, NULL);
-	gaim_blist_add_buddy(b, NULL, g, NULL);
-	serv_got_update(gc, b->name, 1, 0, 0, 0, 0);
-}
-
-static void rendezvous_removefromlocal(GaimConnection *gc, const char *name, const char *domain)
-{
-	GaimAccount *account = gaim_connection_get_account(gc);
-	GaimBuddy *b;
-	GaimGroup *g;
-
-	g = gaim_find_group(domain);
-	if (g == NULL)
-		return;
-
-	b = gaim_find_buddy_in_group(account, name, g);
-	if (b == NULL)
-		return;
-
-	serv_got_update(gc, b->name, 0, 0, 0, 0, 0);
-	gaim_blist_remove_buddy(b);
-}
-
-static void rendezvous_removeallfromlocal(GaimConnection *gc)
-{
-	GaimAccount *account = gaim_connection_get_account(gc);
-	GaimBuddyList *blist;
-	GaimBlistNode *gnode, *cnode, *bnode;
-	GaimBuddy *b;
-
-	/* Go through and remove all buddies that belong to this account */
-	if ((blist = gaim_get_blist()) != NULL) {
-		for (gnode = blist->root; gnode; gnode = gnode->next) {
-			if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
-				continue;
-			for (cnode = gnode->child; cnode; cnode = cnode->next) {
-				if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
-					continue;
-				for (bnode = cnode->child; bnode; bnode = bnode->next) {
-					if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
-						continue;
-					b = (GaimBuddy *)bnode;
-					if (b->account != account)
-						continue;
-					serv_got_update(gc, b->name, 0, 0, 0, 0, 0);
-					gaim_blist_remove_buddy(b);
-				}
-			}
-		}
-	}
-}
-
-static void rendezvous_handle_rr_txt(GaimConnection *gc, ResourceRecord *rr, const gchar *name)
-{
-	RendezvousData *rd = gc->proto_data;
-	RendezvousBuddy *rb;
-	GHashTable *rdata;
-	gchar *tmp1, *tmp2;
-
-	rb = g_hash_table_lookup(rd->buddies, name);
-	if (rb == NULL) {
-		rb = g_malloc0(sizeof(RendezvousBuddy));
-		g_hash_table_insert(rd->buddies, g_strdup(name), rb);
-	}
-
-	rdata = rr->rdata;
-
-	tmp1 = g_hash_table_lookup(rdata, "1st");
-	tmp2 = g_hash_table_lookup(rdata, "last");
-	g_free(rb->firstandlast);
-	rb->firstandlast = g_strdup_printf("%s%s%s",
-							(tmp1 ? tmp1 : ""),
-							(tmp1 || tmp2 ? " " : ""),
-							(tmp2 ? tmp2 : ""));
-	serv_got_alias(gc, name, rb->firstandlast);
-
-	tmp1 = g_hash_table_lookup(rdata, "aim");
-	if (tmp1 != NULL) {
-		g_free(rb->aim);
-		rb->aim = g_strdup(tmp1);
-	}
-
-	tmp1 = g_hash_table_lookup(rdata, "port.p2pj");
-	rb->p2pjport = atoi(tmp1);
-
-	tmp1 = g_hash_table_lookup(rdata, "status");
-	if (tmp1 != NULL) {
-		if (!strcmp(tmp1, "dnd")) {
-			/* Available */
-			rb->status = 0;
-		} else if (!strcmp(tmp1, "away")) {
-			/* Idle */
-			tmp2 = g_hash_table_lookup(rdata, "away");
-			rb->idle = atoi(tmp2);
-			gaim_debug_error("XXX", "User has been idle for %d\n", rb->idle);
-			rb->status = UC_IDLE;
-		} else if (!strcmp(tmp1, "avail")) {
-			/* Away */
-			rb->status = UC_UNAVAILABLE;
-		}
-		serv_got_update(gc, name, 1, 0, 0, 0, rb->status);
-	}
-
-	tmp1 = g_hash_table_lookup(rdata, "msg");
-	if (tmp1 != NULL) {
-		g_free(rb->msg);
-		rb->msg = g_strdup(tmp1);
-	}
-}
-
-/*
- * Parse a resource record and do stuff if we need to.
- */
-static void rendezvous_handle_rr(GaimConnection *gc, ResourceRecord *rr)
-{
-	gchar *name;
-
-	gaim_debug_error("XXX", "Parsing resource record with domain name %s\n", rr->name);
-
-	switch (rr->type) {
-		case RENDEZVOUS_RRTYPE_NULL: {
-			if ((name = rendezvous_extract_name(rr->name)) != NULL) {
-				if (rr->rdlength > 0) {
-					/* Data is a buddy icon */
-					gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc), name, rr->rdata, rr->rdlength);
-				}
-
-				g_free(name);
-			}
-		} break;
-
-		case RENDEZVOUS_RRTYPE_PTR: {
-			gchar *rdata = rr->rdata;
-			if ((name = rendezvous_extract_name(rdata)) != NULL) {
-				if (rr->ttl > 0)
-					rendezvous_addtolocal(gc, name, "Rendezvous");
-				else
-					rendezvous_removefromlocal(gc, name, "Rendezvous");
-
-				g_free(name);
-			}
-		} break;
-
-		case RENDEZVOUS_RRTYPE_TXT: {
-			if ((name = rendezvous_extract_name(rr->name)) != NULL) {
-				rendezvous_handle_rr_txt(gc, rr, name);
-				g_free(name);
-			}
-		} break;
-	}
-}
-
-/****************************/
-/* Icon and Emblem Funtions */
-/****************************/
-static const char* rendezvous_prpl_list_icon(GaimAccount *a, GaimBuddy *b)
-{
-	return "rendezvous";
-}
-
-static void rendezvous_prpl_list_emblems(GaimBuddy *b, char **se, char **sw, char **nw, char **ne)
-{
-	if (GAIM_BUDDY_IS_ONLINE(b)) {
-		if (b->uc & UC_UNAVAILABLE)
-			*se = "away";
-	} else {
-		*se = "offline";
-	}
-}
-
-static gchar *rendezvous_prpl_status_text(GaimBuddy *b)
-{
-	GaimConnection *gc = b->account->gc;
-	RendezvousData *rd = gc->proto_data;
-	RendezvousBuddy *rb;
-	gchar *ret;
-
-	rb = g_hash_table_lookup(rd->buddies, b->name);
-	if ((rb == NULL) || (rb->msg == NULL))
-		return NULL;
-
-	ret = g_strdup(rb->msg);
-
-	return ret;
-}
-
-static gchar *rendezvous_prpl_tooltip_text(GaimBuddy *b)
-{
-	GaimConnection *gc = b->account->gc;
-	RendezvousData *rd = gc->proto_data;
-	RendezvousBuddy *rb;
-	GString *ret;
-
-	rb = g_hash_table_lookup(rd->buddies, b->name);
-	if (rb == NULL)
-		return NULL;
-
-	ret = g_string_new("");
-
-	if (rb->aim != NULL)
-		g_string_append_printf(ret, _("<b>AIM Screen name</b>: %s\n"), rb->aim);
-
-	if (rb->msg != NULL) {
-		if (rb->status == UC_UNAVAILABLE)
-			g_string_append_printf(ret, _("<b>Away Message</b>: %s\n"), rb->msg);
-		else
-			g_string_append_printf(ret, _("<b>Available Message</b>: %s\n"), rb->msg);
-	}
-
-	/* XXX - Fix blist.c so we can prepend the \n's rather than appending them */
-
-	return g_string_free(ret, FALSE);
-}
-
-/****************************/
-/* Connection Funtions      */
-/****************************/
-static void rendezvous_callback(gpointer data, gint source, GaimInputCondition condition)
-{
-	GaimConnection *gc = data;
-	RendezvousData *rd = gc->proto_data;
-	DNSPacket *dns;
-	int i;
-
-	gaim_debug_misc("rendezvous", "Received rendezvous datagram\n");
-
-	dns = mdns_read(rd->fd);
-	if (dns == NULL)
-		return;
-
-	/* Handle the DNS packet */
-	for (i = 0; i < dns->header.numanswers; i++)
-		rendezvous_handle_rr(gc, &dns->answers[i]);
-	for (i = 0; i < dns->header.numauthority; i++)
-		rendezvous_handle_rr(gc, &dns->authority[i]);
-	for (i = 0; i < dns->header.numadditional; i++)
-		rendezvous_handle_rr(gc, &dns->additional[i]);
-
-	mdns_free(dns);
-}
-
-static void rendezvous_prpl_login(GaimAccount *account)
-{
-	GaimConnection *gc = gaim_account_get_connection(account);
-	RendezvousData *rd;
-
-	rd = g_new0(RendezvousData, 1);
-	rd->buddies = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, rendezvous_buddy_free);
-
-	gc->proto_data = rd;
-
-	gaim_connection_update_progress(gc, _("Preparing Buddy List"), 0, RENDEZVOUS_CONNECT_STEPS);
-
-	rendezvous_removeallfromlocal(gc);
-
-	gaim_connection_update_progress(gc, _("Connecting"), 1, RENDEZVOUS_CONNECT_STEPS);
-
-	rd->fd = mdns_establish_socket();
-	if (rd->fd == -1) {
-		gaim_connection_error(gc, _("Unable to login to rendezvous"));
-		return;
-	}
-
-	gc->inpa = gaim_input_add(rd->fd, GAIM_INPUT_READ, rendezvous_callback, gc);
-
-	gaim_connection_set_state(gc, GAIM_CONNECTED);
-
-	mdns_query(rd->fd, "_presence._tcp.local");
-
-#if 0
-	text_record_add("txtvers", "1");
-	text_record_add("status", "avail");
-	text_record_add("1st", gaim_account_get_string(account, "first", "Gaim"));
-	text_record_add("AIM", "markdoliner");
-	text_record_add("version", "1");
-	text_record_add("port.p2pj", "5298");
-	text_record_add("last", gaim_account_get_string(account, "last", _("User")));
-
-	publish(account->username, "_presence._tcp", 5298);
-#endif
-}
-
-static void rendezvous_prpl_close(GaimConnection *gc)
-{
-	RendezvousData *rd = (RendezvousData *)gc->proto_data;
-
-	if (gc->inpa)
-		gaim_input_remove(gc->inpa);
-
-	rendezvous_removeallfromlocal(gc);
-
-	if (!rd)
-		return;
-
-	if (rd->fd >= 0)
-		close(rd->fd);
-
-	g_hash_table_destroy(rd->buddies);
-
-	g_free(rd);
-}
-
-static int rendezvous_prpl_send_im(GaimConnection *gc, const char *who, const char *message, GaimConvImFlags flags)
-{
-	gaim_debug_info("rendezvous", "Sending IM\n");
-
-	return 1;
-}
-
-static void rendezvous_prpl_setaway(GaimConnection *gc, const char *state, const char *text)
-{
-	gaim_debug_error("rendezvous", "Set away, state=%s,  text=%s\n", state, text);
-}
-
-static GaimPlugin *my_protocol = NULL;
-
-static GaimPluginProtocolInfo prpl_info =
-{
-	OPT_PROTO_NO_PASSWORD,
-	NULL,
-	NULL,
-	NULL,
-	rendezvous_prpl_list_icon,
-	rendezvous_prpl_list_emblems,
-	rendezvous_prpl_status_text,
-	rendezvous_prpl_tooltip_text,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	rendezvous_prpl_login,
-	rendezvous_prpl_close,
-	rendezvous_prpl_send_im,
-	NULL,
-	NULL,
-	NULL,
-	rendezvous_prpl_setaway,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL
-};
-
-static GaimPluginInfo info =
-{
-	2,                                                /**< api_version    */
-	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
-	NULL,                                             /**< ui_requirement */
-	0,                                                /**< flags          */
-	NULL,                                             /**< dependencies   */
-	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
-
-	"prpl-rendezvous",                                /**< id             */
-	"Rendezvous",                                     /**< name           */
-	VERSION,                                          /**< version        */
-	                                                  /**  summary        */
-	N_("Rendezvous Protocol Plugin"),
-	                                                  /**  description    */
-	N_("Rendezvous Protocol Plugin"),
-	NULL,                                             /**< author         */
-	GAIM_WEBSITE,                                     /**< homepage       */
-
-	NULL,                                             /**< load           */
-	NULL,                                             /**< unload         */
-	NULL,                                             /**< destroy        */
-
-	NULL,                                             /**< ui_info        */
-	&prpl_info                                        /**< extra_info     */
-};
-
-static void init_plugin(GaimPlugin *plugin)
-{
-	GaimAccountUserSplit *split;
-	GaimAccountOption *option;
-
-	/* Try to avoid making this configurable... */
-	split = gaim_account_user_split_new(_("Host Name"), "localhost", '@');
-	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
-
-	option = gaim_account_option_string_new(_("First Name"), "first", "Gaim");
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-
-	option = gaim_account_option_string_new(_("Last Name"), "last", _("User"));
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-
-	option = gaim_account_option_bool_new(_("Share AIM screen name"), "shareaim", TRUE);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-
-	my_protocol = plugin;
-}
-
-GAIM_INIT_PLUGIN(rendezvous, init_plugin, info);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/rendezvous/mdns.c	Tue Mar 23 03:45:13 2004 +0000
@@ -0,0 +1,541 @@
+/**
+ * @file mdns.c Multicast DNS connection code used by rendezvous.
+ *
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * If you want to understand this, read RFC1035 and 
+ * draft-cheshire-dnsext-multicastdns.txt
+ */
+
+/*
+ * XXX - THIS DOESN'T DO BOUNDS CHECKING!!!  DON'T USE IT ON AN UNTRUSTED
+ * NETWORK UNTIL IT DOES!!!  THERE ARE POSSIBLE REMOTE ACCESS VIA BUFFER
+ * OVERFLOW SECURITY HOLES!!!
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <glib.h>
+
+#include "debug.h"
+
+#include "mdns.h"
+#include "util.h"
+
+int
+mdns_establish_socket()
+{
+	int fd = -1;
+	struct sockaddr_in addr;
+	struct ip_mreq mreq;
+	unsigned char loop;
+	unsigned char ttl;
+	int reuseaddr;
+
+	gaim_debug_info("mdns", "Establishing multicast socket\n");
+
+	/* What's the difference between AF_INET and PF_INET? */
+	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+		gaim_debug_error("mdns", "Unable to create socket: %s\n", strerror(errno));
+		return -1;
+	}
+
+	/* Make the socket non-blocking (although it shouldn't matter) */
+	fcntl(fd, F_SETFL, O_NONBLOCK);
+
+	/* Bind the socket to a local IP and port */
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(5353);
+	addr.sin_addr.s_addr = INADDR_ANY;
+	if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
+		gaim_debug_error("mdns", "Unable to bind socket to interface.\n");
+		close(fd);
+		return -1;
+	}
+
+	/* Ensure loopback is enabled (it should be enabled by default, by let's be sure) */
+	loop = 1;
+	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(unsigned char)) == -1) {
+		gaim_debug_error("mdns", "Error calling setsockopt for IP_MULTICAST_LOOP\n");
+	}
+
+	/* Set TTL to 255--required by mDNS */
+	ttl = 255;
+	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(unsigned char)) == -1) {
+		gaim_debug_error("mdns", "Error calling setsockopt for IP_MULTICAST_TTL\n");
+		close(fd);
+		return -1;
+	}
+
+	/* Join the .local multicast group */
+	mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.251");
+	mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+	if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) {
+		gaim_debug_error("mdns", "Error calling setsockopt for IP_ADD_MEMBERSHIP\n");
+		close(fd);
+		return -1;
+	}
+
+	/* Make the local IP re-usable */
+	reuseaddr = 1;
+	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1) {
+		gaim_debug_error("mdns", "Error calling setsockopt for SO_REUSEADDR: %s\n", strerror(errno));
+	}
+
+	return fd;
+}
+
+int
+mdns_query(int fd, const char *domain)
+{
+	struct sockaddr_in addr;
+	unsigned int querylen;
+	unsigned char *query;
+	char *b, *c;
+	int i, n;
+
+	if (strlen(domain) > 255) {
+		return -EINVAL;
+	}
+
+	/*
+	 * Build the outgoing query packet.  It is made of the header with a
+	 * query made up of the given domain.  The header is 12 bytes.
+	 */
+	querylen = 12 + strlen(domain) + 2 + 4;
+	if (!(query = (unsigned char *)g_malloc(querylen))) {
+		return -ENOMEM;
+	}
+
+	/* The header section */
+	util_put32(&query[0], 0); /* The first 32 bits of the header are all 0's in mDNS */
+	util_put16(&query[4], 1); /* QDCOUNT */
+	util_put16(&query[6], 0); /* ANCOUNT */
+	util_put16(&query[8], 0); /* NSCOUNT */
+	util_put16(&query[10], 0); /* ARCOUNT */
+
+	/* The question section */
+	i = 12; /* Destination in query */
+	b = (char *)domain;
+	while ((c = strchr(b, '.'))) {
+		i += util_put8(&query[i], c - b); /* Length of domain-name segment */
+		memcpy(&query[i], b, c - b); /* Domain-name segment */
+		i += c - b; /* Increment the destination pointer */
+		b = c + 1;
+	}
+	i += util_put8(&query[i], strlen(b)); /* Length of domain-name segment */
+	strcpy(&query[i], b); /* Domain-name segment */
+	i += strlen(b) + 1; /* Increment the destination pointer */
+	i += util_put16(&query[i], 0x000c); /* QTYPE */
+	i += util_put16(&query[i], 0x8001); /* QCLASS */
+
+	/* Actually send the DNS query */
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(5353);
+	addr.sin_addr.s_addr = inet_addr("224.0.0.251");
+	n = sendto(fd, query, querylen, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+	g_free(query);
+
+	if (n == -1) {
+		gaim_debug_error("mdns", "Error sending packet: %d\n", errno);
+		return -1;
+	} else if (n != querylen) {
+		gaim_debug_error("mdns", "Only sent %d of %d bytes of query.\n", n, querylen);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * XXX - Needs bounds checking!
+ *
+ * Read in a domain name from the given buffer starting at the given
+ * offset.  This handles using domain name compression to jump around
+ * the data buffer, if needed.
+ *
+ * @return A null-terminated string representation of the domain name.
+ *         This should be g_free'd when no longer needed.
+ */
+static gchar *
+mdns_read_name(const char *data, int datalen, int dataoffset)
+{
+	GString *ret = g_string_new("");
+	unsigned char tmp;
+
+	while ((tmp = util_get8(&data[dataoffset])) != 0) {
+		dataoffset++;
+
+		if ((tmp & 0xc0) == 0) { /* First two bits are 00 */
+			if (*ret->str)
+				g_string_append_c(ret, '.');
+			g_string_append_len(ret, &data[dataoffset], tmp);
+			dataoffset += tmp;
+
+		} else if ((tmp & 0x40) == 0) { /* First two bits are 10 */
+			/* Reserved for future use */
+
+		} else if ((tmp & 0x80) == 1) { /* First two bits are 01 */
+			/* Reserved for future use */
+
+		} else { /* First two bits are 11 */
+			/* Jump to another position in the data */
+			dataoffset = util_get8(&data[dataoffset]);
+
+		}
+	}
+
+	return g_string_free(ret, FALSE);
+}
+
+/*
+ * XXX - Needs bounds checking!
+ *
+ * Determine how many bytes long a portion of the domain name is
+ * at the given offset.  This does NOT jump around the data array
+ * in the case of domain name compression.
+ *
+ * @return The length of the portion of the domain name.
+ */
+static int
+mdns_read_name_len(const char *data, int datalen, int dataoffset)
+{
+	int startoffset = dataoffset;
+	unsigned char tmp;
+
+	while ((tmp = util_get8(&data[dataoffset++])) != 0) {
+
+		if ((tmp & 0xc0) == 0) { /* First two bits are 00 */
+			dataoffset += tmp;
+
+		} else if ((tmp & 0x40) == 0) { /* First two bits are 10 */
+			/* Reserved for future use */
+
+		} else if ((tmp & 0x80) == 1) { /* First two bits are 01 */
+			/* Reserved for future use */
+
+		} else { /* First two bits are 11 */
+			/* End of this portion of the domain name */
+			dataoffset++;
+			break;
+
+		}
+	}
+
+	return dataoffset - startoffset;
+}
+
+/*
+ * XXX - Needs bounds checking!
+ *
+ */
+static Question *
+mdns_read_questions(int numquestions, const char *data, int datalen, int *offset)
+{
+	Question *ret;
+	int i;
+
+	ret = (Question *)g_malloc0(numquestions * sizeof(Question));
+	for (i = 0; i < numquestions; i++) {
+		ret[i].name = mdns_read_name(data, 0, *offset);
+		*offset += mdns_read_name_len(data, 0, *offset);
+		ret[i].type = util_get16(&data[*offset]); /* QTYPE */
+		*offset += 2;
+		ret[i].class = util_get16(&data[*offset]); /* QCLASS */
+		*offset += 2;
+	}
+
+	return ret;
+}
+
+/*
+ * Read in a chunk of data, probably a buddy icon.
+ *
+ */
+static unsigned char *
+mdns_read_rr_rdata_null(const char *data, int datalen, int offset, unsigned short rdlength)
+{
+	unsigned char *ret = NULL;
+
+	if (offset + rdlength > datalen)
+		return NULL;
+
+	ret = (unsigned char *)g_malloc(rdlength);
+	memcpy(ret, &data[offset], rdlength);
+
+	return ret;
+}
+
+/*
+ * XXX - Needs bounds checking!
+ *
+ */
+static char *
+mdns_read_rr_rdata_ptr(const char *data, int datalen, int offset)
+{
+	char *ret = NULL;
+
+	ret = mdns_read_name(data, datalen, offset);
+
+	return ret;
+}
+
+/*
+ *
+ *
+ */
+static GHashTable *
+mdns_read_rr_rdata_txt(const char *data, int datalen, int offset, unsigned short rdlength)
+{
+	GHashTable *ret = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	int endoffset = offset + rdlength;
+	unsigned char tmp;
+	char buf[256], *key, *value;
+
+	while (offset < endoffset) {
+		/* Read in the length of the next name/value pair */
+		tmp = util_get8(&data[offset]);
+		offset++;
+
+		/* Ensure packet is valid */
+		if (offset + tmp > endoffset)
+			break;
+
+		/* Read in the next name/value pair */
+		strncpy(buf, &data[offset], tmp);
+		offset += tmp;
+
+		if (buf[0] == '=') {
+			/* Name/value pairs beginning with = are silently ignored */
+			continue;
+		}
+
+		/* The value is a substring of buf, starting just after the = */
+		buf[tmp] = '\0';
+		value = strchr(buf, '=');
+		if (value != NULL) {
+			value[0] = '\0';
+			value++;
+		}
+
+		/* Make the key all lowercase */
+		key = g_utf8_strdown(buf, -1);
+		if (!g_hash_table_lookup(ret, key))
+			g_hash_table_insert(ret, key, g_strdup(value));
+		else
+			g_free(key);
+	}
+
+	return ret;
+}
+
+/*
+ * XXX - Needs bounds checking!
+ *
+ */
+static ResourceRecord *
+mdns_read_rr(int numrecords, const char *data, int datalen, int *offset)
+{
+	ResourceRecord *ret;
+	int i;
+
+	ret = (ResourceRecord *)g_malloc0(numrecords * sizeof(ResourceRecord));
+	for (i = 0; i < numrecords; i++) {
+		ret[i].name = mdns_read_name(data, 0, *offset); /* NAME */
+		*offset += mdns_read_name_len(data, 0, *offset);
+		ret[i].type = util_get16(&data[*offset]); /* TYPE */
+		*offset += 2;
+		ret[i].class = util_get16(&data[*offset]); /* CLASS */
+		*offset += 2;
+		ret[i].ttl = util_get32(&data[*offset]); /* TTL */
+		*offset += 4;
+		ret[i].rdlength = util_get16(&data[*offset]); /* RDLENGTH */
+		*offset += 2;
+
+		/* RDATA */
+		switch (ret[i].type) {
+			case RENDEZVOUS_RRTYPE_NULL:
+				ret[i].rdata = mdns_read_rr_rdata_null(data, datalen, *offset, ret[i].rdlength);
+			break;
+
+			case RENDEZVOUS_RRTYPE_PTR:
+				ret[i].rdata = mdns_read_rr_rdata_ptr(data, datalen, *offset);
+			break;
+
+			case RENDEZVOUS_RRTYPE_TXT:
+				ret[i].rdata = mdns_read_rr_rdata_txt(data, datalen, *offset, ret[i].rdlength);
+			break;
+
+			default:
+				ret[i].rdata = NULL;
+			break;
+		}
+		*offset += ret[i].rdlength;
+	}
+
+	return ret;
+}
+
+/*
+ * XXX - Needs bounds checking!
+ *
+ */
+DNSPacket *
+mdns_read(int fd)
+{
+	DNSPacket *ret = NULL;
+	int i; /* Current position in datagram */
+	//char data[512];
+	char data[10096];
+	int datalen;
+	struct sockaddr_in addr;
+	socklen_t addrlen;
+
+	/* Read in an mDNS packet */
+	addrlen = sizeof(struct sockaddr_in);
+	if ((datalen = recvfrom(fd, data, sizeof(data), 0, (struct sockaddr *)&addr, &addrlen)) == -1) {
+		gaim_debug_error("mdns", "Error reading packet: %d\n", errno);
+		return NULL;
+	}
+
+	ret = (DNSPacket *)g_malloc0(sizeof(DNSPacket));
+
+	/* Parse the incoming packet, starting from 0 */
+	i = 0;
+
+	/* The header section */
+	ret->header.id = util_get16(&data[i]); /* ID */
+	i += 2;
+
+	/* For the flags, some bits must be 0 and some must be 1, the rest are ignored */
+	ret->header.flags = util_get16(&data[i]); /* Flags (QR, OPCODE, AA, TC, RD, RA, Z, AD, CD, and RCODE */
+	i += 2;
+	if ((ret->header.flags & 0x8000) == 0) {
+		/* QR should be 1 */
+		g_free(ret);
+		return NULL;
+	}
+	if ((ret->header.flags & 0x7800) != 0) {
+		/* OPCODE should be all 0's */
+		g_free(ret);
+		return NULL;
+	}
+
+	/* Read in the number of other things in the packet */
+	ret->header.numquestions = util_get16(&data[i]);
+	i += 2;
+	ret->header.numanswers = util_get16(&data[i]);
+	i += 2;
+	ret->header.numauthority = util_get16(&data[i]);
+	i += 2;
+	ret->header.numadditional = util_get16(&data[i]);
+	i += 2;
+
+	/* Read in all the questions */
+	ret->questions = mdns_read_questions(ret->header.numquestions, data, datalen, &i);
+
+	/* Read in all resource records */
+	ret->answers = mdns_read_rr(ret->header.numanswers, data, datalen, &i);
+
+	/* Read in all authority records */
+	ret->authority = mdns_read_rr(ret->header.numauthority, data, datalen, &i);
+
+	/* Read in all additional records */
+	ret->additional = mdns_read_rr(ret->header.numadditional, data, datalen, &i);
+
+	/* We should be at the end of the packet */
+	if (i != datalen) {
+		gaim_debug_error("mdns", "Finished parsing before end of DNS packet!  Only parsed %d of %d bytes.", i, datalen);
+		g_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+/**
+ * Free the rdata associated with a given resource record.
+ */
+static void
+mdns_free_rr_rdata(unsigned short type, void *rdata)
+{
+	switch (type) {
+			case RENDEZVOUS_RRTYPE_NULL:
+			case RENDEZVOUS_RRTYPE_PTR:
+				g_free(rdata);
+			break;
+
+			case RENDEZVOUS_RRTYPE_TXT:
+				g_hash_table_destroy(rdata);
+			break;
+	}
+}
+
+/**
+ * Free a given question
+ */
+static void
+mdns_free_q(Question *q)
+{
+	g_free(q->name);
+}
+
+/**
+ * Free a given resource record.
+ */
+static void
+mdns_free_rr(ResourceRecord *rr)
+{
+	g_free(rr->name);
+	mdns_free_rr_rdata(rr->type, rr->rdata);
+}
+
+void
+mdns_free(DNSPacket *dns)
+{
+	int i;
+
+	for (i = 0; i < dns->header.numquestions; i++)
+		mdns_free_q(&dns->questions[i]);
+	for (i = 0; i < dns->header.numanswers; i++)
+		mdns_free_rr(&dns->answers[i]);
+	for (i = 0; i < dns->header.numauthority; i++)
+		mdns_free_rr(&dns->authority[i]);
+	for (i = 0; i < dns->header.numadditional; i++)
+		mdns_free_rr(&dns->additional[i]);
+
+	g_free(dns->questions);
+	g_free(dns->answers);
+	g_free(dns->authority);
+	g_free(dns->additional);
+	g_free(dns);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/rendezvous/mdns.h	Tue Mar 23 03:45:13 2004 +0000
@@ -0,0 +1,144 @@
+/**
+ * @file mdns.h Multicast DNS connection code used by rendezvous.
+ *
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _MDNS_H_
+#define _MDNS_H_
+
+#include <errno.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "debug.h"
+
+/*
+ * Some #define's stolen from libfaim.  Used to put
+ * binary data (bytes, shorts and ints) into an array.
+ */
+#define util_put8(buf, data) ((*(buf) = (unsigned char)(data)&0xff),1)
+#define util_put16(buf, data) ( \
+		(*(buf) = (unsigned char)((data)>>8)&0xff), \
+		(*((buf)+1) = (unsigned char)(data)&0xff),  \
+		2)
+#define util_put32(buf, data) ( \
+		(*((buf)) = (unsigned char)((data)>>24)&0xff), \
+		(*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
+		(*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
+		(*((buf)+3) = (unsigned char)(data)&0xff), \
+		4)
+#define util_get8(buf) ((*(buf))&0xff)
+#define util_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
+#define util_get32(buf) ((((*(buf))<<24)&0xff000000) + \
+		(((*((buf)+1))<<16)&0x00ff0000) + \
+		(((*((buf)+2))<< 8)&0x0000ff00) + \
+		(((*((buf)+3)    )&0x000000ff)))
+
+/*
+ * Merriam-Webster's
+ */
+#define RENDEZVOUS_RRTYPE_A		1
+#define RENDEZVOUS_RRTYPE_NS	2
+#define RENDEZVOUS_RRTYPE_CNAME	5
+#define RENDEZVOUS_RRTYPE_NULL	10
+#define RENDEZVOUS_RRTYPE_PTR	12
+#define RENDEZVOUS_RRTYPE_TXT	16
+
+/*
+ * Express for Men's
+ */
+typedef struct _Header {
+	unsigned short id;
+	unsigned short flags;
+	unsigned short numquestions;
+	unsigned short numanswers;
+	unsigned short numauthority;
+	unsigned short numadditional;
+} Header;
+
+typedef struct _Question {
+	gchar *name;
+	unsigned short type;
+	unsigned short class;
+} Question;
+
+typedef struct ResourceRecord {
+	gchar *name;
+	unsigned short type;
+	unsigned short class;
+	int ttl;
+	unsigned short rdlength;
+	void *rdata;
+} ResourceRecord;
+
+typedef struct _DNSPacket {
+	Header header;
+	Question *questions;
+	ResourceRecord *answers;
+	ResourceRecord *authority;
+	ResourceRecord *additional;
+} DNSPacket;
+
+/*
+ * Bring in 'Da Noise, Bring in 'Da Functions
+ */
+
+/**
+ * Create a multicast socket that can be used for sending and 
+ * receiving multicast DNS packets.  The socket joins the
+ * link-local multicast group (224.0.0.251).
+ *
+ * @return The file descriptor of the new socket, or -1 if
+ *         there was an error establishing the socket.
+ */
+int mdns_establish_socket();
+
+/**
+ * Send a multicast DNS query for the given domain across the given
+ * socket.
+ *
+ * @param fd The file descriptor of a pre-established socket to
+ *        be used for sending the outgoing mDNS query.
+ * @param domain This is the domain name you wish to query.  It should 
+ *        be of the format "_presence._tcp.local" for example.
+ * @return 0 if sucessful.
+ */
+int mdns_query(int fd, const char *domain);
+
+/**
+ *
+ *
+ */
+DNSPacket *mdns_read(int fd);
+
+/**
+ * Free a DNSPacket structure.
+ *
+ * @param dns The DNSPacket that you want to free.
+ */
+void mdns_free(DNSPacket *dns);
+
+#endif /* _MDNS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/rendezvous/rendezvous.c	Tue Mar 23 03:45:13 2004 +0000
@@ -0,0 +1,530 @@
+/*
+ * gaim - Rendezvous Protocol Plugin
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "internal.h"
+
+#include "account.h"
+#include "accountopt.h"
+#include "blist.h"
+#include "conversation.h"
+#include "debug.h"
+#include "prpl.h"
+
+#include "mdns.h"
+#include "util.h"
+
+#define RENDEZVOUS_CONNECT_STEPS 2
+
+typedef struct _RendezvousData {
+	int fd;
+	GHashTable *buddies;
+} RendezvousData;
+
+typedef struct _RendezvousBuddy {
+	gchar *firstandlast;
+	gchar *aim;
+	int p2pjport;
+	int status;
+	int idle;
+	gchar *msg;
+} RendezvousBuddy;
+
+#define UC_IDLE 2
+
+/****************************/
+/* Utility Functions        */
+/****************************/
+static void rendezvous_buddy_free(gpointer data)
+{
+	RendezvousBuddy *rb = data;
+
+	g_free(rb->firstandlast);
+	g_free(rb->msg);
+	g_free(rb);
+}
+
+/*
+ * Extract the "user@host" name from a full presence domain
+ * of the form "user@host._presence._tcp.local"
+ *
+ * @return If the domain is NOT a _presence._tcp.local domain
+ *         then return NULL.  Otherwise return a newly allocated
+ *         null-terminated string containing the "user@host" for
+ *         the given domain.  This string should be g_free'd
+ *         when no longer needed.
+ */
+static gchar *rendezvous_extract_name(gchar *domain)
+{
+	gchar *ret, *suffix;
+
+	if (!g_str_has_suffix(domain, "._presence._tcp.local"))
+		return NULL;
+
+	suffix = strstr(domain, "._presence._tcp.local");
+	ret = g_strndup(domain, suffix - domain);
+
+	return ret;
+}
+
+/****************************/
+/* Buddy List Functions     */
+/****************************/
+static void rendezvous_addtolocal(GaimConnection *gc, const char *name, const char *domain)
+{
+	GaimAccount *account = gaim_connection_get_account(gc);
+	GaimBuddy *b;
+	GaimGroup *g;
+
+	g = gaim_find_group(domain);
+	if (g == NULL) {
+		g = gaim_group_new(domain);
+		gaim_blist_add_group(g, NULL);
+	}
+
+	b = gaim_find_buddy_in_group(account, name, g);
+	if (b != NULL)
+		return;
+
+	b = gaim_buddy_new(account, name, NULL);
+	gaim_blist_add_buddy(b, NULL, g, NULL);
+	serv_got_update(gc, b->name, 1, 0, 0, 0, 0);
+}
+
+static void rendezvous_removefromlocal(GaimConnection *gc, const char *name, const char *domain)
+{
+	GaimAccount *account = gaim_connection_get_account(gc);
+	GaimBuddy *b;
+	GaimGroup *g;
+
+	g = gaim_find_group(domain);
+	if (g == NULL)
+		return;
+
+	b = gaim_find_buddy_in_group(account, name, g);
+	if (b == NULL)
+		return;
+
+	serv_got_update(gc, b->name, 0, 0, 0, 0, 0);
+	gaim_blist_remove_buddy(b);
+}
+
+static void rendezvous_removeallfromlocal(GaimConnection *gc)
+{
+	GaimAccount *account = gaim_connection_get_account(gc);
+	GaimBuddyList *blist;
+	GaimBlistNode *gnode, *cnode, *bnode;
+	GaimBuddy *b;
+
+	/* Go through and remove all buddies that belong to this account */
+	if ((blist = gaim_get_blist()) != NULL) {
+		for (gnode = blist->root; gnode; gnode = gnode->next) {
+			if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
+				continue;
+			for (cnode = gnode->child; cnode; cnode = cnode->next) {
+				if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
+					continue;
+				for (bnode = cnode->child; bnode; bnode = bnode->next) {
+					if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
+						continue;
+					b = (GaimBuddy *)bnode;
+					if (b->account != account)
+						continue;
+					serv_got_update(gc, b->name, 0, 0, 0, 0, 0);
+					gaim_blist_remove_buddy(b);
+				}
+			}
+		}
+	}
+}
+
+static void rendezvous_handle_rr_txt(GaimConnection *gc, ResourceRecord *rr, const gchar *name)
+{
+	RendezvousData *rd = gc->proto_data;
+	RendezvousBuddy *rb;
+	GHashTable *rdata;
+	gchar *tmp1, *tmp2;
+
+	rb = g_hash_table_lookup(rd->buddies, name);
+	if (rb == NULL) {
+		rb = g_malloc0(sizeof(RendezvousBuddy));
+		g_hash_table_insert(rd->buddies, g_strdup(name), rb);
+	}
+
+	rdata = rr->rdata;
+
+	tmp1 = g_hash_table_lookup(rdata, "1st");
+	tmp2 = g_hash_table_lookup(rdata, "last");
+	g_free(rb->firstandlast);
+	rb->firstandlast = g_strdup_printf("%s%s%s",
+							(tmp1 ? tmp1 : ""),
+							(tmp1 || tmp2 ? " " : ""),
+							(tmp2 ? tmp2 : ""));
+	serv_got_alias(gc, name, rb->firstandlast);
+
+	tmp1 = g_hash_table_lookup(rdata, "aim");
+	if (tmp1 != NULL) {
+		g_free(rb->aim);
+		rb->aim = g_strdup(tmp1);
+	}
+
+	tmp1 = g_hash_table_lookup(rdata, "port.p2pj");
+	rb->p2pjport = atoi(tmp1);
+
+	tmp1 = g_hash_table_lookup(rdata, "status");
+	if (tmp1 != NULL) {
+		if (!strcmp(tmp1, "dnd")) {
+			/* Available */
+			rb->status = 0;
+		} else if (!strcmp(tmp1, "away")) {
+			/* Idle */
+			tmp2 = g_hash_table_lookup(rdata, "away");
+			rb->idle = atoi(tmp2);
+			gaim_debug_error("XXX", "User has been idle for %d\n", rb->idle);
+			rb->status = UC_IDLE;
+		} else if (!strcmp(tmp1, "avail")) {
+			/* Away */
+			rb->status = UC_UNAVAILABLE;
+		}
+		serv_got_update(gc, name, 1, 0, 0, 0, rb->status);
+	}
+
+	tmp1 = g_hash_table_lookup(rdata, "msg");
+	if (tmp1 != NULL) {
+		g_free(rb->msg);
+		rb->msg = g_strdup(tmp1);
+	}
+}
+
+/*
+ * Parse a resource record and do stuff if we need to.
+ */
+static void rendezvous_handle_rr(GaimConnection *gc, ResourceRecord *rr)
+{
+	gchar *name;
+
+	gaim_debug_error("XXX", "Parsing resource record with domain name %s\n", rr->name);
+
+	switch (rr->type) {
+		case RENDEZVOUS_RRTYPE_NULL: {
+			if ((name = rendezvous_extract_name(rr->name)) != NULL) {
+				if (rr->rdlength > 0) {
+					/* Data is a buddy icon */
+					gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc), name, rr->rdata, rr->rdlength);
+				}
+
+				g_free(name);
+			}
+		} break;
+
+		case RENDEZVOUS_RRTYPE_PTR: {
+			gchar *rdata = rr->rdata;
+			if ((name = rendezvous_extract_name(rdata)) != NULL) {
+				if (rr->ttl > 0)
+					rendezvous_addtolocal(gc, name, "Rendezvous");
+				else
+					rendezvous_removefromlocal(gc, name, "Rendezvous");
+
+				g_free(name);
+			}
+		} break;
+
+		case RENDEZVOUS_RRTYPE_TXT: {
+			if ((name = rendezvous_extract_name(rr->name)) != NULL) {
+				rendezvous_handle_rr_txt(gc, rr, name);
+				g_free(name);
+			}
+		} break;
+	}
+}
+
+/****************************/
+/* Icon and Emblem Funtions */
+/****************************/
+static const char* rendezvous_prpl_list_icon(GaimAccount *a, GaimBuddy *b)
+{
+	return "rendezvous";
+}
+
+static void rendezvous_prpl_list_emblems(GaimBuddy *b, char **se, char **sw, char **nw, char **ne)
+{
+	if (GAIM_BUDDY_IS_ONLINE(b)) {
+		if (b->uc & UC_UNAVAILABLE)
+			*se = "away";
+	} else {
+		*se = "offline";
+	}
+}
+
+static gchar *rendezvous_prpl_status_text(GaimBuddy *b)
+{
+	GaimConnection *gc = b->account->gc;
+	RendezvousData *rd = gc->proto_data;
+	RendezvousBuddy *rb;
+	gchar *ret;
+
+	rb = g_hash_table_lookup(rd->buddies, b->name);
+	if ((rb == NULL) || (rb->msg == NULL))
+		return NULL;
+
+	ret = g_strdup(rb->msg);
+
+	return ret;
+}
+
+static gchar *rendezvous_prpl_tooltip_text(GaimBuddy *b)
+{
+	GaimConnection *gc = b->account->gc;
+	RendezvousData *rd = gc->proto_data;
+	RendezvousBuddy *rb;
+	GString *ret;
+
+	rb = g_hash_table_lookup(rd->buddies, b->name);
+	if (rb == NULL)
+		return NULL;
+
+	ret = g_string_new("");
+
+	if (rb->aim != NULL)
+		g_string_append_printf(ret, _("<b>AIM Screen name</b>: %s\n"), rb->aim);
+
+	if (rb->msg != NULL) {
+		if (rb->status == UC_UNAVAILABLE)
+			g_string_append_printf(ret, _("<b>Away Message</b>: %s\n"), rb->msg);
+		else
+			g_string_append_printf(ret, _("<b>Available Message</b>: %s\n"), rb->msg);
+	}
+
+	/* XXX - Fix blist.c so we can prepend the \n's rather than appending them */
+
+	return g_string_free(ret, FALSE);
+}
+
+/****************************/
+/* Connection Funtions      */
+/****************************/
+static void rendezvous_callback(gpointer data, gint source, GaimInputCondition condition)
+{
+	GaimConnection *gc = data;
+	RendezvousData *rd = gc->proto_data;
+	DNSPacket *dns;
+	int i;
+
+	gaim_debug_misc("rendezvous", "Received rendezvous datagram\n");
+
+	dns = mdns_read(rd->fd);
+	if (dns == NULL)
+		return;
+
+	/* Handle the DNS packet */
+	for (i = 0; i < dns->header.numanswers; i++)
+		rendezvous_handle_rr(gc, &dns->answers[i]);
+	for (i = 0; i < dns->header.numauthority; i++)
+		rendezvous_handle_rr(gc, &dns->authority[i]);
+	for (i = 0; i < dns->header.numadditional; i++)
+		rendezvous_handle_rr(gc, &dns->additional[i]);
+
+	mdns_free(dns);
+}
+
+static void rendezvous_prpl_login(GaimAccount *account)
+{
+	GaimConnection *gc = gaim_account_get_connection(account);
+	RendezvousData *rd;
+
+	rd = g_new0(RendezvousData, 1);
+	rd->buddies = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, rendezvous_buddy_free);
+
+	gc->proto_data = rd;
+
+	gaim_connection_update_progress(gc, _("Preparing Buddy List"), 0, RENDEZVOUS_CONNECT_STEPS);
+
+	rendezvous_removeallfromlocal(gc);
+
+	gaim_connection_update_progress(gc, _("Connecting"), 1, RENDEZVOUS_CONNECT_STEPS);
+
+	rd->fd = mdns_establish_socket();
+	if (rd->fd == -1) {
+		gaim_connection_error(gc, _("Unable to login to rendezvous"));
+		return;
+	}
+
+	gc->inpa = gaim_input_add(rd->fd, GAIM_INPUT_READ, rendezvous_callback, gc);
+
+	gaim_connection_set_state(gc, GAIM_CONNECTED);
+
+	mdns_query(rd->fd, "_presence._tcp.local");
+
+#if 0
+	text_record_add("txtvers", "1");
+	text_record_add("status", "avail");
+	text_record_add("1st", gaim_account_get_string(account, "first", "Gaim"));
+	text_record_add("AIM", "markdoliner");
+	text_record_add("version", "1");
+	text_record_add("port.p2pj", "5298");
+	text_record_add("last", gaim_account_get_string(account, "last", _("User")));
+
+	publish(account->username, "_presence._tcp", 5298);
+#endif
+}
+
+static void rendezvous_prpl_close(GaimConnection *gc)
+{
+	RendezvousData *rd = (RendezvousData *)gc->proto_data;
+
+	if (gc->inpa)
+		gaim_input_remove(gc->inpa);
+
+	rendezvous_removeallfromlocal(gc);
+
+	if (!rd)
+		return;
+
+	if (rd->fd >= 0)
+		close(rd->fd);
+
+	g_hash_table_destroy(rd->buddies);
+
+	g_free(rd);
+}
+
+static int rendezvous_prpl_send_im(GaimConnection *gc, const char *who, const char *message, GaimConvImFlags flags)
+{
+	gaim_debug_info("rendezvous", "Sending IM\n");
+
+	return 1;
+}
+
+static void rendezvous_prpl_setaway(GaimConnection *gc, const char *state, const char *text)
+{
+	gaim_debug_error("rendezvous", "Set away, state=%s,  text=%s\n", state, text);
+}
+
+static GaimPlugin *my_protocol = NULL;
+
+static GaimPluginProtocolInfo prpl_info =
+{
+	OPT_PROTO_NO_PASSWORD,
+	NULL,
+	NULL,
+	NULL,
+	rendezvous_prpl_list_icon,
+	rendezvous_prpl_list_emblems,
+	rendezvous_prpl_status_text,
+	rendezvous_prpl_tooltip_text,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	rendezvous_prpl_login,
+	rendezvous_prpl_close,
+	rendezvous_prpl_send_im,
+	NULL,
+	NULL,
+	NULL,
+	rendezvous_prpl_setaway,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-rendezvous",                                /**< id             */
+	"Rendezvous",                                     /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Rendezvous Protocol Plugin"),
+	                                                  /**  description    */
+	N_("Rendezvous Protocol Plugin"),
+	NULL,                                             /**< author         */
+	GAIM_WEBSITE,                                     /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void init_plugin(GaimPlugin *plugin)
+{
+	GaimAccountUserSplit *split;
+	GaimAccountOption *option;
+
+	/* Try to avoid making this configurable... */
+	split = gaim_account_user_split_new(_("Host Name"), "localhost", '@');
+	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
+
+	option = gaim_account_option_string_new(_("First Name"), "first", "Gaim");
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+											   option);
+
+	option = gaim_account_option_string_new(_("Last Name"), "last", _("User"));
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+											   option);
+
+	option = gaim_account_option_bool_new(_("Share AIM screen name"), "shareaim", TRUE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+											   option);
+
+	my_protocol = plugin;
+}
+
+GAIM_INIT_PLUGIN(rendezvous, init_plugin, info);