~ubuntu-branches/debian/experimental/apt/experimental

« back to all changes in this revision

Viewing changes to test/interactive-helper/aptwebserver.cc

  • Committer: Package Import Robot
  • Author(s): Michael Vogt, David Kalnischkies, Steve Langasek, Michael Vogt
  • Date: 2013-12-07 14:54:31 UTC
  • mto: This revision was merged to the branch mainline in revision 50.
  • Revision ID: package-import@ubuntu.com-20131207145431-8av51jnzb9kz20ow
Tags: 0.9.14
[ David Kalnischkies ]
* merge ubuntus apport reporting changes to reduce diff
* enable NOISE for build logs to enable analyse
* introduce a vendor system to change sources.list
* add a vendor specific file to have configurable entities
* use a substvar to set the archive-keyring in debian/control
* cherry-pick ubuntus (disabled) net-update fixes
* generate apt-key script with vendor info about keys
* drop old /var/state to /var/lib transition artefacts

[ Steve Langasek ]
* prepare-release: declare the packages needed as source build deps.

[ Michael Vogt ]
* enable release based selection for deb-src (closes: 731102)
* document Dpkg::Progress-Fancy (closes: 726169), thanks to James McCoy
* vendor/makefile: fix build error for parallel builds
* Handle SIGWINCH in APT::Progress-Fancy=1

Show diffs side-by-side

added added

removed removed

Lines of Context:
112
112
   date.append(TimeRFC1123(time(NULL)));
113
113
   headers.push_back(date);
114
114
 
115
 
   std::clog << ">>> RESPONSE >>>" << std::endl;
 
115
   std::clog << ">>> RESPONSE to " << client << " >>>" << std::endl;
116
116
   bool Success = true;
117
117
   for (std::list<std::string>::const_iterator h = headers.begin();
118
118
        Success == true && h != headers.end(); ++h)
137
137
   {
138
138
      if (actual == 0)
139
139
         break;
140
 
      if (Success == true)
141
 
         Success &= FileFd::Write(client, buffer, actual);
 
140
      Success &= FileFd::Write(client, buffer, actual);
142
141
   }
143
 
   if (Success == true)
144
 
      Success &= FileFd::Write(client, "\r\n", 2);
 
142
   if (Success == false)
 
143
      std::cerr << "SENDFILE: READ/WRITE ERROR to " << client << std::endl;
145
144
   return Success;
146
145
}
147
146
                                                                        /*}}}*/
148
147
bool sendData(int const client, std::string const &data)                /*{{{*/
149
148
{
150
 
   bool Success = true;
151
 
   Success &= FileFd::Write(client, data.c_str(), data.size());
152
 
   if (Success == true)
153
 
      Success &= FileFd::Write(client, "\r\n", 2);
154
 
   return Success;
 
149
   if (FileFd::Write(client, data.c_str(), data.size()) == false)
 
150
   {
 
151
      std::cerr << "SENDDATA: WRITE ERROR to " << client << std::endl;
 
152
      return false;
 
153
   }
 
154
   return true;
155
155
}
156
156
                                                                        /*}}}*/
