changeset 23373:53db7393829e

merge of '3a93d6a9f17c32fb5379bb3f2294392fac142b92' and '6258290c16427985f40258a4ca1be4e11feafc33'
author Mark Doliner <mark@kingant.net>
date Mon, 16 Jun 2008 01:03:23 +0000
parents 2a5bc3e6da7b (current diff) 0efa36fe7d7c (diff)
children 85fc34efe733 63cfd4ea15ab
files
diffstat 13 files changed, 178 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/blist.h	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/blist.h	Mon Jun 16 01:03:23 2008 +0000
@@ -31,13 +31,20 @@
 
 #include <glib.h>
 
+/** @copydoc _PurpleBuddyList */
 typedef struct _PurpleBuddyList PurpleBuddyList;
+/** @copydoc _PurpleBlistUiOps */
 typedef struct _PurpleBlistUiOps PurpleBlistUiOps;
+/** @copydoc _PurpleBlistNode */
 typedef struct _PurpleBlistNode PurpleBlistNode;
 
+/** @copydoc _PurpleChat */
 typedef struct _PurpleChat PurpleChat;
+/** @copydoc _PurpleGroup */
 typedef struct _PurpleGroup PurpleGroup;
+/** @copydoc _PurpleContact */
 typedef struct _PurpleContact PurpleContact;
+/** @copydoc _PurpleBuddy */
 typedef struct _PurpleBuddy PurpleBuddy;
 
 /**************************************************************************/
--- a/libpurple/cmds.h	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/cmds.h	Mon Jun 16 01:03:23 2008 +0000
@@ -30,6 +30,7 @@
 /**************************************************************************/
 /*@{*/
 
+/** The possible results of running a command with purple_cmd_do_command(). */
 typedef enum _PurpleCmdStatus {
 	PURPLE_CMD_STATUS_OK,
 	PURPLE_CMD_STATUS_FAILED,
@@ -39,16 +40,31 @@
 	PURPLE_CMD_STATUS_WRONG_TYPE,
 } PurpleCmdStatus;
 
+/** Commands registered with the core return one of these values when run.
+ *  Normally, a command will want to return one of the first two; in some
+ *  unusual cases, you might want to have several functions called for a
+ *  particular command; in this case, they should return
+ *  #PURPLE_CMD_RET_CONTINUE to cause the core to fall through to other
+ *  commands with the same name.
+ */
 typedef enum _PurpleCmdRet {
-	PURPLE_CMD_RET_OK,       /**< Everything's okay. Don't look for another command to call. */
+	PURPLE_CMD_RET_OK,       /**< Everything's okay; Don't look for another command to call. */
 	PURPLE_CMD_RET_FAILED,   /**< The command failed, but stop looking.*/
 	PURPLE_CMD_RET_CONTINUE, /**< Continue, looking for other commands with the same name to call. */
 } PurpleCmdRet;
 
 #define PURPLE_CMD_FUNC(func) ((PurpleCmdFunc)func)
 
+/** A function implementing a command, as passed to purple_cmd_register().
+ *
+ *  @todo document the arguments to these functions.
+ * */
 typedef PurpleCmdRet (*PurpleCmdFunc)(PurpleConversation *, const gchar *cmd,
                                   gchar **args, gchar **error, void *data);
