send.c 16.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * HTTP handling functions.
 *
 * Copyright 2003 Ferenc Wagner
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
 */
20

21
#include <winsock2.h>
22
#include <wininet.h>
23
#include <stdio.h>
24
#include <errno.h>
25 26 27

#include "winetest.h"

28 29 30 31 32 33 34 35 36 37 38 39 40 41
#define USER_AGENT  "Winetest Shell"
#define SERVER_NAME "test.winehq.org"
#define URL_PATH "/submit"
#define SEP "--8<--cut-here--8<--"
#define CONTENT_HEADERS "Content-Type: multipart/form-data; boundary=\"" SEP "\"\r\n" \
                        "Content-Length: %u\r\n\r\n"
static const char body1[] = "--" SEP "\r\n"
    "Content-Disposition: form-data; name=\"reportfile\"; filename=\"%s\"\r\n"
    "Content-Type: application/octet-stream\r\n\r\n";
static const char body2[] = "\r\n--" SEP "\r\n"
    "Content-Disposition: form-data; name=\"submit\"\r\n\r\n"
    "Upload File\r\n"
    "--" SEP "--\r\n";

42
static SOCKET
43
open_http (const char *server)
44 45 46 47 48
{
    WSADATA wsad;
    struct sockaddr_in sa;
    SOCKET s;

49
    report (R_STATUS, "Opening HTTP connection to %s", server);
50 51
    if (WSAStartup (MAKEWORD (2,2), &wsad)) return INVALID_SOCKET;

52 53 54
    sa.sin_family = AF_INET;
    sa.sin_port = htons (80);
    sa.sin_addr.s_addr = inet_addr (server);
55
    memset (sa.sin_zero, 0, sizeof(sa.sin_zero));
56 57 58 59 60
    if (sa.sin_addr.s_addr == INADDR_NONE) {
        struct hostent *host = gethostbyname (server);
        if (!host) {
            report (R_ERROR, "Hostname lookup failed for %s", server);
            goto failure;
61
        }
62 63 64 65 66 67 68
        sa.sin_addr.s_addr = ((struct in_addr *)host->h_addr)->s_addr;
    }
    s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET) {
        report (R_ERROR, "Can't open network socket: %d",
                WSAGetLastError ());
        goto failure;
69
    }
70 71 72 73 74 75
    if (!connect (s, (struct sockaddr*)&sa, sizeof (struct sockaddr_in)))
        return s;

    report (R_ERROR, "Can't connect: %d", WSAGetLastError ());
    closesocket (s);
 failure:
76 77 78 79
    WSACleanup ();
    return INVALID_SOCKET;
}

80
static int
81 82 83 84 85 86 87 88
close_http (SOCKET s)
{
    int ret;

    ret = closesocket (s);
    return (WSACleanup () || ret);
}

89
static int
90 91 92 93 94 95
send_buf (SOCKET s, const char *buf, size_t length)
{
    int sent;

    while (length > 0) {
        sent = send (s, buf, length, 0);
96 97 98 99
        if (sent == SOCKET_ERROR) {
            if (errno == EINTR) continue;
            return 1;
        }
100 101 102 103 104 105
        buf += sent;
        length -= sent;
    }
    return 0;
}

106
static int WINAPIV send_str (SOCKET s, ...)
107
{
108
    va_list ap;
109 110 111 112
    char *p;
    int ret;
    size_t len;

113
    va_start (ap, s);
114
    p = vstrmake (&len, ap);
115
    va_end (ap);
116 117
    if (!p) return 1;
    ret = send_buf (s, p, len);
118
    heap_free (p);
119 120 121
    return ret;
}