157
157
void sendError(int const client, int const httpcode, std::string const &request,/*{{{*/
198
198
   addDataHeaders(headers, response);
199
199
   std::string location("Location: ");
200
200
   if (strncmp(uri.c_str(), "http://", 7) != 0)
201
 
      location.append("http://").append(LookupTag(request, "Host")).append("/").append(uri);
 
201
   {
 
202
      location.append("http://").append(LookupTag(request, "Host")).append("/");
 
203
      if (strncmp("/home/", uri.c_str(), strlen("/home/")) == 0 && uri.find("/public_html/") != std::string::npos)
 
204
      {
 
205
         std::string homeuri = SubstVar(uri, "/home/", "~");
 
206
         homeuri = SubstVar(homeuri, "/public_html/", "/");
 
207
         location.append(homeuri);
 
208
      }
 
209
      else
 
210
         location.append(uri);
 
211
   }
202
212
   else
203
213
      location.append(uri);
204
214
   headers.push_back(location);
267
277
           << "</head>" << std::endl
268
278
           << "<body><h1>Index of " << dir << "</h1>" << std::endl
269
279
           << "<table><tr><th>#</th><th>Name</th><th>Size</th><th>Last-Modified</th></tr>" << std::endl;
270
 
   if (dir != ".")
 
280
   if (dir != "./")
271
281
      listing << "<tr><td>d</td><td><a href=\"..\">Parent Directory</a></td><td>-</td><td>-</td></tr>";
272
282
   for (int i = 0; i < counter; ++i) {
273
283
      struct stat fs;
298
308
}
299
309
                                                                        /*}}}*/
300
310
bool parseFirstLine(int const client, std::string const &request,       /*{{{*/
301
 
                    std::string &filename, bool &sendContent,
 
311
                    std::string &filename, std::string &params, bool &sendContent,
302
312
                    bool &closeConnection)
303
313
{
304
314
   if (strncmp(request.c_str(), "HEAD ", 5) == 0)
365
375
      sendError(client, 400, request, sendContent, "Request is absolutePath, but configured to not accept that");
366
376
      return false;
367
377
   }
 
378
 
 
379
   size_t paramspos = filename.find('?');
 
380
   if (paramspos != std::string::npos)
 
381
   {
 
382
      params = filename.substr(paramspos + 1);
 
383
      filename.erase(paramspos);
 
384
   }
 
385
 
368
386
   filename = DeQuoteString(filename);
369
387
 
370
388
   // this is not a secure server, but at least prevent the obvious …
380
398
   // nuke the first character which is a / as we assured above
381
399
   filename.erase(0, 1);
382
400
   if (filename.empty() == true)
383
 
      filename = ".";
 
401
      filename = "./";
 
402
   // support ~user/ uris to refer to /home/user/public_html/ as a kind-of special directory
 
403
   else if (filename[0] == '~')
 
404
   {
 
405
      // /home/user is actually not entirely correct, but good enough for now
 
406
      size_t dashpos = filename.find('/');
 
407
      if (dashpos != std::string::npos)
 
408
      {
 
409
         std::string home = filename.substr(1, filename.find('/') - 1);
 
410
         std::string pubhtml = filename.substr(filename.find('/') + 1);
 
411
         filename = "/home/" + home + "/public_html/" + pubhtml;
 
412
      }
 
413
      else
 
414
         filename = "/home/" + filename.substr(1) + "/public_html/";
 
415
   }
 
416
 
 
417
   // if no filename is given, but a valid directory see if we can use an index or
 
418
   // have to resort to a autogenerated directory listing later on
 
419
   if (DirectoryExists(filename) == true)
 
420
   {
 
421
      std::string const directoryIndex = _config->Find("aptwebserver::directoryindex");
 
422
      if (directoryIndex.empty() == false && directoryIndex == flNotDir(directoryIndex) &&
 
423
            RealFileExists(filename + directoryIndex) == true)
 
424
         filename += directoryIndex;
 
425
   }
 
426
 
384
427
   return true;
385
428
}
386
429
                                                                        /*}}}*/
427
470
   return false;
428
471
}
429
472
                                                                        /*}}}*/
 
473
void * handleClient(void * voidclient)                                  /*{{{*/
 