+/** A unique integer representing a command registered with
+ *  purple_cmd_register(), which can subsequently be passed to
+ *  purple_cmd_unregister() to unregister that command.
+ */
 typedef guint PurpleCmdId;
 
 typedef enum _PurpleCmdPriority {
--- a/libpurple/connection.h	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/connection.h	Mon Jun 16 01:03:23 2008 +0000
@@ -27,6 +27,7 @@
 #ifndef _PURPLE_CONNECTION_H_
 #define _PURPLE_CONNECTION_H_
 
+/** @copydoc _PurpleConnection */
 typedef struct _PurpleConnection PurpleConnection;
 
 /**
@@ -223,6 +224,8 @@
 	void (*_purple_reserved3)(void);
 } PurpleConnectionUiOps;
 
+
+/* Represents an active connection on an account. */
 struct _PurpleConnection
 {
 	PurplePlugin *prpl;            /**< The protocol plugin.               */
--- a/libpurple/conversation.h	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/conversation.h	Mon Jun 16 01:03:23 2008 +0000
@@ -32,11 +32,17 @@
 /**************************************************************************/
 
 
+/** @copydoc _PurpleConversationUiOps */
 typedef struct _PurpleConversationUiOps PurpleConversationUiOps;
+/** @copydoc _PurpleConversation */
 typedef struct _PurpleConversation      PurpleConversation;
+/** @copydoc _PurpleConvIm */
 typedef struct _PurpleConvIm            PurpleConvIm;
+/** @copydoc _PurpleConvChat */
 typedef struct _PurpleConvChat          PurpleConvChat;
+/** @copydoc _PurpleConvChatBuddy */
 typedef struct _PurpleConvChatBuddy     PurpleConvChatBuddy;
+/** @copydoc _PurpleConvMessage */
 typedef struct _PurpleConvMessage       PurpleConvMessage;
 
 /**
--- a/libpurple/core.h	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/core.h	Mon Jun 16 01:03:23 2008 +0000
@@ -1,4 +1,5 @@
 /**
+ * @file core.h Startup and shutdown of libpurple
  * @defgroup core libpurple
  * @see @ref core-signals
  */
@@ -28,12 +29,36 @@
 
 typedef struct PurpleCore PurpleCore;
 
+/** Callbacks that fire at different points of the initialization and teardown
+ *  of libpurple, along with a hook to return descriptive information about the
+ *  UI.
+ */
 typedef struct
 {
+	/** Called just after the preferences subsystem is initialized; the UI
+	 *  could use this callback to add some preferences it needs to be in
+	 *  place when other subsystems are initialized.
+	 */
 	void (*ui_prefs_init)(void);
-	void (*debug_ui_init)(void); /* Unfortunate necessity. */
+	/** Called just after the debug subsystem is initialized, but before
+	 *  just about every other component's initialization.  The UI should
+	 *  use this hook to call purple_debug_set_ui_ops() so that debugging
+	 *  information for other components can be logged during their
+	 *  initialization.
+	 */
+	void (*debug_ui_init)(void);
+	/** Called after all of libpurple has been initialized.  The UI should
+	 *  use this hook to set all other necessary UiOps structures.
+	 *
+	 *  @see @ref ui-ops
+	 */
 	void (*ui_init)(void);
+	/** Called after most of libpurple has been uninitialized. */
 	void (*quit)(void);
+
+	/** Called by purple_core_get_ui_info(); should return the information
+	 *  documented there.
+	 */
 	GHashTable* (*get_ui_info)(void);
 
 	void (*_purple_reserved1)(void);
@@ -64,17 +89,23 @@
 void purple_core_quit(void);
 
 /**
+ * <p>
  * Calls purple_core_quit().  This can be used as the function 
  * passed to purple_timeout_add() when you want to shutdown Purple 
  * in a specified amount of time.  When shutting down Purple 
  * from a plugin, you must use this instead of purple_core_quit();
  * for an immediate exit, use a timeout value of 0: 
- *   purple_timeout_add(0, purple_core_quitcb, NULL);
+ * </p>
+ *
+ * <code>purple_timeout_add(0, purple_core_quitcb, NULL);</code>
+ *
+ * <p>
  * This is ensures that code from your plugin is not being 
  * executed when purple_core_quit() is called.  If the plugin
  * called purple_core_quit() directly, you would get a core dump
  * after purple_core_quit() executes and control returns to your
  * plugin because purple_core_quit() frees all plugins.
+ * </p>
  */
 gboolean purple_core_quit_cb(gpointer unused);
 
@@ -86,7 +117,8 @@
 const char *purple_core_get_version(void);
 
 /**
- * Returns the ID of the UI that is using the core.
+ * Returns the ID of the UI that is using the core, as passed to
+ * purple_core_init().
  *
  * @return The ID of the UI that is currently using the core.
  */
@@ -95,7 +127,7 @@
 /**
  * Returns a handle to the purple core.
  *
- * This is used for such things as signals.
+ * This is used to connect to @ref core-signals "core signals".
  */
 PurpleCore *purple_get_core(void);
 
@@ -114,10 +146,10 @@
 PurpleCoreUiOps *purple_core_get_ui_ops(void);
 
 /**
- * Migrates from .gaim to .purple.
+ * Migrates from <tt>.gaim</tt> to <tt>.purple</tt>.
  *
- * UIs MUST NOT call this if they have been told to use a custom
- * user directory.
+ * UIs <strong>must not</strong> call this if they have been told to use a
+ * custom user directory.
  *
  * @return A boolean indicating success or migration failure. On failure,
  *         the application must display an error to the user and then exit.
@@ -125,20 +157,33 @@
 gboolean purple_core_migrate(void);
 
 /**
- * Ensures that only one instance is running.
+ * Ensures that only one instance is running.  If libpurple is built with D-Bus
+ * support, this checks if another process owns the libpurple bus name and if
+ * so whether that process is using the same configuration directory as this
+ * process.
  *
- * @return A boolean such that @c TRUE indicates that this is the first instance,
- *         whereas @c FALSE indicates that there is another instance running.
+ * @return @c TRUE if this is the first instance of libpurple running;
+ *         @c FALSE if there is another instance running.
  *
  * @since 2.1.0
  */
 gboolean purple_core_ensure_single_instance(void);
 
 /**
- * Returns a hashtable containing various information about the UI
+ * Returns a hash table containing various information about the UI.  The
+ * following well-known entries may be in the table (along with any others the
+ * UI might choose to include):
+ *
+ * <dl>
+ *   <dt><tt>name</tt></dt>
+ *   <dd>the user-readable name for the UI.</dd>
+ *
+ *   <dt><tt>version</tt></dt>
+ *   <dd>a user-readable description of the current version of the UI.</dd>
+ * </dl>
  *
  * @return A GHashTable with strings for keys and values.  This
- * hash table must not be freed.
+ * hash table must not be freed and should not be modified.
  *
  * @since 2.1.0
  *
--- a/libpurple/protocols/jabber/libxmpp.c	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Mon Jun 16 01:03:23 2008 +0000
@@ -76,9 +76,9 @@
 	jabber_roster_remove_buddy,		/* remove_buddy */
 	NULL,							/* remove_buddies */
 	NULL,							/* add_permit */
-	jabber_google_roster_add_deny,	/* add_deny */
+	jabber_google_roster_add_deny,				/* add_deny */
 	NULL,							/* rem_permit */
-	jabber_google_roster_rem_deny,	/* rem_deny */
+	jabber_google_roster_rem_deny,				/* rem_deny */
 	NULL,							/* set_permit_deny */
 	jabber_chat_join,				/* join_chat */
 	NULL,							/* reject_chat */
@@ -105,7 +105,7 @@
 	jabber_roomlist_get_list,		/* roomlist_get_list */
 	jabber_roomlist_cancel,			/* roomlist_cancel */
 	NULL,							/* roomlist_expand_category */
-	jabber_si_xfer_can_receive_file,/* can_receive_file */
+	NULL,							/* can_receive_file */
 	jabber_si_xfer_send,			/* send_file */
 	jabber_si_new_xfer,				/* new_xfer */
 	jabber_offline_message,			/* offline_message */
--- a/libpurple/protocols/jabber/si.c	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/protocols/jabber/si.c	Mon Jun 16 01:03:23 2008 +0000
@@ -623,7 +623,7 @@
 		return;
 	else if(acceptfd == -1) {
 		purple_debug_warning("jabber", "accept: %s\n", g_strerror(errno));
-		/* TODO: This should cancel the ft */
+		/* Don't cancel the ft - allow it to fall to the next streamhost.*/
 		return;
 	}
 
@@ -659,8 +659,11 @@
 
 	jsx = xfer->data;
 
-	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result"))
+	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) {
+		if (type && !strcmp(type, "error"))
+			purple_xfer_cancel_remote(xfer);
 		return;
+	}
 
 	if(!(from = xmlnode_get_attrib(packet, "from")))
 		return;
@@ -718,14 +721,17 @@
 	JabberSIXfer *jsx;
 	JabberIq *iq;
 	xmlnode *query, *streamhost;
-	char *jid, port[6];
-	const char *local_ip, *public_ip, *ft_proxies;
+	const char *ft_proxies;
+	char port[6];
 	GList *tmp;
 	JabberBytestreamsStreamhost *sh, *sh2;
+	int streamhost_count = 0;
 
 	jsx = xfer->data;
 	jsx->listen_data = NULL;
 
+	/* I'm not sure under which conditions this can happen
+	 * (it seems like it shouldn't be possible */
 	if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) {
 		purple_xfer_unref(xfer);
 		return;
@@ -733,13 +739,6 @@
 
 	purple_xfer_unref(xfer);
 
-	if (sock < 0) {
-		purple_xfer_cancel_local(xfer);
-		return;
-	}
-
-	jsx->local_streamhost_fd = sock;
-
 	iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET,
 			"http://jabber.org/protocol/bytestreams");
 	xmlnode_set_attrib(iq->node, "to", xfer->who);
@@ -747,41 +746,45 @@
 
 	xmlnode_set_attrib(query, "sid", jsx->stream_id);
 
-	jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
+	/* If we successfully started listening locally */
+	if (sock >= 0) {
+		gchar *jid;
+		const char *local_ip, *public_ip;
+
+		jsx->local_streamhost_fd = sock;
+
+		jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
 			jsx->js->user->domain, jsx->js->user->resource);
-	xfer->local_port = purple_network_get_port_from_fd(sock);
-	g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
-
-	/* TODO: Should there be an option to not use the local host as a ft proxy?
-	 *       (to prevent revealing IP address, etc.) */
+		xfer->local_port = purple_network_get_port_from_fd(sock);
+		g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
 
-	/* Include the localhost's IP (for in-network transfers) */
-	local_ip = purple_network_get_local_system_ip(jsx->js->fd);
-	if (strcmp(local_ip, "0.0.0.0") != 0)
-	{
-		streamhost = xmlnode_new_child(query, "streamhost");
-		xmlnode_set_attrib(streamhost, "jid", jid);
-		xmlnode_set_attrib(streamhost, "host", local_ip);
-		xmlnode_set_attrib(streamhost, "port", port);
+		/* Include the localhost's IP (for in-network transfers) */
+		local_ip = purple_network_get_local_system_ip(jsx->js->fd);
+		if (strcmp(local_ip, "0.0.0.0") != 0) {
+			streamhost_count++;
+			streamhost = xmlnode_new_child(query, "streamhost");
+			xmlnode_set_attrib(streamhost, "jid", jid);
+			xmlnode_set_attrib(streamhost, "host", local_ip);
+			xmlnode_set_attrib(streamhost, "port", port);
+		}
+
+		/* Include the public IP (assuming that there is a port mapped somehow) */
+		public_ip = purple_network_get_my_ip(jsx->js->fd);
+		if (strcmp(public_ip, local_ip) != 0 && strcmp(public_ip, "0.0.0.0") != 0) {
+			streamhost_count++;
+			streamhost = xmlnode_new_child(query, "streamhost");
+			xmlnode_set_attrib(streamhost, "jid", jid);
+			xmlnode_set_attrib(streamhost, "host", public_ip);
+			xmlnode_set_attrib(streamhost, "port", port);
+		}
+
+		g_free(jid);
+
+		/* The listener for the local proxy */
+		xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
+				jabber_si_xfer_bytestreams_send_connected_cb, xfer);
 	}
 
