/*
	CVSNT mdns helpers - howl
    Copyright (C) 2004-5 Tony Hoyle and March-Hare Software Ltd

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <delayimp.h>
#endif

#include <config.h>
#include "lib/api_system.h"
#include "cvs_string.h"
#include "ServerIO.h"
#include "mdns_howl.h"

sw_result CMdnsHelperHowl::_service_reply(sw_discovery discovery, sw_discovery_oid oid,
				sw_discovery_publish_status	status,	sw_opaque extra)
{
	return ((CMdnsHelperHowl*)extra)->service_reply(discovery,oid,status);
}

sw_result CMdnsHelperHowl::service_reply(sw_discovery discovery, sw_discovery_oid oid,
				sw_discovery_publish_status	status)
{
	static const sw_string status_text[] =
	{
		"Zeroconf data published.",
		"Zeroconf server stopped.",
		"This server name is already in use.  Zeroconf data not published.",
		"Invalid Zeroconf data"
	};

	fprintf(stderr, "%s\n", status_text[status]);
	return SW_OKAY;
}

sw_result CMdnsHelperHowl::_browser_reply(sw_discovery discovery, sw_discovery_oid oid,
			  sw_discovery_browse_status status, sw_uint32 interface_index,
			  sw_const_string name,	sw_const_string	type,
			  sw_const_string domain, sw_opaque_t extra)
{
	return ((CMdnsHelperHowl*)extra)->browser_reply(discovery,oid,status,interface_index,
											name,type,domain);
}

sw_result CMdnsHelperHowl::browser_reply(sw_discovery discovery, sw_discovery_oid oid,
			  sw_discovery_browse_status status, sw_uint32 interface_index,
			  sw_const_string name,	sw_const_string	type,
			  sw_const_string domain)
{
	sw_discovery_resolve_id rid;

	switch (status)
	{
		case SW_DISCOVERY_BROWSE_INVALID:
		case SW_DISCOVERY_BROWSE_ADD_DOMAIN:
		case SW_DISCOVERY_BROWSE_ADD_DEFAULT_DOMAIN:
		case SW_DISCOVERY_BROWSE_REMOVE_DOMAIN:
		case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
		case SW_DISCOVERY_BROWSE_RESOLVED:
			break;
		case SW_DISCOVERY_BROWSE_ADD_SERVICE:
		{
			if (sw_discovery_resolve(discovery, interface_index, name, type, domain, _resolver_reply, this, &rid) != SW_OKAY)
			{
				fprintf(stderr, "resolve failed\n");
			}
		}
		break;

	}

	return SW_OKAY;
}

sw_result CMdnsHelperHowl::_resolver_reply(sw_discovery discovery, sw_discovery_oid oid,
				sw_uint32 interface_index, sw_const_string name,
				sw_const_string type, sw_const_string domain,
				sw_ipv4_address address, sw_port port,
				sw_octets text_record, sw_uint32 text_record_len,
				sw_opaque_t extra)
{
	return ((CMdnsHelperHowl*)extra)->resolver_reply(discovery,oid,interface_index,name,
									type,domain,address,port,text_record,text_record_len);
}

sw_result CMdnsHelperHowl::resolver_reply(sw_discovery discovery, sw_discovery_oid oid,
				sw_uint32 interface_index, sw_const_string name,
				sw_const_string type, sw_const_string domain,
				sw_ipv4_address address, sw_port port,
				sw_octets text_record, sw_uint32 text_record_len)
{
	sw_text_record_iterator	it;
	char key[SW_TEXT_RECORD_MAX_LEN];
	sw_uint8 oval[SW_TEXT_RECORD_MAX_LEN];
	sw_uint32 oval_len;
	sw_result err = SW_OKAY;
	cvs::string txt,tmp;

	sw_discovery_cancel(discovery, oid);
	
	if ((text_record_len > 0) && (text_record) && (*text_record != '\0'))
	{
		err = sw_text_record_iterator_init(&it, text_record, text_record_len);

		while (sw_text_record_iterator_next(it, key, oval, &oval_len) == SW_OKAY)
		{
			cvs::sprintf(tmp,80,"%s=%s\n", key, oval);
			txt+=tmp;
		}

		err = sw_text_record_iterator_fina(it);
	}

	cvs::string fullname;

	char target[32];

	sw_ipv4_address_name(address,target,sizeof(target));


	cvs::sprintf(fullname,80,"%s.%s%s",name,type,domain);
	if(fullname.length()>0 && fullname[fullname.length()-1]=='.')
		fullname.resize(fullname.size()-1);

	if(m_callbacks->srv_fn)
		m_callbacks->srv_fn(fullname.c_str(),port,target,m_userdata);
	if(m_callbacks->txt_fn)
		m_callbacks->txt_fn(fullname.c_str(),txt.c_str(),m_userdata);
	if(m_callbacks->ipv4_fn)
		m_callbacks->ipv4_fn(target,(unsigned char*)&address.m_addr,m_userdata);

	return err;
}


CMdnsHelperHowl::CMdnsHelperHowl()
{
	m_discovery = NULL;
	m_salt = NULL;
}

CMdnsHelperHowl::~CMdnsHelperHowl()
{
	close();
}

int CMdnsHelperHowl::open()
{
#ifdef _WIN32
#if (DELAYLOAD_VERSION >= 0x0200) || (_DELAY_IMP_VER > 1)
	__try
	{
		// __HrLoadAllImportsForDll() is defined in DelayImp.h, but not the
		// one that is included in the PlatformSDK
		if (FAILED(__HrLoadAllImportsForDll("howl.dll")))
		{
			CServerIo::error("Couldn't find howl.dll - failing mdns initialisation\n");
			return -1;
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		CServerIo::error("Couldn't find howl.dll - failing mdns initialisation\n");
		return -1;
	}
#else
#error Visual Studio 2003 required, and the Platform SDK include must be higher in the search order than the VC++ Include Directory $(VCInstallDir).
#endif
#endif
	if (sw_discovery_init(&m_discovery) != SW_OKAY)
	{
		fprintf(stderr, "sw_discovery_init() failed\n");
		return -1;
	}
	return 0;
}

int CMdnsHelperHowl::publish(const char *instance, const char *service, const char *location, int port, const char *text)
{
	sw_result						result;
	sw_discovery_publish_id	id;
	sw_text_record txt;

	if(text && !*text)
		text=NULL;

	if(text)
	{
		sw_text_record_init(&txt);
		sw_text_record_add_string(txt, text);
	}

	char tmp[256];
	strncpy(tmp,service,sizeof(tmp));
	char *p=tmp+strlen(tmp)-1;
	if(strlen(tmp)>0 && *p=='.') *(p--)='\0';
	if(strlen(tmp)>6 && !strcmp(p-5,".local")) *(p-5)='\0';

	service = tmp;

	if ((result = sw_discovery_publish(m_discovery, 0, instance, service, NULL, location, port, text?sw_text_record_bytes(txt):NULL, text?sw_text_record_len(txt):0, _service_reply, this, &id)) != SW_OKAY)
	{
		fprintf(stderr, "publish failed: %d\n", result);
		return -1;
	}

	sw_discovery_salt(m_discovery,&m_salt);

	if(text && *text)
		sw_text_record_fina(txt);

	return 0;
}

int CMdnsHelperHowl::step()
{
	sw_ulong msecs = 500;
	sw_salt_step(m_salt,&msecs);
	return 0;
}

int CMdnsHelperHowl::browse(const char *service, MdnsBrowseCallback *callbacks, void *userdata)
{
	sw_discovery_oid oid;
	m_userdata = userdata;
	m_callbacks = callbacks;

	if(sw_discovery_browse(m_discovery, 0, service, NULL, _browser_reply, this, &oid))
	{
		CServerIo::trace(3,"sw_discovery_browse() failed.\n");
		return -1;
	}

	sw_discovery_salt(m_discovery,&m_salt);

	time_t t;
	time(&m_lastreply);
	do
	{
		step();
		time(&t);
	} while(t<m_lastreply+2);
	return 0;
}

int CMdnsHelperHowl::close()
{
	if(m_discovery)
		sw_discovery_fina(m_discovery);
	m_salt = NULL;
	m_discovery = NULL;
	return 0;
}

extern "C" CMdnsHelperBase *MdnsHelperHowl_Alloc()
{
	return new CMdnsHelperHowl();
}


syntax highlighted by Code2HTML, v. 0.9.1