فهرست منبع

Integrating libs3 library

- Making it compilable on C++ Builder (mainly all non-fixed-length static arrays replaced with dynamic allocation + some casts)
- curl replaced with neon
- libxml2 replaced with Expat
- SSL certificate verification callback
- makefile

Source commit: 935bcc04aa29cd128b6f0c2dfb84dee170e45f9a
Martin Prikryl 8 سال پیش
والد
کامیت
97fc738df6

+ 19 - 0
libs/buildlibs.bat

@@ -80,4 +80,23 @@ exit
 
 :SKIP_PUTTYVS
 
+rem ==== libs3 ====
+
+if exist lib\libs3.lib (
+echo libs3 already built
+goto SKIP_LIBS3
+)
+
+echo Building libs3 ...
+cd libs3
+make all
+cd ..
+
+if not exist lib\libs3.lib (
+echo libs3 build failed
+exit
+)
+
+:SKIP_LIBS3
+
 echo All done

+ 68 - 0
libs/libs3/Makefile

@@ -0,0 +1,68 @@
+# Set your compiler options
+CFLAG=-DWIN32 -DWIN32_LEAN_AND_MEAN -DWINSCP -DNE_LFS -q-c -tWC -tWM -P
+CFLAG = $(CFLAG) -w-aus -w-par
+
+# The output directory for everything interesting
+OUT_D=..\lib
+# The output directory for all the temporary muck
+TMP_D=tmp
+
+CC=bcc32
+MKLIB=tlib /P64 /C
+
+.autodepend
+.PATH.c   = src
+
+# ---------------------------------------------------------------------------
+
+# OBJ_D  - temp object file directory
+OBJ_D=$(TMP_D)
+INCL=inc;inc/mingw;../openssl;../expat/lib;../neon/src
+
+O_LIBS3=$(OUT_D)\libs3.lib
+
+INC=-I$(INCL)
+LIB_CFLAGS=$(INC) $(CFLAG) $(LIB_CFLAG)
+LIBS_DEP=$(O_LIBS3)
+
+#############################################
+LIBS3_OBJS=\
+    $(OBJ_D)\bucket.obj \
+    $(OBJ_D)\bucket_metadata.obj \
+    $(OBJ_D)\error_parser.obj \
+    $(OBJ_D)\general.obj \
+    $(OBJ_D)\mingw_functions.obj \
+    $(OBJ_D)\multipart.obj \
+    $(OBJ_D)\object.obj \
+    $(OBJ_D)\request.obj \
+    $(OBJ_D)\request_context.obj \
+    $(OBJ_D)\response_headers_handler.obj \
+    $(OBJ_D)\service.obj \
+    $(OBJ_D)\service_access_logging.obj \
+    $(OBJ_D)\simplexml.obj \
+    $(OBJ_D)\util.obj
+
+.c.obj:
+    $(CC) -n$(OBJ_D) $(LIB_CFLAGS) -c {$< }
+
+$(O_LIBS3): $(LIBS3_OBJS)
+    if exist $(O_LIBS3) del $(O_LIBS3)
+    $(MKLIB) $(O_LIBS3) @&&!
++$(**: = &^
++)
+!
+
+###################################################################
+all: banner $(TMP_D) $(OUT_D) lib
+
+banner:
+    @echo Building libs3
+    @echo $(LIBS3_OBJS)
+
+$(TMP_D):
+    if not exist "$(TMP_D)" ( mkdir "$(TMP_D)" )
+
+$(OUT_D):
+    if not exist "$(OUT_D)" ( mkdir "$(OUT_D)" )
+
+lib: $(LIBS_DEP)

+ 1 - 1
libs/libs3/inc/error_parser.h

@@ -70,7 +70,7 @@ typedef struct ErrorParser
 // Always call this
 void error_parser_initialize(ErrorParser *errorParser);
 
-S3Status error_parser_add(ErrorParser *errorParser, char *buffer,
+S3Status error_parser_add(ErrorParser *errorParser, const /*WINSCP (const)*/ char *buffer,
                           int bufferSize);
 
 void error_parser_convert_status(ErrorParser *errorParser, S3Status *status);

+ 15 - 0
libs/libs3/inc/libs3.h

@@ -28,7 +28,14 @@
 #define LIBS3_H
 
 #include <stdint.h>
+#ifndef WINSCP
 #include <sys/select.h>
+#endif
+
+#ifdef WINSCP
+#define LIBS3_VER_MAJOR "4"
+#define LIBS3_VER_MINOR "1"
+#endif
 
 
 #ifdef __cplusplus
@@ -1594,6 +1601,13 @@ S3Status S3_create_request_context(S3RequestContext **requestContextReturn);
 void S3_destroy_request_context(S3RequestContext *requestContext);
 
 
+#ifdef WINSCP
+struct ne_ssl_certificate_s;
+typedef int (S3SslCallback)(int failures, const ne_ssl_certificate_s * certificate, void *callbackData);
+
+void S3_set_request_context_ssl_callback(S3RequestContext *requestContext,
+                                         S3SslCallback sslCallback);
+#else
 /**
  * Runs the S3RequestContext until all requests within it have completed,
  * or until an error occurs.
@@ -1703,6 +1717,7 @@ int64_t S3_get_request_context_timeout(S3RequestContext *requestContext);
  */
 void S3_set_request_context_verify_peer(S3RequestContext *requestContext,
                                         int verifyPeer);
+#endif
 
 
 /** **************************************************************************

+ 13 - 0
libs/libs3/inc/mingw/pthread.h

@@ -27,6 +27,12 @@
 #ifndef PTHREAD_H
 #define PTHREAD_H
 
+#ifdef WINSCP
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#endif
+
 // This is a minimal implementation of pthreads on Windows, implementing just
 // the APIs needed by libs3
 
@@ -42,4 +48,11 @@ int pthread_mutex_lock(pthread_mutex_t *mutex);
 int pthread_mutex_unlock(pthread_mutex_t *mutex);
 int pthread_mutex_destroy(pthread_mutex_t *mutex);
 
+#ifdef WINSCP
+char* strtok_r(
+    char *str, 
+    const char *delim, 
+    char **nextp);
+#endif
+
 #endif /* PTHREAD_H */

+ 7 - 7
libs/libs3/inc/request.h

@@ -112,10 +112,14 @@ typedef struct RequestParams
 // (and thus live while a curl_multi is in use).
 typedef struct Request
 {
+#ifdef WINSCP
+    struct S3RequestContext *requestContext;
+#else
     // These put the request on a doubly-linked list of requests in a
     // request context, *if* the request is in a request context (else these
     // will both be 0)
     struct Request *prev, *next;
+#endif
 
     // The status of this Request, as will be reported to the user via the
     // complete callback
@@ -126,11 +130,8 @@ typedef struct Request
     // errors the same way
     int httpResponseCode;
 
-    // The HTTP headers to use for the curl request
-    struct curl_slist *headers;
-
-    // The CURL structure driving the request
-    CURL *curl;
+    ne_session *NeonSession; // WINSCP (neon)
+    ne_request *NeonRequest;
 
     // libcurl requires that the uri be stored outside of the curl handle
     char uri[MAX_URI_SIZE + 1];
@@ -184,8 +185,7 @@ void request_perform(const RequestParams *params, S3RequestContext *context);
 // curl has finished the request
 void request_finish(Request *request);
 
-// Convert a CURLE code to an S3Status
-S3Status request_curl_code_to_status(CURLcode code);
+S3Status request_neon_code_to_status(NeonCode code); // WINSCP (neon)
 
 
 #endif /* REQUEST_H */

+ 4 - 0
libs/libs3/inc/request_context.h

@@ -31,12 +31,16 @@
 
 struct S3RequestContext
 {
+#ifdef WINSCP
+    S3SslCallback *sslCallback;
+#else
     CURLM *curlm;
     
     int verifyPeerSet;
     long verifyPeer;
 
     struct Request *requests;
+#endif
 };
 
 

+ 9 - 2
libs/libs3/inc/response_headers_handler.h

@@ -30,6 +30,9 @@
 #include "libs3.h"
 #include "string_buffer.h"
 #include "util.h"
+#ifdef WINSCP
+#include "ne_request.h"
+#endif
 
 
 typedef struct ResponseHeadersHandler
@@ -56,9 +59,13 @@ typedef struct ResponseHeadersHandler
 void response_headers_handler_initialize(ResponseHeadersHandler *handler);
 
 void response_headers_handler_add(ResponseHeadersHandler *handler,
-                                  char *data, int dataLen);
+                                  const char * header_name, const char * header_value); // WINSCP (neon API)
 
 void response_headers_handler_done(ResponseHeadersHandler *handler, 
-                                   CURL *curl);
+                                   ne_request *NeonRequest); // WINSCP (neon API)
+
+#ifdef WINSCP
+typedef int NeonCode;
+#endif
 
 #endif /* RESPONSE_HEADERS_HANDLER_H */