-	/* Include the public IP (assuming that there is a port mapped somehow) */
-	/* TODO: Check that it isn't the same as above and is a valid IP */
-	public_ip = purple_network_get_my_ip(jsx->js->fd);
-	if (strcmp(public_ip, local_ip) != 0)
-	{
-		streamhost = xmlnode_new_child(query, "streamhost");
-		xmlnode_set_attrib(streamhost, "jid", jid);
-		xmlnode_set_attrib(streamhost, "host", public_ip);
-		xmlnode_set_attrib(streamhost, "port", port);
-	}
-
-	g_free(jid);
-
-	/* The listener for the local proxy */
-	xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
-			jabber_si_xfer_bytestreams_send_connected_cb, xfer);
-
 	/* insert proxies here */
 	ft_proxies = purple_account_get_string(xfer->account, "ft_proxies", NULL);
 	if (ft_proxies) {
@@ -806,11 +809,12 @@
 
 			g_snprintf(port, sizeof(port), "%hu", portnum);
 
-			purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and ft_proxy_list[%i] %p",
+			purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and ft_proxy_list[%i] %p\n",
 							  jsx, jsx->streamhosts, i, ft_proxy_list[i]);
 			if(g_list_find_custom(jsx->streamhosts, ft_proxy_list[i], jabber_si_compare_jid) != NULL)
 				continue;
 
