574 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
			
		
		
	
	
			574 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
//+------------------------------------------------------------------+
 | 
						|
//|                                                     requests.mqh |
 | 
						|
//|                                          Copyright 2023, Omegafx |
 | 
						|
//|                 https://www.mql5.com/en/users/omegajoctan/seller |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
#property copyright "Copyright 2023, Omegafx"
 | 
						|
#property link      "https://www.mql5.com/en/users/omegajoctan/seller"
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| defines                                                          |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
#include <jason.mqh> //https://www.mql5.com/en/code/13663
 | 
						|
#include <errordescription.mqh>
 | 
						|
#include <Arrays\ArrayChar.mqh>
 | 
						|
 | 
						|
struct CResponse
 | 
						|
  {
 | 
						|
   int               status_code; // HTTP status code (e.g., 200, 404)
 | 
						|
   string            text; // Raw response body as string
 | 
						|
 | 
						|
   CJAVal            json; // Parses response as JSON
 | 
						|
   uchar             content[]; // Raw bytes of the response
 | 
						|
   string            headers; // Dictionary of response headers
 | 
						|
   string            cookies; // Cookies set by the server
 | 
						|
   string            url; // Final URL after redirects
 | 
						|
   bool              ok;     // True if status_code < 400
 | 
						|
   uint              elapsed; // Time taken for the response in ms
 | 
						|
   string            reason; // Text reason (e.g., "OK", "Not Found")
 | 
						|
  };
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
class CSession
 | 
						|
  {
 | 
						|
protected:
 | 
						|
                     
 | 
						|
   static string            WebStatusText(int code);
 | 
						|
   /**
 | 
						|
    * Base 64 is an encoding scheme that converts binary data into text
 | 
						|
    * format so that encoded textual data can be easily transported over
 | 
						|
    * network un-corrupted and without any data loss. Base64 is used
 | 
						|
    * commonly in a number of applications including email via MIME, and
 | 
						|
    * storing complex data in XML.
 | 
						|
    */
 | 
						|
   static string Base64Encode(const string text)
 | 
						|
    {
 | 
						|
           uchar src[], dst[], key[];
 | 
						|
   
 | 
						|
           StringToCharArray("", key, 0, StringLen(""));
 | 
						|
           StringToCharArray(text, src, 0, StringLen(text));
 | 
						|
   
 | 
						|
           //--- encode src[] with BASE64
 | 
						|
           int res = CryptEncode(CRYPT_BASE64, src, key, dst);
 | 
						|
   
 | 
						|
           return (res > 0) ? CharArrayToString(dst) : "";
 | 
						|
    }
 | 
						|
   
 | 
						|
   static string Base64Decode(const string text)
 | 
						|
    {
 | 
						|
           uchar src[], dst[], key[];
 | 
						|
   
 | 
						|
           StringToCharArray("", key, 0, StringLen(""));
 | 
						|
           StringToCharArray(text, src, 0, StringLen(text));
 | 
						|
   
 | 
						|
           //--- decode src[] with BASE64
 | 
						|
           int res = CryptDecode(CRYPT_BASE64, src, key, dst);
 | 
						|
   
 | 
						|
           return (res > 0) ? CharArrayToString(dst) : "";
 | 
						|
    }
 | 
						|
   
 | 
						|
   static string StringTrim(string s)
 | 
						|
    {
 | 
						|
      StringTrimLeft(s);
 | 
						|
      StringTrimRight(s);
 | 
						|
      return s;
 | 
						|
    }
 | 
						|
    
 | 
						|
   static string            UpdateHeader(const string headers, const string key, const string value);
 | 
						|
   static string            UpdateHeader(const string headers, const string new_header_key_value_pair);
 | 
						|
   static string            GuessContentType(string filename);
 | 
						|
   
 | 
						|
   static void CArray2Array(const CArrayChar &c_array, char &out_array[])
 | 
						|
    {
 | 
						|
      int size = c_array.Total();
 | 
						|
         
 | 
						|
      ArrayResize(out_array, size);
 | 
						|
      for (int i=0; i<size; i++)
 | 
						|
         out_array[i] = c_array.At(i);  
 | 
						|
    }
 | 
						|
   
 | 
						|
   static string GetFileName(string base_filename)
 | 
						|
    {
 | 
						|
      string basename = base_filename;
 | 
						|
      int pos = StringFind(base_filename, "\\", StringLen(base_filename) - 1);
 | 
						|
      
 | 
						|
      if (pos >= 0) basename = StringSubstr(base_filename, pos + 1);
 | 
						|
         pos = StringFind(basename, "/", StringLen(basename) - 1); // handle Unix-style paths
 | 
						|
         
 | 
						|
      if (pos >= 0) 
 | 
						|
         basename = StringSubstr(basename, pos + 1);
 | 
						|
      
 | 
						|
      return basename;
 | 
						|
    } 
 | 
						|
 
 | 
						|
   static string            URLEncode(const string value);
 | 
						|
   
 | 
						|
   static string            m_headers;
 | 
						|
   static string            m_cookies;
 | 
						|
   
 | 
						|
public:
 | 
						|
                     
 | 
						|
                            CSession(const string headers, const string cookies=""); // Provides headers cookies persistance
 | 
						|
                           ~CSession(void);
 | 
						|
   
 | 
						|
   static void SetCookie(const string cookie)
 | 
						|
      {
 | 
						|
         if (StringLen(m_cookies) > 0)
 | 
						|
            m_cookies += "; ";
 | 
						|
         m_cookies += cookie;
 | 
						|
      }
 | 
						|
      
 | 
						|
   static void              ClearCookies() {    m_cookies = "";     }
 | 
						|
   static void              SetBasicAuth(const string username, const string password);
 | 
						|
   static string            BuildUrlWithParams(string base_url, const string &keys[], const string &values[]);
 | 
						|
   
 | 
						|
   //---
 | 
						|
   
 | 
						|
   static CResponse         request(const string method, const string url, const string data, const string &files[], const string headers = "", const int timeout = 5000, const bool is_json=true);
 | 
						|
   
 | 
						|
   // High-level request helpers
 | 
						|
   static CResponse         get(const string url, const string headers = "", const int timeout = 5000)  
 | 
						|
     {  
 | 
						|
       string files[];
 | 
						|
       return request("GET", url, "", files, headers, timeout, false);   
 | 
						|
     }
 | 
						|
     
 | 
						|
   static CResponse         post(const string url, const string data, const string &files[], const string headers = "", const int timeout = 5000, const bool is_json=true) 
 | 
						|
     {
 | 
						|
       return request("POST", url, data, files, headers, timeout, is_json);  
 | 
						|
     }
 | 
						|
     
 | 
						|
   static CResponse         put(const string url, const string data, const string &files[], const string headers = "", const int timeout = 5000, const bool is_json=true) 
 | 
						|
     { 
 | 
						|
       return request("PUT", url, data, files, headers, timeout, is_json);   
 | 
						|
     }
 | 
						|
     
 | 
						|
   static CResponse         patch(const string url, const string data = "", const string headers = "", const int timeout = 5000, const bool is_json=true) 
 | 
						|
     { 
 | 
						|
       string files[];
 | 
						|
       return request("PATCH", url, data, files, headers, timeout, is_json); 
 | 
						|
     }
 | 
						|
     
 | 
						|
   static CResponse         delete_(const string url, const string headers = "", const int timeout = 5000, const bool is_json=true) 
 | 
						|
     {  
 | 
						|
       string files[];
 | 
						|
       return request("DELETE", url, "", files, headers, timeout, is_json);   
 | 
						|
     }
 | 
						|
  };
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
// static members 
 | 
						|
