/*
 * gaim
 * 
 * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com>
 *
 * 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 <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/timeb.h>
#include <sys/stat.h>
#include <time.h>
#include <glib.h>
#include "debug.h"
#include "libc_internal.h"
#if GLIB_CHECK_VERSION(2,6,0)
# include <glib/gstdio.h>
#else
#define g_remove remove
#define g_rename rename
#define g_stat stat
#endif

static char errbuf[1024];

/* helpers */
static int wgaim_is_socket( int fd ) {
	int optval;
	unsigned int optlen = sizeof(int);

	if( (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&optval, &optlen)) == SOCKET_ERROR ) {
		int error = WSAGetLastError();
		if( error == WSAENOTSOCK )
			return FALSE;
		else {
                        gaim_debug(GAIM_DEBUG_WARNING, "wgaim", "wgaim_is_socket: getsockopt returned error: %d\n", error);
			return FALSE;
		}
	}
	return TRUE;
}

/* socket.h */
int wgaim_socket (int namespace, int style, int protocol) {
	int ret;

	ret = socket( namespace, style, protocol );

	if( ret == INVALID_SOCKET ) {
		errno = WSAGetLastError();
		return -1;
	}
	return ret;
}

int wgaim_connect(int socket, struct sockaddr *addr, u_long length) {
	int ret;

	ret = connect( socket, addr, length );
	
	if( ret == SOCKET_ERROR ) {
		errno = WSAGetLastError();
		if( errno == WSAEWOULDBLOCK )
			errno = WSAEINPROGRESS;
		return -1;
	}
	return 0;
}

int wgaim_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlenptr) {
	if(getsockopt(socket, level, optname, optval, optlenptr) == SOCKET_ERROR ) {
		errno = WSAGetLastError();
		return -1;
	}
	return 0;
}

int wgaim_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen) {
	if(setsockopt(socket, level, optname, optval, optlen) == SOCKET_ERROR ) {
		errno = WSAGetLastError();
		return -1;
	}
	return 0;
}

int wgaim_getsockname(int socket, struct sockaddr *addr, socklen_t *lenptr) {
        if(getsockname(socket, addr, lenptr) == SOCKET_ERROR) {
                errno = WSAGetLastError();
                return -1;
        }
        return 0;
}

int wgaim_bind(int socket, struct sockaddr *addr, socklen_t length) {
        if(bind(socket, addr, length) == SOCKET_ERROR) {
                errno = WSAGetLastError();
                return -1;
        }
        return 0;
}

int wgaim_listen(int socket, unsigned int n) {
        if(listen(socket, n) == SOCKET_ERROR) {
                errno = WSAGetLastError();
                return -1;
        }
        return 0;
}

int wgaim_sendto(int socket, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) {
	int ret;
	if ((ret = sendto(socket, buf, len, flags, to, tolen)
			) == SOCKET_ERROR) {
		errno = WSAGetLastError();
		return -1;
	}
	return ret;
}

/* fcntl.h */
/* This is not a full implementation of fcntl. Update as needed.. */
int wgaim_fcntl(int socket, int command, int val) {
	switch( command ) {
	case F_SETFL:
	{
		int ret=0;

		switch( val ) {
		case O_NONBLOCK:
		{
			u_long imode=1;
			ret = ioctlsocket(socket, FIONBIO, &imode);
			break;
		}
		case 0:
	        {
			u_long imode=0;
			ret = ioctlsocket(socket, FIONBIO, &imode);
			break;
		}
		default:
			errno = EINVAL;
			return -1;
		}/*end switch*/
		if( ret == SOCKET_ERROR ) {
			errno = WSAGetLastError();
			return -1;
		}
		return 0;
	}
	default:
                gaim_debug(GAIM_DEBUG_WARNING, "wgaim", "wgaim_fcntl: Unsupported command\n");
		return -1;
	}/*end switch*/
}