+ 9 - 10
libs/libs3/inc/string_buffer.h

@@ -46,18 +46,17 @@
 
 // Append [len] bytes of [str] to [sb], setting [all_fit] to 1 if it fit, and
 // 0 if it did not
+// WINSCP (CodeGuard compatible implementation)
 #define string_buffer_append(sb, str, len, all_fit)                     \
-    do {                                                                \
-        sb##Len += snprintf(&(sb[sb##Len]), sizeof(sb) - sb##Len - 1,   \
-                            "%.*s", (int) (len), str);                  \
-        if (sb##Len > (int) (sizeof(sb) - 1)) {                         \
-            sb##Len = sizeof(sb) - 1;                                   \
-            all_fit = 0;                                                \
-        }                                                               \
-        else {                                                          \
-            all_fit = 1;                                                \
+    {                                                                   \
+        int _sbai = sizeof(sb) - sb##Len - 1;                           \
+        if (_sbai > (int)len)                                           \
+        {                                                               \
+          _sbai = len;                                                  \
         }                                                               \
-    } while (0)
+        strncat(sb, str, _sbai);                                        \
+        sb##Len += _sbai;                                               \
+    }
 
 
 // Declare a string multibuffer with the given name of the given maximum size

+ 2 - 0
libs/libs3/inc/util.h

@@ -27,8 +27,10 @@
 #ifndef UTIL_H
 #define UTIL_H
 
+#ifndef WINSCP
 #include <curl/curl.h>
 #include <curl/multi.h>
+#endif
 #include <stdint.h>
 #include "libs3.h"
 

+ 6 - 3
libs/libs3/src/bucket.c

@@ -478,7 +478,7 @@ static S3Status make_list_bucket_callback(ListBucketData *lbData)
                        !strcmp(lbData->isTruncated, "1")) ? 1 : 0;
 
     // Convert the contents
-    S3ListBucketContent contents[lbData->contentsCount];
+    S3ListBucketContent * contents = new S3ListBucketContent[lbData->contentsCount]; // WINSCP (heap allocation)
 
     int contentsCount = lbData->contentsCount;
     for (i = 0; i < contentsCount; i++) {
@@ -497,15 +497,18 @@ static S3Status make_list_bucket_callback(ListBucketData *lbData)
 
     // Make the common prefixes array
     int commonPrefixesCount = lbData->commonPrefixesCount;
-    char *commonPrefixes[commonPrefixesCount];
+    char **commonPrefixes = new char*[commonPrefixesCount]; // WINSCP (heap allocation)
     for (i = 0; i < commonPrefixesCount; i++) {
         commonPrefixes[i] = lbData->commonPrefixes[i];
     }
 
-    return (*(lbData->listBucketCallback))
+    S3Status status = (*(lbData->listBucketCallback))
         (isTruncated, lbData->nextMarker,
          contentsCount, contents, commonPrefixesCount,
          (const char **) commonPrefixes, lbData->callbackData);
+    delete[] contents; // WINSCP (heap allocation)
+    delete[] commonPrefixes;
+    return status;
 }
 
 

+ 1 - 1
libs/libs3/src/bucket_metadata.c

@@ -559,7 +559,7 @@ void S3_set_lifecycle(const S3BucketContext *bucketContext,
         0,                                       // contentDispositionFilename
         0,                                       // contentEncoding
        -1,                                       // expires
-        0,                                       // cannedAcl
+        (S3CannedAcl)0,                          // cannedAcl WINSCP (cast)
         0,                                       // metaDataCount
         0,                                       // metaData
         0                                        // useServerSideEncryption

+ 2 - 2
libs/libs3/src/error_parser.c

@@ -82,7 +82,7 @@ static S3Status errorXmlCallback(const char *elementPath, const char *data,
         }
         // OK, must add another unknown error element, if it will fit.
         if (errorParser->s3ErrorDetails.extraDetailsCount ==
-            sizeof(errorParser->extraDetails)) {
+            (int)sizeof(errorParser->extraDetails)) { // WINSCP (cast)
             // Won't fit.  Ignore this one.
             return S3StatusOK;
         }
@@ -131,7 +131,7 @@ void error_parser_initialize(ErrorParser *errorParser)
 }
 
 
-S3Status error_parser_add(ErrorParser *errorParser, char *buffer,
+S3Status error_parser_add(ErrorParser *errorParser, const /*WINSCP (const)*/ char *buffer,
                           int bufferSize)
 {
     if (!errorParser->errorXmlParserInitialized) {

+ 48 - 0
libs/libs3/src/mingw_functions.c

@@ -117,3 +117,51 @@ int uname(struct utsname *u)
 
     return 0;
 }
+
+#ifdef WINSCP
+
+/* 
+ * public domain strtok_r() by Charlie Gordon
+ *
+ *   from comp.lang.c  9/14/2007
+ *
+ *      http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684
+ *
+ *     (Declaration that it's public domain):
+ *      http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c
+ */
+
+char* strtok_r(
+    char *str, 
+    const char *delim, 
+    char **nextp)
+{
+    char *ret;
+
+    if (str == NULL)
+    {
+        str = *nextp;
+    }
+
+    str += strspn(str, delim);
+
+    if (*str == '\0')
+    {
+        return NULL;
+    }
+
+    ret = str;
+
+    str += strcspn(str, delim);
+
+    if (*str)
+    {
+        *str++ = '\0';
+    }
+
+    *nextp = str;
+
+    return ret;
+}
+
+#endif

+ 11 - 5
libs/libs3/src/multipart.c

@@ -531,7 +531,7 @@ static S3Status make_list_multipart_callback(ListMultipartData *lmData)
                        !strcmp(lmData->isTruncated, "1")) ? 1 : 0;
 
     // Convert the contents
-    S3ListMultipartUpload uploads[lmData->uploadsCount];
+    S3ListMultipartUpload * uploads = new S3ListMultipartUpload[lmData->uploadsCount]; // WINSCP (heap allocation)
 
     int uploadsCount = lmData->uploadsCount;
     for (i = 0; i < uploadsCount; i++) {
@@ -551,15 +551,18 @@ static S3Status make_list_multipart_callback(ListMultipartData *lmData)
 
     // Make the common prefixes array
     int commonPrefixesCount = lmData->commonPrefixesCount;
-    char *commonPrefixes[commonPrefixesCount];
+    char **commonPrefixes = new char*[commonPrefixesCount]; // WINSCP (heap allocation)
     for (i = 0; i < commonPrefixesCount; i++) {
         commonPrefixes[i] = lmData->commonPrefixes[i];
     }
 
-    return (*(lmData->listMultipartCallback))
+    S3Status status = (*(lmData->listMultipartCallback))
         (isTruncated, lmData->nextKeyMarker, lmData->nextUploadIdMarker,
          uploadsCount, uploads, commonPrefixesCount,
          (const char **) commonPrefixes, lmData->callbackData);
+    delete[] uploads; // WINSCP (heap allocation)
+    delete[] commonPrefixes;
+    return status;
 }
 
 
@@ -572,7 +575,7 @@ static S3Status make_list_parts_callback(ListPartsData *lpData)
                        !strcmp(lpData->isTruncated, "1")) ? 1 : 0;
 
     // Convert the contents
