#include <ftl/uri.hpp> #include <nlohmann/json.hpp> // #include <filesystem> TODO When available #include <cstdlib> #include <loguru.hpp> #ifndef WIN32 #include <unistd.h> #else #include <direct.h> #endif using ftl::URI; using ftl::uri_t; using std::string; URI::URI(uri_t puri) { _parse(puri); } URI::URI(const std::string &puri) { _parse(puri.c_str()); } URI::URI(const URI &c) { m_valid = c.m_valid; m_host = c.m_host; m_port = c.m_port; m_proto = c.m_proto; m_path = c.m_path; m_pathseg = c.m_pathseg; m_qmap = c.m_qmap; m_base = c.m_base; m_userinfo = c.m_userinfo; m_frag = c.m_frag; } void URI::_parse(uri_t puri) { UriUriA uri; std::string suri = puri; // NOTE: Non-standard additions to allow for Unix style relative file names. if (suri[0] == '.') { char cwdbuf[1024]; if (getcwd(cwdbuf, 1024)) { suri = string("file://") + string(cwdbuf) + suri.substr(1); } } else if (suri[0] == '/') { suri = std::string("file://") + suri; } else if (suri[0] == '~') { #ifdef WIN32 suri = string("file://") + string(std::getenv("HOMEDRIVE")) + string(std::getenv("HOMEPATH")) + suri.substr(1); #else suri = string("file://") + string(std::getenv("HOME")) + suri.substr(1); #endif } #ifdef HAVE_URIPARSESINGLE const char *errpos; if (uriParseSingleUriA(&uri, puri, &errpos) != URI_SUCCESS) { #else UriParserStateA uris; uris.uri = &uri; if (uriParseUriA(&uris, suri.c_str()) != URI_SUCCESS) { #endif m_valid = false; m_host = "none"; m_port = -1; m_proto = SCHEME_NONE; m_path = ""; m_frag = ""; } else { m_host = std::string(uri.hostText.first, uri.hostText.afterLast - uri.hostText.first); std::string prototext = std::string(uri.scheme.first, uri.scheme.afterLast - uri.scheme.first); if (prototext == "tcp") m_proto = SCHEME_TCP; else if (prototext == "udp") m_proto = SCHEME_UDP; else if (prototext == "ftl") m_proto = SCHEME_FTL; else if (prototext == "http") m_proto = SCHEME_HTTP; else if (prototext == "ws") m_proto = SCHEME_WS; else if (prototext == "ipc") m_proto = SCHEME_IPC; else if (prototext == "device") m_proto = SCHEME_DEVICE; else if (prototext == "file") m_proto = SCHEME_FILE; else m_proto = SCHEME_OTHER; m_protostr = prototext; std::string porttext = std::string(uri.portText.first, uri.portText.afterLast - uri.portText.first); m_port = atoi(porttext.c_str()); m_userinfo = std::string(uri.userInfo.first, uri.userInfo.afterLast - uri.userInfo.first); for (auto h=uri.pathHead; h!=NULL; h=h->next) { auto pstr = std::string( h->text.first, h->text.afterLast - h->text.first); m_path += "/"; m_path += pstr; m_pathseg.push_back(pstr); } //string query = std::string(uri.query.first, uri.query.afterLast - uri.query.first); if (uri.query.afterLast - uri.query.first > 0) { UriQueryListA *queryList; int itemCount; if (uriDissectQueryMallocA(&queryList, &itemCount, uri.query.first, uri.query.afterLast) != URI_SUCCESS) { // Failure } UriQueryListA *item = queryList; while (item) { m_qmap[item->key] = item->value; item = item->next; } uriFreeQueryListA(queryList); } uriFreeUriMembersA(&uri); auto fraglast = (uri.query.first != NULL) ? uri.query.first : uri.fragment.afterLast; if (uri.fragment.first != NULL && fraglast - uri.fragment.first > 0) { m_frag = std::string(uri.fragment.first, fraglast - uri.fragment.first); } m_valid = m_proto != SCHEME_NONE && (m_host.size() > 0 || m_path.size() > 0); if (m_valid) { // remove userinfo from base uri const char *start = uri.scheme.first; if (m_userinfo != "") { m_base = std::string(start, uri.userInfo.first - start); start = uri.userInfo.afterLast + 1; } else { m_base = std::string(""); } if (m_qmap.size() > 0) { m_base += std::string(start, uri.query.first - start - 1); } else if (uri.fragment.first != NULL) { m_base += std::string(start, uri.fragment.first - start - 1); } else if (start) { m_base += std::string(start); } else { m_base += std::string(""); } } } } string URI::to_string() const { return (m_qmap.size() > 0) ? m_base + "?" + getQuery() : m_base; } string URI::getPathSegment(int n) const { size_t N = (n < 0) ? m_pathseg.size()+n : n; if (N < 0 || N >= m_pathseg.size()) return ""; else return m_pathseg[N]; } string URI::getBaseURI(int n) { if (n >= (int)m_pathseg.size()) return m_base; if (n >= 0) { string r = m_protostr + string("://") + m_host + ((m_port != 0) ? string(":") + std::to_string(m_port) : ""); for (int i=0; i<n; i++) { r += "/"; r += getPathSegment(i); } return r; } else if (m_pathseg.size()+n >= 0) { string r = m_protostr + string("://") + m_host + ((m_port != 0) ? string(":") + std::to_string(m_port) : ""); size_t N = m_pathseg.size()+n; for (size_t i=0; i<N; i++) { r += "/"; r += getPathSegment(static_cast<int>(i)); } return r; } else return ""; } string URI::getQuery() const { string q; for (auto x : m_qmap) { if (q.length() > 0) q += "&"; q += x.first + "=" + x.second; } return q; }; void URI::setAttribute(const string &key, const string &value) { m_qmap[key] = value; } void URI::setAttribute(const string &key, int value) { m_qmap[key] = std::to_string(value); } void URI::to_json(nlohmann::json &json) { std::string uri = to_string(); if (m_frag.size() > 0) uri += std::string("#") + getFragment(); json["uri"] = uri; for (auto i : m_qmap) { auto *current = &json; size_t pos = 0; size_t lpos = 0; while ((pos = i.first.find('/', lpos)) != std::string::npos) { std::string subobj = i.first.substr(lpos, pos-lpos); current = &((*current)[subobj]); lpos = pos+1; } std::string obj = i.first.substr(lpos); auto p = nlohmann::json::parse(i.second, nullptr, false); if (!p.is_discarded()) { (*current)[obj] = p; } else { (*current)[obj] = i.second; } } } bool URI::hasUserInfo() const { return m_userinfo != ""; } const std::string &URI::getUserInfo() const { return m_userinfo; }