Index: src/include/directorylisting.h
===================================================================
--- src/include/directorylisting.h	(revision 4225)
+++ src/include/directorylisting.h	(working copy)
@@ -20,8 +20,9 @@
 		flag_timestamp_date = 0x10,
 		flag_timestamp_time = 0x20,
 		flag_timestamp_seconds = 0x40,
-
-		flag_timestamp_mask = 0x70
+		flag_timestamp_utc = 0x80,
+		
+		flag_timestamp_mask = 0xF0
 	};
 	int flags;
 
@@ -51,8 +52,28 @@
 	{
 		return (flags & flag_timestamp_seconds) != 0;
 	}
+	inline bool is_utc() const
+	{
+		return (flags & flag_timestamp_utc) != 0;
+	}
+	inline void AdjustTimeToUTC ( const wxTimeSpan &span )
+	{
+		if (has_time() && !is_utc())
+		{
+			time.Add(span);
+			flags |= flag_timestamp_utc;
+		}
+	}
+	inline void AdjustTimeToUTC ( const int offset )
+	{
+		if (offset) {
+			const wxTimeSpan span(0, offset, 0, 0);
+			AdjustTimeToUTC(span);
+		}
+	}
+		
+	
 
-
 	wxString target; // Set to linktarget it link is true
 
 	wxDateTime time;
Index: src/engine/directorylisting.cpp
===================================================================
--- src/engine/directorylisting.cpp	(revision 4225)
+++ src/engine/directorylisting.cpp	(working copy)
@@ -151,9 +151,9 @@
 		if (iter->is_dir())
 			m_hasDirs = true;
 		if (!iter->permissions.empty())
+			m_has_perms = true;
+		if (!iter->ownerGroup.empty())
 			m_has_usergroup = true;
-		if (!iter->ownerGroup.empty())
-			m_has_perms = true;
 		own_entries.push_back(CRefcountObject<CDirentry>(*iter));
 	}
 
Index: src/engine/directorylistingparser.h
===================================================================
--- src/engine/directorylistingparser.h	(revision 4225)
+++ src/engine/directorylistingparser.h	(working copy)
@@ -49,10 +49,21 @@
 	void Reset();
 
 	void SetTimezoneOffset(const wxTimeSpan& span) { m_timezoneOffset = span; }
+	
+	void SetFilezillaDataFormat(const bool filezilla_format) { m_attributeState = attr_file_next; }
 
 	void SetServer(const CServer& server) { m_server = server; };
 
 protected:
+
+	enum attributeStates
+	{
+		attr_disabled = 0,
+		attr_file_next,
+		attr_keep_next,
+		attr_skip_next,
+	};
+
 	CLine *GetLine(bool breakAtEnd, bool& error);
 
 	bool ParseData(bool partial);
@@ -82,6 +93,10 @@
 	// with other formats.
 	bool ParseAsHPNonstop(CLine *pLine, CDirentry &entry);
 
+	// When used with the Filezilla output format, parses attribute information
+	// provided by Filezilla's helper applications
+	bool ParseAsFilezillaAttributes(CLine *pLine, CDirentry &entry, bool ignoreBadPermissions);
+	
 	// Date / time parsers
 	bool ParseUnixDateTime(CLine *pLine, int &index, CDirentry &entry);
 	bool ParseShortDate(CToken &token, CDirentry &entry, bool saneFieldOrder = false);
@@ -115,6 +130,8 @@
 	
 	bool m_maybeMultilineVms;
 
+	int  m_attributeState;
+	
 	wxTimeSpan m_timezoneOffset;
 };
 
Index: src/engine/sftpcontrolsocket.cpp
===================================================================
--- src/engine/sftpcontrolsocket.cpp	(revision 4225)
+++ src/engine/sftpcontrolsocket.cpp	(working copy)
@@ -1083,10 +1083,8 @@
 					for (int i = 0; i < count; ++i)
 					{
 						CDirentry& entry = pData->directoryListing[i];
-						if (!entry.has_time())
-							continue;
 
-						entry.time += span;
+						entry.AdjustTimeToUTC (span);
 					}
 
 					// TODO: Correct cached listings
@@ -1261,7 +1259,8 @@
 	{
 		pData->pParser = new CDirectoryListingParser(this, *m_pCurrentServer);
 		pData->pParser->SetTimezoneOffset(GetTimezoneOffset());
-		if (!Send(_T("ls")))
+		pData->pParser->SetFilezillaDataFormat(true);
+		if (!Send(_T("ls-fz")))
 			return FZ_REPLY_ERROR;
 		return FZ_REPLY_WOULDBLOCK;
 	}
Index: src/engine/directorylistingparser.cpp
===================================================================
--- src/engine/directorylistingparser.cpp	(revision 4225)
+++ src/engine/directorylistingparser.cpp	(working copy)
@@ -1,4 +1,5 @@
 #include <filezilla.h>
+#include <inttypes.h>
 #include "directorylistingparser.h"
 #include "ControlSocket.h"
 
@@ -6,6 +7,7 @@
 #define new DEBUG_NEW
 #endif
 
+
 std::map<wxString, int> CDirectoryListingParser::m_MonthNamesMap;
 
 //#define LISTDEBUG_MVS
@@ -273,7 +275,7 @@
 					else if (c >= 'a' && c <= 'f')
 					{
 						number *= 16;
-						number += c - '0' + 10;
+						number += c - 'a' + 10;
 					}
 					else if (c >= 'A' && c <= 'F')
 					{
@@ -454,6 +456,7 @@
 	m_prevLine = 0;
 	m_fileListOnly = true;
 	m_maybeMultilineVms = false;
+	m_attributeState = attr_disabled;
 
 	if (m_MonthNamesMap.empty())
 	{
@@ -765,7 +768,41 @@
 	CDirentry entry;
 	bool res;
 	int ires;
-
+	
+	if (m_attributeState != attr_disabled &&
+		m_attributeState != attr_file_next)
+	{
+		switch (m_attributeState)
+		{
+		case attr_keep_next:
+			{
+				entry = m_entryList.back();
+				
+				res = ParseAsFilezillaAttributes(pLine, entry, false);
+				
+				if (res) {
+					m_entryList.pop_back();
+					m_entryList.push_back(entry);
+				}
+			}
+			break;
+		case attr_skip_next:
+			{
+				// "." and ".." don't get pushed onto m_entryList
+				res = ParseAsFilezillaAttributes(pLine, entry, true);
+			}
+			break;
+		default:
+			res = false;
+			break;
+		}
+		
+		m_attributeState = attr_file_next;
+		if (res)
+			return res;
+		// On failure fall through to ensure that no directory lines are skipped
+	}
+	
 	if (serverType == ZVM)
 	{
 		res = ParseAsZVM(pLine, entry);
@@ -864,9 +901,16 @@
 	m_fileList.clear();
 	m_fileListOnly = false;
 
+	if (m_attributeState)
+		m_attributeState = attr_keep_next;
+	
 	// Don't add . or ..
 	if (entry.name == _T(".") || entry.name == _T(".."))
+	{
+		if (m_attributeState)
+			m_attributeState = attr_skip_next;
 		return true;
+	}
 
 	if (serverType == VMS && entry.is_dir())
 	{
@@ -876,15 +920,7 @@
 			entry.name = entry.name.Left(pos);
 	}
 
-	{
-		int offset = m_server.GetTimezoneOffset();
-		if (offset && entry.has_time())
-		{
-			// Apply timezone offset
-			wxTimeSpan span(0, offset, 0, 0);
-			entry.time.Add(span);
-		}
-	}
+	entry.AdjustTimeToUTC(m_server.GetTimezoneOffset());
 
 	m_entryList.push_back(entry);
 
@@ -1010,8 +1046,7 @@
 			}
 		}
 
-		if (entry.has_time())
-			entry.time.Add(m_timezoneOffset);
+		entry.AdjustTimeToUTC(m_timezoneOffset);
 
 		return true;
 	}
@@ -1480,8 +1515,7 @@
 	entry.ownerGroup = _T("");
 	entry.permissions = _T("");
 
-	if (entry.has_time())
-		entry.time.Add(m_timezoneOffset);
+	entry.AdjustTimeToUTC(m_timezoneOffset);
 
 	return true;
 }
@@ -1550,6 +1584,126 @@
 	return true;
 }
 
+bool CDirectoryListingParser::ParseAsFilezillaAttributes(CLine *pLine, CDirentry &entry, bool ignoreBadPermissions)
+{
+	int index = 0;
+	CToken token;
+
+	bool begin = false;
+	while (1) {
+		wxString s;
+		if (!pLine->GetToken(index, token))
+			return false;
+		++index;
+		
+		s = token.GetString();
+		
+		if (s == _T("FZ_ATTR_END"))
+			break;
+		
+		if (begin) {
+			CToken dataToken;
+			
+			if (!pLine->GetToken(index, dataToken))
+				return false;
+			++index;
+
+			if (s == _T("SIZE"))
+			{
+				entry.size = dataToken.GetNumber(CToken::hex);
+			}
+			else if (s == _T("UID")) 
+			{
+				// Cdirentry doesn't have a UID member
+			}
+			else if (s == _T("GID"))
+			{
+				// Cdirentry doesn't have a GID member
+			}
+			else if (s == _T("PERM"))
+			{
+			    wxString permissions;
+			    wxLongLong perms = dataToken.GetNumber(CToken::hex);
+
+			    bool isDir = 0 != (perms & 0040000);
+			    
+			    permissions += isDir ? 'd' : '-';
+			    if  (!ignoreBadPermissions &&
+				 (isDir !=
+				  (0 != (entry.flags & CDirentry::flag_dir))))
+			    {
+				    // BUGBUG: log error?
+				    return false;
+			    }
+			    
+			    const wxString rwx = _("rwxrwxrwx");
+			    const int len = rwx.length();
+
+			    for (int i = 0; i < len; ++i) {
+				if ( 0 != (perms & (1 << (len - 1 - i))) ) {
+				    permissions += rwx[i];
+				} else {
+				    permissions += '-';
+				}
+			    }
+
+			    // setuid
+			    if (0 != (perms & 04000))
+			    {
+				    permissions[3] = (permissions[3] == '-') ? 'S' : 's';
+			    }
+
+			    // setgid
+			    if (0 != (perms & 02000))
+			    {
+				    permissions[6] = (permissions[6] == '-') ? 'S' : 's';
+			    }
+
+			    // sticky bit
+			    if (0 != (perms & 01000))
+			    {
+				    permissions[9] = (permissions[9] == '-') ? 'T' : 't';				    
+			    }
+			    
+			    if (entry.permissions != permissions && !ignoreBadPermissions) {
+				    // BUGBUG: log error?
+				    return false;
+			    }
+			}
+			else if (s == _T("ATIME"))
+			{
+				// Cdirentry doesn't have an ATIME member
+			} 
+			else if (s == _T("MTIME"))
+			{
+				wxLongLong number = dataToken.GetNumber(CToken::hex);
+
+				entry.time = wxDateTime((time_t)number.GetValue());
+
+				entry.flags |= CDirentry::flag_timestamp_utc
+					| CDirentry::flag_timestamp_date
+					| CDirentry::flag_timestamp_time
+					| CDirentry::flag_timestamp_seconds;
+
+			}
+			else if (s == _T("CTIME"))
+			{
+				// Cdirentry doesn't have an CTIME member
+			}
+			else
+			{
+				return false;
+			}
+		}
+		else if (s == _T("FZ_ATTR_BEGIN"))
+		{
+			begin = true;
+			continue;
+		}			
+	}	
+	return true;
+}
+
 bool CDirectoryListingParser::ParseAsEplf(CLine *pLine, CDirentry &entry)
 {
 	CToken token;
@@ -1786,8 +1940,7 @@
 		}
 	}
 
-	if (entry.has_time())
-		entry.time.Add(m_timezoneOffset);
+	entry.AdjustTimeToUTC(m_timezoneOffset);
 
 	return true;
 }
@@ -1839,8 +1992,7 @@
 		entry.flags |= CDirentry::flag_dir;
 	}
 	
-	if (entry.has_time())
-		entry.time.Add(m_timezoneOffset);
+	entry.AdjustTimeToUTC(m_timezoneOffset);
 
 	return true;
 }
@@ -2016,8 +2168,7 @@
 		entry.ownerGroup = _T("");
 		entry.permissions = _T("");
 
-		if (entry.has_time())
-			entry.time.Add(m_timezoneOffset);
+		entry.AdjustTimeToUTC(m_timezoneOffset);
 	}
 
 	return true;
@@ -2246,8 +2397,7 @@
 	entry.ownerGroup = _T("");
 	entry.permissions = _T("");
 
-	if (entry.has_time())
-		entry.time.Add(m_timezoneOffset);
+	entry.AdjustTimeToUTC(m_timezoneOffset);
 
 	return true;
 }
@@ -2412,9 +2562,8 @@
 	entry.ownerGroup = _T("");
 	entry.permissions = _T("");
 
-	if (entry.has_time())
-		entry.time.Add(m_timezoneOffset);
-
+	entry.AdjustTimeToUTC(m_timezoneOffset);
+	
 	return true;
 }
 
@@ -2925,9 +3074,8 @@
 	entry.permissions = _T("");
 	entry.target = _T("");
 
-	if (entry.has_time())
-		entry.time.Add(m_timezoneOffset);
-
+	entry.AdjustTimeToUTC(m_timezoneOffset);
+	
 	return true;
 }
 
Index: src/putty/psftp.c
===================================================================
--- src/putty/psftp.c	(revision 4225)
+++ src/putty/psftp.c	(working copy)
@@ -7,6 +7,7 @@
 #include <stdarg.h>
 #include <assert.h>
 #include <limits.h>
