#include <stdlib.h>
#include "TUrl.h"
#include "THashList.h"
#include "TObjArray.h"
#include "TObjString.h"
#include "TEnv.h"
#include "TSystem.h"
#include "TMap.h"
#include "TVirtualMutex.h"
TObjArray *TUrl::fgSpecialProtocols = 0;
THashList *TUrl::fgHostFQDNs = 0;
TVirtualMutex *gURLMutex = 0;
#ifdef R__COMPLETE_MEM_TERMINATION
namespace {
class TUrlCleanup {
TObjArray **fSpecialProtocols;
THashList **fHostFQDNs;
public:
TUrlCleanup(TObjArray **protocols, THashList **hosts) : fSpecialProtocols(protocols),fHostFQDNs(hosts) {}
~TUrlCleanup() {
if (*fSpecialProtocols) (*fSpecialProtocols)->Delete();
delete *fSpecialProtocols;
*fSpecialProtocols = 0;
if (*fHostFQDNs) (*fHostFQDNs)->Delete();
delete *fHostFQDNs;
*fHostFQDNs = 0;
}
};
}
#endif
ClassImp(TUrl)
TUrl::TUrl(const char *url, Bool_t defaultIsFile)
{
SetUrl(url, defaultIsFile);
#ifdef R__COMPLETE_MEM_TERMINATION
static TUrlCleanup cleanup(&fgSpecialProtocols,&fgHostFQDNs);
#endif
}
TUrl::~TUrl()
{
delete fOptionsMap;
}
void TUrl::SetUrl(const char *url, Bool_t defaultIsFile)
{
fOptionsMap = 0;
if (!url || !url[0]) {
fPort = -1;
return;
}
fUrl = "";
fProtocol = "http";
fUser = "";
fPasswd = "";
fHost = "";
fPort = 80;
fFile = "";
fAnchor = "";
fOptions = "";
fFileOA = "";
fHostFQ = "";
if (url[0] == '/')
defaultIsFile = kTRUE;
char *s, sav;
char *u, *u0 = Strip(url);
tryfile:
u = u0;
for (int i = 0; i < GetSpecialProtocols()->GetEntriesFast(); i++) {
TObjString *os = (TObjString*) GetSpecialProtocols()->UncheckedAt(i);
TString s1 = os->GetString();
int l = s1.Length();
Bool_t stripoff = kFALSE;
if (s1.EndsWith("/-")) {
stripoff = kTRUE;
s1 = s1.Strip(TString::kTrailing, '-');
l--;
}
if (!strncmp(u, s1, l)) {
if (s1(0) == '/' && s1(l-1) == '/') {
fProtocol = s1(1, l-2);
if (stripoff)
l--;
else
l = 0;
} else {
fProtocol = s1(0, l-1);
}
if (!strncmp(u+l, "//", 2))
u += l+2;
else
u += l;
fPort = 0;
FindFile(u, kFALSE);
delete [] u0;
return;
}
}
u = u0;
char *x, *t, *s2;
if ((s = strstr(u, ":/")) && u+1 != s) {
if (*(s+2) != '/') {
Error("TUrl", "%s malformed, URL must contain \"://\"", u0);
fPort = -1;
goto cleanup;
}
sav = *s;
*s = 0;
SetProtocol(u, kTRUE);
*s = sav;
s += 3;
} else {
if (defaultIsFile) {
char *newu = new char [strlen("file:") + strlen(u0) + 1];
sprintf(newu, "file:%s", u0);
delete [] u0;
u0 = newu;
goto tryfile;
}
s = u;
}
u = s;
t = s;
again:
if ((s = strchr(t, '@')) && (
((x = strchr(t, '/')) && s < x) ||
((x = strchr(t, '?')) && s < x) ||
((x = strchr(t, '#')) && s < x) ||
(!strchr(t, '/'))
)) {
if (*(s-1) == '\\') {
t = s+1;
goto again;
}
sav = *s;
*s = 0;
if ((s2 = strchr(u, ':'))) {
*s2 = 0;
fUser = u;
*s2 = ':';
s2++;
if (*s2) {
fPasswd = s2;
fPasswd.ReplaceAll("\\@", "@");
}
} else
fUser = u;
*s = sav;
s++;
} else
s = u;
u = s;
if ((s = strchr(u, ':')) || (s = strchr(u, '/')) || (s = strchr(u, '?')) || (s = strchr(u, '#'))) {
if ((strchr (u, ':') > strchr(u, '/')) && (strchr (u, '/')))
s = strchr(u, '/');
sav = *s;
*s = 0;
fHost = u;
*s = sav;
if (sav == ':') {
s++;
if (!*s) {
fPort = -1;
goto cleanup;
}
u = s;
if ((s = strchr(u, '/')) || (s = strchr(u, '?')) || (s = strchr(u, '#'))) {
sav = *s;
*s = 0;
fPort = atoi(u);
*s = sav;
} else {
fPort = atoi(u);
goto cleanup;
}
}
} else {
fHost = u;
goto cleanup;
}
if (!*s) goto cleanup;
u = s;
if (*u == '/' && fHost.Length())
u++;
FindFile(u);
cleanup:
delete [] u0;
}
void TUrl::FindFile(char *u, Bool_t stripDoubleSlash)
{
char *s, sav;
char *opt = strchr(u, '?');
char *anc = strchr(u, '#');
if (opt && anc && opt > anc) {
fPort = -1;
return;
}
if ((s = opt) || (s = anc)) {
sav = *s;
*s = 0;
fFile = u;
if (stripDoubleSlash)
fFile.ReplaceAll("//", "/");
*s = sav;
s++;
if (sav == '?') {
if (!*s) {
return;
}
u = s;
if ((s = strchr(u, '#'))) {
sav = *s;
*s = 0;
fOptions = u;
*s = sav;
s++;
} else {
fOptions = u;
return;
}
}
if (!*s) {
return;
}
} else {
fFile = u;
if (stripDoubleSlash)
fFile.ReplaceAll("//", "/");
return;
}
fAnchor = s;
}
TUrl::TUrl(const TUrl &url) : TObject(url)
{
fUrl = url.fUrl;
fProtocol = url.fProtocol;
fUser = url.fUser;
fPasswd = url.fPasswd;
fHost = url.fHost;
fFile = url.fFile;
fAnchor = url.fAnchor;
fOptions = url.fOptions;
fPort = url.fPort;
fFileOA = url.fFileOA;
fHostFQ = url.fHostFQ;
fOptionsMap = 0;
}
TUrl &TUrl::operator=(const TUrl &rhs)
{
if (this != &rhs) {
TObject::operator=(rhs);
fUrl = rhs.fUrl;
fProtocol = rhs.fProtocol;
fUser = rhs.fUser;
fPasswd = rhs.fPasswd;
fHost = rhs.fHost;
fFile = rhs.fFile;
fAnchor = rhs.fAnchor;
fOptions = rhs.fOptions;
fPort = rhs.fPort;
fFileOA = rhs.fFileOA;
fHostFQ = rhs.fHostFQ;
fOptionsMap = 0;
}
return *this;
}
const char *TUrl::GetUrl(Bool_t withDeflt) const
{
if (((TestBit(kUrlWithDefaultPort) && !withDeflt) ||
(!TestBit(kUrlWithDefaultPort) && withDeflt)) &&
TestBit(kUrlHasDefaultPort))
fUrl = "";
if (IsValid() && fUrl == "") {
for (int i = 0; i < GetSpecialProtocols()->GetEntriesFast(); i++) {
TObjString *os = (TObjString*) GetSpecialProtocols()->UncheckedAt(i);
TString &s = os->String();
int l = s.Length();
if (fProtocol == s(0, l-1)) {
if (fFile[0] == '/')
fUrl = fProtocol + "://" + fFile;
else
fUrl = fProtocol + ":" + fFile;
if (fOptions != "") {
fUrl += "?";
fUrl += fOptions;
}
if (fAnchor != "") {
fUrl += "#";
fUrl += fAnchor;
}
return fUrl;
}
}
Bool_t deflt = kFALSE;
if ((!fProtocol.CompareTo("http") && fPort == 80) ||
(fProtocol.BeginsWith("proof") && fPort == 1093) ||
(fProtocol.BeginsWith("root") && fPort == 1094) ||
(!fProtocol.CompareTo("ftp") && fPort == 20) ||
(!fProtocol.CompareTo("news") && fPort == 119) ||
(!fProtocol.CompareTo("https") && fPort == 443) ||
fPort == 0) {
deflt = kTRUE;
((TUrl *)this)->SetBit(kUrlHasDefaultPort);
}
fUrl = fProtocol + "://";
if (fUser != "") {
fUrl += fUser;
if (fPasswd != "") {
fUrl += ":";
TString passwd = fPasswd;
passwd.ReplaceAll("@", "\\@");
fUrl += passwd;
}
fUrl += "@";
}
if (withDeflt)
((TUrl*)this)->SetBit(kUrlWithDefaultPort);
else
((TUrl*)this)->ResetBit(kUrlWithDefaultPort);
if (!deflt || withDeflt) {
char p[10];
sprintf(p, "%d", fPort);
fUrl = fUrl + fHost + ":" + p + "/" + fFile;
} else
fUrl = fUrl + fHost + "/" + fFile;
if (fOptions != "") {
fUrl += "?";
fUrl += fOptions;
}
if (fAnchor != "") {
fUrl += "#";
fUrl += fAnchor;
}
}
fUrl.ReplaceAll("////", "///");
return fUrl;
}
const char *TUrl::GetHostFQDN() const
{
if (fHostFQ == "") {
TNamed *fqdn = fgHostFQDNs ? (TNamed *) fgHostFQDNs->FindObject(fHost) : 0;
if (!fqdn) {
TInetAddress adr(gSystem->GetHostByName(fHost));
if (adr.IsValid()) {
fHostFQ = adr.GetHostName();
} else
fHostFQ = "-";
R__LOCKGUARD2(gURLMutex);
if (!fgHostFQDNs) {
fgHostFQDNs = new THashList;
fgHostFQDNs->SetOwner();
}
if (fgHostFQDNs && !fgHostFQDNs->FindObject(fHost))
fgHostFQDNs->Add(new TNamed(fHost,fHostFQ));
} else {
fHostFQ = fqdn->GetTitle();
}
}
if (fHostFQ == "-")
return fHost;
return fHostFQ;
}
const char *TUrl::GetFileAndOptions() const
{
if (fFileOA == "") {
fFileOA = fFile;
if (fOptions != "") {
fFileOA += "?";
fFileOA += fOptions;
}
if (fAnchor != "") {
fFileOA += "#";
fFileOA += fAnchor;
}
}
return fFileOA;
}
void TUrl::SetProtocol(const char *proto, Bool_t setDefaultPort)
{
fProtocol = proto;
if (setDefaultPort) {
if (!fProtocol.CompareTo("http"))
fPort = 80;
else if (!fProtocol.CompareTo("https"))
fPort = 443;
else if (fProtocol.BeginsWith("proof"))
fPort = 1093;
else if (fProtocol.BeginsWith("root"))
fPort = 1094;
else if (!fProtocol.CompareTo("ftp"))
fPort = 20;
else if (!fProtocol.CompareTo("news"))
fPort = 119;
else {
fPort = 0;
}
}
fUrl = "";
}
Int_t TUrl::Compare(const TObject *obj) const
{
if (this == obj) return 0;
if (TUrl::Class() != obj->IsA()) return -1;
return TString(GetUrl()).CompareTo(((TUrl*)obj)->GetUrl(), TString::kExact);
}
void TUrl::Print(Option_t *) const
{
if (fPort == -1)
Printf("Illegal URL");
Printf("%s", GetUrl());
}
TObjArray *TUrl::GetSpecialProtocols()
{
static Bool_t usedEnv = kFALSE;
if (!gEnv) {
R__LOCKGUARD2(gURLMutex);
if (!fgSpecialProtocols)
fgSpecialProtocols = new TObjArray;
if (fgSpecialProtocols->GetEntriesFast() == 0)
fgSpecialProtocols->Add(new TObjString("file:"));
return fgSpecialProtocols;
}
if (usedEnv)
return fgSpecialProtocols;
R__LOCKGUARD2(gURLMutex);
if (fgSpecialProtocols)
fgSpecialProtocols->Delete();
if (!fgSpecialProtocols)
fgSpecialProtocols = new TObjArray;
const char *protos = gEnv->GetValue("Url.Special", "file: rfio: hpss: castor: dcache: dcap:");
usedEnv = kTRUE;
if (protos) {
Int_t cnt = 0;
char *p = StrDup(protos);
while (1) {
TObjString *proto = new TObjString(strtok(!cnt ? p : 0, " "));
if (proto->String().IsNull()) {
delete proto;
break;
}
fgSpecialProtocols->Add(proto);
cnt++;
}
delete [] p;
}
return fgSpecialProtocols;
}
void TUrl::ParseOptions() const
{
if (fOptionsMap) return;
TString urloptions = GetOptions();
TObjArray *objOptions = urloptions.Tokenize("&");
for (Int_t n = 0; n < objOptions->GetEntries(); n++) {
TString loption = ((TObjString *) objOptions->At(n))->GetName();
TObjArray *objTags = loption.Tokenize("=");
if (!fOptionsMap) {
fOptionsMap = new TMap;
fOptionsMap->SetOwnerKeyValue();
}
if (objTags->GetEntries() == 2) {
TString key = ((TObjString *) objTags->At(0))->GetName();
TString value = ((TObjString *) objTags->At(1))->GetName();
fOptionsMap->Add(new TObjString(key), new TObjString(value));
} else {
TString key = ((TObjString *) objTags->At(0))->GetName();
fOptionsMap->Add(new TObjString(key), 0);
}
delete objTags;
}
delete objOptions;
}
const char *TUrl::GetValueFromOptions(const char *key) const
{
if (!key) return 0;
ParseOptions();
TObject *option = fOptionsMap ? fOptionsMap->GetValue(key) : 0;
return (option ? ((TObjString*)fOptionsMap->GetValue(key))->GetName(): 0);
}
Int_t TUrl::GetIntValueFromOptions(const char *key) const
{
if (!key) return -1;
ParseOptions();
TObject *option = fOptionsMap ? fOptionsMap->GetValue(key) : 0;
return (option ? (atoi(((TObjString*)fOptionsMap->GetValue(key))->GetName())) : -1);
}
Bool_t TUrl::HasOption(const char *key) const
{
if (!key) return kFALSE;
ParseOptions();
if (fOptionsMap && fOptionsMap->FindObject(key))
return kTRUE;
return kFALSE;
}
void TUrl::CleanRelativePath()
{
Ssiz_t slash = 0;
while ( (slash = fFile.Index("/..") ) != kNPOS) {
Bool_t found = kFALSE;
for (int l = slash-1; l >=0; l--) {
if (fFile[l] == '/') {
fFile.Remove(l, slash+3-l);
found = kTRUE;
break;
}
}
if (!found)
break;
}
}