string CSession::m_headers = "";
 | 
						|
string CSession::m_cookies = "";
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
CSession::CSession(const string headers, const string cookies="") // Provides headers and cookies persistance;
 | 
						|
 { 
 | 
						|
   m_headers = headers; 
 | 
						|
   m_cookies = cookies;
 | 
						|
 } 
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
CSession::~CSession(void)
 | 
						|
  {
 | 
						|
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
CResponse CSession::request(const string method,
 | 
						|
                             const string url,
 | 
						|
                             const string data,
 | 
						|
                             const string &files[],
 | 
						|
                             const string headers = "",
 | 
						|
                             const int timeout = 5000,
 | 
						|
                             const bool is_json=true)
 | 
						|
 {
 | 
						|
   char result[];
 | 
						|
   string result_headers;
 | 
						|
   string temp_headers = m_headers;
 | 
						|
   string boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"; //for setting boundaries between data types and files in the form data
 | 
						|
   CArrayChar final_body; //Final body uchar array
 | 
						|
 | 
						|
   CResponse response; //a structure containing various response fields
 | 
						|
 | 
						|
   // Append user headers
 | 
						|
   if (headers != "")
 | 
						|
      temp_headers += headers + "\r\n";
 | 
						|
 | 
						|
   bool use_multipart = ArraySize(files) > 0; //Check if files are attached 
 | 
						|
 | 
						|
//--- Create a multi part request
 | 
						|
 | 
						|
   if (use_multipart) // If multipart, assemble full body (JSON + files)
 | 
						|
    {
 | 
						|
      temp_headers = UpdateHeader(temp_headers, "Content-Type", "multipart/form-data; boundary=" + boundary + "\r\n"); //Update the headers
 | 
						|
 | 
						|
      //--- JSON part (or form data)
 | 
						|
      if (StringLen(data) > 0)
 | 
						|
      {
 | 
						|
         string json_data = "";
 | 
						|
         if (is_json) //if Json data is given alongside the files 
 | 
						|
          {
 | 
						|
            CJAVal js(NULL, jtUNDEF);
 | 
						|
            if (js.Deserialize(data, CP_UTF8))
 | 
						|
               js.Serialize(json_data); //Serialize the JSON data
 | 
						|
          }
 | 
						|
 | 
						|
         string json_part = "--" + boundary + "\r\n";
 | 
						|
         json_part += "Content-Disposition: form-data; name=\"metadata\"\r\n";
 | 
						|
         json_part += "Content-Type: application/json\r\n\r\n";
 | 
						|
         json_part += json_data + "\r\n";
 | 
						|
 | 
						|
         char json_bytes[];
 | 
						|
         StringToCharArray(json_part, json_bytes, 0, StringLen(json_part), CP_UTF8);
 | 
						|
         
 | 
						|
         final_body.AddArray(json_bytes);
 | 
						|
      }
 | 
						|
 | 
						|
      //--- File parts
 | 
						|
      for (uint i = 0; i < files.Size(); i++)
 | 
						|
       {
 | 
						|
         string filename = GetFileName(files[i]);
 | 
						|
         
 | 
						|
         char file_data[]; //for storing the file data in binary format
 | 
						|
 | 
						|
         int file_handle = FileOpen(filename, FILE_BIN | FILE_SHARE_READ); // Read the file in binary format
 | 
						|
         if (file_handle == INVALID_HANDLE)
 | 
						|
          {
 | 
						|
            printf("func=%s line=%d, Failed to read the file '%s'. Error = %s",__FUNCTION__,__LINE__,filename,ErrorDescription(GetLastError()));
 | 
						|
            continue; //skip to the next file if the current file is invalid
 | 
						|
          }
 | 
						|
 | 
						|
         int fsize = (int)FileSize(file_handle);
 | 
						|
         ArrayResize(file_data, fsize);
 | 
						|
         if (FileReadArray(file_handle, file_data, 0, fsize)==0)
 | 
						|
           {
 | 
						|
              printf("func=%s line=%d, No data found in the file '%s'. Error = %s",__FUNCTION__,__LINE__,filename,ErrorDescription(GetLastError()));
 | 
						|
              FileClose(file_handle);
 | 
						|
              continue; //skip to the next file if the current file is invalid
 | 
						|
           }    
 | 
						|
           
 | 
						|
         FileClose(file_handle); //close the current file
 | 
						|
 | 
						|
         //--- Append files header and content type as detected to the request
 | 
						|
        
 | 
						|
         string file_part = "--" + boundary + "\r\n";
 | 
						|
         file_part += StringFormat("Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n", filename);
 | 
						|
         file_part += StringFormat("Content-Type: %s\r\n\r\n", GuessContentType(filename));
 | 
						|
         
 | 
						|
         char file_header[];
 | 
						|
         StringToCharArray(file_part, file_header, 0, StringLen(file_part), CP_UTF8); //UTF-8 Encoding is a must
 | 
						|
         
 | 
						|
         final_body.AddArray(file_header); //Add the file header
 | 
						|
         final_body.AddArray(file_data); //Add the file in binary format, the actual file 
 | 
						|
         
 | 
						|
         //--- append the new line — critical for HTTP form parsing.
 | 
						|
         
 | 
						|
         final_body.Add('\r');
 | 
						|
         final_body.Add('\n');
 | 
						|
      }
 | 
						|
 | 
						|
      //--- Final boundary
 | 
						|
      string closing = "--" + boundary + "--\r\n";
 | 
						|
      char closing_part[];
 | 
						|
      StringToCharArray(closing, closing_part);
 | 
						|
      
 | 
						|
      final_body.AddArray(closing_part);
 | 
						|
    }
 | 
						|
   else // no files attached
 | 
						|
    {
 | 
						|
      //--- If it's just JSON or plain form data
 | 
						|
      string body_data = data;
 | 
						|
      if (is_json)
 | 
						|
       {
 | 
						|
         CJAVal js(NULL, jtUNDEF);
 | 
						|
         if (js.Deserialize(data, CP_UTF8))
 | 
						|
            js.Serialize(body_data);
 | 
						|
 | 
						|
         temp_headers = UpdateHeader(temp_headers, "Content-Type", "application/json");
 | 
						|
       }
 | 
						|
      else
 | 
						|
         temp_headers = UpdateHeader(temp_headers, headers);
 | 
						|
      
 | 
						|
      //---
 | 
						|
      
 | 
						|
      char array[];      
 | 
						|
      StringToCharArray(body_data, array, 0, StringLen(body_data), CP_UTF8); //Use UTF-8 similar requests in Python, This is very crucial
 | 
						|
      final_body.AddArray(array);
 | 
						|
    }
 | 
						|
   
 | 
						|
   char final_body_char_arr[];
 | 
						|
   CArray2Array(final_body, final_body_char_arr);
 | 
						|
   
 | 
						|
   if (MQLInfoInteger(MQL_DEBUG))
 | 
						|
     Print("Final body:\n",CharArrayToString(final_body_char_arr, 0 , final_body.Total(), CP_UTF8));
 | 
						|
   
 | 
						|
//--- Add cookies if there are any
 | 
						|
   
 | 
						|
   if (StringLen(m_cookies) > 0)
 | 
						|
    temp_headers = UpdateHeader(temp_headers, "Cookie", m_cookies);
 | 
						|
 | 
						|
//--- Send the request
 | 
						|
 | 
						|
   uint start = GetTickCount(); //starting time of the request
 | 
						|
   int status = WebRequest(method, url, temp_headers, timeout, final_body_char_arr, result, result_headers); //trigger a webrequest function
 | 
						|
 | 
						|
   if(status == -1)
 | 
						|
     {
 | 
						|
      PrintFormat("WebRequest failed with error %s", ErrorDescription(GetLastError()));
 | 
						|
      response.status_code = 0;
 | 
						|
      return response;
 | 
						|
     }
 | 
						|
 | 
						|
//--- Fill the response struct
 | 
						|
 | 
						|
   response.elapsed = GetTickCount() - start;
 | 
						|
   response.text = CharArrayToString(result);
 | 
						|
   response.status_code = status;
 | 
						|
   response.headers = result_headers;
 | 
						|
   response.url = url;
 | 
						|
   response.ok = (status >= 200 && status < 400);
 | 
						|
   response.reason = WebStatusText(status);
 | 
						|
   ArrayCopy(response.content, result);
 | 
						|
 | 
						|
//---
 | 
						|
 | 
						|
   CJAVal js;
 | 
						|
   if (js.Deserialize(response.text))
 | 
						|
      response.json = js;
 | 
						|
   
 | 
						|
   
 | 
						|
   response.cookies = response.json[""]["cookies"].ToStr();
 | 
						|
   
 | 
						|
//---
 | 
						|
   
 | 
						|
   return response;
 | 
						|
 }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
string CSession::WebStatusText(int code)
 | 
						|
  {
 | 
						|
   string reason = "";
 | 
						|
   switch(code)
 | 
						|
     {
 | 
						|
      case 200:
 | 
						|
         reason =  "OK";
 | 
						|
         break;
 | 
						|
      case 201:
 | 
						|
         reason = "Created";
 | 
						|
         break;
 | 
						|
      case 400:
 | 
						|
         reason = "Bad Request";
 | 
						|
         break;
 | 
						|
      case 401:
 | 
						|
         reason = "Unauthorized";
 | 
						|
         break;
 | 
						|
      case 403:
 | 
						|
         reason = "Forbidden";
 | 
						|
         break;
 | 
						|
      case 404:
 | 
						|
         reason = "Not Found";
 | 
						|
         break;
 | 
						|
      case 500:
 | 
						|
         reason = "Internal Server Error";
 | 
						|
         break;
 | 
						|
      default:
 | 
						|
         reason = "HTTP " + IntegerToString(code);
 | 
						|
         break;
 | 
						|
     }
 | 
						|
     
 | 
						|
   return reason;
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|         Sets or replaces default headers                         |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
string CSession::UpdateHeader(const string headers, const string key, const string value)
 | 
						|
  {
 | 
						|
   string res_headers = "";
 | 
						|
   if(StringFind(headers, key + ":") >= 0)
 | 
						|
     {
 | 
						|
      // Replace existing header
 | 
						|
      int start = StringFind(headers, key + ":");
 | 
						|
      int end = StringFind(headers, "\r\n", start);
 | 
						|
      
 | 
						|
      if(end == -1) 
 | 
						|
         end = StringLen(headers);
 | 
						|
      
 | 
						|
      res_headers = StringSubstr(headers, 0, start) +
 | 
						|
                    key + ": " + value + "\r\n" +
 | 
						|
                    StringSubstr(headers, end + 2);
 | 
						|
     }
 | 
						|
   else
 | 
						|
     {
 | 
						|
      // Add new header
 | 
						|
      res_headers += key + ": " + value + "\r\n";
 | 
						|
     }
 | 
						|
     
 | 
						|
    return res_headers;
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
string CSession::UpdateHeader(const string headers, const string new_header_key_value_pair)
 | 
						|
{
 | 
						|
   string key = "";
 | 
						|
   string value = "";
 | 
						|
 | 
						|
   // Split the input string into key and value based on the first colon
 | 
						|
   int colon_index = StringFind(new_header_key_value_pair, ":");
 | 
						|
   if(colon_index > -1)
 | 
						|
     {
 | 
						|
      key = StringTrim(StringSubstr(new_header_key_value_pair, 0, colon_index));
 | 
						|
      value = StringTrim(StringSubstr(new_header_key_value_pair, colon_index + 1));
 | 
						|
     }
 | 
						|
   else
 | 
						|
     {
 | 
						|
      // Invalid format; return headers unmodified
 | 
						|
      return headers;
 | 
						|
     }
 | 
						|
 | 
						|
   string res_headers = headers;
 | 
						|
   int start = StringFind(headers, key + ":");
 | 
						|
   if(start >= 0)
 | 
						|
     {
 | 
						|
      // Replace existing header
 | 
						|
      int end = StringFind(headers, "\r\n", start);
 | 
						|
      if(end == -1) 
 | 
						|
         end = StringLen(headers);
 | 
						|
 | 
						|
      res_headers = StringSubstr(headers, 0, start) +
 | 
						|
                    key + ": " + value + "\r\n" +
 | 
						|
                    StringSubstr(headers, end + 2);
 | 
						|
     }
 | 
						|
   else
 | 
						|
     {
 | 
						|
      // Add new header
 | 
						|
      res_headers += key + ": " + value + "\r\n";
 | 
						|
     }
 | 
						|
 | 
						|
   return res_headers;
 | 
						|
}
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
void CSession::SetBasicAuth(const string username, const string password)
 | 
						|
  {
 | 
						|
   string credentials = username + ":" + password;
 | 
						|
   string encoded = Base64Encode(credentials); //Encode the credentials
 | 
						|
      
 | 
						|
   m_headers = UpdateHeader(m_headers, "Authorization", "Basic " + encoded); //Update HTTP headers with the authentication information
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
string CSession::GuessContentType(string filename)
 | 
						|
{
 | 
						|
   StringToLower(filename); // Normalize for case-insensitivity
 | 
						|
 | 
						|
   if(StringFind(filename, ".txt")   >= 0) return "text/plain";
 | 
						|
   if(StringFind(filename, ".json")  >= 0) return "application/json";
 | 
						|
   if(StringFind(filename, ".xml")   >= 0) return "application/xml";
 | 
						|
   if(StringFind(filename, ".csv")   >= 0) return "text/csv";
 | 
						|
   if(StringFind(filename, ".html")  >= 0) return "text/html";
 | 
						|
   if(StringFind(filename, ".htm")   >= 0) return "text/html";
 | 
						|
 | 
						|
   //--- Images
 | 
						|
   if(StringFind(filename, ".png")   >= 0) return "image/png";
 | 
						|
   if(StringFind(filename, ".jpg")   >= 0 || StringFind(filename, ".jpeg") >= 0) return "image/jpeg";
 | 
						|
   if(StringFind(filename, ".gif")   >= 0) return "image/gif";
 | 
						|
   if(StringFind(filename, ".bmp")   >= 0) return "image/bmp";
 | 
						|
   if(StringFind(filename, ".webp")  >= 0) return "image/webp";
 | 
						|
   if(StringFind(filename, ".ico")   >= 0) return "image/x-icon";
 | 
						|
   if(StringFind(filename, ".svg")   >= 0) return "image/svg+xml";
 | 
						|
 | 
						|
   //--- Audio
 | 
						|
   if(StringFind(filename, ".mp3")   >= 0) return "audio/mpeg";
 | 
						|
   if(StringFind(filename, ".wav")   >= 0) return "audio/wav";
 | 
						|
   if(StringFind(filename, ".ogg")   >= 0) return "audio/ogg";
 | 
						|
 | 
						|
   //--- Video
 | 
						|
   if(StringFind(filename, ".mp4")   >= 0) return "video/mp4";
 | 
						|
   if(StringFind(filename, ".avi")   >= 0) return "video/x-msvideo";
 | 
						|
   if(StringFind(filename, ".mov")   >= 0) return "video/quicktime";
 | 
						|
   if(StringFind(filename, ".webm")  >= 0) return "video/webm";
 | 
						|
   if(StringFind(filename, ".mkv")   >= 0) return "video/x-matroska";
 | 
						|
 | 
						|
   //--- Applications
 | 
						|
   if(StringFind(filename, ".pdf")   >= 0) return "application/pdf";
 | 
						|
   if(StringFind(filename, ".zip")   >= 0) return "application/zip";
 | 
						|
   if(StringFind(filename, ".gz")    >= 0) return "application/gzip";
 | 
						|
   if(StringFind(filename, ".tar")   >= 0) return "application/x-tar";
 | 
						|
   if(StringFind(filename, ".rar")   >= 0) return "application/vnd.rar";
 | 
						|
   if(StringFind(filename, ".7z")    >= 0) return "application/x-7z-compressed";
 | 
						|
   if(StringFind(filename, ".exe")   >= 0) return "application/octet-stream";
 | 
						|
   if(StringFind(filename, ".apk")   >= 0) return "application/vnd.android.package-archive";
 | 
						|
 | 
						|
   //--- Microsoft Office
 | 
						|
   if(StringFind(filename, ".doc")   >= 0) return "application/msword";
 | 
						|
   if(StringFind(filename, ".docx")  >= 0) return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
 | 
						|
   if(StringFind(filename, ".xls")   >= 0) return "application/vnd.ms-excel";
 | 
						|
   if(StringFind(filename, ".xlsx")  >= 0) return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
 | 
						|
   if(StringFind(filename, ".ppt")   >= 0) return "application/vnd.ms-powerpoint";
 | 
						|
   if(StringFind(filename, ".pptx")  >= 0) return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
 | 
						|
 | 
						|
   return "application/octet-stream"; // Default fallback
 | 
						|
}
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
string CSession::URLEncode(const string value)
 | 
						|
{
 | 
						|
   uchar bytes[];
 | 
						|
   StringToCharArray(value, bytes, 0, StringLen(value), CP_UTF8);
 | 
						|
   
 | 
						|
   string encoded = "";
 | 
						|
   for (int i = 0; i < ArraySize(bytes); ++i)
 | 
						|
   {
 | 
						|
      uchar c = bytes[i];
 | 
						|
      if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
 | 
						|
          c == '-' || c == '_' || c == '.' || c == '~')
 | 
						|
      {
 | 
						|
         encoded += CharToString(c);
 | 
						|
      }
 | 
						|
      else if (c == ' ')
 | 
						|
      {
 | 
						|
         encoded += "+";
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
         encoded += StringFormat("%%%02X", c);
 | 
						|
      }
 | 
						|
   }
 | 
						|
   return encoded;
 | 
						|
}
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
string CSession::BuildUrlWithParams(string base_url, const string &keys[], const string &values[])
 | 
						|
{  
 | 
						|
   if (keys.Size() != values.Size())
 | 
						|
     {
 | 
						|
       printf("func=%s line=%d, Failed. Keys and values array sizes dimensional mismatch",__FUNCTION__,__LINE__);
 | 
						|
       return "";
 | 
						|
     }   
 | 
						|
 | 
						|
//---
 | 
						|
 | 
						|
   string query = "";
 | 
						|
   for (int i = 0; i < ArraySize(keys); ++i)
 | 
						|
   {
 | 
						|
      if (i > 0)
 | 
						|
         query += "&";
 | 
						|
      query += URLEncode(keys[i]) + "=" + URLEncode(values[i]);
 | 
						|
   }
 | 
						|
 | 
						|
   return base_url + "?" + query;
 | 
						|
}
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 |