/* sys/ioctl.h */
int wgaim_ioctl(int fd, int command, void* val) {
	switch( command ) {
	case FIONBIO:
	{
		if (ioctlsocket(fd, FIONBIO, (unsigned long *)val) == SOCKET_ERROR) {
			errno = WSAGetLastError();
			return -1;
		}
		return 0;
	}
	case SIOCGIFCONF:
	{
		INTERFACE_INFO InterfaceList[20];
		unsigned long nBytesReturned;
		if (WSAIoctl(fd, SIO_GET_INTERFACE_LIST,
				0, 0, &InterfaceList,
				sizeof(InterfaceList), &nBytesReturned,
				0, 0) == SOCKET_ERROR) {
			errno = WSAGetLastError();
			return -1;
		} else {
			int i;
			struct ifconf *ifc = val;
			char *tmp = ifc->ifc_buf;
			int nNumInterfaces =
				nBytesReturned / sizeof(INTERFACE_INFO);
			for (i = 0; i < nNumInterfaces; i++) {
				INTERFACE_INFO ii = InterfaceList[i];
				struct ifreq *ifr = (struct ifreq *) tmp;
				struct sockaddr_in *sa = (struct sockaddr_in *) &ifr->ifr_addr;

				sa->sin_family = ii.iiAddress.AddressIn.sin_family;
				sa->sin_port = ii.iiAddress.AddressIn.sin_port;
				sa->sin_addr.s_addr = ii.iiAddress.AddressIn.sin_addr.s_addr;
				tmp += sizeof(struct ifreq);

				/* Make sure that we can fit in the original buffer */
				if (tmp >= (ifc->ifc_buf + ifc->ifc_len + sizeof(struct ifreq))) {
					break;
				}
			}
			/* Replace the length with the actually used length */
			ifc->ifc_len = ifc->ifc_len - (ifc->ifc_buf - tmp);
			return 0;
		}
	}
	default:
		errno = EINVAL;
		return -1;
	}/*end switch*/
}

/* arpa/inet.h */
int wgaim_inet_aton(const char *name, struct in_addr *addr) {
	if((addr->s_addr = inet_addr(name)) == INADDR_NONE)
		return 0;
	else
		return 1;
}

/* netdb.h */
struct hostent* wgaim_gethostbyname(const char *name) {
	struct hostent *hp;

	if((hp = gethostbyname(name)) == NULL) {
		errno = WSAGetLastError();
		return NULL;
	}
	return hp;
}

/* string.h */
char* wgaim_strerror( int errornum ) {
	if( errornum > WSABASEERR ) {
		sprintf( errbuf, "Windows socket error #%d", errornum );
		return errbuf;
	}
	else
		return strerror( errornum );
}

/* unistd.h */

/*
 *  We need to figure out whether fd is a file or socket handle.
 */
int wgaim_read(int fd, void *buf, unsigned int size) {
	int ret;

	if(wgaim_is_socket(fd)) {
		if((ret = recv(fd, buf, size, 0)) == SOCKET_ERROR) {
			errno = WSAGetLastError();
			if(errno == WSAEWOULDBLOCK)
				errno = EAGAIN;
			return -1;
		}
#if 0
		else if( ret == 0 ) {
			/* connection has been gracefully closed */
			errno = WSAENOTCONN;
			return -1;
		}
#endif
		else {
			/* success reading socket */
			return ret;
		}
	} else {
		/* fd is not a socket handle.. pass it off to read */
		return read(fd, buf, size);
	}
}

int wgaim_write(int fd, const void *buf, unsigned int size) {
	int ret;

	if(wgaim_is_socket(fd)) {
		if((ret = send(fd, buf, size, 0)) == SOCKET_ERROR) {
			errno = WSAGetLastError();
			if(errno == WSAEWOULDBLOCK)
				errno = EAGAIN;
			return -1;
		} else {
			/* success */
			return ret;
		}
	} else
		return write(fd, buf, size);
}

