From 39e810e1b1d809e8fe920cf0f828258880ce2dda Mon Sep 17 00:00:00 2001 From: RD42 <42702181+dashr9230@users.noreply.github.com> Date: Wed, 25 Oct 2023 16:53:42 +0800 Subject: [PATCH] [announce] Implement CHttpClient --- announce/httpclient.cpp | 397 +++++++++++++++++++++++++++++++++++++--- announce/httpclient.h | 99 ++++++++-- 2 files changed, 457 insertions(+), 39 deletions(-) diff --git a/announce/httpclient.cpp b/announce/httpclient.cpp index 810e3e0..9b0396f 100644 --- a/announce/httpclient.cpp +++ b/announce/httpclient.cpp @@ -1,62 +1,413 @@ +#include +#include +#include + +#ifdef WIN32 +# include +#else +# include +# include +# include +# include +# include +# include +# include +#endif + #include "httpclient.h" +#include "runutil.h" -CHttpClient::CHttpClient(char *src) +//---------------------------------------------------- + +CHttpClient::CHttpClient(char *szBindAddress) { - // TODO: CHttpClient::CHttpClient W: 004010C0 L: 08048B74 + memset(&m_Request,0,sizeof(HTTP_REQUEST)); + memset(&m_Response,0,sizeof(HTTP_RESPONSE)); + + m_iHasBindAddress = 0; + memset(m_szBindAddress,0,sizeof(m_szBindAddress)); + if(szBindAddress) { + m_iHasBindAddress = 1; + strcpy(m_szBindAddress,szBindAddress); + } + + m_iError = HTTP_SUCCESS; // Request is successful until otherwise indicated + m_iSocket = (-1); + +// Winsock init +#ifdef WIN32 + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2,2); + WSAStartup(wVersionRequested,&wsaData); +#endif } +//---------------------------------------------------- + CHttpClient::~CHttpClient() { - // TODO: CHttpClient::~CHttpClient W: 00401150 L: 08048C2E +// Winsock cleanup +#ifdef WIN32 + WSACleanup(); +#endif } -int CHttpClient::ProcessURL(int, int, char *src, char *, char *) +//---------------------------------------------------- + +int CHttpClient::ProcessURL(int iType, char *szURL, char *szPostData, char *szReferer) { - // TODO: CHttpClient::ProcessURL W: 004018F0 L: 08048C34 - return 0; + InitRequest(iType,szURL,szPostData,szReferer); + + Process(); + + return m_iError; } -void CHttpClient::GetHeaderValue() +//---------------------------------------------------- + +bool CHttpClient::GetHeaderValue(char *szHeaderName,char *szReturnBuffer, int iBufSize) { - // TODO: CHttpClient::GetHeaderValue W: 00401160 L: 08048C78 + char *szHeaderStart; + char *szHeaderEnd; + int iLengthSearchHeader = strlen(szHeaderName); + int iCopyLength; + + szHeaderStart = Util_stristr(m_Response.header,szHeaderName); + if(!szHeaderStart) { + return false; + } + szHeaderStart+=iLengthSearchHeader+1; + + szHeaderEnd = strchr(szHeaderStart,'\n'); + if(!szHeaderEnd) { + szHeaderEnd = m_Response.header + strlen(m_Response.header); // (END OF STRING) + } + + iCopyLength = szHeaderEnd - szHeaderStart; + if(iBufSize < iCopyLength) { + return false; + } + + memcpy(szReturnBuffer,szHeaderStart,iCopyLength); + szReturnBuffer[iCopyLength] = '\0'; + return true; } -int CHttpClient::Connect(char *name, unsigned short hostshort, char *) +//---------------------------------------------------- + +bool CHttpClient::Connect(char *szHost, int iPort, char *szBindAddress) { - // TODO: CHttpClient::Connect W: 00401680 L: 08048D44 - return 1; + struct sockaddr_in sa, bind_sa; + struct hostent *hp, *bind_hp; + + // Hostname translation + if((hp=(struct hostent *)gethostbyname(szHost)) == NULL ) { + m_iError=HTTP_ERROR_BAD_HOST; + return false; + } + + // Prepare a socket + memset(&sa,0,sizeof(sa)); + memset(&bind_sa,0,sizeof(bind_sa)); + memcpy(&sa.sin_addr,hp->h_addr,hp->h_length); + sa.sin_family = hp->h_addrtype; + sa.sin_port = htons((unsigned short)iPort); + + if(szBindAddress) { + if((bind_hp=(struct hostent *)gethostbyname(szBindAddress)) == NULL ) { + m_iError=HTTP_ERROR_BAD_HOST; + return false; + } + memcpy(&bind_sa.sin_addr,bind_hp->h_addr,bind_hp->h_length); + bind_sa.sin_family = bind_hp->h_addrtype; + bind_sa.sin_port = 0; + } + + if((m_iSocket=socket(AF_INET,SOCK_STREAM,0)) < 0) { + m_iError=HTTP_ERROR_NO_SOCKET; + return false; + } + + if(szBindAddress && bind(m_iSocket,(struct sockaddr *)&bind_sa,sizeof bind_sa) < 0) { + m_iError=HTTP_ERROR_CANT_CONNECT; + return false; + } + + // Try to connect + if(connect(m_iSocket,(struct sockaddr *)&sa,sizeof sa) < 0) { + CloseConnection(); + m_iError=HTTP_ERROR_CANT_CONNECT; + return false; + } + + return true; } +//---------------------------------------------------- + void CHttpClient::CloseConnection() { - // TODO: CHttpClient::CloseConnection L: 08048F22 +#ifdef WIN32 + closesocket(m_iSocket); +#else + close(m_iSocket); +#endif } -int CHttpClient::Send(int, char *s) +//---------------------------------------------------- + +bool CHttpClient::Send(char *szData) { - // TODO: CHttpClient::Send L: 08048F38 - return 0; + if(send(m_iSocket,szData,strlen(szData),0) < 0) { + m_iError = HTTP_ERROR_CANT_WRITE; + return false; + } + return true; } -int CHttpClient::Recv(int, void *buf, size_t n) +//---------------------------------------------------- + +int CHttpClient::Recv(char *szBuffer, int iBufferSize) { - // TODO: CHttpClient::Recv L: 08048F90 - return 0; + return recv(m_iSocket,szBuffer,iBufferSize,0); } -int CHttpClient::InitRequest(int, int, char *src, char *, char *) +//---------------------------------------------------- + +void CHttpClient::InitRequest(int iType, char *szURL, char *szPostData, char *szReferer) { - // TODO: CHttpClient::InitRequest W: 004011F0 L: 08048FBC - return 0; + char port[60]; // port string + char *port_char; // position of ':' if any + unsigned int slash_pos; // position of first '/' numeric + char *slash_ptr; + char szUseURL[512]; // incase we have to cat something to it. + + memset((void*)&m_Request,0,sizeof(HTTP_REQUEST)); + + // Set the request type + m_Request.rtype = iType; + + // Copy the URL to use + strcpy(szUseURL,szURL); + + // Copy the referer + if(szReferer) { + strcpy(m_Request.referer,szReferer); + } + + if(iType==HTTP_POST) { + strcpy(m_Request.data,szPostData); + } + + // Copy hostname from URL + slash_ptr = strchr(szUseURL,'/'); + + if(!slash_ptr) { + strcat(szUseURL,"/"); + slash_ptr = strchr(szUseURL,'/'); + } + + slash_pos=(slash_ptr-szUseURL); + memcpy(m_Request.host,szUseURL,slash_pos); + m_Request.host[slash_pos]='\0'; + + // Copy the rest of the url to the file string. + strcpy(m_Request.file,strchr(szUseURL,'/')); + + // Any special port used in the URL? + if((port_char=strchr(m_Request.host,':'))!=NULL) { + strcpy(port,port_char+1); + *port_char='\0'; + m_Request.port = atoi(port); + } + else { + m_Request.port = 80; + } } +//---------------------------------------------------- + void CHttpClient::Process() { - // TODO: CHttpClient::Process W: 004017D0 L: 0804914C + int header_len; + char request_head[16384]; + + if(m_iHasBindAddress) { + if(!Connect(m_Request.host,m_Request.port,m_szBindAddress)) { + return; + } + } else { + if(!Connect(m_Request.host,m_Request.port)) { + return; + } + } + + // Build the HTTP Header + switch(m_Request.rtype) + { + case HTTP_GET: + header_len = strlen(m_Request.file)+strlen(m_Request.host)+ + (strlen(GET_FORMAT)-8)+strlen(USER_AGENT)+ + strlen(m_Request.referer); + sprintf(request_head,GET_FORMAT,m_Request.file,USER_AGENT,m_Request.referer,m_Request.host); + break; + + case HTTP_HEAD: + header_len = strlen(m_Request.file)+strlen(m_Request.host)+ + (strlen(HEAD_FORMAT)-8)+strlen(USER_AGENT)+ + strlen(m_Request.referer); + sprintf(request_head,HEAD_FORMAT,m_Request.file,USER_AGENT,m_Request.referer,m_Request.host); + break; + + case HTTP_POST: + header_len = strlen(m_Request.data)+strlen(m_Request.file)+ + strlen(m_Request.host)+strlen(POST_FORMAT)+ + strlen(USER_AGENT)+strlen(m_Request.referer); + sprintf(request_head,POST_FORMAT,m_Request.file,USER_AGENT,m_Request.referer,m_Request.host,strlen(m_Request.data),m_Request.data); + break; + } + + //OutputDebugString(request_head); + + if(!Send(request_head)) return; + + HandleEntity(); } +//---------------------------------------------------- + +#define RECV_BUFFER_SIZE 2048 + void CHttpClient::HandleEntity() { - // TODO: CHttpClient::HandleEntity W: 00401350 L: 080493EA + int bytes_total = 0; + int bytes_read = 0; + char buffer[RECV_BUFFER_SIZE]; + char response[MAX_ENTITY_LENGTH]; + + char header[1024]; + char *head_end; + char *pcontent_buf; + char content_len_str[256]; + + bool header_got = false; + bool has_content_len = false; + int header_len = 0; + int content_len = 0; + + while((bytes_read=Recv(buffer,RECV_BUFFER_SIZE)) > 0) + { + bytes_total+=bytes_read; + memcpy(response+(bytes_total-bytes_read),buffer,(unsigned int)bytes_read); + + if(!header_got) + { + if((head_end=strstr(response,"\r\n\r\n"))!=NULL + || (head_end=strstr(response,"\n\n"))!=NULL) + { + + header_got=true; + + header_len=(head_end-response); + memcpy(header,response,header_len); + header[header_len]='\0'; + + if((*(response+header_len))=='\n') /* LFLF */ + { + bytes_total-=(header_len+2); + memmove(response,(response+(header_len+2)),bytes_total); + } + else /* assume CRLFCRLF */ + { + bytes_total-=(header_len+4); + memmove(response,(response+(header_len+4)),bytes_total); + } + + /* find the content-length if available */ + if((pcontent_buf=Util_stristr(header,"CONTENT-LENGTH:"))!=NULL) + { + has_content_len=true; + + pcontent_buf+=16; + while(*pcontent_buf!='\n' && *pcontent_buf) + { + *pcontent_buf++; + content_len++; + } + + pcontent_buf-=content_len; + memcpy(content_len_str,pcontent_buf,content_len); + + if(content_len_str[content_len-1] == '\r') { + content_len_str[content_len-1]='\0'; + } + else { + content_len_str[content_len]='\0'; + } + + content_len=atoi(content_len_str); + if(content_len > MAX_ENTITY_LENGTH) { + CloseConnection(); + m_iError = HTTP_ERROR_CONTENT_TOO_BIG; + return; + } + } + } + } + + if(header_got && has_content_len) + if(bytes_total>=content_len) break; + } + + CloseConnection(); + + response[bytes_total]='\0'; + + // check the returned header + unsigned long *magic = (unsigned long *)header; + + if(*magic != 0x50545448) { // 'HTTP' + m_iError = HTTP_ERROR_MALFORMED_RESPONSE; + return; + } + + // Now fill in the response code + char response_code_str[4]; + response_code_str[0] = *(header+9); + response_code_str[1] = *(header+10); + response_code_str[2] = *(header+11); + response_code_str[3] = '\0'; + m_Response.response_code = atoi(response_code_str); + + // Copy over the document entity strings and sizes + memcpy(m_Response.header,header,header_len+1); + m_Response.header_len = header_len; + memcpy(m_Response.response,response,bytes_total+1); + m_Response.response_len = bytes_total; + + /* + char s[4096]; + m_Response.response[bytes_total] = '\0'; + sprintf(s,"Code: %u\n\n%s\n%s\n",m_Response.response_code,m_Response.header,m_Response.response); + OutputDebugString(s);*/ + + // Try and determine the document type + m_Response.content_type = CONTENT_TYPE_HTML; // default to html + + char szContentType[256]; + + if(GetHeaderValue("CONTENT-TYPE:",szContentType,256) == true) { + if(strstr(szContentType,"text/html") != NULL) { + m_Response.content_type = CONTENT_TYPE_HTML; + } + else if(strstr(szContentType,"text/plain") != NULL) { + m_Response.content_type = CONTENT_TYPE_TEXT; + } else { + m_Response.content_type = CONTENT_TYPE_UNKNOWN; + } + } } + +//---------------------------------------------------- diff --git a/announce/httpclient.h b/announce/httpclient.h index 37e92ea..dd94224 100644 --- a/announce/httpclient.h +++ b/announce/httpclient.h @@ -1,4 +1,59 @@ +#define HTTP_GET 1 +#define HTTP_POST 2 +#define HTTP_HEAD 3 + +//---------------------------------------------------- + +#define MAX_ENTITY_LENGTH 64000 + +//---------------------------------------------------- + +#define HTTP_SUCCESS 0 +#define HTTP_ERROR_BAD_HOST 1 +#define HTTP_ERROR_NO_SOCKET 2 +#define HTTP_ERROR_CANT_CONNECT 3 +#define HTTP_ERROR_CANT_WRITE 4 +#define HTTP_ERROR_CONTENT_TOO_BIG 5 +#define HTTP_ERROR_MALFORMED_RESPONSE 6 + +//---------------------------------------------------- + +#define CONTENT_TYPE_UNKNOWN 0 +#define CONTENT_TYPE_TEXT 1 +#define CONTENT_TYPE_HTML 2 + +//---------------------------------------------------- + +#define USER_AGENT "SAMP/0.30" +#define GET_FORMAT "GET %s HTTP/1.0\r\nAccept: */*\r\nUser-Agent: %s\r\nReferer: http://%s\r\nHost: %s\r\n\r\n" +#define POST_FORMAT "POST %s HTTP/1.0\r\nAccept: */*\r\nUser-Agent: %s\r\nReferer: http://%s\r\nHost: %s\r\nContent-type: application/x-www-form-urlencoded\r\nContent-length: %u\r\n\r\n%s" +#define HEAD_FORMAT "HEAD %s HTTP/1.0\r\nAccept: */*\r\nUser-Agent: %s\r\nReferer: http://%s\r\nHost: %s\r\n\r\n" + +//---------------------------------------------------- + +#pragma pack(1) +typedef struct{ + unsigned short port; /* remote port */ + int rtype; /* request type */ + char host[256]; /* hostname */ + char file[1024]; /* GET/POST request file */ + char data[16384]; /* POST data (if rtype HTTP_POST) */ + char referer[256]; /* http referer. */ +} HTTP_REQUEST; + +//---------------------------------------------------- + +#pragma pack(1) +typedef struct{ + char header[1024]; + char response[MAX_ENTITY_LENGTH]; + unsigned int header_len; + unsigned long response_len; + unsigned int response_code; + unsigned int content_type; +} HTTP_RESPONSE; + //---------------------------------------------------- #pragma pack(1) @@ -6,25 +61,37 @@ class CHttpClient { private: - int field_0; - char field_4[17926]; - char field_460A[65040]; - int field_1441A; - char field_1441E[256]; - int field_1451E; + + int m_iSocket; + HTTP_REQUEST m_Request; + HTTP_RESPONSE m_Response; + int m_iError; + char m_szBindAddress[256]; + int m_iHasBindAddress; + + bool Connect(char *szHost, int iPort, char *szBindAddress=NULL); + void CloseConnection(); + bool Send(char *szData); + int Recv(char *szBuffer, int iBufferSize); + + void InitRequest(int iType, char *szURL, char *szPostData, char *szReferer); + void HandleEntity(); + + void Process(); public: - CHttpClient(char *src); + + int ProcessURL(int iType, char *szURL, char *szData, char *szReferer); + + bool GetHeaderValue(char *szHeaderName,char *szReturnBuffer, int iBufSize); + int GetResponseCode() { return m_Response.response_code; }; + int GetContentType() { return m_Response.content_type; }; + char *GetResponseHeaders() { return m_Response.header; }; + char *GetDocument() { return m_Response.response; }; + int GetDocumentLength() { return m_Response.response_len; }; + + CHttpClient(char *szBindAddress); ~CHttpClient(); - int ProcessURL(int, int, char *src, char *, char *); - void GetHeaderValue(); - int Connect(char *name, unsigned short hostshort, char *); - void CloseConnection(); - int Send(int, char *s); - int Recv(int, void *buf, size_t n); - int InitRequest(int, int, char *src, char *, char *); - void Process(); - void HandleEntity(); }; //----------------------------------------------------