-    S3ListPart Parts[lpData->partsCount];
+    S3ListPart * Parts = new S3ListPart[lpData->partsCount]; // WINSCP (heap allocation)
     int partsCount = lpData->partsCount;
     for (i = 0; i < partsCount; i++) {
         S3ListPart *partDest = &(Parts[i]);
@@ -583,11 +586,14 @@ static S3Status make_list_parts_callback(ListPartsData *lpData)
         partDest->lastModified = parseIso8601Time(partSrc->lastModified);
     }
 
-    return (*(lpData->listPartsCallback))
+    S3Status status =
+        (*(lpData->listPartsCallback))
         (isTruncated, lpData->nextPartNumberMarker, lpData->initiatorId,
          lpData->initiatorDisplayName, lpData->ownerId,
          lpData->ownerDisplayName, lpData->storageClass, partsCount,
          lpData->handlePartsStart, Parts, lpData->callbackData);
+    delete[] Parts; // WINSCP (heap allocation)
+    return status;
 }
 
 

+ 189 - 246
libs/libs3/src/request.c

@@ -29,7 +29,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/utsname.h>
-#include <libxml/parser.h>
+#include <expat.h>
 #include "request.h"
 #include "request_context.h"
 #include "response_headers_handler.h"
@@ -49,8 +49,6 @@
 
 //#define SIGNATURE_DEBUG
 
-static int verifyPeer;
-
 static char userAgentG[USER_AGENT_SIZE];
 
 static pthread_mutex_t requestStackMutexG;
@@ -157,21 +155,10 @@ static void request_headers_done(Request *request)
 
     request->propertiesCallbackMade = 1;
 
-    // Get the http response code
-    long httpResponseCode;
-    request->httpResponseCode = 0;
-    if (curl_easy_getinfo(request->curl, CURLINFO_RESPONSE_CODE,
-                          &httpResponseCode) != CURLE_OK) {
-        // Not able to get the HTTP response code - error
-        request->status = S3StatusInternalError;
-        return;
-    }
-    else {
-        request->httpResponseCode = httpResponseCode;
-    }
+    request->httpResponseCode = ne_get_status(request->NeonRequest)->code; // WINSCP (neon)
 
     response_headers_handler_done(&(request->responseHeadersHandler),
-                                  request->curl);
+                                  request->NeonRequest); // WINSCP (neon)
 
     // Only make the callback if it was a successful request; otherwise we're
     // returning information about the error response itself
@@ -185,25 +172,31 @@ static void request_headers_done(Request *request)
 }
 
 
-static size_t curl_header_func(void *ptr, size_t size, size_t nmemb,
-                               void *data)
+// WINSCP (neon)
+static int neon_header_func(void * userdata, ne_request * NeonRequest, const ne_status * status)
 {
-    Request *request = (Request *) data;
-
-    int len = size * nmemb;
-
-    response_headers_handler_add
-        (&(request->responseHeadersHandler), (char *) ptr, len);
-
-    return len;
+    Request *request = (Request *) userdata;
+
+    void * cursor = NULL;
+    const char * header_name;
+    const char * header_value;
+    while ((cursor = ne_response_header_iterate(NeonRequest, cursor, &header_name, &header_value)) != NULL)
+    {
+        response_headers_handler_add
+            (&(request->responseHeadersHandler), header_name, header_value);
+    }
+
+    return ne_accept_2xx(userdata, NeonRequest, status);
 }
 
 