122
static int
123
send_file_direct (const char * url, const char *name)
124 125
{
    SOCKET s;
126
    HANDLE file;
127
#define BUFLEN 8192
Mike McCormack's avatar
Mike McCormack committed
128
    char buffer[BUFLEN+1];
129
    DWORD bytes_read, filesize;
130
    size_t total, count;
131 132 133
    char *str;
    int ret;

134
    /* RFC 2616 */
135 136 137 138 139 140
    static const char head[] = "POST " URL_PATH " HTTP/1.0\r\n"
        "Host: " SERVER_NAME "\r\n"
        "User-Agent: " USER_AGENT "\r\n"
        CONTENT_HEADERS
        "\r\n";

141 142 143 144 145
    if (url) {
        report (R_WARNING, "Can't submit to an alternative URL");
        return 0;
    }

146
    s = open_http (SERVER_NAME);
147
    if (s == INVALID_SOCKET) return 1;
148

149 150 151
    file = CreateFileA( name, GENERIC_READ,
                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                        NULL, OPEN_EXISTING, 0, NULL );
152 153 154 155 156 157 158 159

    if ((file == INVALID_HANDLE_VALUE) &&
        (GetLastError() == ERROR_INVALID_PARAMETER)) {
        /* FILE_SHARE_DELETE not supported on win9x */
        file = CreateFileA( name, GENERIC_READ,
                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                            NULL, OPEN_EXISTING, 0, NULL );
    }
160 161 162
    if (file == INVALID_HANDLE_VALUE)
    {
        report (R_WARNING, "Can't open file '%s': %u", name, GetLastError());
163 164
        goto abort1;
    }
165

166
    report (R_STATUS, "Sending header");
167
    filesize = GetFileSize( file, NULL );
168 169 170
    str = strmake (&total, body1, name);
    ret = send_str (s, head, filesize + total + sizeof body2 - 1) ||
        send_buf (s, str, total);
171
    heap_free (str);
172
    if (ret) {
173 174
        report (R_WARNING, "Error sending header: %d, %d",
                errno, WSAGetLastError ());
175 176 177
        goto abort2;
    }

178
    report (R_STATUS, "Sending %u bytes of data", filesize);
179
    report (R_PROGRESS, 2, filesize);
180
    total = 0;
181
    while (total < filesize && ReadFile( file, buffer, BUFLEN/2, &bytes_read, NULL )) {
182
        if (aborting) goto abort2;
183
        if (!bytes_read) break;
184 185
        total += bytes_read;
        if (total > filesize) bytes_read -= total - filesize;
186
        if (send_buf (s, buffer, bytes_read)) {
187 188
            report (R_WARNING, "Error sending body: %d, %d",
                    errno, WSAGetLastError ());
189 190
            goto abort2;
        }
191 192
        report (R_DELTA, bytes_read, "Network transfer: In progress");
    }
193
    CloseHandle( file );
194 195

    if (send_buf (s, body2, sizeof body2 - 1)) {
196 197
        report (R_WARNING, "Error sending trailer: %d, %d",
                errno, WSAGetLastError ());
198
        goto abort1;
199
    }
200
    report (R_DELTA, 0, "Network transfer: Done");
201 202

    total = 0;
203
    while ((bytes_read = recv (s, buffer+total, BUFLEN-total, 0))) {
204
        if ((signed)bytes_read == SOCKET_ERROR) {
205
            if (errno == EINTR) continue;
206 207
            report (R_WARNING, "Error receiving reply: %d, %d",
                    errno, WSAGetLastError ());
208 209 210
            goto abort1;
        }
        total += bytes_read;
211
        if (total == BUFLEN) {
212
            report (R_WARNING, "Buffer overflow");
213 214 215 216
            goto abort1;
        }
    }
    if (close_http (s)) {
217 218
        report (R_WARNING, "Error closing connection: %d, %d",
                errno, WSAGetLastError ());
219 220 221
        return 1;
    }

222
    str = strmake (&count, "Received %s (%d bytes).\n",
223
                   name, filesize);
224
    ret = total < count || memcmp (str, buffer + total - count, count) != 0;
225
    heap_free (str);
226 227 228
    if (ret) {
        buffer[total] = 0;
        str = strstr (buffer, "\r\n\r\n");
229 230
        if (!str) str = buffer;
        else str = str + 4;
231
        report (R_ERROR, "Can't submit logfile '%s'. "
232
                "Server response: %s", name, str);
233 234
    }
    return ret;
235 236

 abort2:
237
    CloseHandle( file );
238 239 240 241
 abort1:
    close_http (s);
    return 1;
}
242 243

static int
244
send_file_wininet (const char *url, const char *name)
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
{
    int ret = 0;
    HMODULE wininet_mod = NULL;
    HINTERNET (WINAPI *pInternetOpen)(LPCSTR agent, DWORD access_type, LPCSTR proxy_name, LPCSTR proxy_bypass, DWORD flags);
    HINTERNET (WINAPI *pInternetConnect)(HINTERNET session, LPCSTR server_name, INTERNET_PORT server_port, LPCSTR username, LPCSTR password, DWORD service, DWORD flags, DWORD_PTR *context);
    HINTERNET (WINAPI *pHttpOpenRequest)(HINTERNET connection, LPCSTR verb, LPCSTR object_name, LPCSTR version, LPCSTR referer, LPCSTR *accept_types, DWORD flags, DWORD_PTR context);
    BOOL (WINAPI *pHttpSendRequestEx)(HINTERNET request, LPINTERNET_BUFFERSA buffers_in, LPINTERNET_BUFFERSA buffers_out, DWORD flags, DWORD_PTR context);
    BOOL (WINAPI *pInternetWriteFile)(HINTERNET file, LPCVOID buffer, DWORD number_of_bytes_to_write, LPDWORD number_of_bytes_written);
    BOOL (WINAPI *pHttpEndRequest)(HINTERNET request, LPINTERNET_BUFFERSA buffers_out, DWORD flags, DWORD_PTR context);
    BOOL (WINAPI *pInternetReadFile)(HINTERNET file, LPCVOID buffer, DWORD number_of_bytes_to_read, LPDWORD number_of_bytes_read);
    BOOL (WINAPI *pInternetCloseHandle)(HINTERNET Handle) = NULL;
    HANDLE file = INVALID_HANDLE_VALUE;
    DWORD filesize, bytes_read, bytes_written;
    size_t total, count;
    char *str = NULL;
    HINTERNET session = NULL;
    HINTERNET connection = NULL;
    HINTERNET request = NULL;
    INTERNET_BUFFERSA buffers_in = { 0 };
    char buffer[BUFLEN+1];
265
    URL_COMPONENTSA uc = { 0 };
266 267 268 269

    static const char extra_headers[] =
        CONTENT_HEADERS;

270
    wininet_mod = LoadLibraryA("wininet.dll");
271 272 273 274 275 276 277 278 279 280 281 282 283 284
    if (wininet_mod == NULL)
        goto done;
    pInternetOpen = (void *)GetProcAddress(wininet_mod, "InternetOpenA");
    pInternetConnect = (void *)GetProcAddress(wininet_mod, "InternetConnectA");
    pHttpOpenRequest = (void *)GetProcAddress(wininet_mod, "HttpOpenRequestA");
    pHttpSendRequestEx = (void *)GetProcAddress(wininet_mod, "HttpSendRequestExA");
    pInternetWriteFile = (void *)GetProcAddress(wininet_mod, "InternetWriteFile");
    pHttpEndRequest = (void *)GetProcAddress(wininet_mod, "HttpEndRequestA");
    pInternetReadFile = (void *)GetProcAddress(wininet_mod, "InternetReadFile");
    pInternetCloseHandle = (void *)GetProcAddress(wininet_mod, "InternetCloseHandle");
    if (pInternetOpen == NULL || pInternetConnect == NULL || pHttpOpenRequest == NULL || pHttpSendRequestEx == NULL || pHttpEndRequest == NULL ||
        pInternetWriteFile == NULL || pInternetReadFile == NULL || pInternetCloseHandle == NULL) {
        goto done;
    }
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    if (url) {
        BOOL (WINAPI *pInternetCrackUrlA)(const char *url, DWORD url_length, DWORD flags, URL_COMPONENTSA *ret_comp);
        pInternetCrackUrlA = (void *)GetProcAddress(wininet_mod, "InternetCrackUrlA");
        if (pInternetCrackUrlA == NULL)
            goto done;
        uc.dwStructSize = sizeof(uc);
        uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUserNameLength =
            uc.dwPasswordLength = uc.dwUrlPathLength = uc.dwExtraInfoLength =
            strlen(url) + 1;
        uc.lpszScheme = heap_alloc (uc.dwSchemeLength);
        uc.lpszHostName = heap_alloc (uc.dwHostNameLength);
        uc.lpszUserName = heap_alloc (uc.dwUserNameLength);
        uc.lpszPassword = heap_alloc (uc.dwPasswordLength);
        uc.lpszUrlPath = heap_alloc (uc.dwUrlPathLength);
        uc.lpszExtraInfo = heap_alloc (uc.dwExtraInfoLength);
        if (!pInternetCrackUrlA(url, 0, 0, &uc)) {
            report (R_WARNING, "Can't parse submit URL '%s'", url);
            goto done;
        }
        if ((uc.nScheme != INTERNET_SCHEME_HTTP && uc.nScheme != INTERNET_SCHEME_HTTPS) || *uc.lpszExtraInfo) {
            report (R_WARNING, "Can't submit report to scheme %s or extra info '%s'", uc.lpszScheme, uc.lpszExtraInfo);
            goto done;
        }

    } else {
        uc.nScheme = INTERNET_SCHEME_HTTP;
        uc.lpszHostName = heap_strdup (SERVER_NAME);
        uc.nPort = INTERNET_DEFAULT_HTTP_PORT;
        uc.lpszUserName = heap_strdup ("");
        uc.lpszPassword = heap_strdup ("");
        uc.lpszUrlPath = heap_strdup (URL_PATH);
    }
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335

    ret = 1;

    file = CreateFileA( name, GENERIC_READ,
                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                        NULL, OPEN_EXISTING, 0, NULL );

    if ((file == INVALID_HANDLE_VALUE) &&
        (GetLastError() == ERROR_INVALID_PARAMETER)) {
        /* FILE_SHARE_DELETE not supported on win9x */
        file = CreateFileA( name, GENERIC_READ,
                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                            NULL, OPEN_EXISTING, 0, NULL );
    }
    if (file == INVALID_HANDLE_VALUE) {
        report (R_WARNING, "Can't open file '%s': %u", name, GetLastError());
        goto done;
    }

336 337 338
    report (R_STATUS, "Opening %s connection to %s:%d",
            (uc.nScheme == INTERNET_SCHEME_HTTP ? "http" : "https"),
            uc.lpszHostName, uc.nPort);
339 340 341 342 343
    session = pInternetOpen (USER_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (session == NULL) {
        report (R_WARNING, "Unable to open connection, error %u", GetLastError());
        goto done;
    }
344
    connection = pInternetConnect (session, uc.lpszHostName, uc.nPort, uc.lpszUserName, uc.lpszPassword, INTERNET_SERVICE_HTTP, 0, 0);
345 346 347 348
    if (connection == NULL) {
        report (R_WARNING, "Unable to connect, error %u", GetLastError());
        goto done;
    }
349
    request = pHttpOpenRequest (connection, "POST", uc.lpszUrlPath, NULL, NULL, NULL,
350
                                INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI |
351
                                INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD | (uc.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_FLAG_SECURE : 0), 0);
352 353 354 355 356 357
    if (request == NULL) {
        report (R_WARNING, "Unable to open request, error %u", GetLastError());
        goto done;
    }

    report (R_STATUS, "Sending request");
358
    filesize = GetFileSize( file, NULL );
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
    str = strmake (&total, body1, name);
    memset(&buffers_in, 0, sizeof(INTERNET_BUFFERSA));
    buffers_in.dwStructSize = sizeof(INTERNET_BUFFERSA);
    buffers_in.dwBufferTotal = filesize + total + sizeof body2 - 1;
    buffers_in.lpcszHeader = strmake (&count, extra_headers, buffers_in.dwBufferTotal);
    buffers_in.dwHeadersLength = count;
    if (! pHttpSendRequestEx(request, &buffers_in, NULL, 0, 0)) {
        report (R_WARNING, "Unable to send request, error %u", GetLastError());
        goto done;
    }

    if (! pInternetWriteFile(request, str, total, &bytes_written) || bytes_written != total) {
        report (R_WARNING, "Unable to write body data, error %u", GetLastError());
        goto done;
    }

    report (R_STATUS, "Sending %u bytes of data", filesize);
    report (R_PROGRESS, 2, filesize);
    total = 0;
    while (total < filesize && ReadFile( file, buffer, BUFLEN/2, &bytes_read, NULL )) {
379
        if (aborting) goto done;
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
        if (!bytes_read) break;
        total += bytes_read;
        if (total > filesize) bytes_read -= total - filesize;
        if (! pInternetWriteFile (request, buffer, bytes_read, &bytes_written) || bytes_written != bytes_read) {
            report (R_WARNING, "Error sending body: %u", GetLastError ());
            goto done;
        }
        report (R_DELTA, bytes_read, "Network transfer: In progress");
    }

    if (! pInternetWriteFile(request, body2, sizeof body2 - 1, &bytes_written) || bytes_written != sizeof body2 - 1) {
        report (R_WARNING, "Unable to write final body data, error %u", GetLastError());
        goto done;
    }
    if (! pHttpEndRequest(request, NULL, 0, 0)) {
        report (R_WARNING, "Unable to end request, error %u", GetLastError());
        goto done;
    }
    report (R_DELTA, 0, "Network transfer: Done");

    total = 0;
    do
    {
        if (! pInternetReadFile(request, buffer+total, BUFLEN-total, &bytes_read)) {
            report (R_WARNING, "Error receiving reply: %u", GetLastError ());
            goto done;
        }
        total += bytes_read;
        if (total == BUFLEN) {
            report (R_WARNING, "Buffer overflow");
            goto done;
        }
    }
    while (bytes_read != 0);

    heap_free (str);
    str = strmake (&count, "Received %s (%d bytes).\n",
                   name, filesize);
    if (total < count || memcmp (str, buffer + total - count, count) != 0) {
        buffer[total] = 0;
        report (R_ERROR, "Can't submit logfile '%s'. "
                "Server response: %s", name, buffer);
    }

 done:
425 426 427 428 429 430
    heap_free (uc.lpszScheme);
    heap_free (uc.lpszHostName);
    heap_free (uc.lpszUserName);
    heap_free (uc.lpszPassword);
    heap_free (uc.lpszUrlPath);
    heap_free (uc.lpszExtraInfo);
431 432
    heap_free((void *)buffers_in.lpcszHeader);
    heap_free(str);
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    if (pInternetCloseHandle != NULL && request != NULL)
        pInternetCloseHandle (request);
    if (pInternetCloseHandle != NULL && connection != NULL)
        pInternetCloseHandle (connection);
    if (pInternetCloseHandle != NULL && session != NULL)
        pInternetCloseHandle (session);
    if (file != INVALID_HANDLE_VALUE)
        CloseHandle (file);
    if (wininet_mod != NULL)
        FreeLibrary (wininet_mod);

    return ret;
}

int
448
send_file (const char *url, const char *name)
449
{
450
    return send_file_wininet( url, name ) || send_file_direct( url, name );
451
}