+			streamhost_count++;
 			streamhost = xmlnode_new_child(query, "streamhost");
 			xmlnode_set_attrib(streamhost, "jid", ft_proxy_list[i]);
 			xmlnode_set_attrib(streamhost, "host", ft_proxy_list[i]);
@@ -840,6 +844,7 @@
 		if(g_list_find_custom(jsx->streamhosts, sh->jid, jabber_si_compare_jid) != NULL)
 			continue;
 
+		streamhost_count++;
 		streamhost = xmlnode_new_child(query, "streamhost");
 		xmlnode_set_attrib(streamhost, "jid", sh->jid);
 		xmlnode_set_attrib(streamhost, "host", sh->host);
@@ -855,6 +860,14 @@
 		jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2);
 	}
 
+	/* We have no way of transferring, cancel the transfer */
+	if (streamhost_count == 0) {
+		jabber_iq_free(iq);
+		/* We should probably notify the target, but this really shouldn't ever happen */
+		purple_xfer_cancel_local(xfer);
+		return;
+	}
+
 	jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer);
 
 	jabber_iq_send(iq);
@@ -869,13 +882,14 @@
 	purple_xfer_ref(xfer);
 
 	jsx = xfer->data;
+
+	/* TODO: Should there be an option to not use the local host as a ft proxy?
+	 *       (to prevent revealing IP address, etc.) */
 	jsx->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
 				jabber_si_xfer_bytestreams_listen_cb, xfer);
 	if (jsx->listen_data == NULL) {
-		purple_xfer_unref(xfer);
-		/* XXX: couldn't open a port, we're fscked */
-		purple_xfer_cancel_local(xfer);
-		return;
+		/* We couldn't open a local port.  Perhaps we can use a proxy. */
+		jabber_si_xfer_bytestreams_listen_cb(-1, xfer);
 	}
 
 }
