=== modified file 'apt-pkg/acquire-method.cc' --- apt-pkg/acquire-method.cc 2007-02-12 21:16:53 +0000 +++ apt-pkg/acquire-method.cc 2007-03-06 15:00:48 +0000 @@ -231,6 +231,63 @@ QueueBack = Queue; } /*}}}*/ +// AcqMethod::RequestAuth - Request Authentication /*{{{*/ +// --------------------------------------------------------------------- +/* This sends a 402 Authenticate message to the APT and waits for it + to be ackd */ +bool pkgAcqMethod::RequestAuth(string Site,string Realm,string &User,string &Pass,bool Proxy) +{ + char S[1024]; + + snprintf(S,sizeof(S),"402 Authenticate\nSite: %s\nRealm: %s\nUser: %s\n" + "Password: %s\n", Site.c_str(), Realm.c_str(), User.c_str(), + Pass.c_str()); + if (Proxy) + strcat(S,"Proxy: true\n"); + strcat(S,"\n"); + if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S)) + exit(100); + + vector MyMessages; + + /* Here we read messages until we find a 602, each non 602 message is + appended to the main message list for later processing */ + while (1) + { + if (WaitFd(STDIN_FILENO) == false) + return false; + + if (ReadMessages(STDIN_FILENO,MyMessages) == false) + return false; + + string Message = MyMessages.front(); + MyMessages.erase(MyMessages.begin()); + + // Fetch the message number + char *End; + int Number = strtol(Message.c_str(),&End,10); + if (End == Message.c_str()) + { + cerr << "Malformed message!" << endl; + exit(100); + } + + // Change ack + if (Number == 602) + { + if (StringToBool(LookupTag(Message,"Fail"),false) == false) + { + User = LookupTag(Message,"User"); + Pass = LookupTag(Message,"Password"); + return true; + } + else + return false; + } + Messages.push_back(Message); + } +} + /*}}}*/ // AcqMethod::MediaFail - Syncronous request for new media /*{{{*/ // --------------------------------------------------------------------- /* This sends a 403 Media Failure message to the APT and waits for it === modified file 'apt-pkg/acquire-method.h' --- apt-pkg/acquire-method.h 2007-02-12 21:16:53 +0000 +++ apt-pkg/acquire-method.h 2007-03-05 17:46:59 +0000 @@ -67,6 +67,7 @@ void URIStart(FetchResult &Res); void URIDone(FetchResult &Res,FetchResult *Alt = 0); bool MediaFail(string Required,string Drive); + bool RequestAuth(string Site,string Realm,string &User,string &Pass,bool Proxy); virtual void Exit() {}; public: === modified file 'apt-pkg/acquire-worker.cc' --- apt-pkg/acquire-worker.cc 2007-02-12 21:16:53 +0000 +++ apt-pkg/acquire-worker.cc 2007-03-09 21:24:16 +0000 @@ -321,6 +321,14 @@ _error->Error("Method %s General failure: %s",Access.c_str(),LookupTag(Message,"Message").c_str()); break; + // 402 Authentication Required + case 402: + if (Authenticate(Message) == false) + _error->Error("Method %s Authentication failure: \"%s\" in %s", + Access.c_str(), LookupTag(Message,"Realm").c_str(), + LookupTag(Message,"Site").c_str()); + break; + // 403 Media Change case 403: MediaChange(Message); @@ -565,3 +573,34 @@ Status = string(); } /*}}}*/ +// Worker::Authenticate - Request authentication /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgAcquire::Worker::Authenticate(string Message) +{ + string Site = LookupTag(Message,"Site"); + string Realm = LookupTag(Message,"Realm"); + string User = LookupTag(Message, "User"); + string Pass = LookupTag(Message, "Password"); + bool Proxy = StringToBool(LookupTag(Message,"Proxy"),false); + char S[300]; + bool Ret = true; + + if (Log == 0 || Log->Authenticate(Site,Realm,User,Pass,Proxy) == false) + { + snprintf(S,sizeof(S),"602 Authenticated\nFailed: true\n\n"); + Ret = false; + } + else + { + snprintf(S,sizeof(S),"602 Authenticated\nUser: %s\nPassword: %s\n\n", + User.c_str(), Pass.c_str()); + } + + if (Debug == true) + clog << " -> " << Access << ':' << QuoteString(S,"\n") << endl; + OutQueue += S; + OutReady = true; + return Ret; +} + /*}}}*/ === modified file 'apt-pkg/acquire-worker.h' --- apt-pkg/acquire-worker.h 2007-02-12 21:16:53 +0000 +++ apt-pkg/acquire-worker.h 2007-02-14 21:04:04 +0000 @@ -62,6 +62,7 @@ bool Capabilities(string Message); bool SendConfiguration(); bool MediaChange(string Message); + bool Authenticate(string Message); bool MethodFailure(); void ItemDone(); === modified file 'apt-pkg/acquire.cc' --- apt-pkg/acquire.cc 2007-02-12 21:16:53 +0000 +++ apt-pkg/acquire.cc 2007-03-09 18:28:01 +0000 @@ -883,3 +883,13 @@ FetchedBytes += Size - Resume; } /*}}}*/ +// AcquireStatus::Authenticate - Called to authenticate /*{{{*/ +// --------------------------------------------------------------------- +/* This is used to fetch a username and password from the user */ +bool pkgAcquireStatus::Authenticate(string Site,string Realm,string &User,string &Pass,bool Proxy) +{ + /* The default behavior for all clients is to refuse to authenticate + interactively; this preserves backward compatibility. */ + return false; +} + /*}}}*/ === modified file 'apt-pkg/acquire.h' --- apt-pkg/acquire.h 2007-02-12 21:16:53 +0000 +++ apt-pkg/acquire.h 2007-03-05 17:50:55 +0000 @@ -264,7 +264,10 @@ // Called by items when they have finished a real download virtual void Fetched(unsigned long Size,unsigned long ResumePoint); - + + // Called to authenticate + virtual bool Authenticate(string Site,string Realm,string &User,string &Pass,bool Proxy); + // Called to change media virtual bool MediaChange(string Media,string Drive) = 0; === modified file 'cmdline/acqprogress.cc' --- cmdline/acqprogress.cc 2007-02-12 21:16:54 +0000 +++ cmdline/acqprogress.cc 2007-03-09 21:28:54 +0000 @@ -18,6 +18,8 @@ #include #include +#include +#include #include /*}}}*/ @@ -287,3 +289,97 @@ return bStatus; } /*}}}*/ +// AcqTextStatus::Authenticate - Authenticate the user /*{{{*/ +// --------------------------------------------------------------------- +/* Prompt for a username and password */ +bool AcqTextStatus::Authenticate(string Site,string Realm,string &User,string &Pass,bool Proxy) +{ + if (Quiet > 0) + return false; + + vector::iterator AuthItem; + for (AuthItem = AuthList.begin(); AuthItem != AuthList.end(); AuthItem++) + { + if (AuthItem->Site == Site && AuthItem->Realm == Realm) + { + // Update username and password if they don't match + if (AuthItem->User != User || AuthItem->Password != Pass) + { + User = AuthItem->User; + Pass = AuthItem->Password; + return true; + } + } + } + + cout << '\r' << BlankLine << '\r'; + if (Proxy) + ioprintf(cout,_("Enter username and password for proxy \"%s\" at %s\n"), + Realm.c_str(),Site.c_str()); + else + ioprintf(cout,_("Enter username and password for \"%s\" at %s\n"), + Realm.c_str(),Site.c_str()); + ioprintf(cout,_("Username: ")); + cout << flush; + + char S[1024]; + char C = 0; + size_t idx = 0; + while (C != '\n' && C != '\r' && idx < (sizeof(S) - 1)) + { + read(STDIN_FILENO,&C,1); + S[idx++] = C; + } + S[--idx] = '\0'; + User = S; + + ioprintf(cout,_("Password: ")); + cout << flush; + + // Turn off echo for entering the password + struct termios TermIO; + tcgetattr(STDIN_FILENO, &TermIO); + + struct termios TermIO_noecho; + TermIO_noecho = TermIO; + TermIO_noecho.c_lflag &= !ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &TermIO_noecho); + + C = 0; + idx = 0; + while (C != '\n' && C != '\r' && idx < (sizeof(S) - 1)) + { + read(STDIN_FILENO,&C,1); + S[idx++] = C; + } + S[--idx] = '\0'; + Pass = S; + + // Turn echo back on + tcsetattr(STDIN_FILENO, TCSANOW, &TermIO); + + ioprintf(cout,"\n"); + cout << flush; + + if (AuthItem == AuthList.end()) + { + // Save new credentials + AuthRecord NewAuth; + + NewAuth.Site = Site; + NewAuth.Realm = Realm; + NewAuth.User = User; + NewAuth.Password = Pass; + AuthList.push_back(NewAuth); + } + else + { + // Update stored user and password + AuthItem->User = User; + AuthItem->Password = Pass; + } + + Update = true; + return true; +} + === modified file 'cmdline/acqprogress.h' --- cmdline/acqprogress.h 2007-02-12 21:16:54 +0000 +++ cmdline/acqprogress.h 2007-03-05 17:55:46 +0000 @@ -18,10 +18,21 @@ char BlankLine[1024]; unsigned long ID; unsigned long Quiet; + + struct AuthRecord + { + string Site; + string Realm; + string User; + string Password; + }; + + vector AuthList; public: virtual bool MediaChange(string Media,string Drive); + virtual bool Authenticate(string Site,string Realm,string &User,string &Pass,bool Proxy); virtual void IMSHit(pkgAcquire::ItemDesc &Itm); virtual void Fetch(pkgAcquire::ItemDesc &Itm); virtual void Done(pkgAcquire::ItemDesc &Itm); === modified file 'methods/http.cc' --- methods/http.cc 2007-02-12 21:16:55 +0000 +++ methods/http.cc 2007-03-07 22:13:54 +0000 @@ -39,6 +39,7 @@ #include #include #include +#include #include // Internet stuff @@ -57,6 +58,7 @@ time_t HttpMethod::FailTime = 0; unsigned long PipelineDepth = 10; unsigned long TimeOut = 120; +bool ChokePipe = true; bool Debug = false; @@ -297,7 +299,7 @@ // ServerState::Open - Open a connection to the server /*{{{*/ // --------------------------------------------------------------------- /* This opens a connection to the server. */ -bool ServerState::Open() +bool ServerState::Open(bool ReadProxy) { // Use the already open connection if possible. if (ServerFd != -1) @@ -308,29 +310,34 @@ Out.Reset(); Persistent = true; - // Determine the proxy setting - if (getenv("http_proxy") == 0) + // Reuse the already set credentials if possible. + if (ReadProxy || Proxy.User.empty() || Proxy.Password.empty()) { - string DefProxy = _config->Find("Acquire::http::Proxy"); - string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host); - if (SpecificProxy.empty() == false) - { - if (SpecificProxy == "DIRECT") + // Determine the proxy setting + if (getenv("http_proxy") == 0) + { + string DefProxy = _config->Find("Acquire::http::Proxy"); + string SpecificProxy = _config->Find("Acquire::http::Proxy::" + + ServerName.Host); + if (SpecificProxy.empty() == false) + { + if (SpecificProxy == "DIRECT") + Proxy = ""; + else + Proxy = SpecificProxy; + } + else + Proxy = DefProxy; + } + else + Proxy = getenv("http_proxy"); + + // Parse no_proxy, a , separated list of domains + if (getenv("no_proxy") != 0) + { + if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true) Proxy = ""; - else - Proxy = SpecificProxy; - } - else - Proxy = DefProxy; - } - else - Proxy = getenv("http_proxy"); - - // Parse no_proxy, a , separated list of domains - if (getenv("no_proxy") != 0) - { - if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true) - Proxy = ""; + } } // Determine what host and port to use based on the proxy settings @@ -368,8 +375,8 @@ /*}}}*/ // ServerState::RunHeaders - Get the headers before the data /*{{{*/ // --------------------------------------------------------------------- -/* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header - parse error occured */ +/* Returns 0 if things are OK, 1 if an IO error occursed, 2 if a header + parse error occured and 3 if digest authentication required */ int ServerState::RunHeaders() { State = Header; @@ -510,6 +517,174 @@ return Owner->Flush(this) && !_error->PendingError(); } /*}}}*/ +// ServerState::ProxyLine - Process a proxy line /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool ServerState::ParseAuth(string Line, AuthInfo &Auth) +{ + AuthInfo AuthTmp; + string::size_type SplitPoint = Line.find(' '); + string AuthTypeStr = Line.substr(0,SplitPoint); + + // Test authentication type + if (stringcasecmp(AuthTypeStr,"Digest") == 0) + AuthTmp.Type = AuthInfo::Digest; + else if (stringcasecmp(AuthTypeStr,"Basic") != 0) + return _error->Error(_("Authentication method not supported")); + Line.erase(0, SplitPoint + 1); + + string RealmStr; + string NonceStr; + string QOPStr; + string Unknown; + string AlgoStr; + string OpaqueStr; + + // Divide line in comma-separated substrings + string::size_type FirstPos = Line.find_first_not_of(',',0); + string::size_type LastPos = Line.find_first_of(',',FirstPos); + + while (FirstPos != string::npos || LastPos != string::npos) + { + string Val = Line.substr(FirstPos,LastPos - FirstPos); + // Remove spaces + string::size_type RightmostSpace = Val.find_last_not_of(' '); + if (RightmostSpace != string::npos) + { + Val.erase(RightmostSpace + 1); + string::size_type LeftmostSpace = Val.find_first_not_of(' '); + if (LeftmostSpace != string::npos) + Val.erase(0,LeftmostSpace); + } + else Val.erase(Val.begin(),Val.end()); + // Set string according to attribute name + string *S; + if (stringcasecmp(Val.c_str(),Val.c_str()+6,"realm=") == 0) + { + if (Debug) + clog << "Receiving realm ("; + S = &RealmStr; + } + else if (stringcasecmp(Val.c_str(),Val.c_str()+6,"nonce=") == 0) + { + if (Debug) + clog << "Receiving nonce ("; + S = &NonceStr; + } + else if (stringcasecmp(Val.c_str(), Val.c_str()+4,"qop=") == 0) + { + // Got a list for quality-of-protection, parsing it properly + if (Val.substr(4, 1) == "\"") + { + string::size_type ListStart = Line.find_first_of('"',FirstPos); + string::size_type ListEnd = Line.find_first_of('"',ListStart + 1); + + if (ListEnd == string::npos) + Val = ""; + else if (ListStart != ListEnd) + Val = Line.substr(ListStart + 1,ListEnd - ListStart - 1); + } + if (Debug) + clog << "Receiving qop ("; + S = &QOPStr; + } + else if (stringcasecmp(Val.c_str(),Val.c_str()+10,"algorithm=") == 0) + { + if (Debug) + clog << "Receiving algorithm ("; + S = &AlgoStr; + } + else if (stringcasecmp(Val.c_str(),Val.c_str()+7,"opaque=") == 0) + { + if (Debug) + clog << "Receiving opaque ("; + S = &OpaqueStr; + } + else S = NULL; + + if (S != NULL) + { + // Capture string (removing quotes if necessary) + string::size_type EndQuote = Val.find_last_of('"'); + string::size_type BeginQuote = Val.find_first_of('"'); + if (EndQuote != BeginQuote) + S->assign(Val.substr(BeginQuote + 1,EndQuote - BeginQuote - 1)); + else if (Val.empty() == false) + { + string::size_type StartVal = Val.find_first_of('='); + S->assign(Val.substr(StartVal + 1)); + } + else + S->assign(""); + if (Debug) + clog << "'" << *S << "')" << endl; + } + FirstPos = Line.find_first_not_of(',',LastPos); + LastPos = Line.find_first_of(',',FirstPos); + } + AuthTmp.Realm = RealmStr; + if (AuthTmp.Type == AuthInfo::Digest) + { + AuthTmp.Nonce = NonceStr; + // Try to agree on quality-of-protection, fail otherwise + if (QOPStr.empty() == false) + { + bool GotAuth = false; + + // Divide line in comma-separated substrings + string::size_type FirstPos = QOPStr.find_first_not_of(',',0); + string::size_type LastPos = QOPStr.find_first_of(',',FirstPos); + + while (FirstPos != string::npos || LastPos != string::npos) + { + string Tmp = QOPStr.substr(FirstPos,LastPos - FirstPos); + /* FIXME: AuthInt not implemented yet. + if (stringcasecmp(Tmp, "auth-int") == 0) + { + AuthTmp.QOP = AuthInfo::AuthInt; + GotAuth = true; + break; + } + else */ + if (stringcasecmp(Tmp,"auth") == 0) + { + AuthTmp.QOP = AuthInfo::Auth; + GotAuth = true; + break; + } + FirstPos = QOPStr.find_first_not_of(',',LastPos); + LastPos = QOPStr.find_first_of(',',FirstPos); + } + if (GotAuth != true) + return _error->Error(_("Unsupported quality of protection")); + } + // Try to agree on common algorithm or fail if it is not recognized + if (AlgoStr.empty() == false) + { + if (stringcasecmp(AlgoStr, "MD5-Sess") == 0) + AuthTmp.Algorithm = AuthInfo::MD5Sess; + else if (stringcasecmp(AlgoStr, "MD5") != 0) + return _error->Error(_("Authentication algorithm not supported")); + } + if (QOPStr.empty() == false) + { + ostringstream Tmp; + MD5Summation CNonce; + + // This is probably good enough for a cnonce + Tmp << AuthTmp.NonceCount << ':' << AuthTmp.Nonce << ':' + << time(NULL) << ':' << rand(); + CNonce.Add(Tmp.str().c_str()); + AuthTmp.Cnonce = CNonce.Result().Value().substr(0,16); + } + } + // Digest authentication is preferable if available + if (Auth.Realm.empty() == true || (AuthTmp.Type == AuthInfo::Digest && + Auth.Type == AuthInfo::Basic)) + Auth = AuthTmp; + return true; +} + /*}}}*/ // ServerState::HeaderLine - Process a header line /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -592,6 +767,20 @@ HaveContent = true; return true; } + + if (stringcasecmp(Tag,"Proxy-Authenticate:") == 0) + { + if (ParseAuth(Val, ProxyAuth) == false) + return _error->Error(_("Bad header line")); + return true; + } + + if (stringcasecmp(Tag,"WWW-Authenticate:") == 0) + { + if (ParseAuth(Val, Auth) == false) + return _error->Error(_("Bad header line")); + return true; + } if (stringcasecmp(Tag,"Content-Range:") == 0) { @@ -631,7 +820,59 @@ return true; } /*}}}*/ - +// HttpMethod::DigestEncode - Encode digest elements /*{{{*/ +// --------------------------------------------------------------------- +/* This append digest elements to the outbound buffer */ +string HttpMethod::DigestEncode(AuthInfo &Auth, URI &Uri, URI &Resource) +{ + ostringstream Stream; + MD5Summation A1; + MD5Summation A2; + MD5Summation Response; + ostringstream Req; + + Req << "username=\"" << Uri.User << "\", nonce=\"" << Auth.Nonce + << "\", uri=\"" << Resource.Path << '"'; + + Stream << Uri.User << ':' << Auth.Realm << ':' << Uri.Password; + A1.Add(Stream.str().c_str()); + + Stream.str(""); + Stream << "GET:" << Resource.Path; + A2.Add(Stream.str().c_str()); + + Stream.str(""); + Stream << A1.Result().Value() << ':'; + if (Auth.QOP == AuthInfo::Unspec) + { + Req << ", realm=\"" << Auth.Realm << '"'; + Stream << Auth.Nonce << ':' << A2.Result().Value(); + } + else + { + ostringstream NCStream; + char *Table[] = {"", "auth", "auth-int"}; + char *QOP = Table[Auth.QOP - AuthInfo::Unspec]; + + // Format NonceCount + NCStream.width(8); + NCStream.fill('0'); + NCStream << hex << ++Auth.NonceCount; + + Stream << Auth.Nonce << ':' << NCStream.str() << ':' << Auth.Cnonce + << ':' << QOP << ':' << A2.Result().Value(); + + Req << ", qop=" << QOP << ", realm=\"" << Auth.Realm << "\", cnonce=\"" + << Auth.Cnonce << "\", nc=" << NCStream.str(); + } + Response.Add(Stream.str().c_str()); + Req << ", response=\"" << Response.Result().Value(); + if (Auth.Opaque.empty() == false) + Req << "\", opaque=\"" << Auth.Opaque; + Req << '"'; + return Req.str(); +} + /*}}}*/ // HttpMethod::SendReq - Send the HTTP request /*{{{*/ // --------------------------------------------------------------------- /* This places the http request in the outbound buffer */ @@ -708,13 +949,27 @@ } if (Proxy.User.empty() == false || Proxy.Password.empty() == false) - Req += string("Proxy-Authorization: Basic ") + - Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n"; - - if (Uri.User.empty() == false || Uri.Password.empty() == false) - Req += string("Authorization: Basic ") + - Base64Encode(Uri.User + ":" + Uri.Password) + "\r\n"; - + { + if (ProxyAuth.Type == AuthInfo::Digest) + Req += string("Proxy-Authorization: Digest ") + + DigestEncode(ProxyAuth, Proxy, Uri) + "\r\n"; + else + Req += string("Proxy-Authorization: Basic ") + + Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n"; + } + + if (Server->ServerName.User.empty() == false || + Server->ServerName.Password.empty() == false) + { + if (Server->Auth.Type == AuthInfo::Digest) + Req += string("Authorization: Digest ") + + DigestEncode(Server->Auth, Server->ServerName, Uri) + "\r\n"; + else + Req += string("Authorization: Basic ") + + Base64Encode(Server->ServerName.User + ":" + + Server->ServerName.Password) + "\r\n"; + } + Req += "User-Agent: Debian APT-HTTP/1.3 ("VERSION")\r\n\r\n"; if (Debug == true) @@ -900,7 +1155,8 @@ 1 - IMS hit 3 - Unrecoverable error 4 - Error with error content page - 5 - Unrecoverable non-server error (close the connection) */ + 5 - Unrecoverable non-server error (close the connection) + 6 - Authorization required */ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) { // Not Modified @@ -912,6 +1168,41 @@ return 1; } + // WWW and proxy authentication + if (Srv->Result == 401 || Srv->Result == 407) + { + bool IsProxy = (Srv->Result == 407); + AuthInfo &Auth = IsProxy ? ProxyAuth : Srv->Auth; + URI &AuthUri = IsProxy ? Proxy : Srv->ServerName; + string AuthUser = AuthUri.User; + string AuthPass = AuthUri.Password; + + // First try default authentication. + if (DefaultAuthUsed == false && (AuthUser.empty() == false || + AuthPass.empty() == false)) + { + DefaultAuthUsed = true; + return 6; + } + + // Strip Authentication Request from failed username and password. + AuthUri.User.clear(); + AuthUri.Password.clear(); + string Host = AuthUri; +#ifdef WITH_SSL + if (AuthUri->Access == "https") + Host += string(_(" (secure)")); +#endif + if (RequestAuth(Host, Auth.Realm, AuthUser, AuthPass, IsProxy) == false) + exit(100); + if (Debug) + clog << "RequestAuth('" << Host << "', '" << Auth.Realm << "', '" + << AuthUser << "', '" << AuthPass << "')" << endl; + AuthUri.User = AuthUser; + AuthUri.Password = AuthPass; + return 6; + } + /* We have a reply we dont handle. This should indicate a perm server failure */ if (Srv->Result < 200 || Srv->Result >= 300) @@ -1003,10 +1294,20 @@ // If pipelining is disabled, we only queue 1 request if (Server->Pipeline == false && Depth >= 0) break; + + if (ChokePipe == true && Depth >= 0) + { + ChokePipe = false; + break; + } // Make sure we stick with the same server if (Server->Comp(I->Uri) == false) + { + ChokePipe = true; break; + } + if (QueueBack == I) Tail = true; if (Tail == true) @@ -1036,6 +1337,13 @@ return true; } /*}}}*/ +// HttpMethod::Authorization - Handle authorization messages /*{{{*/ +// --------------------------------------------------------------------- +bool HttpMethod::Authorization(string Message) +{ + return false; +} + /*}}}*/ // HttpMethod::Loop - Main loop /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -1047,45 +1355,50 @@ Server = 0; int FailCounter = 0; + bool ReadProxy = true; + bool Retry = false; while (1) { - // We have no commands, wait for some to arrive - if (Queue == 0) + if (Retry == false) { - if (WaitFd(STDIN_FILENO) == false) - return 0; - } + // We have no commands, wait for some to arrive + if (Queue == 0) + { + if (WaitFd(STDIN_FILENO) == false) + return 0; + } - /* Run messages, we can accept 0 (no message) if we didn't - do a WaitFd above.. Otherwise the FD is closed. */ - int Result = Run(true); - if (Result != -1 && (Result != 0 || Queue == 0)) - return 100; + /* Run messages, we can accept 0 (no message) if we didn't + do a WaitFd above.. Otherwise the FD is closed. */ + int Result = Run(true); + if (Result != -1 && (Result != 0 || Queue == 0)) + return 100; - if (Queue == 0) - continue; - - // Connect to the server - if (Server == 0 || Server->Comp(Queue->Uri) == false) - { - delete Server; - Server = new ServerState(Queue->Uri,this); + if (Queue == 0) + continue; + + // Connect to the server + if (Server == 0 || Server->Comp(Queue->Uri) == false) + { + delete Server; + Server = new ServerState(Queue->Uri,this); + } + + /* If the server has explicitly said this is the last connection + then we pre-emptively shut down the pipeline and tear down + the connection. This will speed up HTTP/1.0 servers a tad + since we don't have to wait for the close sequence to + complete */ + if (Server->Persistent == false) + Server->Close(); + + // Reset the pipeline + if (Server->ServerFd == -1) + QueueBack = Queue; } - - /* If the server has explicitly said this is the last connection - then we pre-emptively shut down the pipeline and tear down - the connection. This will speed up HTTP/1.0 servers a tad - since we don't have to wait for the close sequence to - complete */ - if (Server->Persistent == false) - Server->Close(); - - // Reset the pipeline - if (Server->ServerFd == -1) - QueueBack = Queue; // Connnect to the host - if (Server->Open() == false) + if (Server->Open(ReadProxy) == false) { Fail(true); delete Server; @@ -1093,8 +1406,17 @@ continue; } - // Fill the pipeline. - Fetch(0); + if (Retry == false) + { + // Fill the pipeline. + Fetch(0); + } + else + { + // Retry last request. + SendReq(Queue,Server->Out); + Retry = false; + } // Fetch the next URL header data from the server. switch (Server->RunHeaders()) @@ -1212,11 +1534,19 @@ break; } + // Got authentication, rerun fetching routine + case 6: + Server->In.Reset(); + SendReq(Queue,Server->Out); + Retry = true; + break; + default: Fail(_("Internal error")); break; } + ReadProxy = false; FailCounter = 0; } === modified file 'methods/http.h' --- methods/http.h 2007-02-12 21:16:55 +0000 +++ methods/http.h 2007-03-07 22:12:26 +0000 @@ -83,6 +83,22 @@ ~CircleBuf() {delete [] Buf; delete Hash;}; }; +struct AuthInfo +{ + string Domain; + string Path; + string Realm; + string Nonce; + string Cnonce; + string Opaque; + int NonceCount; + bool Stale; + enum {Unspec,Auth,AuthInt} QOP; + enum {Basic,Digest} Type; + enum {MD5,MD5Sess} Algorithm; + AuthInfo() : NonceCount(0), Stale(false), QOP(Unspec), Type(Basic), Algorithm(MD5) {}; +}; + struct ServerState { // This is the last parsed Header Line @@ -110,7 +126,9 @@ CircleBuf Out; int ServerFd; URI ServerName; + AuthInfo Auth; + bool ParseAuth(string Line, AuthInfo &Auth); bool HeaderLine(string Line); bool Comp(URI Other) {return Other.Host == ServerName.Host && Other.Port == ServerName.Port;}; void Reset() {Major = 0; Minor = 0; Result = 0; Size = 0; StartPos = 0; @@ -119,7 +137,7 @@ int RunHeaders(); bool RunData(); - bool Open(); + bool Open(bool ReadProxy); bool Close(); ServerState(URI Srv,HttpMethod *Owner); @@ -129,6 +147,7 @@ class HttpMethod : public pkgAcqMethod { void SendReq(FetchItem *Itm,CircleBuf &Out); + string DigestEncode(AuthInfo &Auth, URI &Uri, URI &Resource); bool Go(bool ToFile,ServerState *Srv); bool Flush(ServerState *Srv); bool ServerDie(ServerState *Srv); @@ -136,6 +155,7 @@ virtual bool Fetch(FetchItem *); virtual bool Configuration(string Message); + virtual bool Authorization(string Message); // In the event of a fatal signal this file will be closed and timestamped. static string FailFile; @@ -148,6 +168,7 @@ FileFd *File; ServerState *Server; + bool DefaultAuthUsed; int Loop(); @@ -155,9 +176,11 @@ { File = 0; Server = 0; + DefaultAuthUsed = false; }; }; URI Proxy; +AuthInfo ProxyAuth; #endif