474
{
 
475
   int client = *((int*)(voidclient));
 
476
   std::clog << "ACCEPT client " << client << std::endl;
 
477
   std::vector<std::string> messages;
 
478
   while (ReadMessages(client, messages))
 
479
   {
 
480
      bool closeConnection = false;
 
481
      for (std::vector<std::string>::const_iterator m = messages.begin();
 
482
            m != messages.end() && closeConnection == false; ++m) {
 
483
         std::clog << ">>> REQUEST from " << client << " >>>" << std::endl << *m
 
484
            << std::endl << "<<<<<<<<<<<<<<<<" << std::endl;
 
485
         std::list<std::string> headers;
 
486
         std::string filename;
 
487
         std::string params;
 
488
         bool sendContent = true;
 
489
         if (parseFirstLine(client, *m, filename, params, sendContent, closeConnection) == false)
 
490
            continue;
 
491
 
 
492
         // special webserver command request
 
493
         if (filename.length() > 1 && filename[0] == '_')
 
494
         {
 
495
            std::vector<std::string> parts = VectorizeString(filename, '/');
 
496
            if (parts[0] == "_config")
 
497
            {
 
498
               handleOnTheFlyReconfiguration(client, *m, parts);
 
499
               continue;
 
500
            }
 
501
         }
 
502
 
 
503
         // string replacements in the requested filename
 
504
         ::Configuration::Item const *Replaces = _config->Tree("aptwebserver::redirect::replace");
 
505
         if (Replaces != NULL)
 
506
         {
 
507
            std::string redirect = "/" + filename;
 
508
            for (::Configuration::Item *I = Replaces->Child; I != NULL; I = I->Next)
 
509
               redirect = SubstVar(redirect, I->Tag, I->Value);
 
510
            redirect.erase(0,1);
 
511
            if (redirect != filename)
 
512
            {
 
513
               sendRedirect(client, 301, redirect, *m, sendContent);
 
514
               continue;
 
515
            }
 
516
         }
 
517
 
 
518
         ::Configuration::Item const *Overwrite = _config->Tree("aptwebserver::overwrite");
 
519
         if (Overwrite != NULL)
 
520
         {
 
521
            for (::Configuration::Item *I = Overwrite->Child; I != NULL; I = I->Next)
 
522
            {
 
523
               regex_t *pattern = new regex_t;
 
524
               int const res = regcomp(pattern, I->Tag.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB);
 
525
               if (res != 0)
 
526
               {
 
527
                  char error[300];
 
528
                  regerror(res, pattern, error, sizeof(error));
 
529
                  sendError(client, 500, *m, sendContent, error);
 
530
                  continue;
 
531
               }
 
532
               if (regexec(pattern, filename.c_str(), 0, 0, 0) == 0)
 
533
               {
 
534
                  filename = _config->Find("aptwebserver::overwrite::" + I->Tag + "::filename", filename);
 
535
                  if (filename[0] == '/')
 
536
                     filename.erase(0,1);
 
537
                  regfree(pattern);
 
538
                  break;
 
539
               }
 
540
               regfree(pattern);
 
541
            }
 
542
         }
 
543
 
 
544
         // deal with the request
 
545
         if (RealFileExists(filename) == true)
 
546
         {
 
547
            FileFd data(filename, FileFd::ReadOnly);
 
548
            std::string condition = LookupTag(*m, "If-Modified-Since", "");
 
549
            if (_config->FindB("aptwebserver::support::modified-since", true) == true && condition.empty() == false)
 
550
            {
 
551
               time_t cache;
 
552
               if (RFC1123StrToTime(condition.c_str(), cache) == true &&
 
553
                     cache >= data.ModificationTime())
 
554
               {
 
555
                  sendHead(client, 304, headers);
 
556
                  continue;
 
557
               }
 
558
            }
 
559
 
 
560
            if (_config->FindB("aptwebserver::support::range", true) == true)
 
561
               condition = LookupTag(*m, "Range", "");
 
562
            else
 
563
               condition.clear();
 
564
            if (condition.empty() == false && strncmp(condition.c_str(), "bytes=", 6) == 0)
 
565
            {
 
566
               time_t cache;
 
567
               std::string ifrange;
 
568
               if (_config->FindB("aptwebserver::support::if-range", true) == true)
 
569
                  ifrange = LookupTag(*m, "If-Range", "");
 
570
               bool validrange = (ifrange.empty() == true ||
 
571
                     (RFC1123StrToTime(ifrange.c_str(), cache) == true &&
 
572
                      cache <= data.ModificationTime()));
 
573
 
 
574
               // FIXME: support multiple byte-ranges (APT clients do not do this)
 
575
               if (condition.find(',') == std::string::npos)
 
576
               {
 
577
                  size_t start = 6;
 
578
                  unsigned long long filestart = strtoull(condition.c_str() + start, NULL, 10);
 
579
                  // FIXME: no support for last-byte-pos being not the end of the file (APT clients do not do this)
 
580
                  size_t dash = condition.find('-') + 1;
 
581
                  unsigned long long fileend = strtoull(condition.c_str() + dash, NULL, 10);
 
582
                  unsigned long long filesize = data.FileSize();
 
583
                  if ((fileend == 0 || (fileend == filesize && fileend >= filestart)) &&
 
584
                        validrange == true)
 
585
                  {
 
586
                     if (filesize > filestart)
 
587
                     {
 
588
                        data.Skip(filestart);
 
589
                        std::ostringstream contentlength;
 
590
                        contentlength << "Content-Length: " << (filesize - filestart);
 
591
                        headers.push_back(contentlength.str());
 
592
                        std::ostringstream contentrange;
 
593
                        contentrange << "Content-Range: bytes " << filestart << "-"
 
594
                           << filesize - 1 << "/" << filesize;
 
595
                        headers.push_back(contentrange.str());
 
596
                        sendHead(client, 206, headers);
 
597
                        if (sendContent == true)
 
598
                           sendFile(client, data);
 
599
                        continue;
 
600
                     }
 
601
                     else
 
602
                     {
 
603
                        headers.push_back("Content-Length: 0");
 
604
                        std::ostringstream contentrange;
 
605
                        contentrange << "Content-Range: bytes */" << filesize;
 
606
                        headers.push_back(contentrange.str());
 
607
                        sendHead(client, 416, headers);
 
608
                        continue;
 
609
                     }
 
610
                  }
 
611
               }
 
612
            }
 
613
 
 
614
            addFileHeaders(headers, data);
 
615
            sendHead(client, 200, headers);
 
616
            if (sendContent == true)
 
617
               sendFile(client, data);
 
618
         }
 
619
         else if (DirectoryExists(filename) == true)
 
620
         {
 
621
            if (filename[filename.length()-1] == '/')
 
622
               sendDirectoryListing(client, filename, *m, sendContent);
 
623
            else
 
624
               sendRedirect(client, 301, filename.append("/"), *m, sendContent);
 
625
         }
 
626
         else
 
627
            sendError(client, 404, *m, sendContent);
 
628
      }
 
629
      _error->DumpErrors(std::cerr);
 
630
      messages.clear();
 
631
      if (closeConnection == true)
 
632
         break;
 
633
   }
 
634
   close(client);
 
635
   std::clog << "CLOSE client " << client << std::endl;
 
636
   return NULL;
 
637
}
 
