/*!
* This method copies the response data into a supplied buffer.
* It skips the response and header lines and only copies the response body.
* The amount of data copied is limited by the size of the supplied buffer.
* Adds a terminating '\0'.
* @param buffer The buffer to copy the data into.
* @param size The size of `buffer`.
* @param bytesRead The number of bytes copied is written to this parameter.
* @param httpCode The HTTP response code is written to this parameter.
* @return `false` if there is no data to copy, otherwise `true`.
* It will return `false` if the body is empty or cannot be determined.
*/
bool Sodaq_WifiBee::readHTTPResponse(char* buffer, const size_t size,
  size_t& bytesRead, uint16_t& httpCode)
{
  if (_bufferUsed == 0) {
    return false;
  }

  // Read HTTP response code
  parseHTTPResponse(httpCode);

  // Add 4 to start from after the double newline
  char* startPos = strstr((char*)_buffer, "\r\n\r\n") + 4;

  size_t startIndex = startPos - (char*)_buffer;

  if (startIndex < _bufferUsed) {
    bytesRead = ((size - 1) < (_bufferUsed - startIndex)) ? (size - 1) : (_bufferUsed - startIndex);
  }
  else {
    bytesRead = 0;
  }

  memcpy(buffer, startPos, bytesRead);
  buffer[bytesRead] = '\0';

  return true;
}
bool MyUPnP::GetDescription()
{
	if(!Valid())return false;
	CString post, host, addr;
	int port = 0;
	addr = NGetAddressFromUrl(m_description, post, host, port);
	if(addr.IsEmpty())return false;
	CString request = CString(_T("GET ")) + post + _T(" HTTP/1.1\r\nHOST: ") + host + _T("\r\nACCEPT-LANGUAGE: en\r\n\r\n");
	CString response;
	if (!SOAP_action(addr, (uint16)port, request, response)) return false;
	CString result;
	if (!parseHTTPResponse(response, result)) return false;

	m_friendlyname = getProperty(result, _T("friendlyName"));
	m_modelname = getProperty(result, _T("modelName"));
	m_baseurl = getProperty(result, _T("URLBase"));
	if(m_baseurl.IsEmpty())m_baseurl = CString(_T("http://")) + host + _T("/");
	if(m_baseurl[m_baseurl.GetLength() - 1]!='/')m_baseurl += _T("/");
	
	CString serviceType = _T("<serviceType>") + m_name + _T("</serviceType>");
	int pos = result.Find(serviceType);
	if (pos >= 0) {
		result.Delete(0, pos + serviceType.GetLength());
		pos = result.Find(_T("</service>"));
		if (pos >= 0) {
			result = result.Mid(0, pos);
			m_controlurl = getProperty(result, _T("controlURL"));
			if (!m_controlurl.IsEmpty() && m_controlurl[0] == '/') {
				m_controlurl = m_baseurl + m_controlurl.Mid(1);
			}
		}
	}

	return isComplete();
}
bool MyUPnP::InvokeCommand(const CString& name, const CString& args)
{
	if(!isComplete())return false;
	CString post, host, addr;
	int port = 0;
	addr = NGetAddressFromUrl(m_controlurl, post, host, port);
	if(addr.IsEmpty())return false;
	CString cnt;
	CString psr;
	cnt.Append(_T("<?xml version=\"1.0\"?><s:Envelope\r\n    xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n    "));
	cnt.Append(_T("s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n  <s:Body>\r\n    <u:"));
	cnt.Append(name);
	cnt.Append(_T(" xmlns:u=\""));
	cnt.Append(m_name);
	cnt.Append(_T("\">\r\n"));
	cnt.Append(args);
	cnt.Append(_T("    </u:"));
	cnt.Append(name);
	cnt.Append(_T(">\r\n  </s:Body>\r\n</s:Envelope>\r\n\r\n"));
	psr.Append(_T("POST "));
	psr.Append(post);
	psr.Append(_T(" HTTP/1.1\r\nHOST: "));
	psr.Append(host);
	psr.Append(_T("\r\nContent-Length: "));
	psr.Append(getString(CStringA(cnt).GetLength()));
	psr.Append(_T("\r\nContent-Type: text/xml; charset=\"utf-8\"\r\nSOAPAction: \""));
	psr.Append(m_name);
	psr.Append(_T("#"));
	psr.Append(name);
	psr.Append(_T("\"\r\n\r\n"));
	psr.Append(cnt);

	CString response;
	CString request = psr;
	if (!SOAP_action(addr, (uint16)port, request, response)) return false;
	CString result;
	if (!parseHTTPResponse(response, result)) return false;

	return true;
}
/*!
* This method constructs and sends a generic HTTP request.
* @param server The server/host to connect to (IP address or domain).
* @param port The port to connect to.
* @param The HTTP method to use. e.g. "GET", "POST" etc.
* @param location The resource location on the server/host.
* @param headers Any additional headers, each must be followed by a CRLF.
* HOST & Content-Length headers are added automatically.
* @param body The body (can be blank) to send with the request. Must not start with a CRLF.
* @param httpCode The HTTP response code is written to this parameter (if a response is received).
* @return `true` if a connection is established and the data is sent, `false` otherwise.
*/
bool Sodaq_WifiBee::HTTPAction(const char* server, const uint16_t port,
  const char* method, const char* location, const char* headers,
  const char* body, uint16_t& httpCode)
{
  bool result;

  // Open the connection
  result = openConnection(server, port, "net.TCP");

  if (result) {
    createSendBuffer();

    sendAscii(method);
    sendAscii(" ");
    sendAscii(location);
    sendAscii(" HTTP/1.1\\r\\n");

    sendAscii("HOST: ");
    sendAscii(server);
    sendAscii(":");

    char buff[11];
    itoa(port, buff, 10);
    sendAscii(buff);
    sendAscii("\\r\\n");

    if (strcmp(method, "GET") != 0) {
      sendAscii("Content-Length: ");
      itoa(strlen(body), buff, 10);
      sendAscii(buff);
      sendAscii("\\r\\n");
    }

    sendEscapedAscii(headers);
    sendAscii("\\r\\n");

    sendEscapedAscii(body);

    transmitSendBuffer();

    // Wait till we hear that it was sent
    result = skipTillPrompt(SENT_PROMPT, RESPONSE_TIMEOUT);

    // Wait till we get the data received prompt
    if (result) {
      if (skipTillPrompt(RECEIVED_PROMPT, SERVER_RESPONSE_TIMEOUT)) {
        while (skipTillPrompt(RECEIVED_PROMPT, NEXT_PACKET_TIMEOUT)) {
        }

        readServerResponse();
        parseHTTPResponse(httpCode);
      }
      else {
        clearBuffer();
      }
    }

    // The connection might have closed automatically
    closeConnection();
  }

  return result;
}
bool MyUPnP::InternalSearch(int version)
{
	if(version<=0)version = 1;
	m_version = version;

#define NUMBEROFDEVICES	2
	CString devices[][2] = {
		{UPNPPORTMAP1, _T("service")},
		{UPNPPORTMAP0, _T("service")},
		{_T("InternetGatewayDevice"), _T("device")},
	};

	int s = socket(AF_INET, SOCK_DGRAM, 0);
	u_long lv = 1;
	ioctlsocket(s, FIONBIO, &lv);

	int rlen = 0;
	for (int i=0; rlen<=0 && i<500; i++) {
		if (!(i%100)) {
			for (int i=0; i<NUMBEROFDEVICES; i++) {
				m_name.Format(_T("%s%s:%s:%d"), URNPREFIX, devices[i][1], devices[i][0], version);
				CString request;
				request.Format(_T("M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: %d\r\nST: %s\r\n\r\n"),
					6, m_name);
				SSDP_sendRequest(s, UPNPADDR, UPNPPORT, request);
			}
		}

		Sleep(10);

		char buffer[10240];
		rlen = recv(s, buffer, sizeof(buffer), 0);
		if (rlen <= 0) continue;
		closesocket(s);

		CString response = CString(CStringA(buffer, rlen));
		CString result;
		if (!parseHTTPResponse(response, result)) return false;

		for (int d=0; d<NUMBEROFDEVICES; d++) {
			m_name.Format(_T("%s%s:%s:%d"), URNPREFIX, devices[d][1], devices[d][0], version);
			if (result.Find(m_name) >= 0) {
				for (int pos = 0;;) {
					CString line = result.Tokenize(_T("\r\n"), pos);
					if (line.IsEmpty()) return false;
					CString name = line.Mid(0, 9);
					name.MakeUpper();
					if (name == _T("LOCATION:")) {
						line.Delete(0, 9);
						m_description = line;
						m_description.Trim();
						return GetDescription();
					}
				}
			}
		}
	}
	closesocket(s);

	return false;
}