-static size_t curl_read_func(void *ptr, size_t size, size_t nmemb, void *data)
+
+// WINSCP (neon)
+static int neon_read_func(void * userdata, char * buf, size_t len)
 {
-    Request *request = (Request *) data;
+    Request *request = (Request *) userdata;
 
-    int len = size * nmemb;
+    const void * ptr = buf;
 
     // CURL may call this function before response headers are available,
     // so don't assume response headers are available and attempt to parse
@@ -211,7 +204,7 @@ static size_t curl_read_func(void *ptr, size_t size, size_t nmemb, void *data)
     // only after headers are available.
 
     if (request->status != S3StatusOK) {
-        return CURL_READFUNC_ABORT;
+        return 0;
     }
 
     // If there is no data callback, or the data callback has already returned
@@ -231,7 +224,7 @@ static size_t curl_read_func(void *ptr, size_t size, size_t nmemb, void *data)
         (len, (char *) ptr, request->callbackData);
     if (ret < 0) {
         request->status = S3StatusAbortedByCallback;
-        return CURL_READFUNC_ABORT;
+        return 0;
     }
     else {
         if (ret > request->toS3CallbackBytesRemaining) {
@@ -243,29 +236,33 @@ static size_t curl_read_func(void *ptr, size_t size, size_t nmemb, void *data)
 }
 
 
-static size_t curl_write_func(void *ptr, size_t size, size_t nmemb,
-                              void *data)
+// WINSCP (neon)
+static int neon_write_func(void * data, const char * buf, size_t len)
 {
     Request *request = (Request *) data;
 
-    int len = size * nmemb;
+    // WinSCP (ignore empty responses)
+    if (len == 0)
+    {
+        return 0;
+    }
 
     request_headers_done(request);
 
     if (request->status != S3StatusOK) {
-        return 0;
+        return 1;
     }
 
     // On HTTP error, we expect to parse an HTTP error response
     if ((request->httpResponseCode < 200) ||
         (request->httpResponseCode > 299)) {
         request->status = error_parser_add
-            (&(request->errorParser), (char *) ptr, len);
+            (&(request->errorParser), buf, len);
     }
     // If there was a callback registered, make it
     else if (request->fromS3Callback) {
         request->status = (*(request->fromS3Callback))
-            (len, (char *) ptr, request->callbackData);
+            (len, buf, request->callbackData);
     }
     // Else, consider this an error - S3 has sent back data when it was not
     // expected
@@ -273,7 +270,7 @@ static size_t curl_write_func(void *ptr, size_t size, size_t nmemb,
         request->status = S3StatusInternalError;
     }
 
-    return ((request->status == S3StatusOK) ? len : 0);
+    return ((request->status == S3StatusOK) ? 0 : 1);
 }
 
 
@@ -558,41 +555,16 @@ static S3Status compose_standard_headers(const RequestParams *params,
                   S3StatusContentEncodingTooLong);
 
     // Expires
-    if (params->putProperties && (params->putProperties->expires >= 0)) {
-        time_t t = (time_t) params->putProperties->expires;
-        struct tm gmt;
-        strftime(values->expiresHeader, sizeof(values->expiresHeader),
-                 "Expires: %a, %d %b %Y %H:%M:%S UTC", gmtime_r(&t, &gmt));
-    }
-    else {
-        values->expiresHeader[0] = 0;
-    }
+    // WINSCP (not implemented)
+    values->expiresHeader[0] = 0;
 
     // If-Modified-Since
-    if (params->getConditions &&
-        (params->getConditions->ifModifiedSince >= 0)) {
-        time_t t = (time_t) params->getConditions->ifModifiedSince;
-        struct tm gmt;
-        strftime(values->ifModifiedSinceHeader,
-                 sizeof(values->ifModifiedSinceHeader),
-                 "If-Modified-Since: %a, %d %b %Y %H:%M:%S UTC", gmtime_r(&t, &gmt));
-    }
-    else {
-        values->ifModifiedSinceHeader[0] = 0;
-    }
+    // WINSCP (not implemented)
+    values->ifModifiedSinceHeader[0] = 0;
 
     // If-Unmodified-Since header
-    if (params->getConditions &&
-        (params->getConditions->ifNotModifiedSince >= 0)) {
-        time_t t = (time_t) params->getConditions->ifNotModifiedSince;
-        struct tm gmt;
-        strftime(values->ifUnmodifiedSinceHeader,
-                 sizeof(values->ifUnmodifiedSinceHeader),
-                 "If-Unmodified-Since: %a, %d %b %Y %H:%M:%S UTC", gmtime_r(&t, &gmt));
-    }
-    else {
-        values->ifUnmodifiedSinceHeader[0] = 0;
-    }
+    // WINSCP (not implemented)
+    values->ifUnmodifiedSinceHeader[0] = 0;
 
     // If-Match header
     do_get_header("If-Match: %s", ifMatchETag, ifMatchHeader,
@@ -655,7 +627,9 @@ static int headerle(const char *s1, const char *s2, char delim)
         }
         s1++, s2++;
     }
+#ifndef WINSCP
     return 0;
+#endif
 }
 
 
@@ -819,9 +793,9 @@ static void sort_query_string(const char *queryString, char *result)
         tmp++;
     }
 
-    const char* params[numParams];
+    const char** params = new const char*[numParams]; // WINSCP (heap allocation)
 
-    char tokenized[strlen(queryString) + 1];
+    char * tokenized = new char[strlen(queryString) + 1]; // WINSCP (heap allocation)
     strncpy(tokenized, queryString, strlen(queryString) + 1);
 
     char *tok = tokenized;
@@ -849,6 +823,9 @@ static void sort_query_string(const char *queryString, char *result)
         strncat(result, "&", 1);
     }
     result[strlen(result) - 1] = '\0';
+
+    delete[] params; // WINSCP (heap allocation)
+    delete[] tokenized;
 }
 
 