@@ -1205,13 +1219,6 @@
 	return xfer;
 }
 
-gboolean jabber_si_xfer_can_receive_file(PurpleConnection *conn, const char *who)
-{
-	JabberStream *js = conn->proto_data;
-
-	return purple_find_buddy(conn->account, who) && jabber_buddy_find(js, who, FALSE);
-}
-	
 void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file)
 {
 	JabberStream *js;
@@ -1220,9 +1227,6 @@
 
 	js = gc->proto_data;
 
-	if (!jabber_si_xfer_can_receive_file(gc, who))
-		return;
-
 	xfer = jabber_si_new_xfer(gc, who);
 
 	if (file)
--- a/libpurple/protocols/jabber/si.h	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/protocols/jabber/si.h	Mon Jun 16 01:03:23 2008 +0000
@@ -28,7 +28,6 @@
 
 void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet);
 void jabber_si_parse(JabberStream *js, xmlnode *packet);
-gboolean jabber_si_xfer_can_receive_file(PurpleConnection *conn, const char *who);
 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who);
 void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file);
 
--- a/libpurple/protocols/oscar/oscar.c	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Jun 16 01:03:23 2008 +0000
@@ -1977,7 +1977,17 @@
 	}
 	else
 	{
-		purple_prpl_got_user_status(account, info->sn, status_id, NULL);
+		PurpleBuddy *b = purple_find_buddy(account, info->sn);
+		PurplePresence *presence = purple_buddy_get_presence(b);
+		PurpleStatus *old_status = purple_presence_get_active_status(presence);
+		PurpleStatus *new_status = purple_presence_get_status(presence, status_id);
+		
+		/* If our status_id would change with this update, pass it to the core.
+		 * However, if our status_id would not change, do nothing, as we would clear out any existing
+		 * attributes on the status prematurely. purple_got_infoblock() will update the message as needed.
+		 */
+		if (old_status != new_status)
+			purple_prpl_got_user_status(account, info->sn, status_id, NULL);
 	}
 
 	/* Login time stuff */
