Blame src/Http.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2016 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include "trio.h"
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#include <sys/socket.h>
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <stdarg.h>
Packit 8f70b4
#include <time.h>
Packit 4ca704
#include <limits.h>
Packit 8f70b4
#include <fnmatch.h>
Packit 8f70b4
#include <locale.h>
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
#include "Http.h"
Packit 8f70b4
#include "ResMgr.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
#include "url.h"
Packit 8f70b4
#include "HttpAuth.h"
Packit 8f70b4
#include "HttpDir.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "buffer_ssl.h"
Packit 8f70b4
#include "buffer_zlib.h"
Packit 8f70b4
Packit 8f70b4
#include "ascii_ctype.h"
Packit 8f70b4
Packit 8f70b4
#if !HAVE_DECL_STRPTIME
Packit 8f70b4
CDECL char *strptime(const char *buf, const char *format, struct tm *tm);
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#define super NetAccess
Packit 8f70b4
Packit 8f70b4
#define max_buf 0x10000
Packit 8f70b4
Packit 8f70b4
#define HTTP_DEFAULT_PORT	 "80"
Packit 8f70b4
#define HTTP_DEFAULT_PROXY_PORT	 "3128"
Packit 8f70b4
#define HTTPS_DEFAULT_PORT	 "443"
Packit 8f70b4
Packit 8f70b4
enum {
Packit 8f70b4
   H_Continue=100,
Packit 8f70b4
   H_Switching_Protocols=101,
Packit 8f70b4
   H_Processing=102,
Packit 8f70b4
   H_Ok=200,
Packit 8f70b4
   H_Created=201,
Packit 8f70b4
   H_Accepted=202,
Packit 8f70b4
   H_Non_Authoritative_Information=203,
Packit 8f70b4
   H_No_Content=204,
Packit 8f70b4
   H_Reset_Content=205,
Packit 8f70b4
   H_Partial_Content=206,
Packit 8f70b4
   H_Multi_Status=207,
Packit 8f70b4
   H_Moved_Permanently=301,
Packit 8f70b4
   H_Found=302,
Packit 8f70b4
   H_See_Other=303,
Packit 8f70b4
   H_Not_Modified=304,
Packit 8f70b4
   H_Use_Proxy=305,
Packit 8f70b4
   H_Temporary_Redirect=307,
Packit 8f70b4
   H_Bad_Request=400,
Packit 8f70b4
   H_Unauthorized=401,
Packit 8f70b4
   H_Payment_Required=402,
Packit 8f70b4
   H_Forbidden=403,
Packit 8f70b4
   H_Not_Found=404,
Packit 8f70b4
   H_Method_Not_Allowed=405,
Packit 8f70b4
   H_Not_Acceptable=406,
Packit 8f70b4
   H_Proxy_Authentication_Required=407,
Packit 8f70b4
   H_Request_Timeout=408,
Packit 8f70b4
   H_Conflict=409,
Packit 8f70b4
   H_Gone=410,
Packit 8f70b4
   H_Length_Required=411,
Packit 8f70b4
   H_Precondition_Failed=412,
Packit 8f70b4
   H_Request_Entity_Too_Large=413,
Packit 8f70b4
   H_Request_URI_Too_Long=414,
Packit 8f70b4
   H_Unsupported_Media_Type=415,
Packit 8f70b4
   H_Requested_Range_Not_Satisfiable=416,
Packit 8f70b4
   H_Expectation_Failed=417,
Packit 8f70b4
   H_Unprocessable_Entity=422,
Packit 8f70b4
   H_Locked=423,
Packit 8f70b4
   H_Failed_Dependency=424,
Packit 8f70b4
   H_Too_Many_Requests=429,
Packit 8f70b4
   H_Internal_Server_Error=500,
Packit 8f70b4
   H_Not_Implemented=501,
Packit 8f70b4
   H_Bad_Gateway=502,
Packit 8f70b4
   H_Service_Unavailable=503,
Packit 8f70b4
   H_Gateway_Timeout=504,
Packit 8f70b4
   H_HTTP_Version_Not_Supported=505,
Packit 8f70b4
   H_Insufficient_Storage=507,
Packit 8f70b4
};
Packit 8f70b4
Packit 8f70b4
/* Some status code validation macros: */
Packit 8f70b4
#define H_2XX(x)        (((x) >= 200) && ((x) <= 299))
Packit 8f70b4
#define H_5XX(x)        (((x) >= 500) && ((x) <= 599))
Packit 8f70b4
#define H_PARTIAL(x)    ((x) == H_Partial_Content)
Packit 8f70b4
#define H_REDIRECTED(x) (((x) == H_Moved_Permanently) || ((x) == H_Found) || ((x) == H_See_Other) || ((x) == H_Temporary_Redirect))
Packit 8f70b4
#define H_EMPTY(x)	(((x) == H_No_Content) || ((x) == H_Reset_Content))
Packit 8f70b4
#define H_CONTINUE(x)	((x) == H_Continue || (x) == H_Processing)
Packit 8f70b4
#define H_REQUESTED_RANGE_NOT_SATISFIABLE(x) ((x) == H_Requested_Range_Not_Satisfiable)
Packit 8f70b4
#define H_TRANSIENT(x)	((x)==H_Request_Timeout || (x)==H_Bad_Gateway || (x)==H_Service_Unavailable || (x)==H_Gateway_Timeout)
Packit 8f70b4
#define H_UNSUPPORTED(x) ((x)==H_Bad_Request || (x)==H_Not_Implemented)
Packit 8f70b4
#define H_AUTH_REQ(x)	((x)==H_Unauthorized || (x)==H_Proxy_Authentication_Required)
Packit 8f70b4
Packit 8f70b4
#ifndef EINPROGRESS
Packit 8f70b4
#define EINPROGRESS -1
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
enum { CHUNK_SIZE_UNKNOWN=-1 };
Packit 8f70b4
Packit 8f70b4
Http::Connection::Connection(int s,const char *c)
Packit 8f70b4
   : closure(c), sock(s)
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
Http::Connection::~Connection()
Packit 8f70b4
{
Packit 8f70b4
   close(sock);
Packit 8f70b4
   /* make sure we free buffers before ssl */
Packit 8f70b4
   recv_buf=0;
Packit 8f70b4
   send_buf=0;
Packit 8f70b4
}
Packit 8f70b4
void Http::Connection::MakeBuffers()
Packit 8f70b4
{
Packit 8f70b4
   send_buf=new IOBufferFDStream(
Packit 8f70b4
      new FDStream(sock,"<output-socket>"),IOBuffer::PUT);
Packit 8f70b4
   recv_buf=new IOBufferFDStream(
Packit 8f70b4
      new FDStream(sock,"<input-socket>"),IOBuffer::GET);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::Init()
Packit 8f70b4
{
Packit 8f70b4
   state=DISCONNECTED;
Packit 8f70b4
   tunnel_state=NO_TUNNEL;
Packit 8f70b4
   body_size=-1;
Packit 8f70b4
   bytes_received=0;
Packit 8f70b4
   status_code=0;
Packit 8f70b4
   status_consumed=0;
Packit 8f70b4
   proto_version=0x10;
Packit 8f70b4
   sent_eot=false;
Packit 8f70b4
   last_method=0;
Packit 8f70b4
Packit 8f70b4
   default_cwd="/";
Packit 8f70b4
Packit 8f70b4
   keep_alive=false;
Packit 8f70b4
   keep_alive_max=-1;
Packit 8f70b4
Packit 8f70b4
   array_send=0;
Packit 8f70b4
Packit 8f70b4
   chunked=false;
Packit 8f70b4
   chunked_trailer=false;
Packit 8f70b4
   chunk_size=CHUNK_SIZE_UNKNOWN;
Packit 8f70b4
   chunk_pos=0;
Packit 8f70b4
Packit 8f70b4
   request_pos=0;
Packit 8f70b4
Packit 8f70b4
   no_ranges=false;
Packit 8f70b4
   seen_ranges_bytes=false;
Packit 8f70b4
   entity_date_set=false;
Packit 8f70b4
   sending_proppatch=false;
Packit 8f70b4
Packit 8f70b4
   no_cache_this=false;
Packit 8f70b4
   no_cache=false;
Packit 8f70b4
Packit 8f70b4
   auth_sent[0]=auth_sent[1]=0;
Packit 8f70b4
   auth_scheme[0]=auth_scheme[1]=HttpAuth::NONE;
Packit 8f70b4
Packit 8f70b4
   use_propfind_now=true;
Packit 8f70b4
Packit 8f70b4
   retry_after=0;
Packit 8f70b4
Packit 8f70b4
   hftp=false;
Packit 8f70b4
   https=false;
Packit 8f70b4
   use_head=true;
Packit 8f70b4
Packit 8f70b4
   user_agent=0;
Packit 8f70b4
   special=HTTP_NONE;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Http::Http() : super()
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
   Reconfig();
Packit 8f70b4
}
Packit 8f70b4
Http::Http(const Http *f) : super(f)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
   Reconfig();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Http::~Http()
Packit 8f70b4
{
Packit 8f70b4
   Close();
Packit 8f70b4
   Disconnect();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::MoveConnectionHere(Http *o)
Packit 8f70b4
{
Packit 8f70b4
   conn=o->conn.borrow();
Packit 8f70b4
   conn->ResumeInternal();
Packit 8f70b4
   rate_limit=o->rate_limit.borrow();
Packit 8f70b4
   last_method=o->last_method; o->last_method=0;
Packit 8f70b4
   last_uri.move_here(o->last_uri);
Packit 8f70b4
   last_url.move_here(o->last_url);
Packit 8f70b4
   timeout_timer.Reset(o->timeout_timer);
Packit 8f70b4
   state=CONNECTED;
Packit 8f70b4
   tunnel_state=o->tunnel_state;
Packit 8f70b4
   o->Disconnect();
Packit 8f70b4
   ResumeInternal();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::DisconnectLL()
Packit 8f70b4
{
Packit 8f70b4
   Enter(this);
Packit 8f70b4
   rate_limit=0;
Packit 8f70b4
   if(conn)
Packit 8f70b4
   {
Packit 8f70b4
      LogNote(7,_("Closing HTTP connection"));
Packit 8f70b4
      conn=0;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!Error() && !H_AUTH_REQ(status_code))
Packit 8f70b4
      auth_sent[0]=auth_sent[1]=0;
Packit 8f70b4
Packit 8f70b4
   if(state!=DONE && (real_pos>0 || special==HTTP_POST)
Packit 8f70b4
   && !Error() && !H_AUTH_REQ(status_code)) {
Packit 8f70b4
      if(last_method && !strcmp(last_method,"POST"))
Packit 8f70b4
	 SetError(FATAL,_("POST method failed"));
Packit 8f70b4
      else if(ModeIs(STORE))
Packit 8f70b4
	 SetError(STORE_FAILED,0);
Packit 8f70b4
   }
Packit 8f70b4
   if(ModeIs(STORE) && H_AUTH_REQ(status_code))
Packit 8f70b4
      pos=real_pos=request_pos; // resend all the data again
Packit 8f70b4
Packit 8f70b4
   last_method=0;
Packit 8f70b4
   last_uri.unset();
Packit 8f70b4
   last_url.unset();
Packit 8f70b4
   ResetRequestData();
Packit 8f70b4
   state=DISCONNECTED;
Packit 8f70b4
   Leave(this);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::ResetRequestData()
Packit 8f70b4
{
Packit 8f70b4
   body_size=-1;
Packit 8f70b4
   bytes_received=0;
Packit 8f70b4
   real_pos=no_ranges?0:-1;
Packit 8f70b4
   status.set(0);
Packit 8f70b4
   status_consumed=0;
Packit 8f70b4
   line.set(0);
Packit 8f70b4
   sent_eot=false;
Packit 8f70b4
   keep_alive=false;
Packit 8f70b4
   keep_alive_max=-1;
Packit 8f70b4
   array_send=fileset_for_info?fileset_for_info->curr_index():0;
Packit 8f70b4
   chunked=false;
Packit 8f70b4
   chunked_trailer=false;
Packit 8f70b4
   chunk_size=CHUNK_SIZE_UNKNOWN;
Packit 8f70b4
   chunk_pos=0;
Packit 8f70b4
   request_pos=0;
Packit 8f70b4
   propfind=0;
Packit 8f70b4
   inflate=0;
Packit 8f70b4
   seen_ranges_bytes=false;
Packit 8f70b4
   entity_date_set=false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::Close()
Packit 8f70b4
{
Packit 8f70b4
   if(mode==CLOSED)
Packit 8f70b4
      return;
Packit 8f70b4
   if(conn && conn->recv_buf)
Packit 8f70b4
      conn->recv_buf->Roll();	// try to read any remaining data
Packit 8f70b4
   if(conn && keep_alive && (keep_alive_max>0 || keep_alive_max==-1)
Packit 8f70b4
   && !ModeIs(STORE) && !conn->recv_buf->Eof() && (state==RECEIVING_BODY || state==DONE))
Packit 8f70b4
   {
Packit 8f70b4
      conn->recv_buf->Resume();
Packit 8f70b4
      conn->recv_buf->Roll();
Packit 8f70b4
      if(xstrcmp(last_method,"HEAD"))
Packit 8f70b4
      {
Packit 8f70b4
	 // check if all data are in buffer
Packit 8f70b4
	 if(!chunked)	// chunked is a bit complex, so don't handle it
Packit 8f70b4
	 {
Packit 8f70b4
	    bytes_received+=conn->recv_buf->Size();
Packit 8f70b4
	    conn->recv_buf->Skip(conn->recv_buf->Size());
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!(body_size>=0 && bytes_received==body_size))
Packit 8f70b4
	    goto disconnect;
Packit 8f70b4
      }
Packit 8f70b4
      // can reuse the connection.
Packit 8f70b4
      state=CONNECTED;
Packit 8f70b4
      ResetRequestData();
Packit 8f70b4
      rate_limit=0;
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
   disconnect:
Packit 8f70b4
      Disconnect();
Packit 8f70b4
      DontSleep();
Packit 8f70b4
   }
Packit 8f70b4
   array_send=0;
Packit 8f70b4
   no_cache_this=false;
Packit 8f70b4
   auth_sent[0]=auth_sent[1]=0;
Packit 8f70b4
   auth_scheme[0]=auth_scheme[1]=HttpAuth::NONE;
Packit 8f70b4
   no_ranges=!QueryBool("use-range",hostname);
Packit 8f70b4
   use_propfind_now=QueryBool("use-propfind",hostname);
Packit 8f70b4
   special=HTTP_NONE;
Packit 8f70b4
   special_data.set(0);
Packit 8f70b4
   sending_proppatch=false;
Packit 8f70b4
   super::Close();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::Send(const xstring& str)
Packit 8f70b4
{
Packit 8f70b4
   if(str.length()==0)
Packit 8f70b4
      return;
Packit 8f70b4
   LogSend(5,str);
Packit 8f70b4
   conn->send_buf->Put(str);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::Send(const char *format,...)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va,format);
Packit 8f70b4
   xstring& str=xstring::vformat(format,va);
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   Send(str);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::Send(const HttpHeader *hdr)
Packit 8f70b4
{
Packit 8f70b4
   Send("%s: %s\r\n",hdr->GetName(),hdr->GetValue());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::AppendHostEncoded(xstring& buf,const char *host)
Packit 8f70b4
{
Packit 8f70b4
   if(is_ipv6_address(host))
Packit 8f70b4
      buf.append('[').append(host).append(']');
Packit 8f70b4
   else
Packit 8f70b4
      buf.append_url_encoded(host,URL_HOST_UNSAFE);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SendMethod(const char *method,const char *efile)
Packit 8f70b4
{
Packit 8f70b4
   xstring& stripped_hostname=xstring::get_tmp(hostname);
Packit 8f70b4
   stripped_hostname.truncate_at('%');
Packit 8f70b4
   xstring ehost;
Packit 8f70b4
   AppendHostEncoded(ehost,xidna_to_ascii(stripped_hostname));
Packit 8f70b4
   if(portname) {
Packit 8f70b4
      ehost.append(':');
Packit 8f70b4
      ehost.append(url::encode(portname,URL_PORT_UNSAFE));
Packit 8f70b4
   }
Packit 8f70b4
   if(!use_head && !strcmp(method,"HEAD"))
Packit 8f70b4
      method="GET";
Packit 8f70b4
   last_method=method;
Packit 8f70b4
   if(file_url)
Packit 8f70b4
   {
Packit 8f70b4
      efile=file_url;
Packit 8f70b4
      if(!proxy)
Packit 8f70b4
	 efile+=url::path_index(efile);
Packit 8f70b4
      else if(!strncmp(efile,"hftp://",7))
Packit 8f70b4
	 efile++;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(hftp && mode!=LONG_LIST && mode!=CHANGE_DIR && mode!=MAKE_DIR
Packit 8f70b4
   && mode!=REMOVE && mode!=REMOVE_DIR
Packit 8f70b4
   && (strlen(efile)<7 || strncmp(efile+strlen(efile)-7,";type=",6))
Packit 8f70b4
   && QueryBool("use-type",hostname))
Packit 8f70b4
   {
Packit 8f70b4
      efile=xstring::format("%s;type=%c",efile,ascii?'a':'i');
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   /*
Packit 8f70b4
      Handle the case when the user has not given us
Packit 8f70b4
      get http://foobar.org (note the absense of the trailing /
Packit 8f70b4
Packit 8f70b4
      It fixes segfault with a certain webserver which I've
Packit 8f70b4
      seen ... (Geoffrey Lee <glee@gnupilgrims.org>).
Packit 8f70b4
   */
Packit 8f70b4
   if(*efile=='\0')
Packit 8f70b4
      efile="/";
Packit 8f70b4
Packit 8f70b4
   last_uri.set(efile+(proxy?url::path_index(efile):0));
Packit 8f70b4
   if(last_uri.length()==0)
Packit 8f70b4
      last_uri.set("/");
Packit 8f70b4
   if(proxy)
Packit 8f70b4
      last_url.set(efile);
Packit 8f70b4
Packit 8f70b4
   Send("%s %s HTTP/1.1\r\n",method,efile);
Packit 8f70b4
   Send("Host: %s\r\n",ehost.get());
Packit 8f70b4
   if(user_agent && user_agent[0])
Packit 8f70b4
      Send("User-Agent: %s\r\n",user_agent);
Packit 8f70b4
   if(!hftp)
Packit 8f70b4
   {
Packit 8f70b4
      const char *content_type=0;
Packit 8f70b4
      if(!strcmp(method,"PUT"))
Packit 8f70b4
	 content_type=Query("put-content-type",hostname);
Packit 8f70b4
      else if(!strcmp(method,"POST"))
Packit 8f70b4
	 content_type=Query("post-content-type",hostname);
Packit 8f70b4
      if(content_type && content_type[0])
Packit 8f70b4
	 Send("Content-Type: %s\r\n",content_type);
Packit 8f70b4
Packit 8f70b4
      const char *accept=Query("accept",hostname);
Packit 8f70b4
      if(accept && accept[0])
Packit 8f70b4
	 Send("Accept: %s\r\n",accept);
Packit 8f70b4
      accept=Query("accept-language",hostname);
Packit 8f70b4
      if(accept && accept[0])
Packit 8f70b4
	 Send("Accept-Language: %s\r\n",accept);
Packit 8f70b4
      accept=Query("accept-charset",hostname);
Packit 8f70b4
      if(accept && accept[0])
Packit 8f70b4
	 Send("Accept-Charset: %s\r\n",accept);
Packit 8f70b4
      accept=Query("accept-encoding",hostname);
Packit 8f70b4
      if(accept && accept[0])
Packit 8f70b4
	 Send("Accept-Encoding: %s\r\n",accept);
Packit 8f70b4
Packit 8f70b4
      const char *referer=Query("referer",hostname);
Packit 8f70b4
      const char *slash="";
Packit 8f70b4
      if(!xstrcmp(referer,"."))
Packit 8f70b4
      {
Packit 8f70b4
	 referer=GetConnectURL(NO_USER+NO_PASSWORD);
Packit 8f70b4
	 if(last_char(referer)!='/' && !cwd.is_file)
Packit 8f70b4
	    slash="/";
Packit 8f70b4
      }
Packit 8f70b4
      if(referer && referer[0])
Packit 8f70b4
	 Send("Referer: %s%s\r\n",referer,slash);
Packit 8f70b4
Packit 8f70b4
      xstring cookie;
Packit 8f70b4
      MakeCookie(cookie,hostname,efile+(proxy?url::path_index(efile):0));
Packit 8f70b4
      if(cookie.length()>0)
Packit 8f70b4
	 Send("Cookie: %s\r\n",cookie.get());
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SendBasicAuth(const char *tag,const char *auth)
Packit 8f70b4
{
Packit 8f70b4
   if(!auth || !*auth)
Packit 8f70b4
      return;
Packit 8f70b4
   int auth_len=strlen(auth);
Packit 8f70b4
   char *buf64=string_alloca(base64_length(auth_len)+1);
Packit 8f70b4
   base64_encode(auth,buf64,auth_len);
Packit 8f70b4
   Send("%s: Basic %s\r\n",tag,buf64);
Packit 8f70b4
}
Packit 8f70b4
void Http::SendBasicAuth(const char *tag,const char *user,const char *pass)
Packit 8f70b4
{
Packit 8f70b4
   /* Basic scheme */
Packit 8f70b4
   SendBasicAuth(tag,xstring::cat(user,":",pass,NULL));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SendAuth(HttpAuth::target_t target,const char *user,const char *uri)
Packit 8f70b4
{
Packit 8f70b4
   auth_scheme[target]=HttpAuth::NONE;
Packit 8f70b4
   if(!user)
Packit 8f70b4
      return;
Packit 8f70b4
   HttpAuth *auth=HttpAuth::Get(target,GetFileURL(file,NO_USER),user);
Packit 8f70b4
   if(auth && auth->Update(last_method,uri)) {
Packit 8f70b4
      auth_sent[target]++;
Packit 8f70b4
      Send(auth->GetHeader());
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SendProxyAuth()
Packit 8f70b4
{
Packit 8f70b4
   SendAuth(HttpAuth::PROXY,proxy_user,last_url);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SendAuth()
Packit 8f70b4
{
Packit 8f70b4
   if(hftp && !auth_scheme[HttpAuth::WWW] && user && pass && QueryBool("use-authorization",proxy)) {
Packit 8f70b4
      SendBasicAuth("Authorization",user,pass);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   SendAuth(HttpAuth::WWW,user?user:auth_user,last_uri);
Packit 8f70b4
}
Packit 8f70b4
void Http::SendCacheControl()
Packit 8f70b4
{
Packit 8f70b4
   const char *cc_setting=Query("cache-control",hostname);
Packit 8f70b4
   const char *cc_no_cache=(no_cache || no_cache_this)?"no-cache":0;
Packit 8f70b4
   if(!*cc_setting)
Packit 8f70b4
      cc_setting=0;
Packit 8f70b4
   if(!cc_setting && !cc_no_cache)
Packit 8f70b4
      return;
Packit 8f70b4
   int cc_no_cache_len=xstrlen(cc_no_cache);
Packit 8f70b4
   if(cc_no_cache && cc_setting)
Packit 8f70b4
   {
Packit 8f70b4
      const char *pos=strstr(cc_setting,cc_no_cache);
Packit 8f70b4
      if(pos && (pos==cc_setting || pos[-1]==' ')
Packit 8f70b4
	     && (pos[cc_no_cache_len]==0 || pos[cc_no_cache_len]==' '))
Packit 8f70b4
	 cc_no_cache=0, cc_no_cache_len=0;
Packit 8f70b4
   }
Packit 8f70b4
   xstring& cc=xstring::join(",",2,cc_no_cache,cc_setting);
Packit 8f70b4
   if(*cc)
Packit 8f70b4
      Send("Cache-Control: %s\r\n",cc.get());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Http::ModeSupported()
Packit 8f70b4
{
Packit 8f70b4
   switch((open_mode)mode)
Packit 8f70b4
   {
Packit 8f70b4
   case CLOSED:
Packit 8f70b4
   case QUOTE_CMD:
Packit 8f70b4
   case LIST:
Packit 8f70b4
   case CHANGE_MODE:
Packit 8f70b4
   case LINK:
Packit 8f70b4
   case SYMLINK:
Packit 8f70b4
      return false;
Packit 8f70b4
   case CONNECT_VERIFY:
Packit 8f70b4
   case RETRIEVE:
Packit 8f70b4
   case STORE:
Packit 8f70b4
   case MAKE_DIR:
Packit 8f70b4
   case CHANGE_DIR:
Packit 8f70b4
   case ARRAY_INFO:
Packit 8f70b4
   case REMOVE_DIR:
Packit 8f70b4
   case REMOVE:
Packit 8f70b4
   case LONG_LIST:
Packit 8f70b4
   case RENAME:
Packit 8f70b4
      return true;
Packit 8f70b4
   case MP_LIST:
Packit 8f70b4
#if USE_EXPAT
Packit 8f70b4
      return QueryBool("use-propfind",hostname);
Packit 8f70b4
#else
Packit 8f70b4
      // without XML parser it is meaningless to retrieve XML file info.
Packit 8f70b4
      return false;
Packit 8f70b4
#endif
Packit 8f70b4
   }
Packit 8f70b4
   abort(); // should not happen
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::DirFile(xstring& path,const xstring& ecwd,const xstring& efile) const
Packit 8f70b4
{
Packit 8f70b4
   const int base=path.length();
Packit 8f70b4
Packit 8f70b4
   if(efile[0]=='/') {
Packit 8f70b4
      path.append(efile);
Packit 8f70b4
   } else if(efile[0]=='~' || ecwd.length()==0 || (ecwd.eq("~") && !hftp)) {
Packit 8f70b4
      path.append('/');
Packit 8f70b4
      path.append(efile);
Packit 8f70b4
   } else {
Packit 8f70b4
      size_t min_len=path.length()+1;
Packit 8f70b4
      if(ecwd[0]!='/')
Packit 8f70b4
	 path.append('/');
Packit 8f70b4
      path.append(ecwd);
Packit 8f70b4
      if(ecwd.last_char()!='/' && efile.length()>0)
Packit 8f70b4
	 path.append('/');
Packit 8f70b4
Packit 8f70b4
      // reduce . and .. at beginning of efile:
Packit 8f70b4
      //  * get the minimum path length (so that we don't remove ~user)
Packit 8f70b4
      //  * skip .; handle .. using basename_ptr to chomp the path.
Packit 8f70b4
      if(path[min_len]=='~') {
Packit 8f70b4
	 while(path[min_len] && path[min_len]!='/')
Packit 8f70b4
	    ++min_len;
Packit 8f70b4
	 if(path[min_len]=='/')
Packit 8f70b4
	    ++min_len;
Packit 8f70b4
      }
Packit 8f70b4
      const char *e=efile;
Packit 8f70b4
      while(e[0]=='.') {
Packit 8f70b4
	 if(e[1]=='/' || e[1]==0)
Packit 8f70b4
	    ++e;
Packit 8f70b4
	 else if(e[1]=='.' && (e[2]=='/' || e[2]==0)) {
Packit 8f70b4
	    if(path.length()<=min_len)
Packit 8f70b4
	       break;
Packit 8f70b4
	    const char *bn=basename_ptr(path+min_len);
Packit 8f70b4
	    path.truncate(bn-path);
Packit 8f70b4
	    e+=2;
Packit 8f70b4
	 } else
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(*e=='/')
Packit 8f70b4
	    ++e;
Packit 8f70b4
      }
Packit 8f70b4
      path.append(e);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   // remove "/~" or "/~/"
Packit 8f70b4
   if(path[base+1]=='~' && path[base+2]==0)
Packit 8f70b4
      path.truncate(base+1);
Packit 8f70b4
   else if(path[base+1]=='~' && path[base+2]=='/')
Packit 8f70b4
      path.set_substr(base,2,"");
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SendPropfind(const xstring& efile,int depth)
Packit 8f70b4
{
Packit 8f70b4
   SendMethod("PROPFIND",efile);
Packit 8f70b4
   Send("Depth: %d\r\n",depth);
Packit 8f70b4
   if(allprop.length()>0)
Packit 8f70b4
   {
Packit 8f70b4
      Send("Content-Type: text/xml\r\n");
Packit 8f70b4
      Send("Content-Length: %d\r\n",int(allprop.length()));
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void Http::SendPropfindBody()
Packit 8f70b4
{
Packit 8f70b4
   Send(allprop);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const xstring& Http::FormatLastModified(time_t lm)
Packit 8f70b4
{
Packit 8f70b4
   static const char weekday_names[][4]={
Packit 8f70b4
      "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
Packit 8f70b4
   };
Packit 8f70b4
   const struct tm *t=gmtime(&lm);
Packit 8f70b4
   return xstring::format("%s, %2d %s %04d %02d:%02d:%02d GMT",
Packit 8f70b4
      weekday_names[t->tm_wday],t->tm_mday,month_names[t->tm_mon],
Packit 8f70b4
      t->tm_year+1900,t->tm_hour,t->tm_min,t->tm_sec);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SendProppatch(const xstring& efile)
Packit 8f70b4
{
Packit 8f70b4
   SendMethod("PROPPATCH",efile);
Packit 8f70b4
   xstring prop(
Packit 8f70b4
      ""
Packit 8f70b4
      "<propertyupdate xmlns=\"DAV:\">"
Packit 8f70b4
	 "<set>"
Packit 8f70b4
	    "<prop>"
Packit 8f70b4
	       "<getlastmodified>");
Packit 8f70b4
   prop.append(FormatLastModified(entity_date)).append(
Packit 8f70b4
	       "</getlastmodified>"
Packit 8f70b4
	    "</prop>"
Packit 8f70b4
	 "</set>"
Packit 8f70b4
      "</propertyupdate>");
Packit 8f70b4
   Send("Content-Type: text/xml\r\n");
Packit 8f70b4
   Send("Content-Length: %d\r\n",int(prop.length()));
Packit 8f70b4
   Send("\r\n");
Packit 8f70b4
   Send(prop);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SendRequest(const char *connection,const char *f)
Packit 8f70b4
{
Packit 8f70b4
   xstring efile;
Packit 8f70b4
   xstring ecwd;
Packit 8f70b4
   bool add_slash=true;
Packit 8f70b4
Packit 8f70b4
   if(mode==CHANGE_DIR && new_cwd && new_cwd->url)
Packit 8f70b4
   {
Packit 8f70b4
      const char *efile_c=new_cwd->url+url::path_index(new_cwd->url);
Packit 8f70b4
      if(!*efile_c)
Packit 8f70b4
	 efile_c="/";
Packit 8f70b4
      efile.set(efile_c);
Packit 8f70b4
      add_slash=false;
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
      efile.set(url::encode(f,URL_PATH_UNSAFE));
Packit 8f70b4
Packit 8f70b4
   if(cwd.url)
Packit 8f70b4
      ecwd.set(cwd.url+url::path_index(cwd.url));
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      ecwd.set(url::encode(cwd,URL_PATH_UNSAFE));
Packit 8f70b4
      if(hftp && ecwd[0]=='/' && ecwd[1]!='~')
Packit 8f70b4
      {
Packit 8f70b4
	 // root directory in ftp urls needs special encoding. (/%2Fpath)
Packit 8f70b4
	 ecwd.set_substr(1,0,"%2F");
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(cwd.is_file)
Packit 8f70b4
   {
Packit 8f70b4
      if(efile[0])
Packit 8f70b4
	 ecwd.truncate(basename_ptr(ecwd+(!strncmp(ecwd,"/~",2)))-ecwd);
Packit 8f70b4
      add_slash=false;
Packit 8f70b4
   }
Packit 8f70b4
   if(mode==CHANGE_DIR && new_cwd && !new_cwd->url)
Packit 8f70b4
      add_slash=!new_cwd->is_file;
Packit 8f70b4
Packit 8f70b4
   xstring pfile;
Packit 8f70b4
   if(proxy && !https)
Packit 8f70b4
   {
Packit 8f70b4
      const char *proto="http";
Packit 8f70b4
      if(hftp)
Packit 8f70b4
	 proto="ftp";
Packit 8f70b4
      pfile.vset(proto,"://",NULL);
Packit 8f70b4
      if(hftp && user && pass)
Packit 8f70b4
      {
Packit 8f70b4
	 pfile.append(url::encode(user,URL_USER_UNSAFE));
Packit 8f70b4
	 if(!QueryBool("use-authorization",proxy))
Packit 8f70b4
	 {
Packit 8f70b4
	    pfile.append(':');
Packit 8f70b4
	    pfile.append(url::encode(pass,URL_PASS_UNSAFE));
Packit 8f70b4
	 }
Packit 8f70b4
	 pfile.append('@');
Packit 8f70b4
      }
Packit 8f70b4
      AppendHostEncoded(pfile,hostname);
Packit 8f70b4
      if(portname)
Packit 8f70b4
      {
Packit 8f70b4
	 pfile.append(':');
Packit 8f70b4
	 pfile.append(url::encode(portname,URL_PORT_UNSAFE));
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      pfile.set("");
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   DirFile(pfile,ecwd,efile);
Packit 8f70b4
   efile.set(pfile);
Packit 8f70b4
Packit 8f70b4
   if(pos==0)
Packit 8f70b4
      real_pos=0;
Packit 8f70b4
   if(ModeIs(STORE))    // can't seek before writing
Packit 8f70b4
      real_pos=pos;
Packit 8f70b4
Packit 8f70b4
#ifdef DEBUG_MP_LIST
Packit 8f70b4
   if(mode==RETRIEVE && file[0]==0)
Packit 8f70b4
      mode=MP_LIST;
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
   switch((open_mode)mode)
Packit 8f70b4
   {
Packit 8f70b4
   case CLOSED:
Packit 8f70b4
   case CONNECT_VERIFY:
Packit 8f70b4
      abort(); // cannot happen
Packit 8f70b4
Packit 8f70b4
   case QUOTE_CMD:
Packit 8f70b4
      switch(special)
Packit 8f70b4
      {
Packit 8f70b4
      case HTTP_POST:
Packit 8f70b4
	 entity_size=special_data.length();
Packit 8f70b4
	 goto send_post;
Packit 8f70b4
      case HTTP_MOVE:
Packit 8f70b4
      case HTTP_COPY:
Packit 8f70b4
	 SendMethod(special==HTTP_MOVE?"MOVE":"COPY",efile);
Packit 8f70b4
	 Send("Destination: %s\r\n",special_data.get());
Packit 8f70b4
	 break;
Packit 8f70b4
      case HTTP_PROPFIND:
Packit 8f70b4
	 SendMethod("PROPFIND",efile);
Packit 8f70b4
	 Send("Depth: 1\r\n"); // directory listing required
Packit 8f70b4
	 break;
Packit 8f70b4
      case HTTP_NONE:
Packit 8f70b4
	 abort(); // cannot happen
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case LIST:
Packit 8f70b4
   case CHANGE_MODE:
Packit 8f70b4
   case LINK:
Packit 8f70b4
   case SYMLINK:
Packit 8f70b4
      abort(); // unsupported
Packit 8f70b4
Packit 8f70b4
   case RETRIEVE:
Packit 8f70b4
   retrieve:
Packit 8f70b4
      SendMethod("GET",efile);
Packit 8f70b4
      if(pos>0 && !no_ranges)
Packit 8f70b4
      {
Packit 8f70b4
	 if(limit==FILE_END)
Packit 8f70b4
	    Send("Range: bytes=%lld-\r\n",(long long)pos);
Packit 8f70b4
	 else
Packit 8f70b4
	    Send("Range: bytes=%lld-%lld\r\n",(long long)pos,(long long)limit-1);
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case STORE:
Packit 8f70b4
      if(sending_proppatch) {
Packit 8f70b4
	 SendProppatch(efile);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      if(hftp || strcasecmp(Query("put-method",hostname),"POST"))
Packit 8f70b4
	 SendMethod("PUT",efile);
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
      send_post:
Packit 8f70b4
	 SendMethod("POST",efile);
Packit 8f70b4
	 pos=0;
Packit 8f70b4
      }
Packit 8f70b4
      if(entity_size>=0)
Packit 8f70b4
	 Send("Content-length: %lld\r\n",(long long)(entity_size-pos));
Packit 8f70b4
      if(pos>0 && entity_size<0)
Packit 8f70b4
      {
Packit 8f70b4
	 request_pos=pos;
Packit 8f70b4
	 if(limit==FILE_END)
Packit 8f70b4
	    Send("Range: bytes=%lld-\r\n",(long long)pos);
Packit 8f70b4
	 else
Packit 8f70b4
	    Send("Range: bytes=%lld-%lld\r\n",(long long)pos,(long long)limit-1);
Packit 8f70b4
      }
Packit 8f70b4
      else if(pos>0)
Packit 8f70b4
      {
Packit 8f70b4
	 request_pos=pos;
Packit 8f70b4
	 Send("Range: bytes=%lld-%lld/%lld\r\n",(long long)pos,
Packit 8f70b4
		     (long long)((limit==FILE_END || limit>entity_size ? entity_size : limit)-1),
Packit 8f70b4
		     (long long)entity_size);
Packit 8f70b4
      }
Packit 4ca704
      if((entity_date!=NO_DATE) && (entity_date>0L && entity_date
Packit 8f70b4
      {
Packit 4ca704
	 char d[256];
Packit 4ca704
	 strftime(d, sizeof(d), "%a, %d %b %H:%M:%S %Y GMT", gmtime(&entity_date));
Packit 4ca704
	 Send("Last-Modified: %s\r\n",d);
Packit 8f70b4
	 Send("X-OC-MTime: %ld\r\n",(long)entity_date);	 // for OwnCloud
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case CHANGE_DIR:
Packit 8f70b4
   case LONG_LIST:
Packit 8f70b4
   case MP_LIST:
Packit 8f70b4
   case MAKE_DIR:
Packit 8f70b4
      if(last_char(efile)!='/' && add_slash)
Packit 8f70b4
	 efile.append('/');
Packit 8f70b4
      if(mode==CHANGE_DIR)
Packit 8f70b4
      {
Packit 8f70b4
	 if(use_propfind_now)
Packit 8f70b4
	    SendPropfind(efile,0);
Packit 8f70b4
	 else
Packit 8f70b4
	    SendMethod("HEAD",efile);
Packit 8f70b4
      }
Packit 8f70b4
      else if(mode==LONG_LIST)
Packit 8f70b4
	 goto retrieve;
Packit 8f70b4
      else if(mode==MAKE_DIR)
Packit 8f70b4
      {
Packit 8f70b4
	 if(QueryBool("use-mkcol"))
Packit 8f70b4
	    SendMethod("MKCOL",efile);
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    SendMethod("PUT",efile);
Packit 8f70b4
	    Send("Content-Length: 0\r\n");
Packit 8f70b4
	 }
Packit 8f70b4
	 pos=entity_size=0;
Packit 8f70b4
      }
Packit 8f70b4
      else if(mode==MP_LIST)
Packit 8f70b4
      {
Packit 8f70b4
	 SendPropfind(efile,1);
Packit 8f70b4
	 pos=0;
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case(REMOVE):
Packit 8f70b4
      SendMethod("DELETE",efile);
Packit 8f70b4
      Send("Depth: 0\r\n"); // deny directory removal
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case(REMOVE_DIR):
Packit 8f70b4
      if(efile.last_char()!='/')
Packit 8f70b4
	 efile.append('/');
Packit 8f70b4
      SendMethod("DELETE",efile);
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case ARRAY_INFO:
Packit 8f70b4
      if(use_propfind_now)
Packit 8f70b4
	 SendPropfind(efile,0);
Packit 8f70b4
      else
Packit 8f70b4
	 SendMethod("HEAD",efile);
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case RENAME:
Packit 8f70b4
      {
Packit 8f70b4
	 SendMethod("MOVE",efile);
Packit 8f70b4
	 Send("Destination: %s\r\n",GetFileURL(file1).get());
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(proxy && !https)
Packit 8f70b4
      SendProxyAuth();
Packit 8f70b4
   SendAuth();
Packit 8f70b4
   if(no_cache || no_cache_this)
Packit 8f70b4
      Send("Pragma: no-cache\r\n"); // for HTTP/1.0 compatibility
Packit 8f70b4
   SendCacheControl();
Packit 8f70b4
   if(mode==ARRAY_INFO && !use_head)
Packit 8f70b4
      connection="close";
Packit 8f70b4
   else if(!ModeIs(STORE))
Packit 8f70b4
      connection="keep-alive";
Packit 8f70b4
   if(mode!=ARRAY_INFO || connection)
Packit 8f70b4
      Send("Connection: %s\r\n",connection?connection:"close");
Packit 8f70b4
   Send("\r\n");
Packit 8f70b4
   if(special==HTTP_POST)
Packit 8f70b4
   {
Packit 8f70b4
      if(special_data)
Packit 8f70b4
	 Send("%s",special_data.get());
Packit 8f70b4
      entity_size=NO_SIZE;
Packit 8f70b4
   }
Packit 8f70b4
   else if(!xstrcmp(last_method,"PROPFIND"))
Packit 8f70b4
      SendPropfindBody();
Packit 8f70b4
Packit 8f70b4
   keep_alive=false;
Packit 8f70b4
   chunked=false;
Packit 8f70b4
   chunked_trailer=false;
Packit 8f70b4
   chunk_size=CHUNK_SIZE_UNKNOWN;
Packit 8f70b4
   chunk_pos=0;
Packit 8f70b4
   request_pos=0;
Packit 8f70b4
   inflate=0;
Packit 8f70b4
   no_ranges=!QueryBool("use-range",hostname);
Packit 8f70b4
Packit 8f70b4
   conn->send_buf->SetPos(0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Http::SendArrayInfoRequest()
Packit 8f70b4
{
Packit 8f70b4
   // skip to next needed file
Packit 8f70b4
   for(FileInfo *fi=fileset_for_info->curr(); fi; fi=fileset_for_info->next())
Packit 8f70b4
      if(fi->need)
Packit 8f70b4
	 break;
Packit 8f70b4
   if(array_send<fileset_for_info->curr_index())
Packit 8f70b4
      array_send=fileset_for_info->curr_index();
Packit 8f70b4
Packit 8f70b4
   if(state!=CONNECTED)
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   int m=1;
Packit 8f70b4
   if(keep_alive && use_head)
Packit 8f70b4
   {
Packit 8f70b4
      m=keep_alive_max;
Packit 8f70b4
      if(m==-1)
Packit 8f70b4
	 m=100;
Packit 8f70b4
   }
Packit 8f70b4
   int req_count=0;
Packit 8f70b4
   while(array_send-fileset_for_info->curr_index()
Packit 8f70b4
   && array_send<fileset_for_info->count())
Packit 8f70b4
   {
Packit 8f70b4
      FileInfo *fi=(*fileset_for_info)[array_send++];
Packit 8f70b4
      if(fi->need==0)
Packit 8f70b4
	 continue;
Packit 8f70b4
      xstring *name=&fi->name;
Packit 8f70b4
      if(fi->filetype==fi->DIRECTORY && name->last_char()!='/') {
Packit 8f70b4
	 name=&xstring::get_tmp(*name);
Packit 8f70b4
	 name->append('/');
Packit 8f70b4
      }
Packit 8f70b4
      if(fi->uri)
Packit 8f70b4
	 file_url.set(dir_file(GetConnectURL(),fi->uri));
Packit 8f70b4
      else
Packit 8f70b4
	 file_url.unset();
Packit 8f70b4
      SendRequest(array_send==fileset_for_info->count()-1 ? 0 : "keep-alive", *name);
Packit 8f70b4
      req_count++;
Packit 8f70b4
   }
Packit 8f70b4
   return req_count;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::ProceedArrayInfo()
Packit 8f70b4
{
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      // skip to next needed file
Packit 8f70b4
      FileInfo *fi=fileset_for_info->next();
Packit 8f70b4
      if(!fi || fi->need)
Packit 8f70b4
	 break;
Packit 8f70b4
   }
Packit 8f70b4
   if(!fileset_for_info->curr())
Packit 8f70b4
   {
Packit 8f70b4
      LogNote(10,"that was the last file info");
Packit 8f70b4
      // received all requested info.
Packit 8f70b4
      state=DONE;
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   // we can avoid reconnection if server supports it.
Packit 8f70b4
   if(keep_alive && (keep_alive_max>1 || keep_alive_max==-1)
Packit 8f70b4
   && (use_head || use_propfind_now))
Packit 8f70b4
   {
Packit 8f70b4
      // we'll have to receive next header, unset the status
Packit 8f70b4
      status.set(0);
Packit 8f70b4
      status_code=0;
Packit 8f70b4
Packit 8f70b4
      state=CONNECTED;
Packit 8f70b4
      SendArrayInfoRequest();
Packit 8f70b4
      state=RECEIVING_HEADER;
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      Disconnect();
Packit 8f70b4
      DontSleep();
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::NewAuth(const char *hdr,HttpAuth::target_t target,const char *user,const char *pass)
Packit 8f70b4
{
Packit 8f70b4
   if(!user || !pass)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   // FIXME: keep a request queue, get the URI from the queue.
Packit 8f70b4
   const char *uri=GetFileURL(file,NO_USER);
Packit 8f70b4
Packit 8f70b4
   Ref<HttpAuth::Challenge> chal(new HttpAuth::Challenge(hdr));
Packit 8f70b4
   bool stale=chal->GetParam("stale").eq_nc("true");
Packit 8f70b4
Packit 8f70b4
   if(auth_sent[target]>(stale?1:0))
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   HttpAuth::scheme_t new_scheme=chal->GetSchemeCode();
Packit 8f70b4
   if(new_scheme<=auth_scheme[target])
Packit 8f70b4
      return;
Packit 8f70b4
   if(HttpAuth::New(target,uri,chal.borrow(),user,pass))
Packit 8f70b4
      auth_scheme[target]=new_scheme;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::HandleHeaderLine(const char *name,const char *value)
Packit 8f70b4
{
Packit 8f70b4
   // use a perfect hash
Packit 8f70b4
#define hh(L,C) ((L)+(C)*3)
Packit 8f70b4
#define hhc(S,C) hh(sizeof((S))-1,(C))
Packit 8f70b4
#define case_hh(S,C) case hhc((S),(C)): if(strcasecmp(name,(S))) break;
Packit 8f70b4
   switch(hh(strlen(name),c_toupper(name[0]))) {
Packit 8f70b4
   case_hh("Content-Length",'C') {
Packit 8f70b4
      long long bs=0;
Packit 8f70b4
      if(1!=sscanf(value,"%lld",&bs))
Packit 8f70b4
	 return;
Packit 8f70b4
      if(bs<0) // try to workaround broken servers
Packit 8f70b4
	 bs+=0x100000000LL;
Packit 8f70b4
      body_size=bs;
Packit 8f70b4
      if(mode==ARRAY_INFO && H_2XX(status_code)
Packit 8f70b4
      && xstrcmp(last_method,"PROPFIND"))
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *fi=fileset_for_info->curr();
Packit 8f70b4
	 fi->SetSize(body_size);
Packit 8f70b4
	 TrySuccess();
Packit 8f70b4
      }
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   case_hh("Content-Range",'C') {
Packit 8f70b4
      long long first,last,fsize;
Packit 8f70b4
      if(H_REQUESTED_RANGE_NOT_SATISFIABLE(status_code))
Packit 8f70b4
      {
Packit 8f70b4
	 if(sscanf(value,"%*[^/]/%lld",&fsize)!=1)
Packit 8f70b4
	    return;
Packit 8f70b4
	 if(opt_size)
Packit 8f70b4
	    *opt_size=fsize;
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      if(sscanf(value,"%*s %lld-%lld/%lld",&first,&last,&fsize)!=3)
Packit 8f70b4
	 return;
Packit 8f70b4
      real_pos=first;
Packit 8f70b4
      if(last==-1)
Packit 8f70b4
	 last=fsize-first-1;
Packit 8f70b4
      if(body_size<0)
Packit 8f70b4
	 body_size=last-first+1;
Packit 8f70b4
      if(!ModeIs(STORE) && !ModeIs(MAKE_DIR))
Packit 8f70b4
	 entity_size=fsize;
Packit 8f70b4
      if(opt_size && H_2XX(status_code))
Packit 8f70b4
	 *opt_size=fsize;
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   case_hh("Last-Modified",'L') {
Packit 8f70b4
      if(!H_2XX(status_code))
Packit 8f70b4
	 return;
Packit 8f70b4
Packit 8f70b4
      time_t t=Http::atotm(value);
Packit 8f70b4
      if(t==ATOTM_ERROR)
Packit 8f70b4
	 return;
Packit 8f70b4
Packit 8f70b4
      if(opt_date)
Packit 8f70b4
	 *opt_date=t;
Packit 8f70b4
Packit 8f70b4
      if(mode==ARRAY_INFO && !propfind)
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *fi=fileset_for_info->curr();
Packit 8f70b4
	 fi->SetDate(t,0);
Packit 8f70b4
	 TrySuccess();
Packit 8f70b4
      }
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   case_hh("Location",'L')
Packit 8f70b4
      if(value[0]=='/' && value[1]=='/')
Packit 8f70b4
	 location.vset(GetProto(),":",value,NULL);
Packit 8f70b4
      else if(value[0]=='/')
Packit 8f70b4
	 location.vset(GetConnectURL().get(),value,NULL);
Packit 8f70b4
      else
Packit 8f70b4
	 location.set(value);
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   case_hh("Retry-After",'R')
Packit 8f70b4
      retry_after=0;
Packit 8f70b4
      sscanf(value,"%ld",&retry_after);
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   case_hh("Keep-Alive",'K') {
Packit 8f70b4
      keep_alive=true;
Packit 8f70b4
      const char *m=strstr(value,"max=");
Packit 8f70b4
      if(m) {
Packit 8f70b4
	 if(sscanf(m+4,"%d",&keep_alive_max)!=1)
Packit 8f70b4
	    keep_alive=false;
Packit 8f70b4
      } else
Packit 8f70b4
	 keep_alive_max=100;
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   case_hh("Proxy-Connection",'P')
Packit 8f70b4
      goto case_Connection;
Packit 8f70b4
   case_hh("Connection",'C')
Packit 8f70b4
   case_Connection:
Packit 8f70b4
      if(!strcasecmp(value,"keep-alive"))
Packit 8f70b4
	 keep_alive=true;
Packit 8f70b4
      else if(!strcasecmp(value,"close"))
Packit 8f70b4
	 keep_alive=false;
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   case_hh("Transfer-Encoding",'T')
Packit 8f70b4
      if(!strcasecmp(value,"identity"))
Packit 8f70b4
	 return;
Packit 8f70b4
      if(!strcasecmp(value,"chunked"))
Packit 8f70b4
      {
Packit 8f70b4
	 chunked=true;
Packit 8f70b4
	 chunked_trailer=false;
Packit 8f70b4
	 chunk_size=CHUNK_SIZE_UNKNOWN;	  // expecting first chunk
Packit 8f70b4
	 chunk_pos=0;
Packit 8f70b4
      }
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   case_hh("Content-Encoding",'C')
Packit 8f70b4
      content_encoding.set(value);
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   case_hh("Accept-Ranges",'A')
Packit 8f70b4
      if(!strcasecmp(value,"none"))
Packit 8f70b4
	 no_ranges=true;
Packit 8f70b4
      if(strstr(value,"bytes"))
Packit 8f70b4
	 seen_ranges_bytes=true;
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   case_hh("Set-Cookie",'S')
Packit 8f70b4
      if(!hftp && QueryBool("set-cookies",hostname))
Packit 8f70b4
	 SetCookie(value);
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   case_hh("Content-Disposition",'C') {
Packit 8f70b4
      const char *filename=strstr(value,"filename=");
Packit 8f70b4
      if(!filename)
Packit 8f70b4
	 return;
Packit 8f70b4
      filename=HttpHeader::extract_quoted_value(filename+9);
Packit 8f70b4
      SetSuggestedFileName(filename);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   case_hh("Content-Type",'C') {
Packit 8f70b4
      entity_content_type.set(value);
Packit 8f70b4
      const char *cs=strstr(value,"charset=");
Packit 8f70b4
      if(cs)
Packit 8f70b4
      {
Packit 8f70b4
	 cs=HttpHeader::extract_quoted_value(cs+8);
Packit 8f70b4
	 entity_charset.set(cs);
Packit 8f70b4
      }
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   case_hh("WWW-Authenticate",'W') {
Packit 8f70b4
      if(status_code!=H_Unauthorized)
Packit 8f70b4
	 return;
Packit 8f70b4
      if(user && pass)
Packit 8f70b4
	 NewAuth(value,HttpAuth::WWW,user,pass);
Packit 8f70b4
      else
Packit 8f70b4
	 NewAuth(value,HttpAuth::WWW,auth_user,auth_pass);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   case_hh("Proxy-Authenticate",'P') {
Packit 8f70b4
      if(status_code!=H_Proxy_Authentication_Required)
Packit 8f70b4
	 return;
Packit 8f70b4
      NewAuth(value,HttpAuth::PROXY,proxy_user,proxy_pass);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   case_hh("X-OC-MTime",'X') {
Packit 8f70b4
      if(!strcasecmp(value,"accepted"))
Packit 8f70b4
	 entity_date_set=true;
Packit 8f70b4
   }
Packit 8f70b4
   default:
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   LogNote(10,"unhandled header line `%s'",name);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static
Packit 8f70b4
const char *find_eol(const char *p,int len,int *eol_size)
Packit 8f70b4
{
Packit 8f70b4
   *eol_size=1;
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      if(p[0]=='\n')
Packit 8f70b4
	 return p;
Packit 8f70b4
      if(i+1
Packit 8f70b4
      {
Packit 8f70b4
	 *eol_size=2;
Packit 8f70b4
	 return p;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   *eol_size=0;
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::GetBetterConnection(int level)
Packit 8f70b4
{
Packit 8f70b4
   if(level==0)
Packit 8f70b4
      return;
Packit 8f70b4
   for(FA *fo=FirstSameSite(); fo!=0; fo=NextSameSite(fo))
Packit 8f70b4
   {
Packit 8f70b4
      Http *o=(Http*)fo; // we are sure it is Http.
Packit 8f70b4
Packit 8f70b4
      if(!o->conn || o->state==CONNECTING)
Packit 8f70b4
	 continue;
Packit 8f70b4
Packit 8f70b4
      if(o->tunnel_state==TUNNEL_WAITING)
Packit 8f70b4
	 continue;
Packit 8f70b4
Packit 8f70b4
      if(o->state!=CONNECTED || o->mode!=CLOSED)
Packit 8f70b4
      {
Packit 8f70b4
	 if(level<2)
Packit 8f70b4
	    continue;
Packit 8f70b4
	 if(!connection_takeover || (o->priority>=priority && !o->IsSuspended()))
Packit 8f70b4
	    continue;
Packit 8f70b4
	 o->Disconnect();
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      // so borrow the connection
Packit 8f70b4
      MoveConnectionHere(o);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Http::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   int res;
Packit 8f70b4
   const char *error;
Packit 8f70b4
   const char *buf;
Packit 8f70b4
   int len;
Packit 8f70b4
Packit 8f70b4
   // check if idle time exceeded
Packit 8f70b4
   if(mode==CLOSED && conn && idle_timer.Stopped())
Packit 8f70b4
   {
Packit 8f70b4
      LogNote(1,_("Closing idle connection"));
Packit 8f70b4
      Disconnect();
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(home.path==0)
Packit 8f70b4
      set_home(default_cwd);
Packit 8f70b4
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(propfind)
Packit 8f70b4
   {
Packit 8f70b4
      if(propfind->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 propfind=0;
Packit 8f70b4
	 if(mode==CHANGE_DIR)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(NO_FILE,propfind->ErrorText());
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(propfind->ErrorFatal())
Packit 8f70b4
	    fileset_for_info->next();
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(propfind->Eof())
Packit 8f70b4
      {
Packit 8f70b4
	 LogNote(9,"got EOF on PROPFIND reply");
Packit 8f70b4
	 const char *b;
Packit 8f70b4
	 int len;
Packit 8f70b4
	 propfind->Get(&b,&len;;
Packit 8f70b4
	 Ref<FileSet> fs(HttpListInfo::ParseProps(b,len,GetCwd()));
Packit 8f70b4
	 propfind=0;
Packit 8f70b4
	 if(fs)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(mode==CHANGE_DIR)
Packit 8f70b4
	    {
Packit 8f70b4
	       fs->rewind();
Packit 8f70b4
	       FileInfo *fi=fs->curr();
Packit 8f70b4
	       if(fi && fi->Has(fi->TYPE))
Packit 8f70b4
	       {
Packit 8f70b4
		  LogNote(9,"new-cwd: %s",fi->GetLongName());
Packit 8f70b4
		  new_cwd->is_file=(fi->filetype!=fi->DIRECTORY);
Packit 8f70b4
		  if(new_cwd->url.last_char()=='/' && new_cwd->is_file)
Packit 8f70b4
		     new_cwd->url.rtrim('/');
Packit 8f70b4
		  else if(new_cwd->url.last_char()!='/' && !new_cwd->is_file)
Packit 8f70b4
		     new_cwd->url.append('/');
Packit 8f70b4
	       }
Packit 8f70b4
	    }
Packit 8f70b4
	    else if(mode==ARRAY_INFO)
Packit 8f70b4
	       fileset_for_info->Merge(fs);
Packit 8f70b4
	 }
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 if(mode==CHANGE_DIR)
Packit 8f70b4
	 {
Packit 8f70b4
	    cwd.Set(new_cwd);
Packit 8f70b4
	    cache->SetDirectory(this, "", !cwd.is_file);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 else if(mode==ARRAY_INFO)
Packit 8f70b4
	    ProceedArrayInfo();
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case DISCONNECTED:
Packit 8f70b4
      if(mode==CLOSED || !hostname)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(ModeIs(STORE) && pos>0 && entity_size>=0 && pos>=entity_size)
Packit 8f70b4
      {
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(mode==ARRAY_INFO)
Packit 8f70b4
      {
Packit 8f70b4
	 // check if we have anything to request
Packit 8f70b4
	 SendArrayInfoRequest();
Packit 8f70b4
	 if(!fileset_for_info->curr())
Packit 8f70b4
	 {
Packit 8f70b4
	    state=DONE;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(!hftp && mode==QUOTE_CMD && !special)
Packit 8f70b4
      {
Packit 8f70b4
      handle_quote_cmd:
Packit 8f70b4
	 if(file && !strncasecmp(file,"Set-Cookie ",11))
Packit 8f70b4
	    SetCookie(file+11);
Packit 8f70b4
	 else if(file && !strncasecmp(file,"POST ",5))
Packit 8f70b4
	    special=HTTP_POST;
Packit 8f70b4
	 else if(file && !strncasecmp(file,"COPY ",5))
Packit 8f70b4
	    special=HTTP_COPY;
Packit 8f70b4
	 else if(file && !strncasecmp(file,"MOVE ",5))
Packit 8f70b4
	    special=HTTP_MOVE;
Packit 8f70b4
	 else if(file && !strncasecmp(file,"PROPFIND ",9))
Packit 8f70b4
	    special=HTTP_PROPFIND;
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(NOT_SUPP,0);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(special)
Packit 8f70b4
	 {
Packit 8f70b4
	    // METHOD encoded_path data
Packit 8f70b4
	    const char *scan=file;
Packit 8f70b4
	    while(*scan && *scan!=' ')
Packit 8f70b4
	       scan++;
Packit 8f70b4
	    while(*scan==' ')
Packit 8f70b4
	       scan++;
Packit 8f70b4
	    file_url.set(https?"https://":"http://");
Packit 8f70b4
	    AppendHostEncoded(file_url,hostname);
Packit 8f70b4
	    if(portname)
Packit 8f70b4
	    {
Packit 8f70b4
	       file_url.append(':');
Packit 8f70b4
	       file_url.append_url_encoded(portname,URL_PORT_UNSAFE);
Packit 8f70b4
	    }
Packit 8f70b4
	    if(*scan!='/' && cwd)
Packit 8f70b4
	    {
Packit 8f70b4
	       if(cwd[0]!='/')
Packit 8f70b4
		  file_url.append('/');
Packit 8f70b4
	       file_url.append_url_encoded(cwd,URL_PATH_UNSAFE);
Packit 8f70b4
	    }
Packit 8f70b4
	    if(*scan!='/' && file_url.last_char()!='/')
Packit 8f70b4
	       file_url.append('/');
Packit 8f70b4
	    file_url.append(scan);
Packit 8f70b4
	    file_url.truncate_at(' ');
Packit 8f70b4
Packit 8f70b4
	    scan=strchr(scan,' ');
Packit 8f70b4
	    while(scan && *scan==' ')
Packit 8f70b4
	       scan++;
Packit 8f70b4
	    special_data.set(scan);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(!special && !ModeSupported())
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(NOT_SUPP);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(hftp)
Packit 8f70b4
      {
Packit 8f70b4
	 if(!proxy)
Packit 8f70b4
	 {
Packit 8f70b4
	    // problem here: hftp cannot work without proxy
Packit 8f70b4
	    SetError(FATAL,_("ftp over http cannot work without proxy, set hftp:proxy."));
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      // walk through Http classes and try to find identical idle session
Packit 8f70b4
      // first try "easy" cases of session take-over.
Packit 8f70b4
      for(int i=0; i<3; i++)
Packit 8f70b4
      {
Packit 8f70b4
	 if(i>=2 && (connection_limit==0 || connection_limit>CountConnections()))
Packit 8f70b4
	    break;
Packit 8f70b4
	 GetBetterConnection(i);
Packit 8f70b4
	 if(state!=DISCONNECTED)
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(!resolver && mode!=CONNECT_VERIFY && !ReconnectAllowed())
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(https)
Packit 8f70b4
	 m|=Resolve(HTTPS_DEFAULT_PORT,"https","tcp");
Packit 8f70b4
      else
Packit 8f70b4
	 m|=Resolve(HTTP_DEFAULT_PORT,"http","tcp");
Packit 8f70b4
      if(!peer)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(mode==CONNECT_VERIFY)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(!ReconnectAllowed())
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(!NextTry())
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
Packit 8f70b4
      retry_after=0;
Packit 8f70b4
Packit 8f70b4
      res=SocketCreateTCP(peer[peer_curr].sa.sa_family);
Packit 8f70b4
      if(res==-1)
Packit 8f70b4
      {
Packit 8f70b4
	 saved_errno=errno;
Packit 8f70b4
	 if(peer_curr+1
Packit 8f70b4
	 {
Packit 8f70b4
	    peer_curr++;
Packit 8f70b4
	    retries--;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(NonFatalError(saved_errno))
Packit 8f70b4
	    return m;
Packit 8f70b4
	 SetError(SEE_ERRNO,xstring::format(
Packit 8f70b4
	    _("cannot create socket of address family %d"),
Packit 8f70b4
	    peer[peer_curr].sa.sa_family));
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      conn=new Connection(res,hostname);
Packit 8f70b4
Packit 8f70b4
      SayConnectingTo();
Packit 8f70b4
      res=SocketConnect(conn->sock,&peer[peer_curr]);
Packit 8f70b4
      if(res==-1 && errno!=EINPROGRESS)
Packit 8f70b4
      {
Packit 8f70b4
	 saved_errno=errno;
Packit 8f70b4
	 NextPeer();
Packit 8f70b4
	 LogError(0,"connect: %s\n",strerror(saved_errno));
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 if(NotSerious(saved_errno))
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 goto system_error;
Packit 8f70b4
      }
Packit 8f70b4
      state=CONNECTING;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      timeout_timer.Reset();
Packit 8f70b4
Packit 8f70b4
   case CONNECTING:
Packit 8f70b4
      res=Poll(conn->sock,POLLOUT,&error);
Packit 8f70b4
      if(res==-1)
Packit 8f70b4
      {
Packit 8f70b4
	 LogError(0,_("Socket error (%s) - reconnecting"),error);
Packit 8f70b4
	 Disconnect(error);
Packit 8f70b4
	 NextPeer();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(!(res&POLLOUT))
Packit 8f70b4
      {
Packit 8f70b4
	 if(CheckTimeout())
Packit 8f70b4
	 {
Packit 8f70b4
	    NextPeer();
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 Block(conn->sock,POLLOUT);
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      state=CONNECTED;
Packit 8f70b4
#if USE_SSL
Packit 8f70b4
      if(proxy?!strncmp(proxy,"https://",8):https)
Packit 8f70b4
      {
Packit 8f70b4
	 conn->MakeSSLBuffers();
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
#endif
Packit 8f70b4
      {
Packit 8f70b4
	 conn->MakeBuffers();
Packit 8f70b4
#if USE_SSL
Packit 8f70b4
	 if(proxy && https)
Packit 8f70b4
	 {
Packit 8f70b4
	    // have to setup a tunnel.
Packit 8f70b4
	    xstring ehost;
Packit 8f70b4
	    AppendHostEncoded(ehost,hostname);
Packit 8f70b4
	    const char *port_to_use=portname?portname.get():HTTPS_DEFAULT_PORT;
Packit 8f70b4
	    const char *eport=url::encode(port_to_use,URL_PORT_UNSAFE);
Packit 8f70b4
	    Send("CONNECT %s:%s HTTP/1.1\r\n",ehost.get(),eport);
Packit 8f70b4
	    SendProxyAuth();
Packit 8f70b4
	    Send("\r\n");
Packit 8f70b4
	    tunnel_state=TUNNEL_WAITING;
Packit 8f70b4
	    state=RECEIVING_HEADER;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
#endif // USE_SSL
Packit 8f70b4
      }
Packit 8f70b4
      /*fallthrough*/
Packit 8f70b4
   case CONNECTED:
Packit 8f70b4
      if(mode==CONNECT_VERIFY)
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
Packit 8f70b4
      if(mode==QUOTE_CMD && !special)
Packit 8f70b4
	 goto handle_quote_cmd;
Packit 8f70b4
      if(conn->recv_buf->Eof())
Packit 8f70b4
      {
Packit 8f70b4
	 LogError(0,_("Peer closed connection"));
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(mode==CLOSED)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(!special && !ModeSupported())
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(NOT_SUPP);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      ExpandTildeInCWD();
Packit 8f70b4
      if(ModeIs(STORE) && pos>0 && entity_size>=0 && pos>=entity_size)
Packit 8f70b4
      {
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(mode==ARRAY_INFO)
Packit 8f70b4
      {
Packit 8f70b4
	 if(SendArrayInfoRequest()==0) {
Packit 8f70b4
	    // nothing to do
Packit 8f70b4
	    state=DONE;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 LogNote(9,_("Sending request..."));
Packit 8f70b4
	 SendRequest();
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      state=RECEIVING_HEADER;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      if(ModeIs(STORE))
Packit 8f70b4
	 rate_limit=new RateLimit(hostname);
Packit 8f70b4
Packit 8f70b4
   case RECEIVING_HEADER:
Packit 8f70b4
      if(conn->send_buf->Error() || conn->recv_buf->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 if((ModeIs(STORE) || special) && status_code && !H_2XX(status_code))
Packit 8f70b4
	    goto pre_RECEIVING_BODY;   // assume error.
Packit 8f70b4
      handle_buf_error:
Packit 8f70b4
	 if(conn->send_buf->Error())
Packit 8f70b4
	 {
Packit 8f70b4
	    LogError(0,"send: %s",conn->send_buf->ErrorText());
Packit 8f70b4
	    if(conn->send_buf->ErrorFatal())
Packit 8f70b4
	       SetError(FATAL,conn->send_buf->ErrorText());
Packit 8f70b4
	 }
Packit 8f70b4
	 if(conn->recv_buf->Error())
Packit 8f70b4
	 {
Packit 8f70b4
	    LogError(0,"recv: %s",conn->recv_buf->ErrorText());
Packit 8f70b4
	    if(conn->recv_buf->ErrorFatal())
Packit 8f70b4
	       SetError(FATAL,conn->recv_buf->ErrorText());
Packit 8f70b4
	 }
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      timeout_timer.Reset(conn->send_buf->EventTime());
Packit 8f70b4
      timeout_timer.Reset(conn->recv_buf->EventTime());
Packit 8f70b4
      if(CheckTimeout())
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      conn->recv_buf->Get(&buf,&len;;
Packit 8f70b4
      if(!buf)
Packit 8f70b4
      {
Packit 8f70b4
	 // eof
Packit 8f70b4
	 LogError(0,_("Hit EOF while fetching headers"));
Packit 8f70b4
	 // workaround some broken servers
Packit 8f70b4
	 if(H_REDIRECTED(status_code) && location)
Packit 8f70b4
	    goto pre_RECEIVING_BODY;
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(len>0)
Packit 8f70b4
      {
Packit 8f70b4
	 int eol_size;
Packit 8f70b4
	 const char *eol=find_eol(buf,len,&eol_size);
Packit 8f70b4
	 if(eol)
Packit 8f70b4
	 {
Packit 8f70b4
	    // empty line indicates end of headers.
Packit 8f70b4
	    if(eol==buf && status)
Packit 8f70b4
	    {
Packit 8f70b4
	       LogRecv(4,"");
Packit 8f70b4
	       conn->recv_buf->Skip(eol_size);
Packit 8f70b4
	       if(tunnel_state==TUNNEL_WAITING)
Packit 8f70b4
	       {
Packit 8f70b4
		  if(H_2XX(status_code))
Packit 8f70b4
		  {
Packit 8f70b4
#if USE_SSL
Packit 8f70b4
		     if(https)
Packit 8f70b4
			conn->MakeSSLBuffers();
Packit 8f70b4
#endif
Packit 8f70b4
		     tunnel_state=TUNNEL_ESTABLISHED;
Packit 8f70b4
		     ResetRequestData();
Packit 8f70b4
		     state=CONNECTED;
Packit 8f70b4
		     return MOVED;
Packit 8f70b4
		  }
Packit 8f70b4
	       }
Packit 8f70b4
	       if(chunked_trailer)
Packit 8f70b4
	       {
Packit 8f70b4
		  chunked_trailer=false;
Packit 8f70b4
		  chunked=false;
Packit 8f70b4
		  if(propfind) {
Packit 8f70b4
		     // we avoid the DONE state since we have yet to handle propfind data
Packit 8f70b4
		     propfind->PutEOF();
Packit 8f70b4
		     state=CONNECTED;
Packit 8f70b4
		  } else
Packit 8f70b4
		     state=DONE;
Packit 8f70b4
		  return MOVED;
Packit 8f70b4
	       }
Packit 8f70b4
	       if(H_CONTINUE(status_code))
Packit 8f70b4
	       {
Packit 8f70b4
		  status.set(0);
Packit 8f70b4
		  status_code=0;
Packit 8f70b4
		  return MOVED;
Packit 8f70b4
	       }
Packit 8f70b4
	       if(mode==ARRAY_INFO)
Packit 8f70b4
	       {
Packit 8f70b4
		  if(!xstrcmp(last_method,"PROPFIND"))
Packit 8f70b4
		  {
Packit 8f70b4
		     if(H_UNSUPPORTED(status_code))
Packit 8f70b4
		     {
Packit 8f70b4
			ResMgr::Set("http:use-propfind",hostname,"no");
Packit 8f70b4
			use_propfind_now=false;
Packit 8f70b4
			Disconnect();
Packit 8f70b4
			DontSleep();
Packit 8f70b4
			return MOVED;
Packit 8f70b4
		     }
Packit 8f70b4
		     goto pre_RECEIVING_BODY;
Packit 8f70b4
		  }
Packit 8f70b4
		  FileInfo *fi=fileset_for_info->curr();
Packit 8f70b4
		  if(H_REDIRECTED(status_code)) {
Packit 8f70b4
		     HandleRedirection();
Packit 8f70b4
		     if(location)
Packit 8f70b4
			fi->SetRedirect(location);
Packit 8f70b4
		  } else if(H_2XX(status_code) && !fi->Has(fi->TYPE)) {
Packit 8f70b4
		     fi->SetType(last_uri.last_char()=='/'?fi->DIRECTORY:fi->NORMAL);
Packit 8f70b4
		  }
Packit 8f70b4
		  ProceedArrayInfo();
Packit 8f70b4
		  return MOVED;
Packit 8f70b4
	       }
Packit 8f70b4
	       else if(ModeIs(STORE) || ModeIs(MAKE_DIR) || sending_proppatch)
Packit 8f70b4
	       {
Packit 8f70b4
		  if((sent_eot || pos==entity_size || sending_proppatch) && H_2XX(status_code))
Packit 8f70b4
		  {
Packit 8f70b4
		     state=DONE;
Packit 8f70b4
		     Disconnect();
Packit 8f70b4
		     state=DONE;
Packit 8f70b4
		     if(ModeIs(STORE) && entity_date!=NO_DATE && !entity_date_set
Packit 8f70b4
		     && use_propfind_now) {
Packit 8f70b4
			// send PROPPATCH in a separate request.
Packit 8f70b4
			sending_proppatch=true;
Packit 8f70b4
			state=DISCONNECTED;
Packit 8f70b4
		     }
Packit 8f70b4
		     return MOVED;
Packit 8f70b4
		  }
Packit 8f70b4
		  if(H_2XX(status_code))
Packit 8f70b4
		  {
Packit 8f70b4
		     // should never happen
Packit 8f70b4
		     LogError(0,"Unexpected success, the server did not accept full request body");
Packit 8f70b4
		     Disconnect();
Packit 8f70b4
		     return MOVED;
Packit 8f70b4
		  }
Packit 8f70b4
		  // going to pre_RECEIVING_BODY to catch error
Packit 8f70b4
	       }
Packit 8f70b4
	       goto pre_RECEIVING_BODY;
Packit 8f70b4
	    }
Packit 8f70b4
	    len=eol-buf;
Packit 8f70b4
	    line.nset(buf,len);
Packit 8f70b4
Packit 8f70b4
	    conn->recv_buf->Skip(len+eol_size);
Packit 8f70b4
Packit 8f70b4
	    LogRecv(4,line);
Packit 8f70b4
	    m=MOVED;
Packit 8f70b4
Packit 8f70b4
	    if(status==0)
Packit 8f70b4
	    {
Packit 8f70b4
	       // it's status line
Packit 8f70b4
	       status.set(line);
Packit 8f70b4
	       int ver_major,ver_minor;
Packit 8f70b4
	       if(3!=sscanf(status,"HTTP/%d.%d %n%d",&ver_major,&ver_minor,
Packit 8f70b4
		     &status_consumed,&status_code))
Packit 8f70b4
	       {
Packit 8f70b4
		  // simple 0.9 ?
Packit 8f70b4
		  proto_version=0x09;
Packit 8f70b4
		  status_code=H_Ok;
Packit 8f70b4
		  LogError(0,_("Could not parse HTTP status line"));
Packit 8f70b4
		  if(ModeIs(STORE))
Packit 8f70b4
		  {
Packit 8f70b4
		     state=DONE;
Packit 8f70b4
		     Disconnect();
Packit 8f70b4
		     state=DONE;
Packit 8f70b4
		     return MOVED;
Packit 8f70b4
		  }
Packit 8f70b4
		  conn->recv_buf->UnSkip(len+eol_size);
Packit 8f70b4
		  goto pre_RECEIVING_BODY;
Packit 8f70b4
	       }
Packit 8f70b4
	       proto_version=(ver_major<<4)+ver_minor;
Packit 8f70b4
Packit 8f70b4
	       // HTTP/1.1 does keep-alive by default
Packit 8f70b4
	       if(proto_version>=0x11)
Packit 8f70b4
		  keep_alive=true;
Packit 8f70b4
Packit 8f70b4
	       if(!H_2XX(status_code))
Packit 8f70b4
	       {
Packit 8f70b4
		  if(H_CONTINUE(status_code))
Packit 8f70b4
		     return MOVED;
Packit 8f70b4
Packit 8f70b4
		  if(H_5XX(status_code)) // server failed, try another
Packit 8f70b4
		     NextPeer();
Packit 8f70b4
		  if(status_code==H_Gateway_Timeout)
Packit 8f70b4
		  {
Packit 8f70b4
		     const char *cc=Query("cache-control");
Packit 8f70b4
		     if(cc && strstr(cc,"only-if-cached"))
Packit 8f70b4
		     {
Packit 8f70b4
			if(mode!=ARRAY_INFO)
Packit 8f70b4
			{
Packit 8f70b4
			   SetError(NO_FILE,_("Object is not cached and http:cache-control has only-if-cached"));
Packit 8f70b4
			   return MOVED;
Packit 8f70b4
			}
Packit 8f70b4
			status_code=H_Not_Acceptable; // so that no retry will be attempted
Packit 8f70b4
		     }
Packit 8f70b4
		  }
Packit 8f70b4
		  // check for retriable codes
Packit 8f70b4
		  if(H_TRANSIENT(status_code))
Packit 8f70b4
		  {
Packit 8f70b4
		     Disconnect();
Packit 8f70b4
		     return MOVED;
Packit 8f70b4
		  }
Packit 8f70b4
		  if(status_code==H_Too_Many_Requests)
Packit 8f70b4
		  {
Packit 8f70b4
		     Disconnect();
Packit 8f70b4
		     if(retry_after)
Packit 8f70b4
			reconnect_timer.StopDelayed(retry_after);
Packit 8f70b4
		     return MOVED;
Packit 8f70b4
		  }
Packit 8f70b4
Packit 8f70b4
		  if(mode==ARRAY_INFO)
Packit 8f70b4
		     TrySuccess();
Packit 8f70b4
Packit 8f70b4
		  return MOVED;
Packit 8f70b4
	       }
Packit 8f70b4
	    }
Packit 8f70b4
	    else
Packit 8f70b4
	    {
Packit 8f70b4
	       // header line.
Packit 8f70b4
	       char *colon=strchr(line.get_non_const(),':');
Packit 8f70b4
	       if(colon)
Packit 8f70b4
	       {
Packit 8f70b4
		  *colon=0; // terminate the header tag
Packit 8f70b4
		  const char *value=colon+1;
Packit 8f70b4
		  while(*value==' ')
Packit 8f70b4
		     value++;
Packit 8f70b4
		  HandleHeaderLine(line,value);
Packit 8f70b4
	       }
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(ModeIs(STORE) && (!status || H_CONTINUE(status_code)) && !sent_eot)
Packit 8f70b4
	 Block(conn->sock,POLLOUT);
Packit 8f70b4
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   pre_RECEIVING_BODY:
Packit 8f70b4
Packit 8f70b4
      // 204 No Content
Packit 8f70b4
      if(H_EMPTY(status_code) && body_size<0)
Packit 8f70b4
	 body_size=0;
Packit 8f70b4
Packit 8f70b4
      if(H_REDIRECTED(status_code))
Packit 8f70b4
      {
Packit 8f70b4
	 // check if it is redirection to the same server
Packit 8f70b4
	 // or to directory instead of file.
Packit 8f70b4
	 // FIXME.
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(H_REQUESTED_RANGE_NOT_SATISFIABLE(status_code))
Packit 8f70b4
      {
Packit 8f70b4
	 // file is smaller than requested
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if((status_code==H_Unauthorized && auth_scheme[HttpAuth::WWW])
Packit 8f70b4
      || (status_code==H_Proxy_Authentication_Required && auth_scheme[HttpAuth::PROXY])) {
Packit 8f70b4
	 // retry with authentication
Packit 8f70b4
	 retries--;
Packit 8f70b4
	 state=RECEIVING_BODY;
Packit 8f70b4
	 LogErrorText();
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 DontSleep();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(!H_2XX(status_code))
Packit 8f70b4
      {
Packit 8f70b4
	 xstring err;
Packit 8f70b4
	 int code=NO_FILE;
Packit 8f70b4
Packit 8f70b4
	 if(H_REDIRECTED(status_code))
Packit 8f70b4
	 {
Packit 8f70b4
	    HandleRedirection();
Packit 8f70b4
	    err.setf("%s (%s -> %s)",status+status_consumed,file.get(),
Packit 8f70b4
				    location?location.get():"nowhere");
Packit 8f70b4
	    code=FILE_MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    const char *closure=file;
Packit 8f70b4
	    if(H_UNSUPPORTED(status_code) || status_code==H_Method_Not_Allowed)
Packit 8f70b4
	    {
Packit 8f70b4
	       if(H_UNSUPPORTED(status_code))
Packit 8f70b4
	       {
Packit 8f70b4
		  if(!xstrcmp(last_method,"PROPFIND"))
Packit 8f70b4
		     ResMgr::Set("http:use-propfind",hostname,"no");
Packit 8f70b4
		  if(!xstrcmp(last_method,"MKCOL"))
Packit 8f70b4
		     ResMgr::Set("http:use-mkcol",hostname,"no");
Packit 8f70b4
	       }
Packit 8f70b4
	       if(mode==CHANGE_DIR && !xstrcmp(last_method,"PROPFIND"))
Packit 8f70b4
	       {
Packit 8f70b4
		  use_propfind_now=false;
Packit 8f70b4
		  Disconnect();
Packit 8f70b4
		  DontSleep();
Packit 8f70b4
		  return MOVED;
Packit 8f70b4
	       }
Packit 8f70b4
	       code=NOT_SUPP;
Packit 8f70b4
	       closure=last_method;
Packit 8f70b4
	    }
Packit 8f70b4
	    if(closure && closure[0])
Packit 8f70b4
	       err.setf("%s (%s)",status+status_consumed,closure);
Packit 8f70b4
	    else
Packit 8f70b4
	       err.setf("%s (%s%s)",status+status_consumed,cwd.path.get(),
Packit 8f70b4
				       (last_char(cwd)=='/')?"":"/");
Packit 8f70b4
	 }
Packit 8f70b4
	 state=RECEIVING_BODY;
Packit 8f70b4
	 LogErrorText();
Packit 8f70b4
	 if(mode==ARRAY_INFO)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(!H_TRANSIENT(status_code))
Packit 8f70b4
	       fileset_for_info->next();
Packit 8f70b4
	    Disconnect();
Packit 8f70b4
	    DontSleep();
Packit 8f70b4
	 }
Packit 8f70b4
	 else
Packit 8f70b4
	    SetError(code,err);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(!xstrcmp(last_method,"PROPFIND")
Packit 8f70b4
      && (mode==ARRAY_INFO || mode==CHANGE_DIR)) {
Packit 8f70b4
	 LogNote(9,"accepting XML for PROPFIND...");
Packit 8f70b4
	 propfind=new IOBufferFileAccess(this);
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(mode==CHANGE_DIR && !propfind)
Packit 8f70b4
      {
Packit 8f70b4
	 cwd.Set(new_cwd);
Packit 8f70b4
	 cache->SetDirectory(this, "", !cwd.is_file);
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      // Many servers send application/x-gzip with x-gzip encoding,
Packit 8f70b4
      // don't decode in such a case.
Packit 8f70b4
      if(CompressedContentEncoding() && !CompressedContentType()
Packit 8f70b4
      && QueryBool("decode",hostname)) {
Packit 8f70b4
	 // inflated size is unknown beforehand
Packit 8f70b4
	 entity_size=NO_SIZE;
Packit 8f70b4
	 if(opt_size)
Packit 8f70b4
	    *opt_size=NO_SIZE;
Packit 8f70b4
	 // start the inflation
Packit 8f70b4
	 inflate=new DirectedBuffer(DirectedBuffer::GET);
Packit 8f70b4
	 inflate->SetTranslator(new DataInflator());
Packit 8f70b4
      }
Packit 8f70b4
      // sometimes it's possible to derive entity size from body size.
Packit 8f70b4
      if(entity_size==NO_SIZE && body_size!=NO_SIZE
Packit 8f70b4
      && pos==0 && !ModeIs(STORE) && !ModeIs(MAKE_DIR) && !inflate) {
Packit 8f70b4
	 entity_size=body_size;
Packit 8f70b4
	 if(opt_size && H_2XX(status_code))
Packit 8f70b4
	    *opt_size=body_size;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      LogNote(9,_("Receiving body..."));
Packit 8f70b4
      rate_limit=new RateLimit(hostname);
Packit 8f70b4
      if(real_pos<0) // assume Range: did not work
Packit 8f70b4
      {
Packit 8f70b4
	 if(!ModeIs(STORE) && !ModeIs(MAKE_DIR) && body_size>=0)
Packit 8f70b4
	 {
Packit 8f70b4
	    entity_size=body_size;
Packit 8f70b4
	    if(opt_size && H_2XX(status_code))
Packit 8f70b4
	       *opt_size=entity_size;
Packit 8f70b4
	 }
Packit 8f70b4
	 real_pos=0;
Packit 8f70b4
      }
Packit 8f70b4
      state=RECEIVING_BODY;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      /*passthrough*/
Packit 8f70b4
   case RECEIVING_BODY:
Packit 8f70b4
      if(conn->recv_buf->Error() || conn->send_buf->Error())
Packit 8f70b4
	 goto handle_buf_error;
Packit 8f70b4
      if(conn->recv_buf->Size()>=rate_limit->BytesAllowedToGet())
Packit 8f70b4
      {
Packit 8f70b4
	 conn->recv_buf->Suspend();
Packit 8f70b4
	 TimeoutS(1);
Packit 8f70b4
      }
Packit 8f70b4
      else if(conn->recv_buf->Size()>=max_buf)
Packit 8f70b4
      {
Packit 8f70b4
	 conn->recv_buf->Suspend();
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 if(conn->recv_buf->IsSuspended())
Packit 8f70b4
	 {
Packit 8f70b4
	    conn->recv_buf->Resume();
Packit 8f70b4
	    if(conn->recv_buf->Size()>0 || (conn->recv_buf->Size()==0 && conn->recv_buf->Eof()))
Packit 8f70b4
	       m=MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 timeout_timer.Reset(conn->send_buf->EventTime());
Packit 8f70b4
	 timeout_timer.Reset(conn->recv_buf->EventTime());
Packit 8f70b4
	 if(conn->recv_buf->Size()==0)
Packit 8f70b4
	 {
Packit 8f70b4
	    // check if ranges were emulated by squid
Packit 8f70b4
	    bool no_ranges_if_timeout=(bytes_received==0 && !seen_ranges_bytes);
Packit 8f70b4
	    if(CheckTimeout())
Packit 8f70b4
	    {
Packit 8f70b4
	       if(no_ranges_if_timeout)
Packit 8f70b4
	       {
Packit 8f70b4
		  no_ranges=true;
Packit 8f70b4
		  real_pos=0; // so that pget would know immediately.
Packit 8f70b4
	       }
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   case DONE:
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
Packit 8f70b4
system_error:
Packit 8f70b4
   assert(saved_errno!=0);
Packit 8f70b4
   if(NonFatalError(saved_errno))
Packit 8f70b4
      return m;
Packit 8f70b4
   SetError(SEE_ERRNO,0);
Packit 8f70b4
   Disconnect();
Packit 8f70b4
   return MOVED;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::HandleRedirection()
Packit 8f70b4
{
Packit 8f70b4
   bool is_url=(location && url::is_url(location));
Packit 8f70b4
   if(location && !is_url
Packit 8f70b4
   && mode==QUOTE_CMD && !strncasecmp(file,"POST ",5)
Packit 8f70b4
   && tunnel_state!=TUNNEL_WAITING)
Packit 8f70b4
   {
Packit 8f70b4
      const char *the_file=file;
Packit 8f70b4
Packit 8f70b4
      const char *scan=file+5;
Packit 8f70b4
      while(*scan==' ')
Packit 8f70b4
	 scan++;
Packit 8f70b4
      char *the_post_file=alloca_strdup(scan);
Packit 8f70b4
      char *space=strchr(the_post_file,' ');
Packit 8f70b4
      if(space)
Packit 8f70b4
	 *space=0;
Packit 8f70b4
      the_file=the_post_file;
Packit 8f70b4
Packit 8f70b4
      char *new_location=alloca_strdup2(GetConnectURL(),
Packit 8f70b4
			   strlen(the_file)+strlen(location));
Packit 8f70b4
      int p_ind=url::path_index(new_location);
Packit 8f70b4
      if(location[0]=='/')
Packit 8f70b4
	 strcpy(new_location+p_ind,location);
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 if(the_file[0]=='/')
Packit 8f70b4
	    strcpy(new_location+p_ind,the_file);
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    char *slash=strrchr(new_location,'/');
Packit 8f70b4
	    strcpy(slash+1,the_file);
Packit 8f70b4
	 }
Packit 8f70b4
	 char *slash=strrchr(new_location,'/');
Packit 8f70b4
	 strcpy(slash+1,location);
Packit 8f70b4
      }
Packit 8f70b4
      location.set(new_location);
Packit 8f70b4
   } else if(is_url && !hftp) {
Packit 8f70b4
      ParsedURL url(location);
Packit 8f70b4
      if(url.proto.eq(GetProto()) && !xstrcasecmp(url.host,hostname)
Packit 8f70b4
      && user && !url.user) {
Packit 8f70b4
	 // use the same user name after redirect to the same site.
Packit 8f70b4
	 url.user.set(user);
Packit 8f70b4
	 location.truncate();
Packit 8f70b4
	 url.CombineTo(location);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess *Http::New() { return new Http(); }
Packit 8f70b4
FileAccess *HFtp::New() { return new HFtp(); }
Packit 8f70b4
Packit 8f70b4
void  Http::ClassInit()
Packit 8f70b4
{
Packit 8f70b4
   // register the class
Packit 8f70b4
   Register("http",Http::New);
Packit 8f70b4
   Register("hftp",HFtp::New);
Packit 8f70b4
#if USE_SSL
Packit 8f70b4
   Register("https",Https::New);
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SuspendInternal()
Packit 8f70b4
{
Packit 8f70b4
   super::SuspendInternal();
Packit 8f70b4
   if(conn)
Packit 8f70b4
      conn->SuspendInternal();
Packit 8f70b4
}
Packit 8f70b4
void Http::ResumeInternal()
Packit 8f70b4
{
Packit 8f70b4
   if(conn)
Packit 8f70b4
      conn->ResumeInternal();
Packit 8f70b4
   super::ResumeInternal();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Http::Read(Buffer *buf,int size)
Packit 8f70b4
{
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return error_code;
Packit 8f70b4
   if(mode==CLOSED)
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(state==DONE)
Packit 8f70b4
      return 0;	  // eof
Packit 8f70b4
   int res=DO_AGAIN;
Packit 8f70b4
   if(state==RECEIVING_BODY && real_pos>=0)
Packit 8f70b4
   {
Packit 8f70b4
      Enter(this);
Packit 8f70b4
      res=_Read(buf,size);
Packit 8f70b4
      if(res>0)
Packit 8f70b4
      {
Packit 8f70b4
	 pos+=res;
Packit 8f70b4
	 if(rate_limit)
Packit 8f70b4
	    rate_limit->BytesGot(res);
Packit 8f70b4
	 TrySuccess();
Packit 8f70b4
      }
Packit 8f70b4
      Leave(this);
Packit 8f70b4
   }
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
void Http::_Skip(int to_skip)
Packit 8f70b4
{
Packit 8f70b4
   if(inflate)
Packit 8f70b4
      inflate->Skip(to_skip);
Packit 8f70b4
   else
Packit 8f70b4
      conn->recv_buf->Skip(to_skip);
Packit 8f70b4
   _UpdatePos(to_skip);
Packit 8f70b4
}
Packit 8f70b4
void Http::_UpdatePos(int to_skip)
Packit 8f70b4
{
Packit 8f70b4
   if(!inflate) {
Packit 8f70b4
      if(chunked)
Packit 8f70b4
	 chunk_pos+=to_skip;
Packit 8f70b4
      bytes_received+=to_skip;
Packit 8f70b4
   }
Packit 8f70b4
   real_pos+=to_skip;
Packit 8f70b4
}
Packit 8f70b4
int Http::_Read(Buffer *buf,int size)
Packit 8f70b4
{
Packit 8f70b4
   const char *buf1;
Packit 8f70b4
   int size1;
Packit 8f70b4
   Buffer *src_buf=conn->recv_buf.get_non_const();
Packit 8f70b4
get_again:
Packit 8f70b4
   if(conn->recv_buf->Size()==0 && conn->recv_buf->Error())
Packit 8f70b4
   {
Packit 8f70b4
      LogError(0,"recv: %s",conn->recv_buf->ErrorText());
Packit 8f70b4
      if(conn->recv_buf->ErrorFatal())
Packit 8f70b4
	 SetError(FATAL,conn->recv_buf->ErrorText());
Packit 8f70b4
      Disconnect();
Packit 8f70b4
      return DO_AGAIN;
Packit 8f70b4
   }
Packit 8f70b4
   conn->recv_buf->Get(&buf1,&size1);
Packit 8f70b4
   if(buf1==0) // eof
Packit 8f70b4
   {
Packit 8f70b4
      LogNote(9,_("Hit EOF"));
Packit 8f70b4
      if(bytes_received
Packit 8f70b4
      {
Packit 8f70b4
	 LogError(0,_("Received not enough data, retrying"));
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      }
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   if(!chunked)
Packit 8f70b4
   {
Packit 8f70b4
      if(body_size>=0 && bytes_received>=body_size
Packit 8f70b4
      && (!inflate || inflate->Size()==0))
Packit 8f70b4
      {
Packit 8f70b4
	 LogNote(9,_("Received all"));
Packit 8f70b4
	 return 0; // all received
Packit 8f70b4
      }
Packit 8f70b4
      if(entity_size>=0 && pos>=entity_size)
Packit 8f70b4
      {
Packit 8f70b4
	 LogNote(9,_("Received all (total)"));
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(size1==0 && (!inflate || inflate->Size()==0))
Packit 8f70b4
      return DO_AGAIN;
Packit 8f70b4
   if(chunked && size1>0)
Packit 8f70b4
   {
Packit 8f70b4
      if(chunked_trailer && state==RECEIVING_HEADER)
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      const char *nl;
Packit 8f70b4
      if(chunk_size==CHUNK_SIZE_UNKNOWN) // expecting first/next chunk
Packit 8f70b4
      {
Packit 8f70b4
	 nl=(const char*)memchr(buf1,'\n',size1);
Packit 8f70b4
	 if(nl==0)  // not yet
Packit 8f70b4
	 {
Packit 8f70b4
	 not_yet:
Packit 8f70b4
	    if(conn->recv_buf->Eof())
Packit 8f70b4
	       Disconnect();	 // connection closed too early
Packit 8f70b4
	    return DO_AGAIN;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!is_ascii_xdigit(*buf1)
Packit 8f70b4
	 || sscanf(buf1,"%lx",&chunk_size)!=1)
Packit 8f70b4
	 {
Packit 8f70b4
	    Fatal(_("chunked format violated"));
Packit 8f70b4
	    return FATAL;
Packit 8f70b4
	 }
Packit 8f70b4
	 conn->recv_buf->Skip(nl-buf1+1);
Packit 8f70b4
	 chunk_pos=0;
Packit 8f70b4
	 LogNote(9,"next chunk size: %ld",chunk_size);
Packit 8f70b4
	 goto get_again;
Packit 8f70b4
      }
Packit 8f70b4
      if(chunk_size==0) // eof
Packit 8f70b4
      {
Packit 8f70b4
	 LogNote(9,_("Received last chunk"));
Packit 8f70b4
	 // headers may follow
Packit 8f70b4
	 chunked_trailer=true;
Packit 8f70b4
	 state=RECEIVING_HEADER;
Packit 8f70b4
	 body_size=bytes_received;
Packit 8f70b4
	 Timeout(0);
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      }
Packit 8f70b4
      if(chunk_pos==chunk_size)
Packit 8f70b4
      {
Packit 8f70b4
	 if(size1<2)
Packit 8f70b4
	    goto not_yet;
Packit 8f70b4
	 if(buf1[0]!='\r' || buf1[1]!='\n')
Packit 8f70b4
	 {
Packit 8f70b4
	    Fatal(_("chunked format violated"));
Packit 8f70b4
	    return FATAL;
Packit 8f70b4
	 }
Packit 8f70b4
	 conn->recv_buf->Skip(2);
Packit 8f70b4
	 chunk_size=CHUNK_SIZE_UNKNOWN;
Packit 8f70b4
	 goto get_again;
Packit 8f70b4
      }
Packit 8f70b4
      // ok, now we may get portion of data
Packit 8f70b4
      if(size1>chunk_size-chunk_pos)
Packit 8f70b4
	 size1=chunk_size-chunk_pos;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!chunked)
Packit 8f70b4
   {
Packit 8f70b4
      // limit by body_size.
Packit 8f70b4
      if(body_size>=0 && size1+bytes_received>=body_size)
Packit 8f70b4
	 size1=body_size-bytes_received;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   int bytes_allowed=0x10000000;
Packit 8f70b4
   if(rate_limit)
Packit 8f70b4
      bytes_allowed=rate_limit->BytesAllowedToGet();
Packit 8f70b4
Packit 8f70b4
   if(inflate) {
Packit 8f70b4
      // do the inflation, if there are not enough inflated data
Packit 8f70b4
      if(size1>bytes_allowed)
Packit 8f70b4
	 size1=bytes_allowed;
Packit 8f70b4
      if(inflate->Size()<size && size1>0) {
Packit 8f70b4
	 inflate->PutTranslated(buf1,size1);
Packit 8f70b4
	 conn->recv_buf->Skip(size1);
Packit 8f70b4
	 if(chunked)
Packit 8f70b4
	    chunk_pos+=size1;
Packit 8f70b4
	 bytes_received+=size1;
Packit 8f70b4
	 if(inflate->Error())
Packit 8f70b4
	    SetError(FATAL,inflate->ErrorText());
Packit 8f70b4
      }
Packit 8f70b4
      inflate->Get(&buf1,&size1);
Packit 8f70b4
      src_buf=inflate.get_non_const();
Packit 8f70b4
   } else {
Packit 8f70b4
      if(size1>bytes_allowed)
Packit 8f70b4
	 size1=bytes_allowed;
Packit 8f70b4
   }
Packit 8f70b4
   if(size1==0)
Packit 8f70b4
      return DO_AGAIN;
Packit 8f70b4
   if(norest_manual && real_pos==0 && pos>0)
Packit 8f70b4
      return DO_AGAIN;
Packit 8f70b4
   if(real_pos
Packit 8f70b4
   {
Packit 8f70b4
      off_t to_skip=pos-real_pos;
Packit 8f70b4
      if(to_skip>size1)
Packit 8f70b4
	 to_skip=size1;
Packit 8f70b4
      _Skip(to_skip);
Packit 8f70b4
      goto get_again;
Packit 8f70b4
   }
Packit 8f70b4
   if(size>size1)
Packit 8f70b4
      size=size1;
Packit 8f70b4
   size=buf->MoveDataHere(src_buf,size);
Packit 8f70b4
   _UpdatePos(size);
Packit 8f70b4
   return size;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Http::Done()
Packit 8f70b4
{
Packit 8f70b4
   if(mode==CLOSED)
Packit 8f70b4
      return OK;
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return error_code;
Packit 8f70b4
   if(state==DONE)
Packit 8f70b4
      return OK;
Packit 8f70b4
   if(mode==CONNECT_VERIFY && (peer || conn))
Packit 8f70b4
      return OK;
Packit 8f70b4
   if((mode==REMOVE || mode==REMOVE_DIR || mode==RENAME)
Packit 8f70b4
   && state==RECEIVING_BODY)
Packit 8f70b4
      return OK;
Packit 8f70b4
   return IN_PROGRESS;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Http::Buffered()
Packit 8f70b4
{
Packit 8f70b4
   if(!ModeIs(STORE) || !conn || !conn->send_buf)
Packit 8f70b4
      return 0;
Packit 8f70b4
   return conn->send_buf->Size()+SocketBuffered(conn->sock);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Http::Write(const void *buf,int size)
Packit 8f70b4
{
Packit 8f70b4
   if(!ModeIs(STORE))
Packit 8f70b4
      return(0);
Packit 8f70b4
Packit 8f70b4
   Resume();
Packit 8f70b4
   Do();
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return(error_code);
Packit 8f70b4
Packit 8f70b4
   if(state!=RECEIVING_HEADER || status!=0 || conn->send_buf->Size()!=0)
Packit 8f70b4
      return DO_AGAIN;
Packit 8f70b4
Packit 8f70b4
   {
Packit 8f70b4
      int allowed=rate_limit->BytesAllowedToPut();
Packit 8f70b4
      if(allowed==0)
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      if(size>allowed)
Packit 8f70b4
	 size=allowed;
Packit 8f70b4
   }
Packit 8f70b4
   if(size+conn->send_buf->Size()>=max_buf)
Packit 8f70b4
      size=max_buf-conn->send_buf->Size();
Packit 8f70b4
   if(entity_size!=NO_SIZE && pos+size>entity_size)
Packit 8f70b4
   {
Packit 8f70b4
      size=entity_size-pos;
Packit 8f70b4
      // tried to write more than originally requested. Make it retry with Open:
Packit 8f70b4
      if(size==0)
Packit 8f70b4
	 return STORE_FAILED;
Packit 8f70b4
   }
Packit 8f70b4
   if(size<=0)
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   conn->send_buf->Put((const char*)buf,size);
Packit 8f70b4
Packit 8f70b4
   if(retries>0 && conn->send_buf->GetPos()-conn->send_buf->Size()>Buffered()+0x1000)
Packit 8f70b4
      TrySuccess();
Packit 8f70b4
   rate_limit->BytesPut(size);
Packit 8f70b4
   pos+=size;
Packit 8f70b4
   real_pos+=size;
Packit 8f70b4
   return(size);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Http::SendEOT()
Packit 8f70b4
{
Packit 8f70b4
   if(sent_eot)
Packit 8f70b4
      return OK;
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return(error_code);
Packit 8f70b4
   if(ModeIs(STORE))
Packit 8f70b4
   {
Packit 8f70b4
      if(state==RECEIVING_HEADER && conn->send_buf->Size()==0)
Packit 8f70b4
      {
Packit 8f70b4
	 if(entity_size==NO_SIZE || pos
Packit 8f70b4
	 {
Packit 8f70b4
	    shutdown(conn->sock,1);
Packit 8f70b4
	    keep_alive=false;
Packit 8f70b4
	 }
Packit 8f70b4
	 sent_eot=true;
Packit 8f70b4
	 return(OK);
Packit 8f70b4
      }
Packit 8f70b4
      return(DO_AGAIN);
Packit 8f70b4
   }
Packit 8f70b4
   return(OK);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Http::StoreStatus()
Packit 8f70b4
{
Packit 8f70b4
   if(!sent_eot && state==RECEIVING_HEADER)
Packit 8f70b4
      SendEOT();
Packit 8f70b4
   return Done();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *Http::CurrentStatus()
Packit 8f70b4
{
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case DISCONNECTED:
Packit 8f70b4
      if(hostname)
Packit 8f70b4
      {
Packit 8f70b4
	 if(resolver)
Packit 8f70b4
	    return(_("Resolving host address..."));
Packit 8f70b4
	 if(!ReconnectAllowed())
Packit 8f70b4
	    return DelayingMessage();
Packit 8f70b4
      }
Packit 8f70b4
      return "";
Packit 8f70b4
   case CONNECTING:
Packit 8f70b4
      return(_("Connecting..."));
Packit 8f70b4
   case CONNECTED:
Packit 8f70b4
      return(_("Connection idle"));
Packit 8f70b4
   case RECEIVING_HEADER:
Packit 8f70b4
      if(ModeIs(STORE) && !sent_eot && !status)
Packit 8f70b4
	 return(_("Sending data"));
Packit 8f70b4
      if(tunnel_state==TUNNEL_WAITING)
Packit 8f70b4
	 return(_("Connecting..."));
Packit 8f70b4
      if(!status)
Packit 8f70b4
	 return(_("Waiting for response..."));
Packit 8f70b4
      return(_("Fetching headers..."));
Packit 8f70b4
   case RECEIVING_BODY:
Packit 8f70b4
      return(_("Receiving data"));
Packit 8f70b4
   case DONE:
Packit 8f70b4
      return "";
Packit 8f70b4
   }
Packit 8f70b4
   abort();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::Reconfig(const char *name)
Packit 8f70b4
{
Packit 8f70b4
   const char *c=hostname;
Packit 8f70b4
Packit 8f70b4
   super::Reconfig(name);
Packit 8f70b4
Packit 8f70b4
   no_cache = !QueryBool("cache",c);
Packit 8f70b4
   if(!hftp && NoProxy(hostname))
Packit 8f70b4
      SetProxy(0);
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      const char *p=0;
Packit 8f70b4
      if(hftp && vproto && !strcmp(vproto,"ftp"))
Packit 8f70b4
      {
Packit 8f70b4
	 p=ResMgr::Query("ftp:proxy",c);
Packit 8f70b4
	 if(p && strncmp(p,"http://",7) && strncmp(p,"https://",8))
Packit 8f70b4
	    p=0;
Packit 8f70b4
      }
Packit 8f70b4
      if(!p)
Packit 8f70b4
      {
Packit 8f70b4
	 if(https)
Packit 8f70b4
	    p=ResMgr::Query("https:proxy",c);
Packit 8f70b4
	 else
Packit 8f70b4
	    p=Query("proxy",c);
Packit 8f70b4
	 // if no hftp:proxy is specified, try http:proxy.
Packit 8f70b4
	 if(hftp && !p)
Packit 8f70b4
	    p=ResMgr::Query("http:proxy",c);
Packit 8f70b4
      }
Packit 8f70b4
      SetProxy(p);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(conn)
Packit 8f70b4
      SetSocketBuffer(conn->sock);
Packit 8f70b4
   if(proxy && proxy_port==0)
Packit 8f70b4
      proxy_port.set(HTTP_DEFAULT_PROXY_PORT);
Packit 8f70b4
Packit 8f70b4
   user_agent=ResMgr::Query("http:user-agent",c);
Packit 8f70b4
   use_propfind_now=(use_propfind_now && QueryBool("use-propfind",c));
Packit 8f70b4
   no_ranges=(no_ranges || !QueryBool("use-range",hostname));
Packit 8f70b4
Packit 8f70b4
   if(QueryBool("use-allprop",c)) {
Packit 8f70b4
      allprop.set(   // PROPFIND request
Packit 8f70b4
	 ""
Packit 8f70b4
	 "<propfind xmlns=\"DAV:\">"
Packit 8f70b4
	   "<allprop/>"
Packit 8f70b4
	 "</propfind>\r\n");
Packit 8f70b4
   } else {
Packit 8f70b4
      allprop.unset();
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!user || !pass) {
Packit 8f70b4
      // get auth info from http:authorization setting
Packit 8f70b4
      const char *auth_c=Query("authorization",hostname);
Packit 8f70b4
      if(auth_c && *auth_c) {
Packit 8f70b4
	 char *auth=alloca_strdup(auth_c);
Packit 8f70b4
	 char *colon=strchr(auth,':');
Packit 8f70b4
	 if(colon) {
Packit 8f70b4
	    *colon=0;
Packit 8f70b4
	    auth_user.set(auth);
Packit 8f70b4
	    auth_pass.set(colon+1);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Http::SameSiteAs(const FileAccess *fa) const
Packit 8f70b4
{
Packit 8f70b4
   if(!SameProtoAs(fa))
Packit 8f70b4
      return false;
Packit 8f70b4
   Http *o=(Http*)fa;
Packit 8f70b4
   return(!xstrcasecmp(hostname,o->hostname) && !xstrcmp(portname,o->portname)
Packit 8f70b4
   && !xstrcmp(user,o->user) && !xstrcmp(pass,o->pass));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Http::SameLocationAs(const FileAccess *fa) const
Packit 8f70b4
{
Packit 8f70b4
   if(!SameSiteAs(fa))
Packit 8f70b4
      return false;
Packit 8f70b4
   Http *o=(Http*)fa;
Packit 8f70b4
   if(cwd!=o->cwd)
Packit 8f70b4
      return false;
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::ResetLocationData()
Packit 8f70b4
{
Packit 8f70b4
   super::ResetLocationData();
Packit 8f70b4
   Reconfig();
Packit 8f70b4
   state=DISCONNECTED;
Packit 8f70b4
   use_propfind_now=QueryBool("use-propfind",hostname);
Packit 8f70b4
   no_ranges=!QueryBool("use-range",hostname);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
DirList *Http::MakeDirList(ArgV *args)
Packit 8f70b4
{
Packit 8f70b4
   return new HttpDirList(this,args);
Packit 8f70b4
}
Packit 8f70b4
#include "FileGlob.h"
Packit 8f70b4
Glob *Http::MakeGlob(const char *pattern)
Packit 8f70b4
{
Packit 8f70b4
   return new GenericGlob(this,pattern);
Packit 8f70b4
}
Packit 8f70b4
ListInfo *Http::MakeListInfo(const char *path)
Packit 8f70b4
{
Packit 8f70b4
   return new HttpListInfo(this,path);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Http::CookieClosureMatch(const char *closure_c,
Packit 8f70b4
			      const char *hostname,const char *efile)
Packit 8f70b4
{
Packit 8f70b4
   if(!closure_c)
Packit 8f70b4
      return true;
Packit 8f70b4
   char *closure=alloca_strdup2(closure_c,1);
Packit 8f70b4
   char *path=0;
Packit 8f70b4
Packit 8f70b4
   char *scan=closure;
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      char *slash=strchr(scan,';');
Packit 8f70b4
      if(!slash)
Packit 8f70b4
	 break;
Packit 8f70b4
      *slash++=0;
Packit 8f70b4
      while(*slash && *slash==' ')
Packit 8f70b4
	 slash++;
Packit 8f70b4
      if(!strncmp(slash,"path=",5))
Packit 8f70b4
	 path=slash+5;
Packit 8f70b4
      else if(!strncmp(slash,"secure",6) && (slash[6]==';' || slash[6]==0))
Packit 8f70b4
      {
Packit 8f70b4
	 if(!https)
Packit 8f70b4
	    return false;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(closure[0] && 0!=fnmatch(closure,hostname,FNM_PATHNAME))
Packit 8f70b4
      return false;
Packit 8f70b4
   if(!path)
Packit 8f70b4
      return true;
Packit 8f70b4
   int path_len=strlen(path);
Packit 8f70b4
   if(path_len>0 && path[path_len-1]=='/')
Packit 8f70b4
      path_len--;
Packit 8f70b4
   if(!strncmp(efile,path,path_len)
Packit 8f70b4
   && (efile[path_len]==0 || efile[path_len]=='/'))
Packit 8f70b4
      return true;
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::CookieMerge(xstring &all,const char *cookie_c)
Packit 8f70b4
{
Packit 8f70b4
   char *value=alloca_strdup(cookie_c);
Packit 8f70b4
Packit 8f70b4
   for(char *entry=strtok(value,";"); entry; entry=strtok(0,";"))
Packit 8f70b4
   {
Packit 8f70b4
      if(*entry==' ')
Packit 8f70b4
	 entry++;
Packit 8f70b4
      if(*entry==0)
Packit 8f70b4
	 break;
Packit 8f70b4
      if(!strncasecmp(entry,"path=",5)
Packit 8f70b4
      || !strncasecmp(entry,"expires=",8)
Packit 8f70b4
      || !strncasecmp(entry,"domain=",7)
Packit 8f70b4
      || (!strncasecmp(entry,"secure",6)
Packit 8f70b4
	  && (entry[6]==' ' || entry[6]==0 || entry[6]==';')))
Packit 8f70b4
	 continue; // filter out path= expires= domain= secure
Packit 8f70b4
Packit 8f70b4
      char *c_name=entry;
Packit 8f70b4
      char *c_value=strchr(entry,'=');
Packit 8f70b4
      if(c_value)
Packit 8f70b4
	 *c_value++=0;
Packit 8f70b4
      else
Packit 8f70b4
	 c_value=c_name, c_name=0;
Packit 8f70b4
      int c_name_len=xstrlen(c_name);
Packit 8f70b4
Packit 8f70b4
      for(unsigned i=all.skip_all(0,' '); i
Packit 8f70b4
      {
Packit 8f70b4
	 const char *scan=all+i;
Packit 8f70b4
	 const char *semicolon=strchr(scan,';');
Packit 8f70b4
	 const char *eq=strchr(scan,'=');
Packit 8f70b4
	 if(semicolon && eq>semicolon)
Packit 8f70b4
	    eq=0;
Packit 8f70b4
	 if((eq==0 && c_name==0)
Packit 8f70b4
	 || (eq-scan==c_name_len && !strncmp(scan,c_name,c_name_len)))
Packit 8f70b4
	 {
Packit 8f70b4
	    // remove old cookie.
Packit 8f70b4
	    if(!semicolon)
Packit 8f70b4
	       all.truncate(i);
Packit 8f70b4
	    else
Packit 8f70b4
	       all.set_substr(i,all.skip_all(semicolon+1-all,' ')-i,"",0);
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!semicolon)
Packit 8f70b4
	    break;
Packit 8f70b4
	 i=semicolon+1-all;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      // append cookie.
Packit 8f70b4
      all.rtrim(' ');
Packit 8f70b4
      all.rtrim(';');
Packit 8f70b4
      int c_len=all.length();
Packit 8f70b4
      if(c_len>0 && all[c_len-1]!=';')
Packit 8f70b4
	 all.append("; ");
Packit 8f70b4
      if(c_name)
Packit 8f70b4
	 all.vappend(c_name,"=",c_value,NULL);
Packit 8f70b4
      else
Packit 8f70b4
	 all.append(c_value);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::MakeCookie(xstring &all_cookies,const char *hostname,const char *efile)
Packit 8f70b4
{
Packit 8f70b4
   Resource *scan=0;
Packit 8f70b4
   const char *closure;
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      const char *cookie=ResMgr::QueryNext("http:cookie",&closure,&scan;;
Packit 8f70b4
      if(cookie==0)
Packit 8f70b4
	 break;
Packit 8f70b4
      if(!CookieClosureMatch(closure,hostname,efile))
Packit 8f70b4
	 continue;
Packit 8f70b4
      CookieMerge(all_cookies,cookie);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::SetCookie(const char *value_const)
Packit 8f70b4
{
Packit 8f70b4
   char *value=alloca_strdup(value_const);
Packit 8f70b4
   const char *domain=hostname;
Packit 8f70b4
   const char *path=0;
Packit 8f70b4
   bool secure=false;
Packit 8f70b4
Packit 8f70b4
   for(char *entry=strtok(value,";"); entry; entry=strtok(0,";"))
Packit 8f70b4
   {
Packit 8f70b4
      while(*entry==' ')   // skip spaces.
Packit 8f70b4
	 entry++;
Packit 8f70b4
      if(*entry==0)
Packit 8f70b4
	 break;
Packit 8f70b4
Packit 8f70b4
      if(!strncasecmp(entry,"expires=",8))
Packit 8f70b4
	 continue; // not used yet (FIXME)
Packit 8f70b4
Packit 8f70b4
      if(!strncasecmp(entry,"secure",6)
Packit 8f70b4
      && (entry[6]==' ' || entry[6]==0 || entry[6]==';'))
Packit 8f70b4
      {
Packit 8f70b4
	 secure=true;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(!strncasecmp(entry,"path=",5))
Packit 8f70b4
      {
Packit 8f70b4
	 path=alloca_strdup(entry+5);
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(!strncasecmp(entry,"domain=",7))
Packit 8f70b4
      {
Packit 8f70b4
	 char *new_domain=alloca_strdup(entry+6);
Packit 8f70b4
	 if(new_domain[1]=='.')
Packit 8f70b4
	    new_domain[0]='*';
Packit 8f70b4
	 else
Packit 8f70b4
	    new_domain++;
Packit 8f70b4
	 char *end=strchr(new_domain,';');
Packit 8f70b4
	 if(end)
Packit 8f70b4
	    *end=0;
Packit 8f70b4
	 domain=new_domain;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   xstring closure(domain);
Packit 8f70b4
   if(path && path[0] && strcmp(path,"/"))
Packit 8f70b4
      closure.append(";path=").append(path);
Packit 8f70b4
   if(secure)
Packit 8f70b4
      closure.append(";secure");
Packit 8f70b4
Packit 8f70b4
   xstring c(Query("cookie",closure));
Packit 8f70b4
   CookieMerge(c,value_const);
Packit 8f70b4
   ResMgr::Set("http:cookie",closure,c);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#if USE_SSL
Packit 8f70b4
#undef super
Packit 8f70b4
#define super Http
Packit 8f70b4
Https::Https()
Packit 8f70b4
{
Packit 8f70b4
   https=true;
Packit 8f70b4
   res_prefix="http";
Packit 8f70b4
}
Packit 8f70b4
Https::~Https()
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
Https::Https(const Https *o) : super(o)
Packit 8f70b4
{
Packit 8f70b4
   https=true;
Packit 8f70b4
   res_prefix="http";
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
}
Packit 8f70b4
FileAccess *Https::New(){ return new Https();}
Packit 8f70b4
Packit 8f70b4
void Http::Connection::MakeSSLBuffers()
Packit 8f70b4
{
Packit 8f70b4
   ssl=new lftp_ssl(sock,lftp_ssl::CLIENT,closure);
Packit 8f70b4
   ssl->load_keys();
Packit 8f70b4
   IOBufferSSL *send_buf_ssl=new IOBufferSSL(ssl,IOBuffer::PUT);
Packit 8f70b4
   IOBufferSSL *recv_buf_ssl=new IOBufferSSL(ssl,IOBuffer::GET);
Packit 8f70b4
   send_buf=send_buf_ssl;
Packit 8f70b4
   recv_buf=recv_buf_ssl;
Packit 8f70b4
}
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#undef super
Packit 8f70b4
#define super Http
Packit 8f70b4
HFtp::HFtp()
Packit 8f70b4
{
Packit 8f70b4
   hftp=true;
Packit 8f70b4
   default_cwd="~";
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
}
Packit 8f70b4
HFtp::~HFtp()
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
HFtp::HFtp(const HFtp *o) : super(o)
Packit 8f70b4
{
Packit 8f70b4
   hftp=true;
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
}
Packit 8f70b4
void HFtp::Login(const char *u,const char *p)
Packit 8f70b4
{
Packit 8f70b4
   super::Login(u,p);
Packit 8f70b4
   if(u)
Packit 8f70b4
   {
Packit 8f70b4
      home.Set("~");
Packit 8f70b4
      cwd.Set(home,false,0);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void HFtp::Reconfig(const char *name)
Packit 8f70b4
{
Packit 8f70b4
   super::Reconfig(name);
Packit 8f70b4
   use_head=QueryBool("use-head");
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Http::LogErrorText()
Packit 8f70b4
{
Packit 8f70b4
   if(!conn || !conn->recv_buf)
Packit 8f70b4
      return;
Packit 8f70b4
   conn->recv_buf->Roll();
Packit 8f70b4
   int size=conn->recv_buf->Size();
Packit 8f70b4
   if(size==0)
Packit 8f70b4
      return;
Packit 8f70b4
   Buffer tmpbuf;
Packit 8f70b4
   size=_Read(&tmpbuf,size);
Packit 8f70b4
   if(size<=0)
Packit 8f70b4
      return;
Packit 8f70b4
   tmpbuf.SpaceAdd(size);
Packit 8f70b4
   const char *buf0=tmpbuf.Get();
Packit 8f70b4
   char *buf=alloca_strdup(buf0);
Packit 8f70b4
   remove_tags(buf);
Packit 8f70b4
   for(char *line=strtok(buf,"\n"); line; line=strtok(0,"\n")) {
Packit 8f70b4
      rtrim(line);
Packit 8f70b4
      if(*line)
Packit 8f70b4
	 Log::global->Format(4,"<--* %s\n",line);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
/* The functions http_atotm and check_end are taken from wget */
Packit 8f70b4
#define ISSPACE(c) is_ascii_space((c))
Packit 8f70b4
#define ISDIGIT(c) is_ascii_digit((c))
Packit 8f70b4
Packit 8f70b4
/* Check whether the result of strptime() indicates success.
Packit 8f70b4
   strptime() returns the pointer to how far it got to in the string.
Packit 8f70b4
   The processing has been successful if the string is at `GMT' or
Packit 8f70b4
   `+X', or at the end of the string.
Packit 8f70b4
Packit 8f70b4
   In extended regexp parlance, the function returns 1 if P matches
Packit 8f70b4
   "^ *(GMT|[+-][0-9]|$)", 0 otherwise.  P being NULL (a valid result of
Packit 8f70b4
   strptime()) is considered a failure and 0 is returned.  */
Packit 8f70b4
static int
Packit 8f70b4
check_end (const char *p)
Packit 8f70b4
{
Packit 8f70b4
  if (!p)
Packit 8f70b4
    return 0;
Packit 8f70b4
  while (ISSPACE (*p))
Packit 8f70b4
    ++p;
Packit 8f70b4
  if (!*p
Packit 8f70b4
      || (p[0] == 'G' && p[1] == 'M' && p[2] == 'T')
Packit 8f70b4
      || (p[0] == 'U' && p[1] == 'T' && p[2] == 'C')
Packit 8f70b4
      || ((p[0] == '+' || p[1] == '-') && ISDIGIT (p[1])))
Packit 8f70b4
    return 1;
Packit 8f70b4
  else
Packit 8f70b4
    return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* Convert TIME_STRING time to time_t.  TIME_STRING can be in any of
Packit 8f70b4
   the three formats RFC2068 allows the HTTP servers to emit --
Packit 8f70b4
   RFC1123-date, RFC850-date or asctime-date.  Timezones are ignored,
Packit 8f70b4
   and should be GMT.
Packit 8f70b4
Packit 8f70b4
   We use strptime() to recognize various dates, which makes it a
Packit 8f70b4
   little bit slacker than the RFC1123/RFC850/asctime (e.g. it always
Packit 8f70b4
   allows shortened dates and months, one-digit days, etc.).  It also
Packit 8f70b4
   allows more than one space anywhere where the specs require one SP.
Packit 8f70b4
   The routine should probably be even more forgiving (as recommended
Packit 8f70b4
   by RFC2068), but I do not have the time to write one.
Packit 8f70b4
Packit 8f70b4
   Return the computed time_t representation, or ATOTM_ERROR if all the
Packit 8f70b4
   schemes fail.
Packit 8f70b4
Packit 8f70b4
   Needless to say, what we *really* need here is something like
Packit 8f70b4
   Marcus Hennecke's atotm(), which is forgiving, fast, to-the-point,
Packit 8f70b4
   and does not use strptime().  atotm() is to be found in the sources
Packit 8f70b4
   of `phttpd', a little-known HTTP server written by Peter Erikson.  */
Packit 8f70b4
time_t
Packit 8f70b4
Http::atotm (const char *time_string)
Packit 8f70b4
{
Packit 8f70b4
  struct tm t;
Packit 8f70b4
Packit 8f70b4
  /* Roger Beeman says: "This function dynamically allocates struct tm
Packit 8f70b4
     t, but does no initialization.  The only field that actually
Packit 8f70b4
     needs initialization is tm_isdst, since the others will be set by
Packit 8f70b4
     strptime.  Since strptime does not set tm_isdst, it will return
Packit 8f70b4
     the data structure with whatever data was in tm_isdst to begin
Packit 8f70b4
     with.  For those of us in timezones where DST can occur, there
Packit 8f70b4
     can be a one hour shift depending on the previous contents of the
Packit 8f70b4
     data area where the data structure is allocated."  */
Packit 8f70b4
  t.tm_isdst = -1;
Packit 8f70b4
Packit 8f70b4
  /* Note that under foreign locales Solaris strptime() fails to
Packit 8f70b4
     recognize English dates, which renders this function useless.  I
Packit 8f70b4
     assume that other non-GNU strptime's are plagued by the same
Packit 8f70b4
     disease.  We solve this by setting only LC_MESSAGES in
Packit 8f70b4
     i18n_initialize(), instead of LC_ALL.
Packit 8f70b4
Packit 8f70b4
     Another solution could be to temporarily set locale to C, invoke
Packit 8f70b4
     strptime(), and restore it back.  This is slow and dirty,
Packit 8f70b4
     however, and locale support other than LC_MESSAGES can mess other
Packit 8f70b4
     things, so I rather chose to stick with just setting LC_MESSAGES.
Packit 8f70b4
Packit 8f70b4
     Also note that none of this is necessary under GNU strptime(),
Packit 8f70b4
     because it recognizes both international and local dates.  */
Packit 8f70b4
Packit 8f70b4
  /* NOTE: We don't use `%n' for white space, as OSF's strptime uses
Packit 8f70b4
     it to eat all white space up to (and including) a newline, and
Packit 8f70b4
     the function fails if there is no newline (!).
Packit 8f70b4
Packit 8f70b4
     Let's hope all strptime() implementations use ` ' to skip *all*
Packit 8f70b4
     whitespace instead of just one (it works that way on all the
Packit 8f70b4
     systems I've tested it on).  */
Packit 8f70b4
Packit 8f70b4
   time_t ut=ATOTM_ERROR;
Packit 8f70b4
Packit 8f70b4
   setlocale(LC_TIME,"C"); // we need english month and week day names
Packit 8f70b4
Packit 8f70b4
   /* RFC1123: Thu, 29 Jan 1998 22:12:57 */
Packit 8f70b4
   if (check_end (strptime (time_string, "%a, %d %b %Y %T", &t)))
Packit 8f70b4
      ut=mktime_from_utc (&t);
Packit 8f70b4
   /* RFC850:  Thu, 29-Jan-98 22:12:57 */
Packit 8f70b4
   else if (check_end (strptime (time_string, "%a, %d-%b-%y %T", &t)))
Packit 8f70b4
      ut=mktime_from_utc (&t);
Packit 8f70b4
   /* asctime: Thu Jan 29 22:12:57 1998 */
Packit 8f70b4
   else if (check_end (strptime (time_string, "%a %b %d %T %Y", &t)))
Packit 8f70b4
      ut=mktime_from_utc (&t);
Packit 8f70b4
Packit 8f70b4
   setlocale(LC_TIME,"");  // restore locale
Packit 8f70b4
Packit 8f70b4
   return ut;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Http::IsCompressed(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   static const char *const values[] = {
Packit 8f70b4
      "x-gzip", "gzip", "deflate", "compress", "x-compress", NULL
Packit 8f70b4
   };
Packit 8f70b4
   for(const char *const *v=values; *v; v++)
Packit 8f70b4
      if(!strcmp(s,*v))
Packit 8f70b4
	 return true;
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Http::CompressedContentEncoding() const
Packit 8f70b4
{
Packit 8f70b4
   return content_encoding && IsCompressed(content_encoding);
Packit 8f70b4
}
Packit 8f70b4
bool Http::CompressedContentType() const
Packit 8f70b4
{
Packit 8f70b4
   if(file.ends_with(".gz") || file.ends_with(".Z") || file.ends_with(".tgz"))
Packit 8f70b4
      return true;
Packit 8f70b4
   static const char app[]="application/";
Packit 8f70b4
   return entity_content_type && entity_content_type.begins_with(app)
Packit 8f70b4
      && IsCompressed(entity_content_type+sizeof(app)-1);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#include "modconfig.h"
Packit 8f70b4
#ifdef MODULE_PROTO_HTTP
Packit 8f70b4
void module_init()
Packit 8f70b4
{
Packit 8f70b4
   Http::ClassInit();
Packit 8f70b4
}
Packit 8f70b4
#endif