// DownloadHttp.cpp: implementation of the CDownloadHttp class.

//

//

#include "stdafx.h"

#include "downtest.h"

#include "DownloadHttp.h"

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif

void DownloadNotify ( int nIndex, UINT nNotityType, LPVOID lpNotifyData, LPVOID pDownloadMTR );

//

// Construction/Destruction

//

CDownloadHttp::CDownloadHttp()

{

}

CDownloadHttp::~CDownloadHttp()

{

}

BOOL CDownloadHttp::DownloadOnce()

{

 // 不需要下载了

 int nWillDownloadSize = Get_WillDownloadSize();    // 本次应该下载的字节数

 int nDownloadedSize = Get_DownloadedSize ();    // 已下载字节数

 if ( nWillDownloadSize > 0 && nDownloadedSize >= nWillDownloadSize )

  return DownloadEnd(TRUE);

 if ( !CDownloadPub::DownloadOnce () )

  return DownloadEnd(FALSE);

 char szTailData[NET_BUFFER_SIZE] = {0};

 int nTailSize = sizeof(szTailData);

 if ( !RequestHttpData ( TRUE, szTailData, &nTailSize ) )

  return DownloadEnd(FALSE);

 // 从HTTP服务器中读取数据,并保存到文件中

 return DownloadEnd ( RecvDataAndSaveToFile(m_SocketClient,szTailData, nTailSize) );

}

BOOL CDownloadHttp::RequestHttpData(BOOL bGet, char *szTailData/*=NULL*/, int *pnTailSize/*=NULL*/ )

{

 int nTailSizeTemp = 0;

 BOOL bRetryRequest = TRUE;

 while ( bRetryRequest )

 {

  CString csReq = GetRequestStr ( bGet );

  CString csResponse;

  nTailSizeTemp = pnTailSize?(*pnTailSize):0;

  if ( !SendRequest ( csReq, csResponse, szTailData, &nTailSizeTemp ) )

   return FALSE;

  CString csReferer_Old = m_csReferer;

  CString csDownloadUrl_Old = m_csDownloadUrl;

  CString csServer_Old = m_csServer;

  CString csObject_Old = m_csObject;

  USHORT nPort_Old = m_nPort;

  CString csProtocolType_Old = m_csProtocolType;

  if ( !ParseResponseString ( csResponse, bRetryRequest ) )

  {

   if ( !m_csCookieFlag.IsEmpty () )

   {

    m_csCookieFlag.Empty();

    return FALSE;

   }

   m_csReferer = csReferer_Old;

   m_csDownloadUrl = csDownloadUrl_Old;

   m_csServer = csServer_Old;

   m_csObject = csObject_Old;

   m_nPort = nPort_Old;

   m_csProtocolType = csProtocolType_Old;

   m_csCookieFlag = "Flag=UUIISPoweredByUUSoft";

   bRetryRequest = TRUE;

  }

 }

 if ( pnTailSize )

  *pnTailSize = nTailSizeTemp;

 return TRUE;

}

//

// 获取远程站点信息,如:是否支持断点续传、要下载的文件大小和创建时间等

//

BOOL CDownloadHttp::GetRemoteSiteInfo_Pro()

{

 BOOL bRet = FALSE;

 if ( !CDownloadPub::GetRemoteSiteInfo_Pro() )

  goto finished;

 if ( !RequestHttpData ( TRUE ) )

  goto finished;

 bRet = TRUE;

finished:

 return bRet;

}

CString CDownloadHttp::GetRequestStr(BOOL bGet)