--- a/libpurple/prpl.h	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/prpl.h	Mon Jun 16 01:03:23 2008 +0000
@@ -31,6 +31,7 @@
 #define _PURPLE_PRPL_H_
 
 typedef struct _PurplePluginProtocolInfo PurplePluginProtocolInfo;
+/** @copydoc _PurpleAttentionType */
 typedef struct _PurpleAttentionType PurpleAttentionType;
 
 /**************************************************************************/
@@ -99,6 +100,9 @@
 	gboolean secret;
 };
 
+/** Represents "nudges" and "buzzes" that you may send to a buddy to attract
+ *  their attention (or vice-versa).
+ */
 struct _PurpleAttentionType
 {
 	const char *name;                  /**< Shown in GUI elements */
--- a/libpurple/roomlist.h	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/roomlist.h	Mon Jun 16 01:03:23 2008 +0000
@@ -30,6 +30,7 @@
 typedef struct _PurpleRoomlist PurpleRoomlist;
 typedef struct _PurpleRoomlistRoom PurpleRoomlistRoom;
 typedef struct _PurpleRoomlistField PurpleRoomlistField;
+/** @copydoc _PurpleRoomlistUiOps */
 typedef struct _PurpleRoomlistUiOps PurpleRoomlistUiOps;
 
 /**
--- a/libpurple/sound.h	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/sound.h	Mon Jun 16 01:03:23 2008 +0000
@@ -55,6 +55,9 @@
 
 } PurpleSoundEventID;
 
+/** Operations used by the core to request that particular sound files, or the
+ *  sound associated with a particular event, should be played.
+ */
 typedef struct _PurpleSoundUiOps
 {
 	void (*init)(void);
--- a/libpurple/util.c	Fri Jun 13 18:35:25 2008 +0000
+++ b/libpurple/util.c	Mon Jun 16 01:03:23 2008 +0000
@@ -832,6 +832,11 @@
 				if (offset_positive)
 					tzoff *= -1;
 			}
+			else if ((*c == 'Z') && (c = c + 1))
+			{
+				/* 'Z' = Zulu = UTC */
+				tzoff = 0;
+			}
 			else if (utc)
 			{
 				static struct tm tmptm;