638
                                                                        /*}}}*/
 
639
 
430
640
int main(int const argc, const char * argv[])
431
641
{
432
642
   CommandLine::Args Args[] = {
447
657
   // create socket, bind and listen to it {{{
448
658
   // ignore SIGPIPE, this can happen on write() if the socket closes connection
449
659
   signal(SIGPIPE, SIG_IGN);
 
660
   // we don't care for our slaves, so ignore their death
 
661
   signal(SIGCHLD, SIG_IGN);
 
662
 
450
663
   int sock = socket(AF_INET6, SOCK_STREAM, 0);
451
664
   if(sock < 0)
452
665
   {
514
727
 
515
728
   std::clog << "Serving ANY file on port: " << port << std::endl;
516
729
 
517
 
   listen(sock, 1);
 
730
   int const slaves = _config->FindB("aptwebserver::slaves", SOMAXCONN);
 
731
   listen(sock, slaves);
518
732
   /*}}}*/
519
733
 
520
734
   _config->CndSet("aptwebserver::response-header::Server", "APT webserver");
521
735
   _config->CndSet("aptwebserver::response-header::Accept-Ranges", "bytes");
522
 
 
523
 
   std::vector<std::string> messages;
524
 
   int client;
525
 
   while ((client = accept(sock, NULL, NULL)) != -1)
 
736
   _config->CndSet("aptwebserver::directoryindex", "index.html");
 
737
 
 
738
   std::list<int> accepted_clients;
 
739
 
 
740
   while (true)
526
741
   {
527
 
      std::clog << "ACCEPT client " << client
528
 
                << " on socket " << sock << std::endl;
529
 
 
530
 
      while (ReadMessages(client, messages))
531
 
      {
532
 
         bool closeConnection = false;
533
 
         for (std::vector<std::string>::const_iterator m = messages.begin();
534
 
              m != messages.end() && closeConnection == false; ++m) {
535
 
            std::clog << ">>> REQUEST >>>>" << std::endl << *m
536
 
                      << std::endl << "<<<<<<<<<<<<<<<<" << std::endl;
537
 
            std::list<std::string> headers;
538
 
            std::string filename;
539
 
            bool sendContent = true;
540
 
            if (parseFirstLine(client, *m, filename, sendContent, closeConnection) == false)
541
 
               continue;
542
 
 
543
 
            // special webserver command request
544
 
            if (filename.length() > 1 && filename[0] == '_')
545
 
            {
546
 
               std::vector<std::string> parts = VectorizeString(filename, '/');
547
 
               if (parts[0] == "_config")
548
 
               {
549
 
                  handleOnTheFlyReconfiguration(client, *m, parts);
550
 
                  continue;
551
 
               }
552
 
            }
553
 
 
554
 
            // string replacements in the requested filename
555
 
            ::Configuration::Item const *Replaces = _config->Tree("aptwebserver::redirect::replace");
556
 
            if (Replaces != NULL)
557
 
            {
558
 
               std::string redirect = "/" + filename;
559
 
               for (::Configuration::Item *I = Replaces->Child; I != NULL; I = I->Next)
560
 
                  redirect = SubstVar(redirect, I->Tag, I->Value);
561
 
               redirect.erase(0,1);
562
 
               if (redirect != filename)
563
 
               {
564
 
                  sendRedirect(client, 301, redirect, *m, sendContent);
565
 
                  continue;
566
 
               }
567
 
            }
568
 
 
569
 
            ::Configuration::Item const *Overwrite = _config->Tree("aptwebserver::overwrite");
570
 
            if (Overwrite != NULL)
571
 
            {
572
 
               for (::Configuration::Item *I = Overwrite->Child; I != NULL; I = I->Next)
573
 
               {
574
 
                  regex_t *pattern = new regex_t;
575
 
                  int const res = regcomp(pattern, I->Tag.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB);
576
 
                  if (res != 0)
577
 
                  {
578
 
                     char error[300];
579
 
                     regerror(res, pattern, error, sizeof(error));
580
 
                     sendError(client, 500, *m, sendContent, error);
581
 
                     continue;
582
 
                  }
583
 
                  if (regexec(pattern, filename.c_str(), 0, 0, 0) == 0)
584
 
                  {
585
 
                      filename = _config->Find("aptwebserver::overwrite::" + I->Tag + "::filename", filename);
586
 
                      if (filename[0] == '/')
587
 
                         filename.erase(0,1);
588
 
                      regfree(pattern);
589
 
                      break;
590
 
                  }
591
 
                  regfree(pattern);
592
 
               }
593
 
            }
594
 
 
595
 
            // deal with the request
596
 
            if (RealFileExists(filename) == true)
597
 
            {
598
 
               FileFd data(filename, FileFd::ReadOnly);
599
 
               std::string condition = LookupTag(*m, "If-Modified-Since", "");
600
 
               if (condition.empty() == false)
601
 
               {
602
 
                  time_t cache;
603
 
                  if (RFC1123StrToTime(condition.c_str(), cache) == true &&
604
 
                        cache >= data.ModificationTime())
605
 
                  {
606
 
                     sendHead(client, 304, headers);
607
 
                     continue;
608
 
                  }
609
 
               }
610
 
 
611
 
               if (_config->FindB("aptwebserver::support::range", true) == true)
612
 
                  condition = LookupTag(*m, "Range", "");
613
 
               else
614
 
                  condition.clear();
615
 
               if (condition.empty() == false && strncmp(condition.c_str(), "bytes=", 6) == 0)
616
 
               {
617
 
                  time_t cache;
618
 
                  std::string ifrange;
619
 
                  if (_config->FindB("aptwebserver::support::if-range", true) == true)
620
 
                     ifrange = LookupTag(*m, "If-Range", "");
621
 
                  bool validrange = (ifrange.empty() == true ||
622
 
                        (RFC1123StrToTime(ifrange.c_str(), cache) == true &&
623
 
                         cache <= data.ModificationTime()));
624
 
 
625
 
                  // FIXME: support multiple byte-ranges (APT clients do not do this)
626
 
                  if (condition.find(',') == std::string::npos)
627
 
                  {
628
 
                     size_t start = 6;
629
 
                     unsigned long long filestart = strtoull(condition.c_str() + start, NULL, 10);
630
 
                     // FIXME: no support for last-byte-pos being not the end of the file (APT clients do not do this)
631
 
                     size_t dash = condition.find('-') + 1;
632
 
                     unsigned long long fileend = strtoull(condition.c_str() + dash, NULL, 10);
633
 
                     unsigned long long filesize = data.FileSize();
634
 
                     if ((fileend == 0 || (fileend == filesize && fileend >= filestart)) &&
635
 
                           validrange == true)
636
 
                     {
637
 
                        if (filesize > filestart)
638
 
                        {
639
 
                           data.Skip(filestart);
640
 
                           std::ostringstream contentlength;
641
 
                           contentlength << "Content-Length: " << (filesize - filestart);
642
 
                           headers.push_back(contentlength.str());
643
 
                           std::ostringstream contentrange;
644
 
                           contentrange << "Content-Range: bytes " << filestart << "-"
645
 
                              << filesize - 1 << "/" << filesize;
646
 
                           headers.push_back(contentrange.str());
647
 
                           sendHead(client, 206, headers);
648
 
                           if (sendContent == true)
649
 
                              sendFile(client, data);
650
 
                           continue;
651
 
                        }
652
 
                        else
653
 
                        {
654
 
                           headers.push_back("Content-Length: 0");
655
 
                           std::ostringstream contentrange;
656
 
                           contentrange << "Content-Range: bytes */" << filesize;
657
 
                           headers.push_back(contentrange.str());
658
 
                           sendHead(client, 416, headers);
659
 
                           continue;
660
 
                        }
661
 
                     }
662
 
                  }
663
 
               }
664
 
 
665
 
               addFileHeaders(headers, data);
666
 
               sendHead(client, 200, headers);
667
 
               if (sendContent == true)
668
 
                  sendFile(client, data);
669
 
            }
670
 
            else if (DirectoryExists(filename) == true)
671
 
            {
672
 
               if (filename == "." || filename[filename.length()-1] == '/')
673
 
                  sendDirectoryListing(client, filename, *m, sendContent);
674
 
               else
675
 
                  sendRedirect(client, 301, filename.append("/"), *m, sendContent);
676
 
            }
677
 
            else
678
 
               sendError(client, 404, *m, sendContent);
679
 
         }
680
 
         _error->DumpErrors(std::cerr);
681
 
         messages.clear();
682
 
         if (closeConnection == true)
683
 
            break;
684
 
      }
685
 
 
686
 
      std::clog << "CLOSE client " << client
687
 
                << " on socket " << sock << std::endl;
688
 
      close(client);
 
742
      int client = accept(sock, NULL, NULL);
 
743
      if (client == -1)
 
744
      {
 
745
         if (errno == EINTR)
 
746
            continue;
 
747
         _error->Errno("accept", "Couldn't accept client on socket %d", sock);
 
748
         _error->DumpErrors(std::cerr);
 
749
         return 6;
 
750
      }
 
751
 
 
752
      pthread_attr_t attr;
 
753
      if (pthread_attr_init(&attr) != 0 || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
 
754
      {
 
755
         _error->Errno("pthread_attr", "Couldn't set detach attribute for a fresh thread to handle client %d on socket %d", client, sock);
 
756
         _error->DumpErrors(std::cerr);
 
757
         close(client);
 
758
         continue;
 
759
      }
 
760
 
 
761
      pthread_t tid;
 
762
      // thats rather dirty, but we need to store the client socket somewhere safe
 
763
      accepted_clients.push_front(client);
 
764
      if (pthread_create(&tid, &attr, &handleClient, &(*accepted_clients.begin())) != 0)
 
765
      {
 
766
         _error->Errno("pthread_create", "Couldn't create a fresh thread to handle client %d on socket %d", client, sock);
 
767
         _error->DumpErrors(std::cerr);
 
768
         close(client);
 
769
         continue;
 
770
      }
689
771
   }
690
772
   pidfile.Close();
691
773