{

 CString strVerb;

 if( bGet )

  strVerb = _T("GET ");

 else

  strVerb = _T("HEAD ");


 CString csReq, strAuth, strRange;

 csReq  = strVerb  + m_csObject + " HTTP/1.1/r/n";

 if ( !m_csUsername.IsEmpty () )

 {

  strAuth = _T("");

  Base64Encode ( m_csUsername + ":" + m_csPassword, strAuth );

  csReq += "Authorization: Basic " + strAuth + "/r/n";

 }


 CString csPort;

 if ( m_nPort != DEFAULT_HTTP_PORT )

  csPort.Format ( ":%d", m_nPort );

 csReq += "Host: " + m_csServer + csPort + "/r/n";

 csReq += "Accept: */*/r/n";

 csReq += "Pragma: no-cache/r/n";

 csReq += "Cache-Control: no-cache/r/n";

 csReq += "User-Agent: "+m_csUserAgent+"/r/n";

 if( m_csReferer.IsEmpty() )

 {

  m_csReferer = GetRefererFromURL ();

 }

 csReq += "Referer: "+m_csReferer+"/r/n";

 csReq += "Connection: close/r/n";

 if ( !m_csCookieFlag.IsEmpty() )

 {

  csReq += "Cookie: " + m_csCookieFlag + "/r/n";

 }


 // 指定要下载的文件范围

 CString csEndPos;

 int nWillDownloadStartPos = Get_WillDownloadStartPos (); // 开始位置

 int nWillDownloadSize = Get_WillDownloadSize();    // 本次应该下载的字节数

 int nDownloadedSize = Get_DownloadedSize ();    // 已下载字节数

 if ( nWillDownloadSize > 0 )

  csEndPos.Format ( "%d", nWillDownloadStartPos+nWillDownloadSize-1 );

 ASSERT ( nWillDownloadSize < 0 || nDownloadedSize < nWillDownloadSize );

 strRange.Format ( _T("Range: bytes=%d-%s/r/n"), nWillDownloadStartPos+nDownloadedSize, csEndPos );

 csReq += strRange;

 csReq += "/r/n";


 return csReq;

}

//

// 向服务器提交请求,并得到返回字符串

//

BOOL CDownloadHttp::SendRequest(LPCTSTR lpszReq, CString &csResponse, char *szTailData/*=NULL*/, int *pnTailSize/*=NULL*/ )

{

 m_SocketClient.Disconnect ();

 if ( !Connect () ) return FALSE;

 if ( !m_SocketClient.SendString ( lpszReq ) )

 {

  return FALSE;

 }

 for ( int i=0; ; i++ )

 {

  char szRecvBuf[NET_BUFFER_SIZE] = {0};

  int nReadSize = m_SocketClient.Receive ( szRecvBuf, sizeof(szRecvBuf) );

  if ( nReadSize <= 0 )

  {

   Log ( L_WARNING, "(%d) Receive response data failed", m_nIndex );

   return FALSE;

  }

  csResponse += szRecvBuf;

  char *p = strstr ( szRecvBuf, "/r/n/r/n" );

  if ( p )

  {

   if ( szTailData && pnTailSize && *pnTailSize > 0 )

   {

    p += 4;

    int nOtioseSize = nReadSize - int( p - szRecvBuf );

    *pnTailSize = MIN ( nOtioseSize, *pnTailSize );

    memcpy ( szTailData, p, *pnTailSize );

   }

#ifdef _DEBUG

   int nPos = csResponse.Find ( "/r/n/r/n", 0 );

   CString csDump;

   if ( nPos >= 0 ) csDump = csResponse.Left ( nPos );

   else csDump = csResponse;

   Log ( L_NORMAL, "(%d) HTTP server response : /r/n<<<++++++++++++++++++++++++/r/n%s/r/n<<<++++++++++++++++++++++++",

    m_nIndex, csDump );

#endif

   break;

  }

 }

 return TRUE;

}

DWORD CDownloadHttp::GetResponseCode(CString csLineText)

{

 csLineText.MakeLower ();

 ASSERT ( csLineText.Find ( "http/", 0 ) >= 0 );

 int nPos = csLineText.Find ( " ", 0 );

 if ( nPos < 0 ) return 0;

 CString csCode = csLineText.Mid ( nPos + 1 );

 csCode.TrimLeft(); csCode.TrimRight();

 nPos = csCode.Find ( " ", 0 );

 if ( nPos < 0 ) nPos = csCode.GetLength() - 1;

 csCode = csCode.Left ( nPos );

 return (DWORD)atoi(csCode);

}

