Blame src/Http.cc

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