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