/***************************************************************************
* GVOClient.hpp - VO client class *
* ----------------------------------------------------------------------- *
* copyright (C) 2013-2014 by Juergen Knoedlseder *
* ----------------------------------------------------------------------- *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
***************************************************************************/
/**
* @file GVOClient.cpp
* @brief VO client class implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include // std::getenv() function
#include // std::fopen(), etc. functions
#include // std::memset() function
#include // close() function
#include // getaddrinfo() function
#include // socket(), connect() functions
#include "GVOClient.hpp"
#include "GException.hpp"
#include "GTools.hpp"
/* __ Method name definitions ____________________________________________ */
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GVOClient::GVOClient(void)
{
// Initialise members
init_members();
// Find Hub
find_hub();
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] client VO client.
***************************************************************************/
GVOClient::GVOClient(const GVOClient& client)
{
// Initialise members
init_members();
// Copy members
copy_members(client);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GVOClient::~GVOClient(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] client VO client.
* @return VO client.
***************************************************************************/
GVOClient& GVOClient::operator= (const GVOClient& client)
{
// Execute only if object is not identical
if (this != &client) {
// Free members
free_members();
// Initialise members
init_members();
// Copy members
copy_members(client);
} // endif: object was not identical
// Return
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear object.
*
* Reset object to a clean initial state.
***************************************************************************/
void GVOClient::clear(void)
{
// Free memory and initialise members
free_members();
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone object
***************************************************************************/
GVOClient* GVOClient::clone(void) const
{
// Clone client
return new GVOClient(*this);
}
/***********************************************************************//**
* @brief Register client to SAMP Hub
*
* Connects the VO client to the Hub. A socket to the Hub is opened and the
* client is registered at the Hub. Metadata are sent.
***************************************************************************/
void GVOClient::connect(void)
{
// Register to Hub
register_to_hub();
// Send meta data
send_metadata();
// Return
return;
}
/***********************************************************************//**
* @brief Unregister client from SAMP Hub
*
* Disconnects the VO client from the Hub. The client is unregistered and the
* socket to the Hub is closed.
***************************************************************************/
void GVOClient::disconnect(void)
{
// Unregister from Hub
unregister_from_hub();
// Close socket
if (m_socket != -1) {
close(m_socket);
m_socket = -1;
}
// Return
return;
}
/***********************************************************************//**
* @brief Signals if client has Hub information
*
* @return True if client has Hub information, false otherwise.
*
* Checks if all mandatory Hub tokens are non-empty.
***************************************************************************/
bool GVOClient::has_hub(void) const
{
// Return Hub information status
return (!m_secret.empty() && !m_hub_url.empty() && !m_version.empty());
}
/***********************************************************************//**
* @brief Signals if client is connected to Hub
*
* @return True if client is connected to Hub, false otherwise.
*
* Checks if a client is connected to Hub.
***************************************************************************/
bool GVOClient::is_connected(void) const
{
// Return
return false;
}
/***********************************************************************//**
* @brief Returns Hub response a XML object
*
* @return Hub response.
***************************************************************************/
GXml GVOClient::response(void) const
{
// Declare empty XML document
GXml xml;
// Continue only if connection has been established
if (m_socket != -1) {
// Receive a string
std::string response = receive_string();
// Find start of XML text
size_t start = response.find("ai_next) {
// Create socket
m_socket = socket(ptr->ai_family,
ptr->ai_socktype,
ptr->ai_protocol);
// Connect to socket if socket is valid
if (m_socket != -1) {
if (::connect(m_socket,
ptr->ai_addr,
ptr->ai_addrlen) == -1) {
close(m_socket);
m_socket = -1;
}
}
} // endfor: looped through all results
} // endif: server information was valid
} // endif: host and port information found
// Return
return;
}
/***********************************************************************//**
* @brief Register client at SAMP Hub
***************************************************************************/
void GVOClient::register_to_hub(void)
{
// Connect to Hub
connect_to_hub();
// Continue only if Hub connection has been established
if (m_socket != -1) {
// Declare message
std::string msg = "";
// Set metadata header
msg.append("\n");
msg.append("\n");
msg.append("samp.hub.register\n");
msg.append("\n");
msg.append(""+m_secret+"\n");
msg.append("\n");
msg.append("\n");
// Post message
post_string(msg);
// Get Hub response
GXml xml = response();
// Extract Hub and client identifiers
m_client_key = get_response_value(xml, "samp.private-key");
m_hub_id = get_response_value(xml, "samp.hub-id");
m_client_id = get_response_value(xml, "samp.self-id");
} // endif: Hub connection has been established
// Return
return;
}
/***********************************************************************//**
* @brief Unregister client from SAMP Hub
***************************************************************************/
void GVOClient::unregister_from_hub(void)
{
// Connect to Hub
connect_to_hub();
// Continue only if Hub connection has been established
if (m_socket != -1) {
// Declare message
std::string msg = "";
// Set metadata header
msg.append("\n");
msg.append("\n");
msg.append("samp.hub.unregister\n");
msg.append("\n");
msg.append(""+m_client_key+"\n");
msg.append("\n");
msg.append("\n");
// Post message
post_string(msg);
// Get Hub response
GXml xml = response();
// Reset Hub and client identifiers
m_client_key.clear();
m_hub_id.clear();
m_client_id.clear();
} // endif: Hub connection has been established
// Return
return;
}
/***********************************************************************//**
* @brief Send client metadata to Hub
***************************************************************************/
void GVOClient::send_metadata(void)
{
// Connect to Hub. This was needed to post the metadata to the Hub.
// Apparently, the connection is lost after each write/read cycle.
// Maybe this is socket standard?
connect_to_hub();
// Continue only if Hub connection has been established
if (m_socket != -1) {
// Declare message
std::string msg = "";
// Set metadata header
msg.append("\n");
msg.append("\n");
msg.append("samp.hub.declareMetadata\n");
msg.append("\n");
msg.append(""+m_client_key+"\n");
msg.append("\n");
// Set SAMP name
msg.append("\n");
msg.append("samp.name\n");
msg.append(""+m_name+"\n");
msg.append("\n");
// Set SAMP description text
msg.append("\n");
msg.append("samp.description.text\n");
msg.append("GammaLib client\n");
msg.append("\n");
// Set SAMP icon URL
msg.append("\n");
msg.append("samp.icon.url\n");
msg.append("http://a.fsdn.com/allura/p/gammalib/icon\n");
msg.append("\n");
// Set author affiliation
msg.append("\n");
msg.append("author.affiliation\n");
msg.append("IRAP, Toulouse, France\n");
msg.append("\n");
// Set author e-mail
msg.append("\n");
msg.append("author.email\n");
msg.append("jurgen.knodlseder@irap.omp.eu\n");
msg.append("\n");
// Set author name
msg.append("\n");
msg.append("author.name\n");
msg.append("Juergen Knoedlseder\n");
msg.append("\n");
// Set metadata trailer
msg.append("\n");
msg.append("\n");
msg.append("\n");
// Post message
post_string(msg);
// Get Hub response
GXml xml = response();
} // endif: Hub connection has been established
// Return
return;
}
/***********************************************************************//**
* @brief Returns value for a SAMP Hub response parameter
*
* @param[in] xml Hub response XML document.
* @param[in] name Parameter name.
* @return Parameter value.
*
* Returns value for a SAMP Hub response parameter. If the specified
* parameter was not found or if the response structure is not compliant,
* an empty string is returned.
***************************************************************************/
std::string GVOClient::get_response_value(const GXml& xml,
const std::string& name) const
{
// Declare value
std::string value = "";
// Search for value of specified member
/*
const GXmlNode* node = xml.element("methodResponse", 0);
if (node != NULL) {
node = node->element("params", 0);
if (node != NULL) {
node = node->element("param", 0);
if (node != NULL) {
node = node->element("value", 0);
if (node != NULL) {
node = node->element("struct", 0);
if (node != NULL) {
int num = node->elements("member");
for (int i = 0; i < num; ++i) {
const GXmlNode* member = node->element("member", i);
std::string one_name;
std::string one_value;
get_name_value_pair(member, one_name, one_value);
if (one_name == name) {
value = one_value;
break;
}
}
}
}
}
}
}
*/
const GXmlNode* node = xml.element("methodResponse > params > param > value > struct");
if (node != NULL) {
int num = node->elements("member");
for (int i = 0; i < num; ++i) {
const GXmlNode* member = node->element("member", i);
std::string one_name;
std::string one_value;
get_name_value_pair(member, one_name, one_value);
if (one_name == name) {
value = one_value;
break;
}
}
}
// Return value
return value;
}
/***********************************************************************//**
* @brief Extract name / value pair from XML node
*
* @param[in] node Pointer to XML node.
* @param[out] name Name string.
* @param[out] value Value string.
*
* Extracts a name / value pair from a XML node. If the XML node pointer is
* NULL, the name and value strings will be empty.
***************************************************************************/
void GVOClient::get_name_value_pair(const GXmlNode* node,
std::string& name,
std::string& value) const
{
// Clear name and value strings
name.clear();
value.clear();
// Continue only if node is valid
if (node != NULL) {
// Get name node and extract text content
const GXmlNode* ptr = node->element("name", 0);
if (ptr != NULL) {
const GXmlText* text = static_cast((*ptr)[0]);
if (text != NULL) {
name = text->text();
}
}
// Get value node and extract text content
ptr = node->element("value", 0);
if (ptr != NULL) {
const GXmlText* text = static_cast((*ptr)[0]);
if (text != NULL) {
value = text->text();
}
}
}
// Return
return;
}
/***********************************************************************//**
* @brief Post string content to Hub
*
* @param[in] content String content to post
*
* Posts the content of a string to the Hub.
*
* The method does nothing if no Hub connection has been established.
***************************************************************************/
void GVOClient::post_string(const std::string& content) const
{
// Continue only if Hub connection has been established
if (m_socket != -1) {
// Determine content length
int length = content.length();
// Set prefix
std::string prefix = "POST /xmlrpc HTTP/1.0\n"
"User-Agent: GammaLib\n"
"Content-Type: text/xml\n"
"Content-Length: "+gammalib::str(length)+"\n\n";
// Build post string
std::string post = prefix + content;
// Send content to socket
bool done = false;
do {
int length = post.length();
int sent_length = send(m_socket, post.c_str(), length, 0);
if (sent_length < length) {
post = post.substr(sent_length, std::string::npos);
}
else {
done = true;
}
} while (!done);
} // endif: Hub connection had been established
// Return
return;
}
/***********************************************************************//**
* @brief Receive string content from Hub
*
* @return String received from Hub.
*
* Reads information sent by Hub into a string.
*
* The method does nothing if no Hub connection has been established.
***************************************************************************/
std::string GVOClient::receive_string(void) const
{
// Initialise empty string
std::string result = "";
// Continue only if Hub connection has been established
if (m_socket != -1) {
// Define buffer
char buffer[1001];
// Read buffer until it is empty
int n = 0;
do {
n = recv(m_socket, buffer, 1000, 0);
if (n > 0) {
buffer[n+1] = '\0';
result.append(std::string(buffer));
}
} while (n > 0);
} // endif: Hub connection had been established
// Return result
return result;
}
/***********************************************************************//**
* @brief Returns SAMP Hub lockfile URL
*
* @return SAMP Hub lockfile URL (empty if no lockfile was found).
*
* Implements IVOA standard REC-SAMP-1.3-20120411.
***************************************************************************/
std::string GVOClient::get_hub_lockfile(void) const
{
// Initialise result
std::string url = "";
// Check for existence of the SAMP_HUB environment variable first
char* hub_ptr = std::getenv("SAMP_HUB");
if (hub_ptr != NULL) {
// Check for mandatory std-lockurl: prefix (no other prefixe is
// supported so far)
std::string lockurl = std::string(hub_ptr);
if (lockurl.compare(0, 12, "std-lockurl:") == 0) {
// Extract URL
url = lockurl.substr(12, std::string::npos);
} // endif: std-lockurl: prefix found
}
// ... otherwise the lockfile should be $HOME/.samp
else {
// Get user's HOME directory path as the prefix of the full
// path. If the HOME environment variable is not set we
// expect that .samp is in the local directory. This is non
// standard, but prevents for creating an exception here.
std::string prefix = "";
char* home_ptr = std::getenv("HOME");
if (home_ptr != NULL) {
prefix = std::string(home_ptr) + "/";
}
// Set filename
url = prefix + ".samp";
} // endelse: no SAMP_HUB environment variable found
// Return URL
return url;
}