BOOL CDownloadHttp::ParseResponseString ( CString csResponseString, OUT BOOL &bRetryRequest )

{

 bRetryRequest = FALSE;

 // 获取返回代码

 CString csOneLine = GetOneLine ( csResponseString );

 DWORD dwResponseCode = GetResponseCode ( csOneLine );

 if ( dwResponseCode < 1 )

 {

  Log ( L_WARNING, "(%d) Received error response code : %s", m_nIndex, csOneLine );

  return FALSE;

 }


 int nPos = 0;

 // 请求文件被重定向

 if( dwResponseCode >= 300 && dwResponseCode < 400 )

 {

  bRetryRequest = TRUE;

  // 得到请求文件新的URL

  CString csRedirectFileName = FindAfterFlagString ( "location:", csResponseString );


  // 设置 Referer

  m_csReferer = GetRefererFromURL ();


  // 重定向到其他的服务器

  nPos = csRedirectFileName.Find("://");

  if ( nPos >= 0 )

  {

   m_csDownloadUrl = csRedirectFileName;

   // 检验要下载的URL是否有效

   if ( !ParseURL ( m_csDownloadUrl, m_csServer, m_csObject, m_nPort, m_csProtocolType ) )

   {

    Log ( L_WARNING, "(%d) Redirect media path [%s] invalid", m_nIndex, m_csDownloadUrl );

    return FALSE;

   }

   return TRUE;

  }


  // 重定向到本服务器的其他地方

  csRedirectFileName.Replace ( "//", "/" );

  // 重定向于根目录

  if( csRedirectFileName[0] == '/' )

  {

   m_csObject = csRedirectFileName;

   DownloadNotify ( -1, NOTIFY_TYPE_GOT_REMOTE_FILENAME, (LPVOID)(LPCTSTR)(GetDownloadObjectFileName()), m_pDownloadMTR );

   return TRUE;

  }


  // 定向于相对当前目录

  int nParentDirCount = 0;

  nPos = csRedirectFileName.Find ( "../" );

  while ( nPos >= 0 )

  {

   csRedirectFileName = csRedirectFileName.Mid(nPos+3);

   nParentDirCount++;

   nPos = csRedirectFileName.Find("../");

  }

  for (int i=0; i<=nParentDirCount; i++)

  {

   nPos = m_csDownloadUrl.ReverseFind('/');

   if (nPos != -1)

    m_csDownloadUrl = m_csDownloadUrl.Left(nPos);

  }

  if ( csRedirectFileName.Find ( "./", 0 ) == 0 )

   csRedirectFileName.Delete ( 0, 2 );

  m_csDownloadUrl = m_csDownloadUrl+"/"+csRedirectFileName;


  return ParseURL ( m_csDownloadUrl, m_csServer, m_csObject, m_nPort, m_csProtocolType );

 }

 // 请求被成功接收、理解和接受

 else if( dwResponseCode >= 200 && dwResponseCode < 300 )

 {

  if ( m_nIndex == -1 ) // 主线程才需要获取文件大小的信息

  {

   // 获取 Content-Length

   CString csDownFileLen = FindAfterFlagString ( "content-length:", csResponseString );

   m_nFileTotalSize = (int) _ttoi( (LPCTSTR)csDownFileLen );

   DownloadNotify ( -1, NOTIFY_TYPE_GOT_REMOTE_FILESIZE, (LPVOID)m_nFileTotalSize, m_pDownloadMTR );

   int nWillDownloadStartPos = Get_WillDownloadStartPos (); // 开始位置

   int nWillDownloadSize = Get_WillDownloadSize();    // 本次应该下载的字节数

   int nDownloadedSize = Get_DownloadedSize ();    // 已下载字节数

   if ( m_nFileTotalSize > 0 && nWillDownloadSize-nDownloadedSize > m_nFileTotalSize )

    Set_WillDownloadSize ( m_nFileTotalSize-nDownloadedSize );

  }


  // 获取服务器文件的最后修改时间

  CString csModifiedTime = FindAfterFlagString ( "last-modified:", csResponseString );

  if ( !csModifiedTime.IsEmpty() )

  {

   m_TimeLastModified = ConvertHttpTimeString(csModifiedTime);

  }

  if ( dwResponseCode == 206 ) // 支持断点续传

  {

   m_bSupportResume = TRUE;

  }

  else       // 不支持断点续传

  {

   m_bSupportResume = FALSE;

  }

  return TRUE;

 }

// Log ( L_WARNING, "(%d) Receive invalid code : %d", m_nIndex, dwResponseCode );

 return FALSE;

}

