posts - 20 , comments - 57 , trackbacks - 0

FtpWebRequest

As I described in my previous post, I use the Windows CE Ftp File Server for remote browsing the device's file system. I also use it to update the device's firmware.

Some time ago I wrote .NET C# code to initiate the firmware upgrade from a desktop Windows PC. I decided to use the .NET System.Net.FtpWebRequest library.

At first everything seemed to work quite well and the code was easy to use. Until at a certain date the firmware upgrade failed. The WireShark trace revealed "an unrecognized command" during the file transfer from the CE device back to the desktop Windows side during the System.Net.WebRequestMethods.Ftp.DownloadFile call. (You might wonder why DownloadFile? Well, I verify what I uploaded before to the device with System.Net.WebRequestMethods.Ftp.UploadFile)

It turned out in the - long - end that when your desktop Windows side has a different locale set other than US English, the System.Net.WebRequestMethods.Ftp.DownloadFile underlying issues an additional ftp "MDTM" (file MoDificationTiMe) request. Even when System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore) was specified in the webRequest.CachePolicy call.

The Windows CE sample Ftp File Server has always been conforming to RFC959. But the "MDTM" command is a later extension specified in RFC3659, which was not understood by the Ftp File Server. Hence the “unrecognized command”.

So by adding the missing RFC3659 commands in the Windows CE Ftp File Server implementation, I could make the desktop C# FtpWebRequest  code work again.

 

Here are my ftpsession.cpp changes

 

WCHAR *pszCmdList[] =

{

// RFC 959

L"USER", ...

// RFC 3659 additions

      L"MDTM", L"SIZE"

};

 

PFUNCCOMMAND pfuncCommand[] =

{

// RFC 959

      CFtpSession::cmdUSER, ...

// RFC 3659 additions

      CFtpSession::cmdMDTM, CFtpSession::cmdSIZE

};

 

And my cmds.cpp additions

 

DWORD CFtpSession::cmdMDTM(WCHAR *pszArg)

{

      VALIDATEARG(pszArg)

 

      WCHAR szFile[MAX_PATH];

      DWORD dwReply = FILE_UNAVAILABLE;

 

      if (0 == GetAbsFileName(pszArg, szFile, MAX_PATH))

      {

            Reply(_sock, FILE_ACTION_NOT_TAKEN);

            return FALSE;

      }

 

      HANDLE hFile = NULL;

      if ( (hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))

                        == INVALID_HANDLE_VALUE)

      {

            Reply(_sock, FILE_UNAVAILABLE);

            return FALSE;

      }

 

      FILETIME CreationTime;

      FILETIME LastAccessTime;

      FILETIME LastWriteTime;

      if (0 == GetFileTime(hFile, &CreationTime, &LastAccessTime, &LastWriteTime))

      {

            Reply(_sock, FILE_UNAVAILABLE);

            return FALSE;

      }

 

      FILETIME ft;

      SYSTEMTIME st;

      WCHAR szLastModifiedTime[20] = L"";

 

      FileTimeToLocalFileTime(&LastWriteTime, &ft);

      FileTimeToSystemTime(&ft, &st);

           

      StringCchPrintfW(szLastModifiedTime, SVSUTIL_ARRLEN(szLastModifiedTime), L"%04d%02d%02d%02d%02d%02d",

            st.wYear, st.wMonth,st.wDay,st.wHour,st.wMinute, st.wSecond);  // make YYYYMMDDhhmmss reply

 

      // Note we adjusted the FILE_STATUS resource string to accommodate the 'szLastModifiedTime' reply.

      // As FILE_STATUS is nowhere else used in the FTP code, this should be no problem.

      Reply(_sock, FILE_STATUS, szLastModifiedTime);

      return TRUE;

}

 

DWORD CFtpSession::cmdSIZE(WCHAR *pszArg)

