]> git.saurik.com Git - apt.git/blame - methods/server.cc
drop incorrect const attribute from DirectoryExists
[apt.git] / methods / server.cc
CommitLineData
7330f4df
DK
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3/* ######################################################################
4
5 HTTP and HTTPS share a lot of common code and these classes are
6 exactly the dumping ground for this common code
7
8 ##################################################################### */
9 /*}}}*/
10// Include Files /*{{{*/
11#include <config.h>
12
7330f4df
DK
13#include <apt-pkg/configuration.h>
14#include <apt-pkg/error.h>
453b82a3
DK
15#include <apt-pkg/fileutl.h>
16#include <apt-pkg/strutl.h>
7330f4df 17
453b82a3
DK
18#include <ctype.h>
19#include <signal.h>
20#include <stdio.h>
21#include <stdlib.h>
7330f4df
DK
22#include <sys/stat.h>
23#include <sys/time.h>
453b82a3 24#include <time.h>
7330f4df 25#include <unistd.h>
7330f4df 26#include <iostream>
453b82a3 27#include <limits>
7330f4df 28#include <map>
453b82a3
DK
29#include <string>
30#include <vector>
7330f4df 31
453b82a3 32#include "server.h"
7330f4df
DK
33
34#include <apti18n.h>
35 /*}}}*/
36using namespace std;
37
38string ServerMethod::FailFile;
39int ServerMethod::FailFd = -1;
40time_t ServerMethod::FailTime = 0;
41
42// ServerState::RunHeaders - Get the headers before the data /*{{{*/
43// ---------------------------------------------------------------------
44/* Returns 0 if things are OK, 1 if an IO error occurred and 2 if a header
45 parse error occurred */
9622b211
MV
46ServerState::RunHeadersResult ServerState::RunHeaders(FileFd * const File,
47 const std::string &Uri)
7330f4df
DK
48{
49 State = Header;
50
51 Owner->Status(_("Waiting for headers"));
52
53 Major = 0;
54 Minor = 0;
55 Result = 0;
6291f60e 56 TotalFileSize = 0;
ed793a19 57 JunkSize = 0;
7330f4df
DK
58 StartPos = 0;
59 Encoding = Closes;
60 HaveContent = false;
61 time(&Date);
62
63 do
64 {
65 string Data;
66 if (ReadHeaderLines(Data) == false)
67 continue;
68
69 if (Owner->Debug == true)
9622b211 70 clog << "Answer for: " << Uri << endl << Data;
7330f4df
DK
71
72 for (string::const_iterator I = Data.begin(); I < Data.end(); ++I)
73 {
74 string::const_iterator J = I;
75 for (; J != Data.end() && *J != '\n' && *J != '\r'; ++J);
76 if (HeaderLine(string(I,J)) == false)
77 return RUN_HEADERS_PARSE_ERROR;
78 I = J;
79 }
80
81 // 100 Continue is a Nop...
82 if (Result == 100)
83 continue;
84
1e3f4083 85 // Tidy up the connection persistence state.
7330f4df
DK
86 if (Encoding == Closes && HaveContent == true)
87 Persistent = false;
88
89 return RUN_HEADERS_OK;
90 }
91 while (LoadNextResponse(false, File) == true);
92
93 return RUN_HEADERS_IO_ERROR;
94}
95 /*}}}*/
96// ServerState::HeaderLine - Process a header line /*{{{*/
97// ---------------------------------------------------------------------
98/* */
99bool ServerState::HeaderLine(string Line)
100{
101 if (Line.empty() == true)
102 return true;
103
104 string::size_type Pos = Line.find(' ');
105 if (Pos == string::npos || Pos+1 > Line.length())
106 {
107 // Blah, some servers use "connection:closes", evil.
108 Pos = Line.find(':');
109 if (Pos == string::npos || Pos + 2 > Line.length())
110 return _error->Error(_("Bad header line"));
111 Pos++;
112 }
113
114 // Parse off any trailing spaces between the : and the next word.
115 string::size_type Pos2 = Pos;
74dedb4a 116 while (Pos2 < Line.length() && isspace_ascii(Line[Pos2]) != 0)
7330f4df 117 Pos2++;
d3e8fbb3 118
7330f4df
DK
119 string Tag = string(Line,0,Pos);
120 string Val = string(Line,Pos2);
d3e8fbb3 121
7330f4df
DK
122 if (stringcasecmp(Tag.c_str(),Tag.c_str()+4,"HTTP") == 0)
123 {
124 // Evil servers return no version
125 if (Line[4] == '/')
126 {
127 int const elements = sscanf(Line.c_str(),"HTTP/%3u.%3u %3u%359[^\n]",&Major,&Minor,&Result,Code);
128 if (elements == 3)
129 {
130 Code[0] = '\0';
0c2dc43d 131 if (Owner != NULL && Owner->Debug == true)
b58e2c7c 132 clog << "HTTP server doesn't give Reason-Phrase for " << std::to_string(Result) << std::endl;
7330f4df
DK
133 }
134 else if (elements != 4)
135 return _error->Error(_("The HTTP server sent an invalid reply header"));
136 }
137 else
138 {
139 Major = 0;
140 Minor = 9;
141 if (sscanf(Line.c_str(),"HTTP %3u%359[^\n]",&Result,Code) != 2)
142 return _error->Error(_("The HTTP server sent an invalid reply header"));
143 }
144
1e3f4083 145 /* Check the HTTP response header to get the default persistence
7330f4df
DK
146 state. */
147 if (Major < 1)
148 Persistent = false;
149 else
150 {
151 if (Major == 1 && Minor == 0)
b6d88f39 152 {
7330f4df 153 Persistent = false;
b6d88f39 154 }
7330f4df 155 else
b6d88f39 156 {
7330f4df 157 Persistent = true;
b6d88f39
JAK
158 if (PipelineAllowed)
159 Pipeline = true;
160 }
7330f4df
DK
161 }
162
163 return true;
d3e8fbb3
DK
164 }
165
7330f4df
DK
166 if (stringcasecmp(Tag,"Content-Length:") == 0)
167 {
168 if (Encoding == Closes)
169 Encoding = Stream;
170 HaveContent = true;
d3e8fbb3 171
ceafe8a6 172 unsigned long long * DownloadSizePtr = &DownloadSize;
4bba5a88 173 if (Result == 416 || (Result >= 300 && Result < 400))
ceafe8a6 174 DownloadSizePtr = &JunkSize;
7330f4df 175
ceafe8a6
MV
176 *DownloadSizePtr = strtoull(Val.c_str(), NULL, 10);
177 if (*DownloadSizePtr >= std::numeric_limits<unsigned long long>::max())
7330f4df 178 return _error->Errno("HeaderLine", _("The HTTP server sent an invalid Content-Length header"));
ceafe8a6 179 else if (*DownloadSizePtr == 0)
7330f4df 180 HaveContent = false;
ceafe8a6
MV
181
182 // On partial content (206) the Content-Length less than the real
183 // size, so do not set it here but leave that to the Content-Range
184 // header instead
6291f60e
MV
185 if(Result != 206 && TotalFileSize == 0)
186 TotalFileSize = DownloadSize;
ceafe8a6 187
7330f4df
DK
188 return true;
189 }
190
191 if (stringcasecmp(Tag,"Content-Type:") == 0)
192 {
193 HaveContent = true;
194 return true;
195 }
d3e8fbb3 196
7330f4df
DK
197 if (stringcasecmp(Tag,"Content-Range:") == 0)
198 {
199 HaveContent = true;
200
201 // §14.16 says 'byte-range-resp-spec' should be a '*' in case of 416
6291f60e 202 if (Result == 416 && sscanf(Val.c_str(), "bytes */%llu",&TotalFileSize) == 1)
ed793a19 203 ; // we got the expected filesize which is all we wanted
6291f60e 204 else if (sscanf(Val.c_str(),"bytes %llu-%*u/%llu",&StartPos,&TotalFileSize) != 2)
7330f4df 205 return _error->Error(_("The HTTP server sent an invalid Content-Range header"));
6291f60e 206 if ((unsigned long long)StartPos > TotalFileSize)
7330f4df 207 return _error->Error(_("This HTTP server has broken range support"));
ceafe8a6
MV
208
209 // figure out what we will download
6291f60e 210 DownloadSize = TotalFileSize - StartPos;
7330f4df
DK
211 return true;
212 }
d3e8fbb3 213
7330f4df
DK
214 if (stringcasecmp(Tag,"Transfer-Encoding:") == 0)
215 {
216 HaveContent = true;
217 if (stringcasecmp(Val,"chunked") == 0)
d3e8fbb3 218 Encoding = Chunked;
7330f4df
DK
219 return true;
220 }
221
222 if (stringcasecmp(Tag,"Connection:") == 0)
223 {
224 if (stringcasecmp(Val,"close") == 0)
225 Persistent = false;
226 if (stringcasecmp(Val,"keep-alive") == 0)
227 Persistent = true;
228 return true;
229 }
d3e8fbb3 230
7330f4df
DK
231 if (stringcasecmp(Tag,"Last-Modified:") == 0)
232 {
233 if (RFC1123StrToTime(Val.c_str(), Date) == false)
234 return _error->Error(_("Unknown date format"));
235 return true;
236 }
237
238 if (stringcasecmp(Tag,"Location:") == 0)
239 {
240 Location = Val;
241 return true;
242 }
243
244 return true;
245}
246 /*}}}*/
247// ServerState::ServerState - Constructor /*{{{*/
2651f1c0
DK
248ServerState::ServerState(URI Srv, ServerMethod *Owner) :
249 DownloadSize(0), ServerName(Srv), TimeOut(120), Owner(Owner)
7330f4df
DK
250{
251 Reset();
252}
253 /*}}}*/
34faa8f7
DK
254bool ServerState::AddPartialFileToHashes(FileFd &File) /*{{{*/
255{
256 File.Truncate(StartPos);
257 return GetHashes()->AddFD(File, StartPos);
258}
259 /*}}}*/
7330f4df 260
7330f4df
DK
261// ServerMethod::DealWithHeaders - Handle the retrieved header data /*{{{*/
262// ---------------------------------------------------------------------
263/* We look at the header data we got back from the server and decide what
264 to do. Returns DealWithHeadersResult (see http.h for details).
265 */
266ServerMethod::DealWithHeadersResult
267ServerMethod::DealWithHeaders(FetchResult &Res)
268{
269 // Not Modified
270 if (Server->Result == 304)
271 {
ce1f3a2c 272 RemoveFile("server", Queue->DestFile);
7330f4df
DK
273 Res.IMSHit = true;
274 Res.LastModified = Queue->LastModified;
4bba5a88 275 Res.Size = 0;
7330f4df
DK
276 return IMS_HIT;
277 }
dcbb364f 278
7330f4df
DK
279 /* Redirect
280 *
281 * Note that it is only OK for us to treat all redirection the same
282 * because we *always* use GET, not other HTTP methods. There are
283 * three redirection codes for which it is not appropriate that we
284 * redirect. Pass on those codes so the error handling kicks in.
285 */
286 if (AllowRedirect
287 && (Server->Result > 300 && Server->Result < 400)
288 && (Server->Result != 300 // Multiple Choices
289 && Server->Result != 304 // Not Modified
290 && Server->Result != 306)) // (Not part of HTTP/1.1, reserved)
291 {
4bba5a88
DK
292 if (Server->Location.empty() == true)
293 ;
7330f4df
DK
294 else if (Server->Location[0] == '/' && Queue->Uri.empty() == false)
295 {
296 URI Uri = Queue->Uri;
297 if (Uri.Host.empty() == false)
298 NextURI = URI::SiteOnly(Uri);
299 else
300 NextURI.clear();
301 NextURI.append(DeQuoteString(Server->Location));
57401c48
DK
302 if (Queue->Uri == NextURI)
303 {
304 SetFailReason("RedirectionLoop");
305 _error->Error("Redirection loop encountered");
306 if (Server->HaveContent == true)
307 return ERROR_WITH_CONTENT_PAGE;
308 return ERROR_UNRECOVERABLE;
309 }
7330f4df
DK
310 return TRY_AGAIN_OR_REDIRECT;
311 }
312 else
313 {
9082a1fc
DK
314 NextURI = DeQuoteString(Server->Location);
315 URI tmpURI = NextURI;
30060442 316 if (tmpURI.Access.find('+') != std::string::npos)
57401c48 317 {
30060442
DK
318 _error->Error("Server tried to trick us into using a specific implementation: %s", tmpURI.Access.c_str());
319 if (Server->HaveContent == true)
320 return ERROR_WITH_CONTENT_PAGE;
321 return ERROR_UNRECOVERABLE;
322 }
323 URI Uri = Queue->Uri;
324 if (Binary.find('+') != std::string::npos)
325 {
326 auto base = Binary.substr(0, Binary.find('+'));
327 if (base != tmpURI.Access)
328 {
329 tmpURI.Access = base + '+' + tmpURI.Access;
330 if (tmpURI.Access == Binary)
331 {
332 std::string tmpAccess = Uri.Access;
333 std::swap(tmpURI.Access, Uri.Access);
334 NextURI = tmpURI;
335 std::swap(tmpURI.Access, Uri.Access);
336 }
337 else
338 NextURI = tmpURI;
339 }
57401c48
DK
340 }
341 if (Queue->Uri == NextURI)
342 {
343 SetFailReason("RedirectionLoop");
344 _error->Error("Redirection loop encountered");
345 if (Server->HaveContent == true)
346 return ERROR_WITH_CONTENT_PAGE;
347 return ERROR_UNRECOVERABLE;
348 }
30060442 349 Uri.Access = Binary;
9082a1fc
DK
350 // same protocol redirects are okay
351 if (tmpURI.Access == Uri.Access)
352 return TRY_AGAIN_OR_REDIRECT;
353 // as well as http to https
4bba5a88 354 else if ((Uri.Access == "http" || Uri.Access == "https+http") && tmpURI.Access == "https")
9082a1fc 355 return TRY_AGAIN_OR_REDIRECT;
4bba5a88 356 else
30060442
DK
357 {
358 auto const tmpplus = tmpURI.Access.find('+');
359 if (tmpplus != std::string::npos && tmpURI.Access.substr(tmpplus + 1) == "https")
360 {
361 auto const uriplus = Uri.Access.find('+');
362 if (uriplus == std::string::npos)
363 {
364 if (Uri.Access == tmpURI.Access.substr(0, tmpplus)) // foo -> foo+https
365 return TRY_AGAIN_OR_REDIRECT;
366 }
367 else if (Uri.Access.substr(uriplus + 1) == "http" &&
368 Uri.Access.substr(0, uriplus) == tmpURI.Access.substr(0, tmpplus)) // foo+http -> foo+https
369 return TRY_AGAIN_OR_REDIRECT;
370 }
371 }
372 _error->Error("Redirection from %s to '%s' is forbidden", Uri.Access.c_str(), NextURI.c_str());
7330f4df
DK
373 }
374 /* else pass through for error message */
375 }
376 // retry after an invalid range response without partial data
377 else if (Server->Result == 416)
378 {
379 struct stat SBuf;
380 if (stat(Queue->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
381 {
dcbb364f
DK
382 bool partialHit = false;
383 if (Queue->ExpectedHashes.usable() == true)
384 {
385 Hashes resultHashes(Queue->ExpectedHashes);
386 FileFd file(Queue->DestFile, FileFd::ReadOnly);
4fc6b757 387 Server->TotalFileSize = file.FileSize();
dcbb364f
DK
388 Server->Date = file.ModificationTime();
389 resultHashes.AddFD(file);
390 HashStringList const hashList = resultHashes.GetHashStringList();
391 partialHit = (Queue->ExpectedHashes == hashList);
392 }
4fc6b757 393 else if ((unsigned long long)SBuf.st_size == Server->TotalFileSize)
dcbb364f
DK
394 partialHit = true;
395 if (partialHit == true)
7330f4df
DK
396 {
397 // the file is completely downloaded, but was not moved
ed793a19
DK
398 if (Server->HaveContent == true)
399 {
4bba5a88
DK
400 // nuke the sent error page
401 Server->RunDataToDevNull();
402 Server->HaveContent = false;
ed793a19 403 }
6291f60e 404 Server->StartPos = Server->TotalFileSize;
7330f4df 405 Server->Result = 200;
7330f4df 406 }
ce1f3a2c 407 else if (RemoveFile("server", Queue->DestFile))
7330f4df
DK
408 {
409 NextURI = Queue->Uri;
410 return TRY_AGAIN_OR_REDIRECT;
411 }
412 }
413 }
414
3a8776a3 415 /* We have a reply we don't handle. This should indicate a perm server
7330f4df
DK
416 failure */
417 if (Server->Result < 200 || Server->Result >= 300)
418 {
4bba5a88
DK
419 if (_error->PendingError() == false)
420 {
421 std::string err;
422 strprintf(err, "HttpError%u", Server->Result);
423 SetFailReason(err);
424 _error->Error("%u %s", Server->Result, Server->Code);
425 }
7330f4df
DK
426 if (Server->HaveContent == true)
427 return ERROR_WITH_CONTENT_PAGE;
428 return ERROR_UNRECOVERABLE;
429 }
430
431 // This is some sort of 2xx 'data follows' reply
432 Res.LastModified = Server->Date;
6291f60e 433 Res.Size = Server->TotalFileSize;
7330f4df
DK
434 return FILE_IS_OPEN;
435}
436 /*}}}*/
437// ServerMethod::SigTerm - Handle a fatal signal /*{{{*/
438// ---------------------------------------------------------------------
1e3f4083 439/* This closes and timestamps the open file. This is necessary to get
7330f4df
DK
440 resume behavoir on user abort */
441void ServerMethod::SigTerm(int)
442{
443 if (FailFd == -1)
444 _exit(100);
9ce3cfc9 445
246bbb61 446 struct timeval times[2];
9ce3cfc9
DK
447 times[0].tv_sec = FailTime;
448 times[1].tv_sec = FailTime;
246bbb61
DK
449 times[0].tv_usec = times[1].tv_usec = 0;
450 utimes(FailFile.c_str(), times);
7330f4df 451 close(FailFd);
9ce3cfc9 452
7330f4df
DK
453 _exit(100);
454}
455 /*}}}*/
456// ServerMethod::Fetch - Fetch an item /*{{{*/
457// ---------------------------------------------------------------------
458/* This adds an item to the pipeline. We keep the pipeline at a fixed
459 depth. */
460bool ServerMethod::Fetch(FetchItem *)
461{
742f67ea 462 if (Server == nullptr || QueueBack == nullptr)
7330f4df
DK
463 return true;
464
742f67ea
DK
465 // If pipelining is disabled, we only queue 1 request
466 auto const AllowedDepth = Server->Pipeline ? PipelineDepth : 0;
467 // how deep is our pipeline currently?
468 decltype(PipelineDepth) CurrentDepth = 0;
469 for (FetchItem const *I = Queue; I != QueueBack; I = I->Next)
470 ++CurrentDepth;
353b7bab
DK
471 if (CurrentDepth > AllowedDepth)
472 return true;
742f67ea
DK
473
474 do {
7330f4df 475 // Make sure we stick with the same server
742f67ea
DK
476 if (Server->Comp(QueueBack->Uri) == false)
477 break;
478
479 bool const UsableHashes = QueueBack->ExpectedHashes.usable();
480 // if we have no hashes, do at most one such request
481 // as we can't fixup pipeling misbehaviors otherwise
482 if (CurrentDepth != 0 && UsableHashes == false)
7330f4df 483 break;
742f67ea
DK
484
485 if (UsableHashes && FileExists(QueueBack->DestFile))
7330f4df 486 {
742f67ea
DK
487 FileFd partial(QueueBack->DestFile, FileFd::ReadOnly);
488 Hashes wehave(QueueBack->ExpectedHashes);
489 if (QueueBack->ExpectedHashes.FileSize() == partial.FileSize())
490 {
491 if (wehave.AddFD(partial) &&
492 wehave.GetHashStringList() == QueueBack->ExpectedHashes)
493 {
494 FetchResult Res;
495 Res.Filename = QueueBack->DestFile;
496 Res.ResumePoint = QueueBack->ExpectedHashes.FileSize();
497 URIStart(Res);
498 // move item to the start of the queue as URIDone will
499 // always dequeued the first item in the queue
500 if (Queue != QueueBack)
501 {
502 FetchItem *Prev = Queue;
503 for (; Prev->Next != QueueBack; Prev = Prev->Next)
504 /* look for the previous queue item */;
505 Prev->Next = QueueBack->Next;
506 QueueBack->Next = Queue;
507 Queue = QueueBack;
508 QueueBack = Prev->Next;
509 }
510 Res.TakeHashes(wehave);
511 URIDone(Res);
512 continue;
513 }
514 else
515 RemoveFile("Fetch-Partial", QueueBack->DestFile);
516 }
7330f4df 517 }
742f67ea
DK
518 auto const Tmp = QueueBack;
519 QueueBack = QueueBack->Next;
520 SendReq(Tmp);
521 ++CurrentDepth;
522 } while (CurrentDepth <= AllowedDepth && QueueBack != nullptr);
523
7330f4df 524 return true;
d3e8fbb3 525}
7330f4df
DK
526 /*}}}*/
527// ServerMethod::Loop - Main loop /*{{{*/
528int ServerMethod::Loop()
529{
7330f4df
DK
530 signal(SIGTERM,SigTerm);
531 signal(SIGINT,SigTerm);
532
533 Server = 0;
534
535 int FailCounter = 0;
536 while (1)
537 {
538 // We have no commands, wait for some to arrive
539 if (Queue == 0)
540 {
541 if (WaitFd(STDIN_FILENO) == false)
542 return 0;
543 }
544
545 /* Run messages, we can accept 0 (no message) if we didn't
546 do a WaitFd above.. Otherwise the FD is closed. */
547 int Result = Run(true);
548 if (Result != -1 && (Result != 0 || Queue == 0))
549 {
550 if(FailReason.empty() == false ||
30060442 551 ConfigFindB("DependOnSTDIN", true) == true)
7330f4df
DK
552 return 100;
553 else
554 return 0;
555 }
556
557 if (Queue == 0)
558 continue;
559
560 // Connect to the server
561 if (Server == 0 || Server->Comp(Queue->Uri) == false)
30060442 562 {
7330f4df 563 Server = CreateServerState(Queue->Uri);
30060442
DK
564 setPostfixForMethodNames(::URI(Queue->Uri).Host.c_str());
565 AllowRedirect = ConfigFindB("AllowRedirect", true);
566 PipelineDepth = ConfigFindI("Pipeline-Depth", 10);
567 Debug = DebugEnabled();
568 }
830a1b8c 569
7330f4df
DK
570 /* If the server has explicitly said this is the last connection
571 then we pre-emptively shut down the pipeline and tear down
572 the connection. This will speed up HTTP/1.0 servers a tad
573 since we don't have to wait for the close sequence to
574 complete */
575 if (Server->Persistent == false)
576 Server->Close();
577
578 // Reset the pipeline
579 if (Server->IsOpen() == false)
580 QueueBack = Queue;
581
582 // Connnect to the host
583 if (Server->Open() == false)
584 {
585 Fail(true);
830a1b8c 586 Server = nullptr;
7330f4df
DK
587 continue;
588 }
589
590 // Fill the pipeline.
591 Fetch(0);
592
593 // Fetch the next URL header data from the server.
9622b211 594 switch (Server->RunHeaders(File, Queue->Uri))
7330f4df
DK
595 {
596 case ServerState::RUN_HEADERS_OK:
597 break;
598
599 // The header data is bad
600 case ServerState::RUN_HEADERS_PARSE_ERROR:
601 {
602 _error->Error(_("Bad header data"));
603 Fail(true);
cc0a4c82 604 Server->Close();
7330f4df
DK
605 RotateDNS();
606 continue;
607 }
608
609 // The server closed a connection during the header get..
610 default:
611 case ServerState::RUN_HEADERS_IO_ERROR:
612 {
613 FailCounter++;
614 _error->Discard();
615 Server->Close();
616 Server->Pipeline = false;
b6d88f39 617 Server->PipelineAllowed = false;
7330f4df
DK
618
619 if (FailCounter >= 2)
620 {
621 Fail(_("Connection failed"),true);
622 FailCounter = 0;
623 }
624
625 RotateDNS();
626 continue;
627 }
628 };
629
630 // Decide what to do.
631 FetchResult Res;
632 Res.Filename = Queue->DestFile;
633 switch (DealWithHeaders(Res))
634 {
635 // Ok, the file is Open
636 case FILE_IS_OPEN:
637 {
638 URIStart(Res);
639
640 // Run the data
641 bool Result = true;
dcd5856b
MV
642
643 // ensure we don't fetch too much
f2b47ba2
MV
644 // we could do "Server->MaximumSize = Queue->MaximumSize" here
645 // but that would break the clever pipeline messup detection
646 // so instead we use the size of the biggest item in the queue
647 Server->MaximumSize = FindMaximumObjectSizeInQueue();
dcd5856b 648
7330f4df
DK
649 if (Server->HaveContent)
650 Result = Server->RunData(File);
651
652 /* If the server is sending back sizeless responses then fill in
653 the size now */
654 if (Res.Size == 0)
655 Res.Size = File->Size();
656
657 // Close the file, destroy the FD object and timestamp it
658 FailFd = -1;
659 delete File;
660 File = 0;
661
662 // Timestamp
246bbb61 663 struct timeval times[2];
9ce3cfc9 664 times[0].tv_sec = times[1].tv_sec = Server->Date;
246bbb61
DK
665 times[0].tv_usec = times[1].tv_usec = 0;
666 utimes(Queue->DestFile.c_str(), times);
7330f4df
DK
667
668 // Send status to APT
669 if (Result == true)
670 {
895417ef
DK
671 Hashes * const resultHashes = Server->GetHashes();
672 HashStringList const hashList = resultHashes->GetHashStringList();
673 if (PipelineDepth != 0 && Queue->ExpectedHashes.usable() == true && Queue->ExpectedHashes != hashList)
674 {
675 // we did not get the expected hash… mhhh:
676 // could it be that server/proxy messed up pipelining?
677 FetchItem * BeforeI = Queue;
678 for (FetchItem *I = Queue->Next; I != 0 && I != QueueBack; I = I->Next)
679 {
680 if (I->ExpectedHashes.usable() == true && I->ExpectedHashes == hashList)
681 {
682 // yes, he did! Disable pipelining and rewrite queue
683 if (Server->Pipeline == true)
684 {
b9c20219 685 Warning(_("Automatically disabled %s due to incorrect response from server/proxy. (man 5 apt.conf)"), "Acquire::http::Pipeline-Depth");
895417ef 686 Server->Pipeline = false;
b6d88f39 687 Server->PipelineAllowed = false;
895417ef
DK
688 // we keep the PipelineDepth value so that the rest of the queue can be fixed up as well
689 }
690 Rename(Res.Filename, I->DestFile);
691 Res.Filename = I->DestFile;
692 BeforeI->Next = I->Next;
693 I->Next = Queue;
694 Queue = I;
695 break;
696 }
697 BeforeI = I;
698 }
699 }
700 Res.TakeHashes(*resultHashes);
7330f4df
DK
701 URIDone(Res);
702 }
703 else
704 {
705 if (Server->IsOpen() == false)
706 {
707 FailCounter++;
708 _error->Discard();
709 Server->Close();
710
711 if (FailCounter >= 2)
712 {
713 Fail(_("Connection failed"),true);
714 FailCounter = 0;
715 }
716
717 QueueBack = Queue;
718 }
719 else
a2d40703
MV
720 {
721 Server->Close();
7330f4df 722 Fail(true);
a2d40703 723 }
7330f4df
DK
724 }
725 break;
726 }
727
728 // IMS hit
729 case IMS_HIT:
730 {
731 URIDone(Res);
732 break;
733 }
734
735 // Hard server error, not found or something
736 case ERROR_UNRECOVERABLE:
737 {
738 Fail();
739 break;
740 }
741
742 // Hard internal error, kill the connection and fail
743 case ERROR_NOT_FROM_SERVER:
744 {
745 delete File;
746 File = 0;
747
748 Fail();
749 RotateDNS();
750 Server->Close();
751 break;
752 }
753
754 // We need to flush the data, the header is like a 404 w/ error text
755 case ERROR_WITH_CONTENT_PAGE:
756 {
757 Fail();
4bba5a88 758 Server->RunDataToDevNull();
7330f4df
DK
759 break;
760 }
57401c48
DK
761
762 // Try again with a new URL
763 case TRY_AGAIN_OR_REDIRECT:
764 {
765 // Clear rest of response if there is content
766 if (Server->HaveContent)
767 Server->RunDataToDevNull();
768 Redirect(NextURI);
769 break;
770 }
7330f4df
DK
771
772 default:
773 Fail(_("Internal error"));
774 break;
775 }
776
777 FailCounter = 0;
778 }
779
780 return 0;
f2b47ba2
MV
781}
782 /*}}}*/
830a1b8c 783unsigned long long ServerMethod::FindMaximumObjectSizeInQueue() const /*{{{*/
f2b47ba2
MV
784{
785 unsigned long long MaxSizeInQueue = 0;
8bb043fc 786 for (FetchItem *I = Queue; I != 0 && I != QueueBack; I = I->Next)
f2b47ba2
MV
787 MaxSizeInQueue = std::max(MaxSizeInQueue, I->MaximumSize);
788 return MaxSizeInQueue;
7330f4df
DK
789}
790 /*}}}*/
30060442
DK
791ServerMethod::ServerMethod(std::string &&Binary, char const * const Ver,unsigned long const Flags) :/*{{{*/
792 aptMethod(std::move(Binary), Ver, Flags), Server(nullptr), File(NULL), PipelineDepth(10),
830a1b8c
DK
793 AllowRedirect(false), Debug(false)
794{
795}
796 /*}}}*/
0568d325
DK
797bool ServerMethod::Configuration(std::string Message) /*{{{*/
798{
799 if (aptMethod::Configuration(Message) == false)
800 return false;
801
802 _config->CndSet("Acquire::tor::Proxy",
803 "socks5h://apt-transport-tor@localhost:9050");
804 return true;
805}
806 /*}}}*/
807bool ServerMethod::AddProxyAuth(URI &Proxy, URI const &Server) const /*{{{*/
808{
809 if (std::find(methodNames.begin(), methodNames.end(), "tor") != methodNames.end() &&
810 Proxy.User == "apt-transport-tor" && Proxy.Password.empty())
811 {
812 std::string pass = Server.Host;
813 pass.erase(std::remove_if(pass.begin(), pass.end(), [](char const c) { return std::isalnum(c) == 0; }), pass.end());
814 if (pass.length() > 255)
815 Proxy.Password = pass.substr(0, 255);
816 else
817 Proxy.Password = std::move(pass);
818 }
819 // FIXME: should we support auth.conf for proxies?
820 return true;
821}
822 /*}}}*/