@@ -863,10 +840,11 @@ static void canonicalize_query_string(const char *queryParams,
 #define append(str) len += sprintf(&(buffer[len]), "%s", str)
 
     if (queryParams && queryParams[0]) {
-        char sorted[strlen(queryParams) * 2];
+        char * sorted = new char[strlen(queryParams) * 2]; // WINSCP (heap allocation)
         sorted[0] = '\0';
         sort_query_string(queryParams, sorted);
         append(sorted);
+        delete[] sorted; // WINSCP (heap allocation)
     }
 
     if (subResource && subResource[0]) {
@@ -943,13 +921,15 @@ static S3Status compose_auth_header(const RequestParams *params,
 
     int len = 0;
 
-    char canonicalRequest[canonicalRequestLen];
+    char * canonicalRequest = new char[canonicalRequestLen]; // WINSCP (heap allocation)
 
+// WINSCP (heap allocation)
 #define buf_append(buf, format, ...)                    \
-    len += snprintf(&(buf[len]), sizeof(buf) - len,     \
+    len += snprintf(&(buf[len]), size - len,     \
                     format, __VA_ARGS__)
 
     canonicalRequest[0] = '\0';
+    int size = canonicalRequestLen; // WINSCP
     buf_append(canonicalRequest, "%s\n", httpMethod);
     buf_append(canonicalRequest, "%s\n", values->canonicalURI);
     buf_append(canonicalRequest, "%s\n", values->canonicalQueryString);
@@ -970,7 +950,9 @@ static S3Status compose_auth_header(const RequestParams *params,
     const unsigned char *rqstData = (const unsigned char*) canonicalRequest;
     SHA256(rqstData, strlen(canonicalRequest), canonicalRequestHash);
 #endif
+    delete[] canonicalRequest; // WINSCP
     char canonicalRequestHashHex[2 * S3_SHA256_DIGEST_LENGTH + 1];
+    size = sizeof(canonicalRequestHashHex); // WINSCP
     canonicalRequestHashHex[0] = '\0';
     int i = 0;
     for (; i < S3_SHA256_DIGEST_LENGTH; i++) {
@@ -985,9 +967,10 @@ static S3Status compose_auth_header(const RequestParams *params,
     snprintf(scope, sizeof(scope), "%.8s/%s/s3/aws4_request",
              values->requestDateISO8601, awsRegion);
 
-    char stringToSign[17 + 17 + SIGNATURE_SCOPE_SIZE + 1
-        + strlen(canonicalRequestHashHex)];
-    snprintf(stringToSign, sizeof(stringToSign), "AWS4-HMAC-SHA256\n%s\n%s\n%s",
+    const int stringToSignLen = 17 + 17 + SIGNATURE_SCOPE_SIZE + 1
+        + strlen(canonicalRequestHashHex); // WINSCP (heap allocation)
+    char * stringToSign = new char[stringToSignLen];
+    snprintf(stringToSign, stringToSignLen, "AWS4-HMAC-SHA256\n%s\n%s\n%s",
              values->requestDateISO8601, scope, canonicalRequestHashHex);
 
 #ifdef SIGNATURE_DEBUG
@@ -995,8 +978,9 @@ static S3Status compose_auth_header(const RequestParams *params,
 #endif
 
     const char *secretAccessKey = params->bucketContext.secretAccessKey;
-    char accessKey[strlen(secretAccessKey) + 5];
-    snprintf(accessKey, sizeof(accessKey), "AWS4%s", secretAccessKey);
+    const int accessKeyLen = strlen(secretAccessKey) + 5; // WINSCP (heap allocation)
+    char * accessKey = new char[accessKeyLen];
+    snprintf(accessKey, accessKeyLen, "AWS4%s", secretAccessKey);
 
 #ifdef __APPLE__
     unsigned char dateKey[S3_SHA256_DIGEST_LENGTH];
@@ -1039,8 +1023,11 @@ static S3Status compose_auth_header(const RequestParams *params,
          (const unsigned char*) stringToSign, strlen(stringToSign),
          finalSignature, NULL);
 #endif
+    delete[] accessKey; // WINSCP
+    delete[] stringToSign; // WINSCP
 
     len = 0;
+    size = sizeof(values->requestSignatureHex); // WINSCP
     values->requestSignatureHex[0] = '\0';
     for (i = 0; i < S3_SHA256_DIGEST_LENGTH; i++) {
         buf_append(values->requestSignatureHex, "%02x", finalSignature[i]);
@@ -1127,111 +1114,132 @@ static S3Status compose_uri(char *buffer, int bufferSize,
     return S3StatusOK;
 }
 
-// Sets up the curl handle given the completely computed RequestParams
-static S3Status setup_curl(Request *request,
+#ifdef WINSCP
+int neon_ssl_callback(void * user_data, int failures, const ne_ssl_certificate * certificate)
+{
+    Request *request = (Request *) user_data;
+
+    int result = NE_ERROR;
+    if (request->requestContext->sslCallback != NULL)
+    {
+        result = request->requestContext->sslCallback(failures, certificate, request->callbackData);
+    }
+    return result;
+}
+#endif
+
+
+// WINSCP (neon)
+// Sets up the neon handle given the completely computed RequestParams
+static S3Status setup_neon(Request *request,
                            const RequestParams *params,
                            const RequestComputedValues *values)
 {
-    CURLcode status;
+    NeonCode status;
+
+    ne_uri uri;
+    if (ne_uri_parse(request->uri, &uri) != 0)
+    {
+        return S3StatusFailedToInitializeRequest;
+    }
 
-#define curl_easy_setopt_safe(opt, val)                                 \
-    if ((status = curl_easy_setopt                                      \
-         (request->curl, opt, val)) != CURLE_OK) {                      \
-        return S3StatusFailedToInitializeRequest;                       \
+    int port = uri.port;
+    if (port == 0)
+    {
+      port = ne_uri_defaultport(uri.scheme);
     }
+    request->NeonSession = ne_session_create(uri.scheme, uri.host, port);
 
-    // Debugging only
-    // curl_easy_setopt_safe(CURLOPT_VERBOSE, 1);
+    char method[64];
+    strcpy(method, "GET");
+    switch (params->httpRequestType) {
+    case HttpRequestTypeHEAD:
+        strcpy(method, "HEAD");
+        break;
+    case HttpRequestTypePOST:
+        strcpy(method, "POST");
+        break;
 
-    // Set private data to request for the benefit of S3RequestContext
-    curl_easy_setopt_safe(CURLOPT_PRIVATE, request);
+    case HttpRequestTypePUT:
+    case HttpRequestTypeCOPY:
+        strcpy(method, "PUT");
+        break;
+    case HttpRequestTypeDELETE:
+        strcpy(method, "DELETE");
+        break;
+    default: // HttpRequestTypeGET
+        break;
+    }
 
-    // Set header callback and data
-    curl_easy_setopt_safe(CURLOPT_HEADERDATA, request);
-    curl_easy_setopt_safe(CURLOPT_HEADERFUNCTION, &curl_header_func);
+    ne_buffer * buf = ne_buffer_create();
+    ne_buffer_zappend(buf, uri.path);
+    if (uri.query != NULL)
+    {
+        ne_buffer_concat(buf, "?", uri.query, NULL);
+    }
+    request->NeonRequest = ne_request_create(request->NeonSession, method, buf->data);
+    ne_buffer_destroy(buf);
+    ne_uri_free(&uri);
 
+    // Set header callback and data
     // Set read callback, data, and readSize
-    curl_easy_setopt_safe(CURLOPT_READFUNCTION, &curl_read_func);
-    curl_easy_setopt_safe(CURLOPT_READDATA, request);
+    ne_add_response_body_reader(request->NeonRequest, neon_header_func, neon_write_func, request);
 
     // Set write callback and data
-    curl_easy_setopt_safe(CURLOPT_WRITEFUNCTION, &curl_write_func);
-    curl_easy_setopt_safe(CURLOPT_WRITEDATA, request);
-
-    // Ask curl to parse the Last-Modified header.  This is easier than
-    // parsing it ourselves.
-    curl_easy_setopt_safe(CURLOPT_FILETIME, 1);
-
-    // Curl docs suggest that this is necessary for multithreaded code.
-    // However, it also points out that DNS timeouts will not be honored
-    // during DNS lookup, which can be worked around by using the c-ares
-    // library, which we do not do yet.
-    curl_easy_setopt_safe(CURLOPT_NOSIGNAL, 1);
+    if ((params->httpRequestType == HttpRequestTypePUT) ||
+        (params->httpRequestType == HttpRequestTypePOST))
+    {
+        ne_set_request_body_provider(request->NeonRequest, (ne_off_t)params->toS3CallbackTotalSize, neon_read_func, request);
+    }
 
-    // Turn off Curl's built-in progress meter
-    curl_easy_setopt_safe(CURLOPT_NOPROGRESS, 1);
+    // WINSCP (Last-Modified parsed in response_headers_handler_done)
 
     // xxx todo - support setting the proxy for Curl to use (can't use https
     // for proxies though)
 
     // xxx todo - support setting the network interface for Curl to use
 
-    // I think this is useful - we don't need interactive performance, we need
-    // to complete large operations quickly
-    curl_easy_setopt_safe(CURLOPT_TCP_NODELAY, 1);
-
-    // Don't use Curl's 'netrc' feature
-    curl_easy_setopt_safe(CURLOPT_NETRC, CURL_NETRC_IGNORED);
+    // WINSCP (neon sets "nodelay" unconditionally)
 
-    // Don't verify S3's certificate unless S3_INIT_VERIFY_PEER is set.
-    // The request_context may be set to override this
-    curl_easy_setopt_safe(CURLOPT_SSL_VERIFYPEER, verifyPeer);
+    // WINSCP (we should verify peer always)
+    ne_ssl_set_verify(request->NeonSession, neon_ssl_callback, request);
+    ne_ssl_trust_default_ca(request->NeonSession);
 
     // Follow any redirection directives that S3 sends
-    curl_easy_setopt_safe(CURLOPT_FOLLOWLOCATION, 1);
-
-    // A safety valve in case S3 goes bananas with redirects
-    curl_easy_setopt_safe(CURLOPT_MAXREDIRS, 10);
+    // TODO
 
     // Set the User-Agent; maybe Amazon will track these?
-    curl_easy_setopt_safe(CURLOPT_USERAGENT, userAgentG);
-
-    // Set the low speed limit and time; we abort transfers that stay at
-    // less than 1K per second for more than 15 seconds.
-    // xxx todo - make these configurable
-    // xxx todo - allow configurable max send and receive speed
-    curl_easy_setopt_safe(CURLOPT_LOW_SPEED_LIMIT, 1024);
-    curl_easy_setopt_safe(CURLOPT_LOW_SPEED_TIME, 15);
-
+    ne_set_useragent(request->NeonSession, userAgentG);
 
     if (params->timeoutMs > 0) {
-        curl_easy_setopt_safe(CURLOPT_TIMEOUT_MS, params->timeoutMs);
+        ne_set_read_timeout(request->NeonSession, params->timeoutMs);
+
+        ne_set_connect_timeout(request->NeonSession, params->timeoutMs);
+    }
+
+#define do_add_header(header) \
+    { \
+        char * buf = new char[strlen(header) + 1]; \
+        strcpy(buf, header); \
+        char * p = strchr(buf, ':'); \
+        if (p != NULL) \
+        { \
+            *p = '\0'; \
+            p++; \
+            while (is_blank(p[0])) p++; \
+            ne_add_request_header(request->NeonRequest, buf, p); \
+        } \
+        delete[] buf; \
     }
 
 
     // Append standard headers
 #define append_standard_header(fieldName)                               \
     if (values-> fieldName [0]) {                                       \
-        request->headers = curl_slist_append(request->headers,          \
-                                             values-> fieldName);       \
-    }
-
-    // Would use CURLOPT_INFILESIZE_LARGE, but it is buggy in libcurl
-    if ((params->httpRequestType == HttpRequestTypePUT) ||
-        (params->httpRequestType == HttpRequestTypePOST)) {
-        char header[256];
-        snprintf(header, sizeof(header), "Content-Length: %llu",
-                 (unsigned long long) params->toS3CallbackTotalSize);
-        request->headers = curl_slist_append(request->headers, header);
-        request->headers = curl_slist_append(request->headers,
-                                             "Transfer-Encoding:");
-    }
-    else if (params->httpRequestType == HttpRequestTypeCOPY) {
-        request->headers = curl_slist_append(request->headers,
-                                             "Transfer-Encoding:");
+        do_add_header(values-> fieldName);                               \
     }
 
-    append_standard_header(hostHeader);
+    // WINSCP (hostHeader is added implicitly by neon based on uri)
     append_standard_header(cacheControlHeader);
     append_standard_header(contentTypeHeader);
     append_standard_header(md5Header);
@@ -1248,35 +1256,7 @@ static S3Status setup_curl(Request *request,
     // Append x-amz- headers
     int i;
     for (i = 0; i < values->amzHeadersCount; i++) {
-        request->headers =
-            curl_slist_append(request->headers, values->amzHeaders[i]);
-    }
-
-    // Set the HTTP headers
-    curl_easy_setopt_safe(CURLOPT_HTTPHEADER, request->headers);
-
-    // Set URI
-    curl_easy_setopt_safe(CURLOPT_URL, request->uri);
-
-    // Set request type.
-    switch (params->httpRequestType) {
-    case HttpRequestTypeHEAD:
-        curl_easy_setopt_safe(CURLOPT_NOBODY, 1);
-        break;
-    case HttpRequestTypePOST:
-        curl_easy_setopt_safe(CURLOPT_CUSTOMREQUEST, "POST");
-        curl_easy_setopt_safe(CURLOPT_UPLOAD, 1);
-        break;
-
-    case HttpRequestTypePUT:
-    case HttpRequestTypeCOPY:
-        curl_easy_setopt_safe(CURLOPT_UPLOAD, 1);
-        break;
-    case HttpRequestTypeDELETE:
-        curl_easy_setopt_safe(CURLOPT_CUSTOMREQUEST, "DELETE");
-        break;
-    default: // HttpRequestTypeGET
-        break;
+        do_add_header(values->amzHeaders[i]);
     }
 
     return S3StatusOK;
@@ -1285,17 +1265,10 @@ static S3Status setup_curl(Request *request,
 
 static void request_deinitialize(Request *request)
 {
-    if (request->headers) {
-        curl_slist_free_all(request->headers);
-    }
+    ne_request_destroy(request->NeonRequest);
+    ne_session_destroy(request->NeonSession);
 
     error_parser_deinitialize(&(request->errorParser));
-
-    // curl_easy_reset prevents connections from being re-used for some
-    // reason.  This makes HTTP Keep-Alive meaningless and is very bad for
-    // performance.  But it is necessary to allow curl to work properly.
-    // xxx todo figure out why
-    curl_easy_reset(request->curl);
 }
 
 
@@ -1321,18 +1294,17 @@ static S3Status request_get(const RequestParams *params,
     }
     // Else there wasn't one available in the request stack, so create one
     else {
-        if (!(request = (Request *) malloc(sizeof(Request)))) {
+        if ((request = (Request *) malloc(sizeof(Request))) == NULL) {
             return S3StatusOutOfMemory;
         }
-        if (!(request->curl = curl_easy_init())) {
-            free(request);
-            return S3StatusFailedToInitializeRequest;
-        }
+        request->NeonSession = NULL;
     }
 
     // Initialize the request
+    #ifndef WINSCP
     request->prev = 0;
     request->next = 0;
+    #endif
 
     // Request status is initialized to no error, will be updated whenever
     // an error occurs
@@ -1340,22 +1312,17 @@ static S3Status request_get(const RequestParams *params,
 
     S3Status status;
 
-    // Start out with no headers
-    request->headers = 0;
-
     // Compute the URL
     if ((status = compose_uri
          (request->uri, sizeof(request->uri),
           &(params->bucketContext), values->urlEncodedKey,
           params->subResource, params->queryParams)) != S3StatusOK) {
-        curl_easy_cleanup(request->curl);
         free(request);
         return status;
     }
 
-    // Set all of the curl handle options
-    if ((status = setup_curl(request, params, values)) != S3StatusOK) {
-        curl_easy_cleanup(request->curl);
+    // Set all of the handle options
+    if ((status = setup_neon(request, params, values)) != S3StatusOK) {
         free(request);
         return status;
     }
@@ -1387,7 +1354,6 @@ static S3Status request_get(const RequestParams *params,
 static void request_destroy(Request *request)
 {
     request_deinitialize(request);
-    curl_easy_cleanup(request->curl);
     free(request);
 }
 
@@ -1415,12 +1381,7 @@ static void request_release(Request *request)
 S3Status request_api_initialize(const char *userAgentInfo, int flags,
                                 const char *defaultHostName)
 {
-    if (curl_global_init(CURL_GLOBAL_ALL &
-                         ~((flags & S3_INIT_WINSOCK) ? 0 : CURL_GLOBAL_WIN32))
-        != CURLE_OK) {
-        return S3StatusInternalError;
-    }
-    verifyPeer = (flags & S3_INIT_VERIFY_PEER) != 0;
+    // WINSCP (winsock must be initialized by caller)
 
     if (!defaultHostName) {
         defaultHostName = S3_DEFAULT_HOSTNAME;
@@ -1453,7 +1414,7 @@ S3Status request_api_initialize(const char *userAgentInfo, int flags,
              "Mozilla/4.0 (Compatible; %s; libs3 %s.%s; %s)",
              userAgentInfo, LIBS3_VER_MAJOR, LIBS3_VER_MINOR, platform);
 
-    xmlInitParser();
+    // WINSCP (Expat does not need global initialization)
     return S3StatusOK;
 }
 
@@ -1462,7 +1423,7 @@ void request_api_deinitialize()
 {
     pthread_mutex_destroy(&requestStackMutexG);
 
-    xmlCleanupParser();
+    // WINSCP (Expat does not need global initialization)
     while (requestStackCountG--) {
         request_destroy(requestStackG[requestStackCountG]);
     }
@@ -1484,7 +1445,7 @@ static S3Status setup_request(const RequestParams *params,
 
     time_t now = time(NULL);
     struct tm gmt;
-    gmtime_r(&now, &gmt);
+    gmtime_s(&now, &gmt); // WINSCP (gmtime_s)
     strftime(computed->requestDateISO8601, sizeof(computed->requestDateISO8601),
              "%Y%m%dT%H%M%SZ", &gmt);
 
@@ -1533,8 +1494,6 @@ void request_perform(const RequestParams *params, S3RequestContext *context)
 {
     Request *request;
     S3Status status;
-    int verifyPeerRequest = verifyPeer;
-    CURLcode curlstatus;
 
 #define return_status(status)                                           \
     (*(params->completeCallback))(status, 0, params->callbackData);     \
@@ -1551,19 +1510,11 @@ void request_perform(const RequestParams *params, S3RequestContext *context)
     if ((status = request_get(params, &computed, &request)) != S3StatusOK) {
         return_status(status);
     }
-    if (context && context->verifyPeerSet) {
-        verifyPeerRequest = context->verifyPeerSet;
-    }
-    // Allow per-context override of verifyPeer
-    if (verifyPeerRequest != verifyPeer) {
-        if ((curlstatus = curl_easy_setopt(request->curl,
-                                           CURLOPT_SSL_VERIFYPEER,
-                                           context->verifyPeer))
-            != CURLE_OK) {
-            return_status(S3StatusFailedToInitializeRequest);
-        }
-    }
+    // WINSCP (we should always verify the peer)
 
+    #ifdef WINSCP
+    request->requestContext = context;
+    #else
     // If a RequestContext was provided, add the request to the curl multi
     if (context) {
         CURLMcode code = curl_multi_add_handle(context->curlm, request->curl);
@@ -1587,10 +1538,12 @@ void request_perform(const RequestParams *params, S3RequestContext *context)
         }
     }
     // Else, perform the request immediately
-    else {
-        CURLcode code = curl_easy_perform(request->curl);
-        if ((code != CURLE_OK) && (request->status == S3StatusOK)) {
-            request->status = request_curl_code_to_status(code);
+    else 
+    #endif
+    {
+        NeonCode code = ne_request_dispatch(request->NeonRequest);
+        if ((code != NE_OK) && (request->status == S3StatusOK)) {
+            request->status = request_neon_code_to_status(code);
         }
 
         // Finish the request, ensuring that all callbacks have been made, and
@@ -1680,28 +1633,15 @@ void request_finish(Request *request)
 }
 
 
-S3Status request_curl_code_to_status(CURLcode code)
+S3Status request_neon_code_to_status(NeonCode code)
 {
     switch (code) {
-    case CURLE_OUT_OF_MEMORY:
-        return S3StatusOutOfMemory;
-    case CURLE_COULDNT_RESOLVE_PROXY:
-    case CURLE_COULDNT_RESOLVE_HOST:
+    case NE_LOOKUP:
         return S3StatusNameLookupError;
-    case CURLE_COULDNT_CONNECT:
+    case NE_CONNECT:
         return S3StatusFailedToConnect;
-    case CURLE_WRITE_ERROR:
-    case CURLE_OPERATION_TIMEDOUT:
+    case NE_TIMEOUT:
         return S3StatusErrorRequestTimeout;
-    case CURLE_PARTIAL_FILE:
-        return S3StatusOK;
-#if LIBCURL_VERSION_NUM >= 0x071101 /* 7.17.1 */
-    case CURLE_PEER_FAILED_VERIFICATION:
-#else
-    case CURLE_SSL_PEER_CERTIFICATE:
-#endif
-    case CURLE_SSL_CACERT:
-        return S3StatusServerFailedVerification;
     default:
         return S3StatusInternalError;
     }
@@ -1723,8 +1663,11 @@ S3Status S3_generate_authenticated_query_string
         expires = MAX_EXPIRES;
     }
 
+    // WinSCP
     RequestParams params =
-    { http_request_method_to_type(httpMethod), *bucketContext, key, NULL,
+    { http_request_method_to_type(httpMethod),
+        { bucketContext->hostName, bucketContext->bucketName, bucketContext->protocol, bucketContext->uriStyle, bucketContext->accessKeyId, bucketContext->secretAccessKey, bucketContext->securityToken, bucketContext->authRegion },
+        key, NULL,
         resource,
         NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 0, NULL, NULL, NULL, 0};
 

+ 20 - 0
libs/libs3/src/request_context.c

@@ -24,7 +24,9 @@
  *
  ************************************************************************** **/
 
+#ifndef WINSCP
 #include <curl/curl.h>
+#endif
 #include <stdlib.h>
 #include <sys/select.h>
 #include "request.h"
@@ -40,6 +42,9 @@ S3Status S3_create_request_context(S3RequestContext **requestContextReturn)
         return S3StatusOutOfMemory;
     }
     
+#ifdef WINSCP
+    (*requestContextReturn)->sslCallback = NULL;
+#else
     if (!((*requestContextReturn)->curlm = curl_multi_init())) {
         free(*requestContextReturn);
         return S3StatusOutOfMemory;
@@ -48,6 +53,7 @@ S3Status S3_create_request_context(S3RequestContext **requestContextReturn)
     (*requestContextReturn)->requests = 0;
     (*requestContextReturn)->verifyPeer = 0;
     (*requestContextReturn)->verifyPeerSet = 0;
+#endif
 
     return S3StatusOK;
 }
@@ -55,6 +61,7 @@ S3Status S3_create_request_context(S3RequestContext **requestContextReturn)
 
 void S3_destroy_request_context(S3RequestContext *requestContext)
 {
+#ifndef WINSCP
     // For each request in the context, remove curl handle, call back its done
     // method with 'interrupted' status
     Request *r = requestContext->requests, *rFirst = r;
@@ -69,11 +76,22 @@ void S3_destroy_request_context(S3RequestContext *requestContext)
     } while (r != rFirst);
 
     curl_multi_cleanup(requestContext->curlm);
+#endif
 
     free(requestContext);
 }
 
 
+#ifdef WINSCP
+
+void S3_set_request_context_ssl_callback(S3RequestContext *requestContext,
+                                         S3SslCallback sslCallback)
+{
+    requestContext->sslCallback = sslCallback;
+}
+
+#else
+
 S3Status S3_runall_request_context(S3RequestContext *requestContext)
 {
     int requestsRemaining;
@@ -199,3 +217,5 @@ void S3_set_request_context_verify_peer(S3RequestContext *requestContext,
     requestContext->verifyPeerSet = 1;
     requestContext->verifyPeer = (verifyPeer != 0);
 }
+
+#endif

+ 18 - 55
libs/libs3/src/response_headers_handler.c

@@ -26,8 +26,13 @@
 
 #include <ctype.h>
 #include <string.h>
+#ifndef WINSCP
 #include <strings.h>
+#endif
 #include "response_headers_handler.h"
+#ifdef WINSCP
+#include "ne_dates.h"
+#endif
 
 
 void response_headers_handler_initialize(ResponseHeadersHandler *handler)
@@ -49,10 +54,9 @@ void response_headers_handler_initialize(ResponseHeadersHandler *handler)
 
 
 void response_headers_handler_add(ResponseHeadersHandler *handler,
-                                  char *header, int len)
+                                  const char * header_name, const char * header_value) // WINSCP (neon API)
 {
     S3ResponseProperties *responseProperties = &(handler->responseProperties);
-    char *end = &(header[len]);
     
     // Curl might call back the header function after the body has been
     // received, for 'chunked encoded' contents.  We don't handle this as of
@@ -65,54 +69,16 @@ void response_headers_handler_add(ResponseHeadersHandler *handler,
     // This sucks, but it shouldn't happen - S3 should not be sending back
     // really long headers.
     if (handler->responsePropertyStringsSize == 
-        (sizeof(handler->responsePropertyStrings) - 1)) {
+        (int)(sizeof(handler->responsePropertyStrings) - 1)) { // WINSCP (cast)
         return;
     }
 
-    // It should not be possible to have a header line less than 3 long
-    if (len < 3) {
-        return;
-    }
-
-    // Skip whitespace at beginning of header; there never should be any,
-    // but just to be safe
-    while (is_blank(*header)) {
-        header++;
-    }
-
-    // The header must end in \r\n, so skip back over it, and also over any
-    // trailing whitespace
-    end -= 3;
-    while ((end > header) && is_blank(*end)) {
-        end--;
-    }
-    if (!is_blank(*end)) {
-        end++;
-    }
-
-    if (end == header) {
-        // totally bogus
-        return;
-    }
-
-    *end = 0;
-    
-    // Find the colon to split the header up
-    char *c = header;
-    while (*c && (*c != ':')) {
-        c++;
-    }
-    
-    int namelen = c - header;
-
-    // Now walk c past the colon
-    c++;
-    // Now skip whitespace to the beginning of the value
-    while (is_blank(*c)) {
-        c++;
-    }
+    int valuelen = strlen(header_value), fit;
+    const char * c = header_value;
+    int namelen = strlen(header_name);
+    const char * header = header_name;
 
-    int valuelen = (end - c) + 1, fit;
+    #define strncasecmp strnicmp // WINSCP
 
     if (!strncasecmp(header, "x-amz-request-id", namelen)) {
         responseProperties->requestId = 
@@ -155,11 +121,11 @@ void response_headers_handler_add(ResponseHeadersHandler *handler,
                       sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1)) {
         // Make sure there is room for another x-amz-meta header
         if (handler->responseProperties.metaDataCount ==
-            sizeof(handler->responseMetaData)) {
+            (int)sizeof(handler->responseMetaData)) { // WINSCP (cast)
             return;
         }
         // Copy the name in
-        char *metaName = &(header[sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1]);
+        const char *metaName = &(header[sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1]);
         int metaNameLen = 
             (namelen - (sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1));
         char *copiedName = 
@@ -201,14 +167,11 @@ void response_headers_handler_add(ResponseHeadersHandler *handler,
 }
 
 
-void response_headers_handler_done(ResponseHeadersHandler *handler, CURL *curl)
+void response_headers_handler_done(ResponseHeadersHandler *handler, ne_request *NeonRequest) // WINSCP (neon API)
 {
-    // Now get the last modification time from curl, since it's easiest to let
-    // curl parse it
-    time_t lastModified;
-    if (curl_easy_getinfo
-        (curl, CURLINFO_FILETIME, &lastModified) == CURLE_OK) {
-        handler->responseProperties.lastModified = lastModified;
+    const char * value = ne_get_response_header(NeonRequest, "Last-Modified");
+    if (value != NULL) {
+        handler->responseProperties.lastModified = ne_httpdate_parse(value);
     }
     
     handler->done = 1;

+ 15 - 6
libs/libs3/src/simplexml.c

@@ -24,10 +24,12 @@
  *
  ************************************************************************** **/
 
-#include <libxml/parser.h>
+#include <expat.h> // WINSCP (expat)
 #include <string.h>
 #include "simplexml.h"
 
+typedef XML_Char xmlChar; // WINSCP (expat)
+
 // Use libxml2 for parsing XML.  XML is severely overused in modern
 // computing.  It is useful for only a very small subset of tasks, but
 // software developers who don't know better and are afraid to go against the
@@ -45,12 +47,14 @@
 // S3 appears to only use ASCII anyway.
 
 
+#ifndef WINSCP
 static xmlEntityPtr saxGetEntity(void *user_data, const xmlChar *name)
 {
     (void) user_data;
 
     return xmlGetPredefinedEntity(name);
 }
+#endif
 
 
 static void saxStartElement(void *user_data, const xmlChar *nameUtf8,
@@ -135,6 +139,7 @@ static void saxError(void *user_data, const char *msg, ...)
 }
 
 
+#ifndef WINSCP
 static struct _xmlSAXHandler saxHandlerG =
 {
     0, // internalSubsetSAXFunc
@@ -170,6 +175,7 @@ static struct _xmlSAXHandler saxHandlerG =
     0, // endElementNsSAX2Func
     0 // xmlStructuredErrorFunc serror;
 };
+#endif
 
 void simplexml_initialize(SimpleXml *simpleXml, 
                           SimpleXmlCallback *callback, void *callbackData)
@@ -185,7 +191,7 @@ void simplexml_initialize(SimpleXml *simpleXml,
 void simplexml_deinitialize(SimpleXml *simpleXml)
 {
     if (simpleXml->xmlParser) {
-        xmlFreeParserCtxt(simpleXml->xmlParser);
+        XML_ParserFree((XML_Parser)simpleXml->xmlParser); // WINSCP (expat)
     }
 }
 
@@ -193,13 +199,16 @@ void simplexml_deinitialize(SimpleXml *simpleXml)
 S3Status simplexml_add(SimpleXml *simpleXml, const char *data, int dataLen)
 {
     if (!simpleXml->xmlParser &&
-        (!(simpleXml->xmlParser = xmlCreatePushParserCtxt
-           (&saxHandlerG, simpleXml, 0, 0, 0)))) {
+        ((simpleXml->xmlParser = XML_ParserCreate(NULL)) == NULL)) { // WINSCP (expat)
         return S3StatusInternalError;
     }
 
-    if (xmlParseChunk((xmlParserCtxtPtr) simpleXml->xmlParser, 
-                      data, dataLen, 0)) {
+    XML_SetElementHandler((XML_Parser)simpleXml->xmlParser, saxStartElement, saxEndElement); // WINSCP (expat)
+    XML_SetCharacterDataHandler((XML_Parser)simpleXml->xmlParser, saxCharacters); // WINSCP (expat)
+    XML_SetUserData((XML_Parser)simpleXml->xmlParser, (void *) simpleXml);
+
+    if (XML_Parse((XML_Parser)simpleXml->xmlParser,
+                      data, dataLen, 0) == 0) {
         return S3StatusXmlParseFailure;
     }
 

+ 2 - 0
libs/libs3/src/util.c

@@ -26,6 +26,8 @@
 
 #include <ctype.h>
 #include <string.h>
+#include <time.h>
+
 #include "util.h"
 
 

+ 4 - 0
source/WinSCP.cbproj

@@ -223,6 +223,10 @@
 			<BuildOrder>2</BuildOrder>
 			<BuildOrder>19</BuildOrder>
 		</CppCompile>
+		<LibFiles Include="..\libs\lib\libs3.lib" Condition="'$(Platform)'=='Win32'">
+			<BuildOrder>34</BuildOrder>
+			<IgnorePath>true</IgnorePath>
+		</LibFiles>
 		<LibFiles Include="FileZilla.lib">
 			<BuildOrder>27</BuildOrder>
 			<IgnorePath>true</IgnorePath>