{

      VALIDATEARG(pszArg);

 

      WCHAR szFile[MAX_PATH];

      DWORD dwReply = FILE_UNAVAILABLE;

 

      if (0 == GetAbsFileName(pszArg, szFile, MAX_PATH))

      {

            Reply(_sock, FILE_ACTION_NOT_TAKEN);

            return FALSE;

      }

 

      HANDLE hFile = NULL;

      if ( (hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))

                        == INVALID_HANDLE_VALUE)

      {

            Reply(_sock, FILE_UNAVAILABLE);

            return FALSE;

      }

 

      DWORD FileSizeHigh = 0;

      DWORD FileSizeLow = GetFileSize(hFile, &FileSizeHigh);

      if (0xFFFFFFFF == FileSizeLow)

      {

            Reply(_sock, FILE_UNAVAILABLE);

            return FALSE;

      }

 

      WCHAR szFileSize[20] = L"";

 

      StringCchPrintfW(szFileSize, SVSUTIL_ARRLEN(szFileSize), L"%d", FileSizeLow);  // we limit to 4Gb?

 

      // Note we adjusted the FILE_STATUS resource string to accommodate the 'szFileSize' reply.

      // As FILE_STATUS is nowhere else used in the FTP code, this should be no problem.

      Reply(_sock, FILE_STATUS, szFileSize);

      return TRUE;

}

 

I only added the “MDTM” and “SIZE” commands, not the 2 other RFC3659 commands “MLST” and “MLSD”. These were not needed for working with the System.Net.FtpWebRequest  class, to my knowledge.

The Ftp File Server also doesn’t support the Compressed and Block data transfer mode, only Stream mode is supported. This made the “SIZE” implementation pretty straightforward.

To test this code, you can also invoke the “MDTM” and “SIZE” methods directly from the System.Net.FtpWebRequest  class by calling System.Net.WebRequestMethods.Ftp.GetDateTimestamp and System.Net.WebRequestMethods.Ftp.GetFileSize respectively.

If you would call System.Net.WebRequestMethods.Ftp.UploadFile from a non US English locale desktop Windows, part of the communication will do exactly what System.Net.WebRequestMethods.Ftp.GetDateTimestamp does.

 

The .NET C# test code

 

private Exception GetLastModifiedTime(string uri, out DateTime lastModified)

{

    lastModified = DateTime.MinValue;

 

    try

    {

        System.Net.FtpWebRequest webRequest = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(uri);

        webRequest.Credentials = Credentials;

 

        webRequest.Method = System.Net.WebRequestMethods.Ftp.GetDateTimestamp;  // "MDTM" command

        webRequest.Proxy = null;

        webRequest.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);

        webRequest.Timeout = 5000;

        webRequest.KeepAlive = false;

        webRequest.UseBinary = true;

        webRequest.ConnectionGroupName = "FTPClient";

 

        using (System.Net.FtpWebResponse response = (System.Net.FtpWebResponse)webRequest.GetResponse())

        {

            using (System.IO.Stream responseStream = response.GetResponseStream())

            {

                using (System.IO.StreamReader reader = new System.IO.StreamReader(responseStream))

                {

                    string input = reader.ReadToEnd();

                    lastModified = response.LastModified;

                }

            }

        }

 

        return null;

 

    }

    catch (Exception ex)

    {

        return ex;

    }

}

 

private Exception GetFileSize(string uri, out long fileSize)

{

    fileSize = -1;

 

    try

    {

        System.Net.FtpWebRequest webRequest = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(uri);

        webRequest.Credentials = Credentials;

 

        webRequest.Method = System.Net.WebRequestMethods.Ftp.GetFileSize;  // "SIZE" command

        webRequest.Proxy = null;

        webRequest.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);

        webRequest.Timeout = 5000;

        webRequest.KeepAlive = false;

        webRequest.UseBinary = true;

        webRequest.ConnectionGroupName = "FTPClient";

 

        using (System.Net.FtpWebResponse response = (System.Net.FtpWebResponse)webRequest.GetResponse())

        {

            using (System.IO.Stream responseStream = response.GetResponseStream())

            {

                using (System.IO.StreamReader reader = new System.IO.StreamReader(responseStream))

                {

                    string input = reader.ReadToEnd();

                    fileSize = response.ContentLength;

                }

            }

        }

 

        return null;

 

    }

    catch (Exception ex)

    {

        return ex;

    }

}

 

Print | posted on Monday, September 2, 2013 8:25 PM | Filed Under [ Windows CE Windows Embedded Compact Ftp FtpWebRequest Microsoft ]

Feedback

No comments posted yet.
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: