| 318 | unsigned long int nContentLength = 0; |
| 319 | bool bUseBasic = false; |
| 320 | bool bUseDigest = false; |
| 321 | wxString digestStr = _T(""); |
| 322 | wxString authStr = _T(""); |
| 323 | wxString header_str(m_pRecvBuffer, wxConvLocal, m_recvBufferPos); |
| 324 | wxStringTokenizer tkz(header_str, wxT("\r\n")); |
| 325 | |
| 326 | while (tkz.HasMoreTokens()) |
| 327 | { |
| 328 | wxString token = tkz.GetNextToken(); |
| 329 | if (token.Mid(0, 15).Lower() == _T("content-length:")) |
| 330 | { |
| 331 | token.Mid(15).Trim(true).Trim(false).ToULong(&nContentLength); |
| 332 | continue; |
| 333 | } |
| 334 | if (token.Mid(0, 26).Lower() == _T("proxy-authenticate: basic ")) |
| 335 | { |
| 336 | bUseBasic = true; |
| 337 | continue; |
| 338 | } |
| 339 | if (token.Mid(0, 27).Lower() == _T("proxy-authenticate: digest ")) |
| 340 | { |
| 341 | authStr = token.Mid(27); |
| 342 | bUseDigest = true; |
| 343 | continue; |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | // read body |
| 348 | unsigned long int nBodyLength = 0; |
| 349 | char *pBody = new char[nContentLength]; |
| 350 | while (nBodyLength < nContentLength) { |
| 351 | int read; |
| 352 | |
| 353 | do_read = nContentLength - nBodyLength; |
| 354 | |
| 355 | read = m_pSocket->Read(pBody + nBodyLength, do_read, error); |
| 356 | if (read == -1) |
| 357 | { |
| 358 | if (error != EAGAIN) |
| 359 | { |
| 360 | delete [] pBody; |
| 361 | m_proxyState = noconn; |
| 362 | CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, error); |
| 363 | CSocketEventDispatcher::Get().SendEvent(evt); |
| 364 | } |
| 365 | else |
| 366 | m_can_read = false; |
| 367 | continue; |
| 368 | } |
| 369 | if (!read) |
| 370 | { |
| 371 | delete [] pBody; |
| 372 | m_proxyState = noconn; |
| 373 | CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNABORTED); |
| 374 | CSocketEventDispatcher::Get().SendEvent(evt); |
| 375 | return; |
| 376 | } |
| 377 | if (m_pSendBuffer) |
| 378 | { |
| 379 | delete [] pBody; |
| 380 | m_proxyState = noconn; |
| 381 | m_pOwner->LogMessage(Debug_Warning, _T("Incoming data before requst fully sent")); |
| 382 | CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNABORTED); |
| 383 | CSocketEventDispatcher::Get().SendEvent(evt); |
| 384 | return; |
| 385 | } |
| 386 | nBodyLength += read; |
| 387 | } |
| 388 | |
| 395 | if (reply.Left(12) == _T("HTTP/1.1 401") || reply.Left(12) == _T("HTTP/1.1 407") || |
| 396 | reply.Left(12) == _T("HTTP/1.0 401") || reply.Left(12) == _T("HTTP/1.0 407")) |
| 397 | { |
| 398 | if (m_doAuth || m_user == _T("")) |
| 399 | { |
| 400 | delete [] pBody; |
| 401 | m_pOwner->LogMessage(Debug_Warning, _("already m_doAuth (%d), or m_user is empty (%s)"), m_doAuth, m_user.c_str()); |
| 402 | m_proxyState = noconn; |
| 403 | CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNRESET); |
| 404 | CSocketEventDispatcher::Get().SendEvent(evt); |
| 405 | return; |
| 406 | } |
| 407 | |
| 408 | if (bUseDigest) |
| 409 | { |
| 410 | wxString realm = _T(""); |
| 411 | wxString nonce = _T(""); |
| 412 | wxString qop = _T(""); |
| 413 | wxStringTokenizer tkz2(authStr, wxT(",")); |
| 414 | while (tkz2.HasMoreTokens()) |
| 415 | { |
| 416 | wxString token2 = tkz2.GetNextToken(); |
| 417 | wxString name = _T(""); |
| 418 | wxString value = _T(""); |
| 419 | wxStringTokenizer tkz3(token2, wxT("=")); |
| 420 | while (tkz3.HasMoreTokens()) |
| 421 | { |
| 422 | wxString token3 = tkz3.GetNextToken(); |
| 423 | if (name == _T("")) |
| 424 | { |
| 425 | name = token3.Lower().Trim(true).Trim(false); |
| 426 | continue; |
| 427 | } |
| 428 | if (token3.Left(1) == _T("\"")) |
| 429 | value = token3.Mid(1, token3.Len()-2).Trim(true).Trim(false); |
| 430 | else |
| 431 | value = token3.Trim(true).Trim(false); |
| 432 | break; |
| 433 | } |
| 434 | if (name == _T("realm")) |
| 435 | realm = value; |
| 436 | else if (name == _T("nonce")) |
| 437 | nonce = value; |
| 438 | else if (name == _T("qop")) |
| 439 | qop = value; |
| 440 | } |
| 441 | if (realm == _T("") || nonce == _T("")) |
| 442 | { |
| 443 | delete [] pBody; |
| 444 | m_pOwner->LogMessage(Debug_Warning, _T("no realm or nonce")); |
| 445 | m_proxyState = conn; |
| 446 | CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0); |
| 447 | CSocketEventDispatcher::Get().SendEvent(evt); |
| 448 | return; |
| 449 | } |
| 450 | // HA1 = md5(username:realm:password) |
| 451 | // if qop = auth or no qop |
| 452 | // HA2 = md5(method:digestURI) |
| 453 | // if qop = auth-int |
| 454 | // HA2 = md5(method:digestURI:md5(body)) |
| 455 | // if qop = auth or auth-int |
| 456 | // response = md5(HA1:nonce:nonceCount:clientNonce:qop:HA2) |
| 457 | // if no qop |
| 458 | // response = md5(HA1:nonce:HA2) |
| 459 | wxString ha1, ha2, response; |
| 460 | wxString s; |
| 461 | bool bAuth = false; |
| 462 | bool bAuthInt = false; |
| 463 | |
| 464 | if (qop != _T("")) |
| 465 | { |
| 466 | wxStringTokenizer tkz4(qop, wxT(",")); |
| 467 | while (tkz4.HasMoreTokens()) |
| 468 | { |
| 469 | wxString token4 = tkz4.GetNextToken(); |
| 470 | if (token4 == _T("auth")) |
| 471 | bAuth = true; |
| 472 | if (token4 == _T("auth-int")) |
| 473 | bAuthInt = true; |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | s = wxString::Format(_T("%s:%s:%s"), m_user.c_str(), realm.c_str(), m_pass.c_str()); |
| 478 | const wxWX2MBbuf s_raw = s.mb_str(wxConvUTF8); |
| 479 | ha1 = ComputeMD5((const char *)s_raw, strlen(s_raw)); |
| 480 | |
| 481 | wxString uri = _T(""); |
| 482 | if (qop == _T("") || bAuth) |
| 483 | { |
| 484 | uri = wxString::Format(_T("%s:%u"), m_host.c_str(), m_port); |
| 485 | s = wxString::Format(_T("CONNECT:%s"), uri.c_str()); |
| 486 | const wxWX2MBbuf s_raw = s.mb_str(wxConvUTF8); |
| 487 | ha2 = ComputeMD5((const char *)s_raw, strlen(s_raw)); |
| 488 | } |
| 489 | else if (bAuthInt) |
| 490 | { |
| 491 | uri = wxString::Format(_T("%s:%u"), m_host.c_str(), m_port); |
| 492 | wxString bodyMD5 = ComputeMD5((const char *)pBody, nBodyLength); |
| 493 | s = wxString::Format(_T("CONNECT:%s:%s"), uri.c_str(), bodyMD5.c_str()); |
| 494 | const wxWX2MBbuf s_raw = s.mb_str(wxConvLocal); |
| 495 | ha2 = ComputeMD5((const char *)s_raw, strlen(s_raw)); |
| 496 | } |
| 497 | else |
| 498 | { |
| 499 | delete [] pBody; |
| 500 | m_pOwner->LogMessage(Debug_Warning, _T("unknown qop: %s"), qop.c_str()); |
| 501 | m_proxyState = conn; |
| 502 | CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0); |
| 503 | CSocketEventDispatcher::Get().SendEvent(evt); |
| 504 | return; |
| 505 | } |
| 506 | |
| 507 | // we don't need body now |
| 508 | delete [] pBody; |
| 509 | if (bAuth || bAuthInt) |
| 510 | { |
| 511 | wxString clientNonce = wxString::Format(_T("%04x%04x"), GetRandomNumber(0, 0xffff), GetRandomNumber(0, 0xffff)); |
| 512 | |
| 513 | s = wxString::Format(_T("%s:%s:%s:%s:%s:%s"), |
| 514 | ha1.c_str(), |
| 515 | nonce.c_str(), |
| 516 | _T("00000001"), |
| 517 | clientNonce.c_str(), |
| 518 | bAuth ? _T("auth") : _T("auth-int"), |
| 519 | ha2.c_str()); |
| 520 | const wxWX2MBbuf s_raw = s.mb_str(wxConvLocal); |
| 521 | response = ComputeMD5((const char *)s_raw, strlen(s_raw)); |
| 522 | |
| 523 | digestStr = wxString::Format(_T("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", qop=%s, nc=00000001, cnonce=\"%s\""), |
| 524 | m_user.c_str(), |
| 525 | realm.c_str(), |
| 526 | nonce.c_str(), |
| 527 | uri.c_str(), |
| 528 | response.c_str(), |
| 529 | bAuth ? _T("auth") : _T("auth-int"), |
| 530 | clientNonce.c_str()); |
| 531 | } |
| 532 | else |
| 533 | { |
| 534 | s = wxString::Format(_T("%s:%s:%s"), |
| 535 | ha1.c_str(), |
| 536 | nonce.c_str(), |
| 537 | ha2.c_str()); |
| 538 | const wxWX2MBbuf s_raw = s.mb_str(wxConvLocal); |
| 539 | response = ComputeMD5((const char *)s_raw, strlen(s_raw)); |
| 540 | |
| 541 | digestStr = wxString::Format(_T("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\""), |
| 542 | m_user.c_str(), |
| 543 | realm.c_str(), |
| 544 | nonce.c_str(), |
| 545 | uri.c_str(), |
| 546 | response.c_str()); |
| 547 | } |
| 548 | |
| 549 | m_doAuth = true; |
| 550 | m_recvBufferPos = 0; |
| 551 | |
| 552 | const wxWX2MBbuf host_raw = m_host.mb_str(wxConvUTF8); |
| 553 | const wxWX2MBbuf digest_raw = digestStr.mb_str(wxConvLocal); |
| 554 | |
| 555 | int challenge_len = digestStr.Len(); |
| 556 | |
| 557 | m_pOwner->LogMessage(Status, _("Sending CONNECT with Digest Authorization")); |
| 558 | |
| 559 | // Bit oversized, but be on the safe side |
| 560 | m_pSendBuffer = new char[70 + m_host.Len() * 2 + 2*5 + challenge_len * 2 + 23 + 40]; |
| 561 | |
| 562 | m_sendBufferLen = sprintf(m_pSendBuffer, "CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nProxy-Authorization: %s\r\nUser-Agent: FileZilla\r\n\r\n", |
| 563 | (const char*)host_raw, m_port, |
| 564 | (const char*)host_raw, m_port, |
| 565 | (const char*)digest_raw); |
| 566 | |
| 567 | wxString host = m_pSocket->GetPeerIP(); |
| 568 | unsigned int port = m_pSocket->GetRemotePort(error); |
| 569 | |
| 570 | m_pSocket->Close(); |
| 571 | int res = m_pSocket->Connect(host, port); |
| 572 | // Treat success same as EINPROGRESS, we wait for connect notification in any case |
| 573 | if (res && res != EINPROGRESS) |
| 574 | { |
| 575 | m_pOwner->LogMessage(Debug_Warning, _T("connect error?")); |
| 576 | m_proxyState = conn; |
| 577 | CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0); |
| 578 | CSocketEventDispatcher::Get().SendEvent(evt); |
| 579 | return; |
| 580 | } |
| 581 | return; |
| 582 | } |
| 583 | |
| 584 | if (bUseBasic) |
| 585 | { |
| 586 | // we don't need body for this |
| 587 | delete [] pBody; |
| 588 | m_doAuth = true; |
| 589 | m_recvBufferPos = 0; |
| 590 | |
| 591 | #if wxUSE_UNICODE |
| 592 | wxWX2MBbuf challenge; |
| 593 | #else |
| 594 | const wxWX2MBbuf challenge; |
| 595 | #endif |
| 596 | const wxWX2MBbuf host_raw = m_host.mb_str(wxConvUTF8); |
| 597 | int challenge_len; |
| 598 | |
| 599 | challenge = base64encode(m_user + _T(":") + m_pass).mb_str(wxConvUTF8); |
| 600 | challenge_len = strlen(challenge); |
| 601 | |
| 602 | m_pOwner->LogMessage(Status, _("Sending CONNECT with Basic Authorization")); |
| 603 | // Bit oversized, but be on the safe side |
| 604 | m_pSendBuffer = new char[70 + strlen(host_raw) * 2 + 2*5 + challenge_len + 23]; |
| 605 | |
| 606 | m_sendBufferLen = sprintf(m_pSendBuffer, "CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nProxy-Authorization: Basic %s\r\nUser-Agent: FileZilla\r\n\r\n", |
| 607 | (const char*)host_raw, m_port, |
| 608 | (const char*)host_raw, m_port, |
| 609 | (const char*)challenge); |
| 610 | |
| 611 | wxString host = m_pSocket->GetPeerIP(); |
| 612 | unsigned int port = m_pSocket->GetRemotePort(error); |
| 613 | |
| 614 | m_pSocket->Close(); |
| 615 | int res = m_pSocket->Connect(host, port); |
| 616 | // Treat success same as EINPROGRESS, we wait for connect notification in any case |
| 617 | if (res && res != EINPROGRESS) |
| 618 | { |
| 619 | m_pOwner->LogMessage(Debug_Warning, _T("connect error?")); |
| 620 | m_proxyState = conn; |
| 621 | CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0); |
| 622 | CSocketEventDispatcher::Get().SendEvent(evt); |
| 623 | return; |
| 624 | } |
| 625 | return; |
| 626 | } |
| 627 | // other authentication method? |
| 628 | } |
| 629 | delete [] pBody; |
| 630 | |