CString CDownloadHttp::FindAfterFlagString(LPCTSTR lpszFoundStr, CString csOrg)

{

 ASSERT ( lpszFoundStr && strlen(lpszFoundStr) > 0 );

 CString csReturing, csFoundStr = GET_SAFE_STRING(lpszFoundStr);

 csFoundStr.MakeLower ();

 CString csOrgLower = csOrg;

 csOrgLower.MakeLower ();

 int nPos = csOrgLower.Find ( csFoundStr );

 if ( nPos < 0 ) return "";

 csReturing = csOrg.Mid ( nPos + csFoundStr.GetLength() );

 nPos = csReturing.Find("/r/n");

 if ( nPos < 0 ) return "";

 csReturing = csReturing.Left(nPos);

 csReturing.TrimLeft();

 csReturing.TrimRight();

 return csReturing;

}

//

// 将 HTTP 服务器表示的时间转换为 CTime 格式,如:Wed, 16 May 2007 14:29:53 GMT

//

CTime CDownloadHttp::ConvertHttpTimeString(CString csTimeGMT)

{

 CString csYear, csMonth, csDay, csTime;

 CTime tReturning = -1;

 int nPos = csTimeGMT.Find ( ",", 0 );

 if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )

  return tReturning;

 csTimeGMT = csTimeGMT.Mid ( nPos + 1 );

 csTimeGMT.TrimLeft(); csTimeGMT.TrimRight ();

 // 日

 nPos = csTimeGMT.Find ( " ", 0 );

 if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )

  return tReturning;

 csDay = csTimeGMT.Left ( nPos );

 csTimeGMT = csTimeGMT.Mid ( nPos + 1 );

 csTimeGMT.TrimLeft(); csTimeGMT.TrimRight ();

 // 月

 nPos = csTimeGMT.Find ( " ", 0 );

 if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )

  return tReturning;

 csMonth = csTimeGMT.Left ( nPos );

 int nMonth = GetMouthByShortStr ( csMonth );

 ASSERT ( nMonth >= 1 && nMonth <= 12 );

 csMonth.Format ( "%02d", nMonth );

 csTimeGMT = csTimeGMT.Mid ( nPos + 1 );

 csTimeGMT.TrimLeft(); csTimeGMT.TrimRight ();

 // 年

 nPos = csTimeGMT.Find ( " ", 0 );

 if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )

  return tReturning;

 csYear = csTimeGMT.Left ( nPos );

 csTimeGMT = csTimeGMT.Mid ( nPos + 1 );

 csTimeGMT.TrimLeft(); csTimeGMT.TrimRight ();

 // 时间

 nPos = csTimeGMT.Find ( " ", 0 );

 if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )

  return tReturning;

 csTime = csTimeGMT.Left ( nPos );

 csTimeGMT = csTimeGMT.Mid ( nPos + 1 );

 CString csFileTimeInfo;

 csFileTimeInfo.Format ( "%s-%s-%s %s", csYear, csMonth, csDay, csTime );

 ConvertStrToCTime ( csFileTimeInfo.GetBuffer(0), tReturning );

 return tReturning;

}