浏览代码

Issue 2321 – Compatibility with new OneDrive WebDAV interface

https://winscp.net/tracker/2321

Now always sending Authorization header, even after receiving session cookies
It seems that the new server/interface does not care about the cookies, but keeping them for compatibility with the old server/interface.

Also normalizing returned URI to CID with the same letter case as the session have.
Though more correct would be to "redirect" user to the correct letter case.

Source commit: f7488c63bc7e42af7877b126f48d1208c39b448f
Martin Prikryl 1 年之前
父节点
当前提交
8246dd6316
共有 3 个文件被更改,包括 70 次插入13 次删除
  1. 19 13
      libs/neon/src/ne_auth.c
  2. 50 0
      source/core/WebDAVFileSystem.cpp
  3. 1 0
      source/core/WebDAVFileSystem.h

+ 19 - 13
libs/neon/src/ne_auth.c

@@ -221,7 +221,8 @@ typedef struct {
     /* Part of the RHS of the response digest. */
     char *response_rhs;
 #ifdef WINSCP
-    char * passport;
+    char * passport_auth_header;
+    char * passport_cookies_header;
     /* In the current implementation, we actually possibly never reuse these two fields */
     ne_uri passport_login_uri;
     char * passport_cookies;
@@ -343,10 +344,15 @@ static void clean_session(auth_session *sess)
 #endif
 #ifdef WINSCP
     ne_uri_free(&sess->passport_login_uri);
-    if (sess->passport)
+    if (sess->passport_auth_header)
     {
-        ne_free(sess->passport);
-        sess->passport = NULL;
+        ne_free(sess->passport_auth_header);
+        sess->passport_auth_header = NULL;
+    }
+    if (sess->passport_cookies_header)
+    {
+        ne_free(sess->passport_cookies_header);
+        sess->passport_cookies_header = NULL;
     }
     if (sess->passport_cookies)
     {
@@ -1450,7 +1456,7 @@ static int passport_challenge(auth_session *sess, int attempt,
     char * org_url;
     int result;
 
-    if (sess->passport == NULL)
+    if (sess->passport_auth_header == NULL)
     {
         struct aux_request_init_data * init_data = ne_get_session_private(session, PASSPORT_REQ_ID);
 
@@ -1600,7 +1606,7 @@ static int passport_challenge(auth_session *sess, int attempt,
 
                 char * buf, * pnt, * key, * val;
                 pnt = buf = ne_strdup(auth_info);
-                while (((sess->passport == NULL) || (success < 0)) &&
+                while (((sess->passport_auth_header == NULL) || (success < 0)) &&
                        (tokenize(&pnt, &key, &val, NULL, 1) == 0))
                 {
                     if (val == NULL)
@@ -1626,13 +1632,13 @@ static int passport_challenge(auth_session *sess, int attempt,
                     }
                     else if (ne_strcasecmp(key, "from-PP") == 0)
                     {
-                        sess->passport = ne_concat("Authorization: ", PASSPORT_NAME, " ", key, "=", val, "\r\n", NULL);
+                        sess->passport_auth_header = ne_concat("Authorization: ", PASSPORT_NAME, " ", key, "=", val, "\r\n", NULL);
                     }
                 }
 
                 ne_free(buf);
 
-                if ((sess->passport == NULL) || (success < 0))
+                if ((sess->passport_auth_header == NULL) || (success < 0))
                 {
                     challenge_error(errmsg, _("Cannot parse Nexus response"));
                     result = -1;
@@ -1653,8 +1659,8 @@ static int passport_challenge(auth_session *sess, int attempt,
 
 static char *request_passport(auth_session *sess, struct auth_request *req)
 {
-    return ne_strdup(sess->passport);
-}
+    return ne_concat(sess->passport_auth_header, sess->passport_cookies_header, NULL);
+}
 
 static void lower_case(char * s)
 {
@@ -1747,12 +1753,12 @@ static int verify_passport_response(struct auth_request *req, auth_session *sess
 
             if (cookie_header != NULL)
             {
-                if (sess->passport != NULL)
+                if (sess->passport_cookies_header != NULL)
                 {
-                    ne_free(sess->passport);
+                    ne_free(sess->passport_cookies_header);
                 }
                 ne_buffer_zappend(cookie_header, "\r\n");
-                sess->passport = ne_buffer_finish(cookie_header);
+                sess->passport_cookies_header = ne_buffer_finish(cookie_header);
             }
         }
     }

+ 50 - 0
source/core/WebDAVFileSystem.cpp

@@ -209,6 +209,7 @@ void __fastcall TWebDAVFileSystem::Open()
   if (FOneDrive)
   {
     FTerminal->LogEvent(L"OneDrive host detected.");
+    FOneDriveInterface = odiUnknown;
   }
 
   size_t Port = Data->PortNumber;
@@ -511,6 +512,18 @@ void __fastcall TWebDAVFileSystem::CollectUsage()
   }
 
   UnicodeString RemoteSystem = FFileSystemInfo.RemoteSystem;
+  if (FOneDrive)
+  {
+    FTerminal->Configuration->Usage->Inc(L"OpenedSessionsWebDAVOneDrive");
+    if (FOneDriveInterface == odiUpperCase)
+    {
+      FTerminal->Configuration->Usage->Inc(L"OpenedSessionsWebDAVOneDriveUpperCase");
+    }
+    else if (FOneDriveInterface == odiLowerCase)
+    {
+      FTerminal->Configuration->Usage->Inc(L"OpenedSessionsWebDAVOneDriveLowerCase");
+    }
+  }
   if (ContainsText(RemoteSystem, L"Microsoft-IIS"))
   {
     FTerminal->Configuration->Usage->Inc(L"OpenedSessionsWebDAVIIS");
@@ -873,6 +886,43 @@ void TWebDAVFileSystem::NeonPropsResult(
     FileSystem->ParsePropResultSet(File.get(), Path, Results);
 
     UnicodeString FileListPath = UnixIncludeTrailingBackslash(FileSystem->AbsolutePath(Data.FileList->Directory, false));
+    if (FileSystem->FOneDrive)
+    {
+      UnicodeString FullFileName = UnixIncludeTrailingBackslash(File->FullFileName);
+      if (Configuration->Usage->Collect && (FileSystem->FOneDriveInterface == odiUnknown) && !IsUnixRootPath(FullFileName))
+      {
+        UnicodeString Cid = FullFileName;
+        if (DebugAlwaysTrue(StartsStr(L"/", Cid)))
+        {
+          Cid.Delete(1, 1);
+          int P = Cid.Pos(L"/");
+          if (P > 0)
+          {
+            Cid.SetLength(P - 1);
+          }
+          UnicodeString CidUpper = UpperCase(Cid);
+          UnicodeString CidLower = LowerCase(Cid);
+          if (CidUpper != CidLower)
+          {
+            if (Cid == CidUpper)
+            {
+              FileSystem->FOneDriveInterface = odiUpperCase;
+              Terminal->LogEvent(L"Detected upper-case OneDrive interface");
+            }
+            else if (Cid == CidLower)
+            {
+              FileSystem->FOneDriveInterface = odiLowerCase;
+              Terminal->LogEvent(L"Detected lower-case OneDrive interface");
+            }
+          }
+        }
+      }
+      // OneDrive is case insensitive and when we enter the directory using a different case, it returns results with the actual case
+      if (StartsText(FileListPath, FullFileName))
+      {
+        File->FullFileName = FileListPath + MidStr(FullFileName, FileListPath.Length() + 1);
+      }
+    }
     if (UnixSamePath(File->FullFileName, FileListPath))
     {
       File->FileName = PARENTDIRECTORY;

+ 1 - 0
source/core/WebDAVFileSystem.h

@@ -178,6 +178,7 @@ private:
   UnicodeString FLastAuthorizationProtocol;
   bool FAuthenticationRetry;
   bool FOneDrive;
+  enum { odiUnknown, odiUpperCase, odiLowerCase } FOneDriveInterface;
 
   void __fastcall CustomReadFile(UnicodeString FileName,
     TRemoteFile *& File, TRemoteFile * ALinkedByFile);