int wgaim_recv(int fd, void *buf, size_t len, int flags) {
	int ret;

	if((ret = recv(fd, buf, len, flags)) == SOCKET_ERROR) {
			errno = WSAGetLastError();
			if(errno == WSAEWOULDBLOCK)
				errno = EAGAIN;
			return -1;
	} else {
		return ret;
	}
}

int wgaim_close(int fd) {
	int ret;

	if( wgaim_is_socket(fd) ) {
		if( (ret = closesocket(fd)) == SOCKET_ERROR ) {
			errno = WSAGetLastError();
			return -1;
		}
		else
			return 0;
	}
	else
		return close(fd);
}

int wgaim_gethostname(char *name, size_t size) {
        if(gethostname(name, size) == SOCKET_ERROR) {
                errno = WSAGetLastError();
			return -1;
        }
        return 0;
}

/* sys/time.h */

int wgaim_gettimeofday(struct timeval *p, struct timezone *z) {
	int res = 0;
	struct _timeb timebuffer;

	if (z != 0) {
		_tzset();
		z->tz_minuteswest = _timezone/60;
		z->tz_dsttime = _daylight;
	}
	
	if (p != 0) {
		_ftime(&timebuffer);
	   	p->tv_sec = timebuffer.time;			/* seconds since 1-1-1970 */
		p->tv_usec = timebuffer.millitm*1000; 	/* microseconds */
	}

	return res;
}

/* stdio.h */

int wgaim_rename (const char *oldname, const char *newname) {
	struct stat oldstat, newstat;

	if(g_stat(oldname, &oldstat) == 0) {
		/* newname exists */
		if(g_stat(newname, &newstat) == 0) {
			/* oldname is a dir */
			if(_S_ISDIR(oldstat.st_mode)) {
				if(!_S_ISDIR(newstat.st_mode)) {
					return g_rename(oldname, newname);
				}
				/* newname is a dir */
				else {
					/* This is not quite right.. If newname is empty and
					   is not a sub dir of oldname, newname should be
					   deleted and oldname should be renamed.
					*/
					gaim_debug(GAIM_DEBUG_WARNING, "wgaim", "wgaim_rename does not behave here as it should\n");
					return g_rename(oldname, newname);
				}
			}
			/* oldname is not a dir */
			else {
				/* newname is a dir */
				if(_S_ISDIR(newstat.st_mode)) {
					errno = EISDIR;
					return -1;
				}
				/* newname is not a dir */
				else {
					g_remove(newname);
					return g_rename(oldname, newname);
				}
			}
		}
		/* newname doesn't exist */
		else
			return g_rename(oldname, newname);
	}
	else {
		/* oldname doesn't exist */
		errno = ENOENT;
		return -1;
	}

}

/* time.h */

struct tm * wgaim_localtime_r (const time_t *time, struct tm *resultp) {
	struct tm* tmptm;

	if(!time)
		return NULL;
	tmptm = localtime(time);
	if(resultp && tmptm)
		return memcpy(resultp, tmptm, sizeof(struct tm));
	else
		return NULL;
}

/*
 * Used by gaim_utf8_strftime() by way of gaim_internal_strftime()
 * in src/util.c
 *
 * Code derived from PostgreSQL src/timezone/pgtz.c:
 * http://developer.postgresql.org/cvsweb.cgi/pgsql/src/timezone/pgtz.c
 */

/*
PostgreSQL Database Management System
(formerly known as Postgres, then as Postgres95)

Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group

Portions Copyright (c) 1994, The Regents of the University of California

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose, without fee, and without a written agreement
is hereby granted, provided that the above copyright notice and this
paragraph and the following two paragraphs appear in all copies.

IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

*/
static struct
{
	char *wstd;		/* Windows name of standard timezone */
	char *wdst;		/* Windows name of daylight timezone */
	char *ustd;		/* Unix name of standard timezone */
	char *udst;		/* Unix name of daylight timezone */
} win32_tzmap[] =
{
	{
		"", "",
		"", "",
	},
	/*
	 * This list was built from the contents of the registry at
	 * "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
	 * on Windows XP Professional SP1
	 */
	{
		"Afghanistan Standard Time", "Afghanistan Daylight Time",
		"AFT", "AFT"
	},
	{
		"Alaskan Standard Time", "Alaskan Daylight Time",
		"AKST", "AKDT"
	},
	{
		"Arab Standard Time", "Arab Daylight Time",
		"AST", "AST"
	},
	{
		"Arabian Standard Time", "Arabian Daylight Time",
		"GST", "GST"
	},
	{
		"Arabic Standard Time", "Arabic Daylight Time",
		"AST", "ADT"
	},
	{
		"Atlantic Standard Time", "Atlantic Daylight Time",
		"AST", "ADT"
	},
	{
		"AUS Central Standard Time", "AUS Central Daylight Time",
		"CST", "CST"
	},
	{
		"AUS Eastern Standard Time", "AUS Eastern Daylight Time",
		"EST", "EST"
	},
	{
		"Azores Standard Time", "Azores Daylight Time",
		"AZOT", "AZOST"
	},
	{
		"Canada Central Standard Time", "Canada Central Daylight Time",
		"CST", "MDT"
	},
	{
		"Cape Verde Standard Time", "Cape Verde Daylight Time",
		"CVT", "CVST"
	},
	{
		"Caucasus Standard Time", "Caucasus Daylight Time",
		"AZT", "AZST"
	},
	{
		"Cen. Australia Standard Time", "Cen. Australia Daylight Time",
		"CST", "CST"
	},
	{
		"Central America Standard Time", "Central America Daylight Time",
		"CST", "CDT"
	},
	{
		"Central Asia Standard Time", "Central Asia Daylight Time",
		"BDT", "BDT"
	},
	{
		"Central Europe Standard Time", "Central Europe Daylight Time",
		"CET", "CEST"
	},
	{
		"Central European Standard Time", "Central European Daylight Time",
		"CET", "CEST"
	},
	{
		"Central Pacific Standard Time", "Central Pacific Daylight Time",
		"NCT", "NCST"
	},
	{
		"Central Standard Time", "Central Daylight Time",
		"CST", "CDT"
	},
	{
		"China Standard Time", "China Daylight Time",
		"HKT", "HKST"
	},
	{
		"Dateline Standard Time", "Dateline Daylight Time",
		"GMT+12", "GMT+12"
	},
	{
		"E. Africa Standard Time", "E. Africa Daylight Time",
		"EAT", "EAT"
	},
	{
		"E. Australia Standard Time", "E. Australia Daylight Time",
		"EST", "EST"
	},
	{
		"E. Europe Standard Time", "E. Europe Daylight Time",
		"EET", "EEST"
	},
	{
		"E. South America Standard Time", "E. South America Daylight Time",
		"BRT", "BRST"
	},
	{
		"Eastern Standard Time", "Eastern Daylight Time",
		"EST", "EDT"
	},
	{
		"Egypt Standard Time", "Egypt Daylight Time",
		"EET", "EEST"
	},
	{
		"Ekaterinburg Standard Time", "Ekaterinburg Daylight Time",
		"YEKT", "YEKST"
	},
	{
		"Fiji Standard Time", "Fiji Daylight Time",
		"FJT", "FJST"
	},
	{
		"FLE Standard Time", "FLE Daylight Time",
		"EET", "EEST"
	},
	{
		"GMT Standard Time", "GMT Daylight Time",
		"GMT", "IST"
	},
	{
		"Greenland Standard Time", "Greenland Daylight Time",
		"WGT", "WGST"
	},
	{
		"Greenwich Standard Time", "Greenwich Daylight Time",
		"WET", "WEST"
	},
	{
		"GTB Standard Time", "GTB Daylight Time",
		"EET", "EEST"
	},
	{
		"Hawaiian Standard Time", "Hawaiian Daylight Time",
		"HST", "HPT"
	},
	{
		"India Standard Time", "India Daylight Time",
		"IST", "IST"
	},
	{
		"Iran Standard Time", "Iran Daylight Time",
		"IRST", "IRDT"
	},
	{
		"Jerusalem Standard Time", "Jerusalem Daylight Time",
		"IST", "IDT"
	},
	{
		"Korea Standard Time", "Korea Daylight Time",
		"KST", "KDT"
	},
	{
		"Mexico Standard Time", "Mexico Daylight Time",
		"CST", "CDT"
	},
	{
		"Mexico Standard Time", "Mexico Daylight Time",
		"BOT", "BOST"
	},
	{
		"Mid-Atlantic Standard Time", "Mid-Atlantic Daylight Time",
		"GST", "GST"
	},
	{
		"Mountain Standard Time", "Mountain Daylight Time",
		"MST", "MDT"
	},
	{
		"Myanmar Standard Time", "Myanmar Daylight Time",
		"MMT", "MMT"
	},
	{
		"N. Central Asia Standard Time", "N. Central Asia Daylight Time",
		"ALMT", "ALMST"
	},
	{
		"Nepal Standard Time", "Nepal Daylight Time",
		"NPT", "NPT"
	},
	{
		"New Zealand Standard Time", "New Zealand Daylight Time",
		"NZST", "NZDT"
	},
	{
		"Newfoundland Standard Time", "Newfoundland Daylight Time",
		"NST", "NDT"
	},
	{
		"North Asia East Standard Time", "North Asia East Daylight Time",
		"IRKT", "IRKST"
	},
	{
		"North Asia Standard Time", "North Asia Daylight Time",
		"KRAT", "KRAST"
	},
	{
		"Pacific SA Standard Time", "Pacific SA Daylight Time",
		"CLT", "CLST"
	},
	{
		"Pacific Standard Time", "Pacific Daylight Time",
		"PST", "PDT"
	},
	{
		"Romance Standard Time", "Romance Daylight Time",
		"CET", "CEST"
	},
	{
		"Russian Standard Time", "Russian Daylight Time",
		"MSK", "MSD"
	},
	{
		"SA Eastern Standard Time", "SA Eastern Daylight Time",
		"ART", "ARST"
	},
	{
		"SA Pacific Standard Time", "SA Pacific Daylight Time",
		"COT", "COST"
	},
	{
		"SA Western Standard Time", "SA Western Daylight Time",
		"VET", "VET"
	},
	{
		"Samoa Standard Time", "Samoa Daylight Time",
		"SST", "NDT"
	},
	{
		"SE Asia Standard Time", "SE Asia Daylight Time",
		"ICT", "ICT"
	},
	{
		"Malay Peninsula Standard Time", "Malay Peninsula Daylight Time",
		"MYT", "MALST"
	},
	{
		"South Africa Standard Time", "South Africa Daylight Time",
		"CAT", "CAT"
	},
	{
		"Sri Lanka Standard Time", "Sri Lanka Daylight Time",
		"LKT", "IST"
	},
	{
		"Taipei Standard Time", "Taipei Daylight Time",
		"CST", "CDT"
	},
	{
		"Tasmania Standard Time", "Tasmania Daylight Time",
		"EST", "EST"
	},
	{
		"Tokyo Standard Time", "Tokyo Daylight Time",
		"JST", "JDT"
	},
	{
		"Tonga Standard Time", "Tonga Daylight Time",
		"TOT", "TOST"
	},
	{
		"US Eastern Standard Time", "US Eastern Daylight Time",
		"EST", "EDT"
	},
	{
		"US Mountain Standard Time", "US Mountain Daylight Time",
		"MST", "MDT"
	},
	{
		"Vladivostok Standard Time", "Vladivostok Daylight Time",
		"VLAT", "VLAST"
	},
	{
		"W. Australia Standard Time", "W. Australia Daylight Time",
		"WST", "WST"
	},

	/* Not mapped in PostgreSQL.
	 *
	 * I mapped this based on the following information... -- rlaager
	 * $ cd /usr/share/zoneinfo/Africa
	 * $ for i in * ; do echo `TZ=Africa/$i date +"%z %Z"` $i ; done | grep +0100
	 * +0100 CET Algiers
	 * +0100 WAT Bangui
	 * +0100 WAT Brazzaville
	 * +0100 CET Ceuta
	 * +0100 WAT Douala
	 * +0100 WAT Kinshasa
	 * +0100 WAT Lagos
	 * +0100 WAT Libreville
	 * +0100 WAT Luanda
	 * +0100 WAT Malabo
	 * +0100 WAT Ndjamena
	 * +0100 WAT Niamey
	 * +0100 WAT Porto-Novo
	 * +0100 CET Tunis
	 **/
	{
		"W. Central Africa Standard Time", "W. Central Africa Daylight Time",
		"WAT", "WAT"
	},

	{
		"W. Europe Standard Time", "W. Europe Daylight Time",
		"CET", "CEST"
	},
	{
		"West Asia Standard Time", "West Asia Daylight Time",
		"PKT", "PKST"
	},
	{
		"West Pacific Standard Time", "West Pacific Daylight Time",
		"ChST", "ChST"
	},
	{
		"Yakutsk Standard Time", "Yakutsk Daylight Time",
		"YAKT", "YAKST"
	},
	{
		NULL, NULL,
		NULL, NULL
	}
};

const char *
wgaim_get_timezone_abbreviation(const struct tm *tm)
{
	int i;
	char tzname[128];
	char localtzname[256];
	HKEY rootKey;
	int idx;

	if (!tm)
	{
		gaim_debug_warning("wgaim", "could not determine current date/time: localtime failed\n");
		return "";
	}

	if (strftime(tzname, sizeof(tzname) - 1, "%Z", tm) == 0)
	{
		gaim_debug_error("wgaim", "timezone name is too long for the buffer\n");
		return "";
	}

	for (i = 0; win32_tzmap[i].wstd != NULL; i++)
	{
		if (strcmp(tzname, win32_tzmap[i].wstd) == 0)
		{
#if 0
			gaim_debug_info("wgaim", "TZ \"%s\" matches Windows timezone \"%s\"\n",
			                win32_tzmap[i].ustd, tzname);
#endif
			/* Cache the Result */
			if (i > 0) {
				if (win32_tzmap[0].wstd[0] != '\0')
					g_free(win32_tzmap[0].wstd);
				win32_tzmap[0].wstd = g_strdup(tzname);
				win32_tzmap[0].ustd = win32_tzmap[i].ustd;
			}

			return win32_tzmap[i].ustd;
		}
		if (strcmp(tzname, win32_tzmap[i].wdst) == 0)
		{
#if 0
			gaim_debug_info("wgaim", "TZ \"%s\" matches Windows timezone \"%s\"\n",
			                win32_tzmap[i].udst, tzname);
#endif
			/* Cache the Result */
			if (i > 0) {
				if (win32_tzmap[0].wdst[0] != '\0')
					g_free(win32_tzmap[0].wdst);
				win32_tzmap[0].wdst = g_strdup(tzname);
				win32_tzmap[0].udst = win32_tzmap[i].udst;
			}

			return win32_tzmap[i].udst;
		}
	}

	/*
	 * Localized Windows versions return localized names for the timezone.
	 * Scan the registry to find the English name, and then try matching
	 * against our table again.
	 */
	memset(localtzname, 0, sizeof(localtzname));
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
			   "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
					 0,
					 KEY_READ,
					 &rootKey) != ERROR_SUCCESS)
	{
		gaim_debug_warning("wgaim", "could not open registry key to identify Windows timezone: %i\n", (int) GetLastError());
		return "";
	}

	for (idx = 0;; idx++)
	{
		char keyname[256];
		char zonename[256];
		DWORD namesize;
		FILETIME lastwrite;
		HKEY key;
		LONG r;

		memset(keyname, 0, sizeof(keyname));
		namesize = sizeof(keyname);
		if ((r = RegEnumKeyEx(rootKey,
							  idx,
							  keyname,
							  &namesize,
							  NULL,
							  NULL,
							  NULL,
							  &lastwrite)) != ERROR_SUCCESS)
		{
			if (r == ERROR_NO_MORE_ITEMS)
				break;
			gaim_debug_warning("wgaim", "could not enumerate registry subkeys to identify Windows timezone: %i\n", (int) r);
			break;
		}

		if ((r = RegOpenKeyEx(rootKey, keyname, 0, KEY_READ, &key)) != ERROR_SUCCESS)
		{
			gaim_debug_warning("wgaim", "could not open registry subkey to identify Windows timezone: %i\n", (int) r);
			break;
		}

		memset(zonename, 0, sizeof(zonename));
		namesize = sizeof(zonename);
		if ((r = RegQueryValueEx(key, "Std", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
		{
			gaim_debug_warning("wgaim", "could not query value for 'std' to identify Windows timezone: %i\n", (int) r);
			RegCloseKey(key);
			break;
		}
		if (strcmp(tzname, zonename) == 0)
		{
			/* Matched zone */
			strcpy(localtzname, keyname);
			RegCloseKey(key);
			break;
		}
		memset(zonename, 0, sizeof(zonename));
		namesize = sizeof(zonename);
		if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
		{
			gaim_debug_warning("wgaim", "could not query value for 'dlt' to identify Windows timezone: %i\n", (int) r);
			RegCloseKey(key);
			break;
		}
		if (strcmp(tzname, zonename) == 0)
		{
			/* Matched DST zone */
			strcpy(localtzname, keyname);
			RegCloseKey(key);
			break;
		}

		RegCloseKey(key);
	}

	RegCloseKey(rootKey);

	if (localtzname[0])
	{
		/* Found a localized name, so scan for that one too */
		for (i = 0; win32_tzmap[i].wstd != NULL; i++)
		{
			if (strcmp(localtzname, win32_tzmap[i].wstd) == 0)
			{
#if 0
				gaim_debug_info("wgaim", "TZ \"%s\" matches localized Windows timezone \"%s\" (\"%s\")\n",
				                win32_tzmap[i].ustd, tzname, localtzname);
#endif
				/* Cache the Result */
				if (win32_tzmap[0].wstd[0] != '\0')
					g_free(win32_tzmap[0].wstd);
				win32_tzmap[0].wstd = g_strdup(tzname);
				win32_tzmap[0].ustd = win32_tzmap[i].ustd;

				return win32_tzmap[i].ustd;
			}
			if (strcmp(localtzname, win32_tzmap[i].wdst) == 0)
			{
#if 0
				gaim_debug_info("wgaim", "TZ \"%s\" matches localized Windows timezone \"%s\" (\"%s\")\n",
				                win32_tzmap[i].udst, tzname, localtzname);
#endif
				/* Cache the Result */
				if (win32_tzmap[0].wdst[0] != '\0')
					g_free(win32_tzmap[0].wdst);

				win32_tzmap[0].wdst = g_strdup(tzname);
				win32_tzmap[0].udst = win32_tzmap[i].udst;

				return win32_tzmap[i].udst;
			}
		}
	}

	gaim_debug_warning("wgaim", "could not find a match for Windows timezone \"%s\"\n", tzname);
	return "";
}