+#include <inttypes.h>
 
 #ifndef _WINDOWS
 #include <locale.h>
@@ -1153,7 +1154,7 @@
  * List a directory. If no arguments are given, list pwd; otherwise
  * list the directory given in words[1].
  */
-int sftp_cmd_ls(struct sftp_command *cmd)
+int sftp_cmd_ls_ex(struct sftp_command *cmd, int extrainfo)
 {
     struct fxp_handle *dirh;
     struct fxp_names *names;
@@ -1265,7 +1266,44 @@
 	 * And print them.
 	 */
 	for (i = 0; i < nnames; i++) {
+	    struct fxp_attrs *attrs = &ournames[i]->attrs;
 	    fzprintf(sftpListentry, "%s", ournames[i]->longname);
+	    
+	    if (extrainfo) {
+		fzprintf_raw(sftpListentry, "FZ_ATTR_BEGIN");
+		if (attrs->flags & SSH_FILEXFER_ATTR_SIZE) {
+		    printf(" SIZE %08l" PRIx32 "%08l" PRIx32, attrs->size.hi, attrs->size.lo);
+		}
+		if (attrs->flags & SSH_FILEXFER_ATTR_UIDGID) {
+		    printf(" UID %08l" PRIx32, attrs->uid);
+		    printf(" GID %08l" PRIx32, attrs->gid);
+		}
+		if (attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+			printf(" PERM %08l" PRIx32, attrs->permissions);
+		}
+/*FZ: Does not work
+		if (attrs->flags & (SSH_FILEXFER_ATTR_CREATETIME | SSH_FILEXFER_ATTR_MODIFICATIONTIME)) {
+		    if (attrs->flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
+			printf(" ATIME %08l" PRIx32 "%08l" PRIx32, attrs->atime.hi, attrs->atime.lo);
+		    }
+		    if (attrs->flags & SSH_FILEXFER_ATTR_CREATETIME) {
+			printf(" CTIME %08l" PRIx32 "%08l" PRIx32, attrs->ctime.hi, attrs->ctime.lo);
+		    }
+		    if (attrs->flags & SSH_FILEXFER_ATTR_MODIFICATIONTIME) {
+			printf(" MTIME %08l" PRIx32 "%08l" PRIx32, attrs->mtime.hi, attrs->mtime.lo);
+		    }
+		}
+		else
+*/
+		if (attrs->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
+		    printf(" ATIME %08l" PRIx32 "%08l" PRIx32, attrs->atime.hi, attrs->atime.lo);
+		    printf(" MTIME %08l" PRIx32 "%08l" PRIx32, attrs->mtime.hi, attrs->mtime.lo);
+		    printf(" CTIME %08l" PRIx32 "%08l" PRIx32, 0L, 0L);
+		}
+		printf(" FZ_ATTR_END\n");
+		fflush(stdout);
+	    }
+
 	    fxp_free_name(ournames[i]);
 	}
 	sfree(ournames);
@@ -1280,6 +1318,14 @@
 
     return 1;
 }
+int sftp_cmd_lsfz(struct sftp_command *cmd)
+{
+    return sftp_cmd_ls_ex (cmd, 1);
+}
+int sftp_cmd_ls(struct sftp_command *cmd)
+{
+    return sftp_cmd_ls_ex (cmd, 0);
+}
 
 /*
  * Change directories. We do this by canonifying the new name, then
@@ -2350,6 +2396,18 @@
 	    sftp_cmd_ls
     },
     {
+	"dir-fz", TRUE, "list remote files",
+	    " [ <directory-name> ]/[ <wildcard> ]\n"
+	    "  List the contents of a specified directory on the server.\n"
+	    "  If <directory-name> is not given, the current working directory\n"
+	    "  is assumed.\n"
+	    "  If <wildcard> is given, it is treated as a set of files to\n"
+	    "  list; otherwise, all files are listed.\n"
+	    "  Special attribute information for each file is returned on\n"
+	    "  a second line for each file\n",
+	    sftp_cmd_lsfz
+    },
+    {
 	"exit", TRUE, "bye", NULL, sftp_cmd_quit
     },
     {
@@ -2398,6 +2456,10 @@
 	    sftp_cmd_ls
     },
     {
+	"ls-fz", TRUE, "dir-fz", NULL,
+	    sftp_cmd_lsfz
+    },
+    {
 	"mget", TRUE, "download multiple files at once",
 	    " [ -r ] [ -- ] <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
 	    "  Downloads many files from the server, storing each one under\n"
