Back to index

nordugrid-arc-nox  1.1.0~rc6
URL.cpp
Go to the documentation of this file.
00001 // -*- indent-tabs-mode: nil -*-
00002 
00003 #ifdef HAVE_CONFIG_H
00004 #include <config.h>
00005 #endif
00006 
00007 #include <fstream>
00008 
00009 #include <unistd.h>
00010 
00011 #include <glibmm/miscutils.h>
00012 
00013 #ifdef WIN32
00014 #include "win32.h"
00015 #endif
00016 
00017 #include <arc/Logger.h>
00018 #include <arc/StringConv.h>
00019 #include <arc/URL.h>
00020 
00021 
00022 namespace Arc {
00023 
00024   static Logger URLLogger(Logger::getRootLogger(), "URL");
00025 
00026   std::map<std::string, std::string> URL::ParseOptions(const std::string& optstring, char separator) {
00027 
00028     std::map<std::string, std::string> options;
00029 
00030     if (optstring.empty())
00031       return options;
00032 
00033     std::string::size_type pos = 0;
00034     while (pos != std::string::npos) {
00035 
00036       std::string::size_type pos2 = optstring.find(separator, pos);
00037 
00038       std::string opt = (pos2 == std::string::npos ?
00039                          optstring.substr(pos) :
00040                          optstring.substr(pos, pos2 - pos));
00041 
00042       pos = pos2;
00043       if (pos != std::string::npos)
00044         pos++;
00045 
00046       pos2 = opt.find('=');
00047       std::string option_name, option_value = "";
00048       if (pos2 == std::string::npos) {
00049         option_name = opt;
00050       } else {
00051         option_name = opt.substr(0, pos2);
00052         option_value = opt.substr(pos2 + 1);
00053       }
00054       options[option_name] = option_value;
00055     }
00056     return options;
00057   }
00058 
00059   static std::list<std::string> ParseAttributes(const std::string& attrstring, char separator) {
00060 
00061     std::list<std::string> attributes;
00062 
00063     if (attrstring.empty())
00064       return attributes;
00065 
00066     std::string::size_type pos = 0;
00067     while (pos != std::string::npos) {
00068 
00069       std::string::size_type pos2 = attrstring.find(separator, pos);
00070 
00071       std::string attr = (pos2 == std::string::npos ?
00072                           attrstring.substr(pos) :
00073                           attrstring.substr(pos, pos2 - pos));
00074 
00075       pos = pos2;
00076       if (pos != std::string::npos)
00077         pos++;
00078 
00079       attributes.push_back(attr);
00080     }
00081     return attributes;
00082   }
00083 
00084 
00085   static std::string AttributeString(const std::list<std::string> attributes,
00086                                      char separator) {
00087 
00088     std::string attrstring;
00089 
00090     if (attributes.empty())
00091       return attrstring;
00092 
00093     for (std::list<std::string>::const_iterator it = attributes.begin();
00094          it != attributes.end(); it++) {
00095       if (it != attributes.begin())
00096         attrstring += separator;
00097       attrstring += *it;
00098     }
00099     return attrstring;
00100   }
00101 
00102 
00103   URL::URL()
00104     : ip6addr(false),
00105       port(-1),
00106       ldapscope(base),
00107       valid(false) {}
00108 
00109   URL::URL(const std::string& url)
00110     : ip6addr(false),
00111       port(-1),
00112       ldapscope(base),
00113       valid(true) {
00114 
00115     std::string::size_type pos, pos2, pos3;
00116 
00117     if (url[0] == '\0') {
00118       valid = false;
00119       return;
00120     }
00121 
00122     if (url[0] == '#') {
00123       URLLogger.msg(ERROR, "URL is not valid: %s", url);
00124       valid = false;
00125       return;
00126     }
00127 
00128     // Looking for protocol separator
00129     pos = url.find(":");
00130     if (pos != std::string::npos) {
00131       // Check if protocol looks like protocol
00132       for(std::string::size_type p = 0; p < pos; ++p) {
00133         char c = url[p];
00134         if(isalnum(c) || (c == '+') || (c == '-') || (c == '.')) continue;
00135         pos = std::string::npos;
00136         break;
00137       }
00138 #ifdef WIN32
00139       // Windows paths look like protocols - additional checks are needed
00140       // So for windows it looks like "disk:\"
00141       // TODO: probably some additional check is needed for "disk:"-like
00142       // paths. If such path can exist at all.
00143       if(pos != std::string::npos) {
00144         if(url[pos+1] == '\\') {
00145           pos = std::string::npos;
00146         }
00147       }
00148 #endif
00149     }
00150     if (pos == std::string::npos) {
00151       // URL does not start from protocol - must be simple path
00152       if (url[0] == '@') {
00153         protocol = "urllist";
00154         path = url.substr(1);
00155       }
00156       else {
00157         protocol = "file";
00158         path = url;
00159       }
00160       if (!Glib::path_is_absolute(path)) {
00161         char cwd[PATH_MAX];
00162         if (getcwd(cwd, PATH_MAX))
00163           path = Glib::build_filename(cwd, path);
00164       }
00165       // Simple paths are not expected to contain any options or metadata
00166       return;
00167     }
00168 
00169     // RFC says protocols should be lowercase and uppercase
00170     // must be converted to lowercase for consistency
00171     protocol = lower(url.substr(0, pos));
00172 
00173     // Checking if protocol followed by host/authority part
00174     // or by path directly
00175     if((url[pos+1] != '/') || (url[pos+2] != '/')) {
00176       // No host part
00177       host = "";
00178       pos += 1;
00179       pos2 = pos; // path start position
00180       path = url.substr(pos2);
00181       // This must be only path - we can accept path only for
00182       // limited set of protocols
00183       if ((protocol == "file" || protocol == "urllist")) {
00184         if (!Glib::path_is_absolute(path)) {
00185           char cwd[PATH_MAX];
00186           if (getcwd(cwd, PATH_MAX))
00187             path = Glib::build_filename(cwd, path);
00188         }
00189         return;
00190       } else if (protocol == "arc") {
00191         // TODO: It is not defined how arc protocol discovers
00192         // entry point in general case.
00193         // For same reason let's assume path must be always
00194         // absolute.
00195         if(url[pos] != '/') {
00196           URLLogger.msg(ERROR, "Illegal URL - path must be absolute");
00197           valid = false;
00198           return;
00199         }
00200       } else {
00201         URLLogger.msg(ERROR, "Illegal URL - no hostname given");
00202         valid = false;
00203         return;
00204       }
00205     } else {
00206       // There is host/authority part in this URL. That also
00207       // means path is absolute if present
00208       pos += 3;
00209 
00210       pos2 = url.find("@", pos);
00211       if (pos2 != std::string::npos) {
00212 
00213         if (protocol == "rc" || protocol == "rls" ||
00214             protocol == "fireman" || protocol == "lfc") {
00215           // Indexing protocols may contain locations
00216 
00217           std::string locstring = url.substr(pos, pos2 - pos);
00218           pos = pos2 + 1;
00219 
00220           pos2 = 0;
00221           while (pos2 != std::string::npos) {
00222 
00223             pos3 = locstring.find('|', pos2);
00224             std::string loc = (pos3 == std::string::npos ?
00225                                locstring.substr(pos2) :
00226                                locstring.substr(pos2, pos3 - pos2));
00227 
00228             pos2 = pos3;
00229             if (pos2 != std::string::npos)
00230               pos2++;
00231 
00232             if (loc[0] == ';')
00233               commonlocoptions = ParseOptions(loc.substr(1), ';');
00234             else {
00235               if (protocol == "rc") {
00236                 pos3 = loc.find(';');
00237                 if (pos3 == std::string::npos)
00238                   locations.push_back(URLLocation(ParseOptions("", ';'), loc));
00239                 else
00240                   locations.push_back(URLLocation(ParseOptions
00241                                                   (loc.substr(pos3 + 1), ';'),
00242                                                   loc.substr(pos3 + 1)));
00243               }
00244               else
00245                 locations.push_back(loc);
00246             }
00247           }
00248         }
00249         else {
00250           pos3 = url.find("/", pos);
00251           if (pos3 == std::string::npos)
00252             pos3 = url.length();
00253           if (pos3 > pos2) {
00254             username = url.substr(pos, pos2 - pos);
00255             pos3 = username.find(':');
00256             if (pos3 != std::string::npos) {
00257               passwd = username.substr(pos3 + 1);
00258               username.resize(pos3);
00259             }
00260             pos = pos2 + 1;
00261           }
00262         }
00263       }
00264 
00265       // Looking for end of host/authority part
00266       pos2 = url.find("/", pos);
00267       if (pos2 == std::string::npos) {
00268         // Path part is empty, host may be empty too
00269         host = url.substr(pos);
00270         path = "";
00271       }
00272       else if (pos2 == pos) {
00273         // Empty host and non-empty absolute path
00274         host = "";
00275         path = url.substr(pos2);
00276       }
00277       else {
00278         // Both host and absolute path present
00279         host = url.substr(pos, pos2 - pos);
00280         path = url.substr(pos2);
00281       }
00282     }
00283 
00284     // At this point path must be absolutely absolute (starts with /) or empty
00285     if ((!path.empty()) && (path[0] != '/')) {
00286       URLLogger.msg(ERROR, "Illegal URL - path must be absolute or empty");
00287       valid = false;
00288       return;
00289     }
00290 
00291     // Extracting port URL options (ARC extension)
00292     if (!host.empty()) {
00293       // Check for [ip6address] notation
00294       // If behaving strictly we should check for valid address 
00295       // inside []. But if we do not do that only drawback is that
00296       // URL may have any hostname inside []. Not really important
00297       // issue.
00298       if(host[0] == '[') {
00299         ip6addr = true;
00300         pos2 = host.find(']');
00301         if(pos2 == std::string::npos) {
00302           URLLogger.msg(ERROR, "Illegal URL - no closing ] for IPv6 address found");
00303           valid = false;
00304           return;
00305         }
00306         // There may be only port or options after closing ]
00307         ++pos2;
00308         if(pos2 < host.length()) {
00309           if((host[pos2] != ':') && (host[pos2] != ';')) {
00310             URLLogger.msg(ERROR, "Illegal URL - closing ] for IPv6 address is at followed by illegal token");
00311             valid = false;
00312             return;
00313           }
00314           if(host[pos2] != ':') pos2 = std::string::npos;
00315         } else {
00316           pos2 = std::string::npos;
00317         }
00318       } else {
00319         pos2 = host.find(':');
00320       }
00321       if (pos2 != std::string::npos) {
00322         pos3 = host.find(';', pos2);
00323         port = stringtoi(pos3 == std::string::npos ?
00324                          host.substr(pos2 + 1) :
00325                          host.substr(pos2 + 1, pos3 - pos2 - 1));
00326       }
00327       else {
00328         pos3 = host.find(';');
00329         pos2 = pos3;
00330       }
00331       if (pos3 != std::string::npos)
00332         urloptions = ParseOptions(host.substr(pos3 + 1), ';');
00333       if (pos2 != std::string::npos) 
00334         host.resize(pos2);
00335       if (ip6addr)
00336         host = host.substr(1,host.length()-2);
00337     }
00338 
00339     if (port == -1) {
00340       if (protocol == "rc")
00341         port = RC_DEFAULT_PORT;
00342       if (protocol == "rls")
00343         port = RLS_DEFAULT_PORT;
00344       if (protocol == "http")
00345         port = HTTP_DEFAULT_PORT;
00346       if (protocol == "https")
00347         port = HTTPS_DEFAULT_PORT;
00348       if (protocol == "httpg")
00349         port = HTTPG_DEFAULT_PORT;
00350       if (protocol == "srm")
00351         port = SRM_DEFAULT_PORT;
00352       if (protocol == "ldap")
00353         port = LDAP_DEFAULT_PORT;
00354       if (protocol == "ftp")
00355         port = FTP_DEFAULT_PORT;
00356       if (protocol == "gsiftp")
00357         port = GSIFTP_DEFAULT_PORT;
00358       if (protocol == "lfc")
00359         port = LFC_DEFAULT_PORT;
00360     }
00361 
00362     if (protocol != "ldap" && protocol != "arc") {
00363       pos2 = path.rfind('=');
00364       if (pos2 != std::string::npos) {
00365         pos3 = path.rfind(':', pos2);
00366         if (pos3 != std::string::npos) {
00367           pos = pos3;
00368           while (pos2 != std::string::npos && pos3 != std::string::npos) {
00369             pos2 = path.rfind('=', pos);
00370             if (pos2 != std::string::npos) {
00371               pos3 = path.rfind(':', pos2);
00372               if (pos3 != std::string::npos)
00373                 pos = pos3;
00374             }
00375           }
00376           metadataoptions = ParseOptions(path.substr(pos + 1), ':');
00377           path = path.substr(0, pos);
00378         }
00379       }
00380     }
00381 
00382     // if protocol = http, get the options after the ?
00383     if (protocol == "http" ||
00384         protocol == "https" ||
00385         protocol == "httpg" ||
00386         protocol == "arc" ||
00387         protocol == "srm") {
00388       pos = path.find("?");
00389       if (pos != std::string::npos) {
00390         httpoptions = ParseOptions(path.substr(pos + 1), '&');
00391         path = path.substr(0, pos);
00392       }
00393     }
00394 
00395     // parse ldap protocol specific attributes
00396     if (protocol == "ldap") {
00397       std::string ldapscopestr;
00398       pos = path.find('?');
00399       if (pos != std::string::npos) {
00400         pos2 = path.find('?', pos + 1);
00401         if (pos2 != std::string::npos) {
00402           pos3 = path.find('?', pos2 + 1);
00403           if (pos3 != std::string::npos) {
00404             ldapfilter = path.substr(pos3 + 1);
00405             ldapscopestr = path.substr(pos2 + 1, pos3 - pos2 - 1);
00406           }
00407           else
00408             ldapscopestr = path.substr(pos2 + 1);
00409           ldapattributes = ParseAttributes(path.substr(pos + 1,
00410                                                        pos2 - pos - 1), ',');
00411         }
00412         else
00413           ldapattributes = ParseAttributes(path.substr(pos + 1), ',');
00414         path = path.substr(0, pos);
00415       }
00416       if (ldapscopestr == "base")
00417         ldapscope = base;
00418       else if (ldapscopestr == "one")
00419         ldapscope = onelevel;
00420       else if (ldapscopestr == "sub")
00421         ldapscope = subtree;
00422       else if (!ldapscopestr.empty())
00423         URLLogger.msg(ERROR, "Unknown LDAP scope %s - using base",
00424                       ldapscopestr);
00425       if (ldapfilter.empty())
00426         ldapfilter = "(objectClass=*)";
00427       if (path.find("/",1) != std::string::npos)
00428         path = Path2BaseDN(path);
00429       else
00430         path.erase(0,1);
00431     }
00432 
00433     // Normally host/authority names are case-insensitive
00434     host = lower(host);
00435   }
00436 
00437   URL::~URL() {}
00438 
00439   const std::string& URL::Protocol() const {
00440     return protocol;
00441   }
00442 
00443   void URL::ChangeProtocol(const std::string& newprot) {
00444     protocol = lower(newprot);
00445   }
00446 
00447   bool URL::IsSecureProtocol() const {
00448     return protocol == "gsiftp" ||
00449            protocol == "https" ||
00450            protocol == "httpg" ||
00451            protocol == "rc" ||
00452            protocol == "rls" ||
00453            protocol == "srm" ||
00454            protocol == "arc" ||
00455            protocol == "fireman" ||
00456            protocol == "lfc";
00457   }
00458 
00459   const std::string& URL::Username() const {
00460     return username;
00461   }
00462 
00463   const std::string& URL::Passwd() const {
00464     return passwd;
00465   }
00466 
00467   const std::string& URL::Host() const {
00468     return host;
00469   }
00470 
00471   void URL::ChangeHost(const std::string& newhost) {
00472     host = lower(newhost);
00473   }
00474 
00475   int URL::Port() const {
00476     return port;
00477   }
00478 
00479   void URL::ChangePort(int newport) {
00480     port = newport;
00481   }
00482 
00483   const std::string& URL::Path() const {
00484     return path;
00485   }
00486 
00487   std::string URL::FullPath() const {
00488     std::string fullpath;
00489 
00490     if (!path.empty())
00491       fullpath += path;
00492 
00493     if (!httpoptions.empty())
00494       fullpath += '?' + OptionString(httpoptions, '&');
00495 
00496     if (!ldapattributes.empty() || (ldapscope != base) || !ldapfilter.empty())
00497       fullpath += '?' + AttributeString(ldapattributes, ',');
00498 
00499     if ((ldapscope != base) || !ldapfilter.empty()) {
00500       switch (ldapscope) {
00501       case base:
00502         fullpath += "?base";
00503         break;
00504 
00505       case onelevel:
00506         fullpath += "?one";
00507         break;
00508 
00509       case subtree:
00510         fullpath += "?sub";
00511         break;
00512       }
00513     }
00514 
00515     if (!ldapfilter.empty())
00516       fullpath += '?' + ldapfilter;
00517 
00518     return fullpath;
00519   }
00520 
00521   void URL::ChangePath(const std::string& newpath) {
00522     path = newpath;
00523 
00524     // parse basedn in case of ldap-protocol
00525     if (protocol == "ldap") {
00526       if (path.find("/") != std::string::npos)
00527         path = Path2BaseDN(path);
00528 
00529     // add absolute path for relative file URLs
00530     } else if (protocol == "file" || protocol == "urllist") {
00531       if(!Glib::path_is_absolute(path)) {
00532         char cwd[PATH_MAX];
00533         if (getcwd(cwd, PATH_MAX))
00534           path = Glib::build_filename(cwd, path);
00535       }
00536     }
00537 
00538     // for generic URL just make sure path has leading /
00539     else if ((path[0] != '/') && (!path.empty())) {
00540       URLLogger.msg(WARNING, "Attempt to assign relative path to URL - making it absolute");
00541       path = "/" + path;
00542     }
00543 
00544   }
00545 
00546   const std::map<std::string, std::string>& URL::HTTPOptions() const {
00547     return httpoptions;
00548   }
00549 
00550   const std::string& URL::HTTPOption(const std::string& option,
00551                                      const std::string& undefined) const {
00552     std::map<std::string, std::string>::const_iterator
00553     opt = httpoptions.find(option);
00554     if (opt != httpoptions.end())
00555       return opt->second;
00556     else
00557       return undefined;
00558   }
00559 
00560   const std::map<std::string, std::string>& URL::MetaDataOptions() const {
00561     return metadataoptions;
00562   }
00563 
00564   const std::string& URL::MetaDataOption(const std::string& option,
00565                                          const std::string& undefined) const {
00566     std::map<std::string, std::string>::const_iterator opt = metadataoptions.find(option);
00567     if (opt != metadataoptions.end())
00568       return opt->second;
00569     else
00570       return undefined;
00571   }
00572 
00573   const std::list<std::string>& URL::LDAPAttributes() const {
00574     return ldapattributes;
00575   }
00576 
00577   void URL::AddLDAPAttribute(const std::string& attribute) {
00578     ldapattributes.push_back(attribute);
00579   }
00580 
00581   URL::Scope URL::LDAPScope() const {
00582     return ldapscope;
00583   }
00584 
00585   void URL::ChangeLDAPScope(const Scope newscope) {
00586     ldapscope = newscope;
00587   }
00588 
00589   const std::string& URL::LDAPFilter() const {
00590     return ldapfilter;
00591   }
00592 
00593   void URL::ChangeLDAPFilter(const std::string& newfilter) {
00594     ldapfilter = newfilter;
00595   }
00596 
00597   const std::map<std::string, std::string>& URL::Options() const {
00598     return urloptions;
00599   }
00600 
00601   const std::string& URL::Option(const std::string& option,
00602                                  const std::string& undefined) const {
00603     std::map<std::string, std::string>::const_iterator
00604     opt = urloptions.find(option);
00605     if (opt != urloptions.end())
00606       return opt->second;
00607     else
00608       return undefined;
00609   }
00610 
00611   void URL::AddOption(const std::string& option, const std::string& value,
00612                       bool overwrite) {
00613     if (!overwrite && urloptions.find(option) != urloptions.end())
00614       return;
00615     urloptions[option] = value;
00616   }
00617 
00618   const std::list<URLLocation>& URL::Locations() const {
00619     return locations;
00620   }
00621 
00622   const std::map<std::string, std::string>& URL::CommonLocOptions() const {
00623     return commonlocoptions;
00624   }
00625 
00626   const std::string& URL::CommonLocOption(const std::string& option,
00627                                           const std::string& undefined) const {
00628     std::map<std::string, std::string>::const_iterator
00629     opt = commonlocoptions.find(option);
00630     if (opt != commonlocoptions.end())
00631       return opt->second;
00632     else
00633       return undefined;
00634   }
00635 
00636   std::string URL::fullstr() const {
00637 
00638     std::string urlstr;
00639 
00640     if (!username.empty())
00641       urlstr += username;
00642 
00643     if (!passwd.empty())
00644       urlstr += ':' + passwd;
00645 
00646     for (std::list<URLLocation>::const_iterator it = locations.begin();
00647          it != locations.end(); it++) {
00648       if (it != locations.begin())
00649         urlstr += '|';
00650       urlstr += it->fullstr();
00651     }
00652 
00653     if (!locations.empty() && !commonlocoptions.empty())
00654       urlstr += '|';
00655 
00656     if (!commonlocoptions.empty())
00657       urlstr += ';' + OptionString(commonlocoptions, ';');
00658 
00659     if (!username.empty() || !passwd.empty() || !locations.empty())
00660       urlstr += '@';
00661 
00662     if (!host.empty()) {
00663       if(ip6addr) 
00664         urlstr += "[" + host + "]";
00665       else
00666         urlstr += host;
00667     }
00668 
00669     if (port != -1)
00670       urlstr += ':' + tostring(port);
00671 
00672     if (!urloptions.empty())
00673       urlstr += ';' + OptionString(urloptions, ';');
00674 
00675     if (!protocol.empty()) {
00676       if (!urlstr.empty())
00677         urlstr = protocol + "://" + urlstr;
00678       else
00679         urlstr = protocol + ":";
00680     }
00681 
00682     // Constructor makes sure path is absolute or empty.
00683     // ChangePath() also makes such check.
00684     if ( protocol == "ldap") // Unfortunately ldap is special case
00685       urlstr += '/';
00686     urlstr += path;
00687 
00688     // If there is nothing at this point there is no sense
00689     // to add any options
00690     if (urlstr.empty())
00691       return urlstr;
00692 
00693     if (!httpoptions.empty())
00694       urlstr += '?' + OptionString(httpoptions, '&');
00695 
00696     if (!ldapattributes.empty() || (ldapscope != base) || !ldapfilter.empty())
00697       urlstr += '?' + AttributeString(ldapattributes, ',');
00698 
00699     if ((ldapscope != base) || !ldapfilter.empty()) {
00700       switch (ldapscope) {
00701       case base:
00702         urlstr += "?base";
00703         break;
00704 
00705       case onelevel:
00706         urlstr += "?one";
00707         break;
00708 
00709       case subtree:
00710         urlstr += "?sub";
00711         break;
00712       }
00713     }
00714 
00715     if (!ldapfilter.empty())
00716       urlstr += '?' + ldapfilter;
00717 
00718     if (!metadataoptions.empty())
00719       urlstr += ':' + OptionString(metadataoptions, ':');
00720 
00721     return urlstr;
00722   }
00723 
00724   std::string URL::str() const {
00725 
00726     std::string urlstr;
00727 
00728     if (!username.empty())
00729       urlstr += username;
00730 
00731     if (!passwd.empty())
00732       urlstr += ':' + passwd;
00733 
00734     if (!username.empty() || !passwd.empty())
00735       urlstr += '@';
00736 
00737     if (!host.empty()) {
00738       if(ip6addr)
00739         urlstr += "[" + host + "]";
00740       else
00741         urlstr += host;
00742     }
00743 
00744     if (port != -1)
00745       urlstr += ':' + tostring(port);
00746 
00747     if (!protocol.empty()) {
00748       if (!urlstr.empty())
00749         urlstr = protocol + "://" + urlstr;
00750       else
00751         urlstr = protocol + ":";
00752     }
00753 
00754     // Constructor makes sure path is absolute or empty.
00755     // ChangePath also makes such check.
00756     if ( protocol == "ldap") // Unfortunately ldap is special case
00757       urlstr += '/';
00758     urlstr += path;
00759 
00760     // If there is nothing at this point there is no sense
00761     // to add any options
00762     if (urlstr.empty())
00763       return urlstr;
00764 
00765     if (!httpoptions.empty())
00766       urlstr += '?' + OptionString(httpoptions, '&');
00767 
00768     if (!ldapattributes.empty() || (ldapscope != base) || !ldapfilter.empty())
00769       urlstr += '?' + AttributeString(ldapattributes, ',');
00770 
00771     if ((ldapscope != base) || !ldapfilter.empty()) {
00772       switch (ldapscope) {
00773       case base:
00774         urlstr += "?base";
00775         break;
00776 
00777       case onelevel:
00778         urlstr += "?one";
00779         break;
00780 
00781       case subtree:
00782         urlstr += "?sub";
00783         break;
00784       }
00785     }
00786 
00787     if (!ldapfilter.empty())
00788       urlstr += '?' + ldapfilter;
00789 
00790     if (!metadataoptions.empty())
00791       urlstr += ':' + OptionString(metadataoptions, ':');
00792 
00793     return urlstr;
00794   }
00795 
00796   std::string URL::ConnectionURL() const {
00797 
00798     std::string urlstr;
00799     if (!protocol.empty())
00800       urlstr = protocol + "://";
00801 
00802     if (!host.empty()) {
00803       if(ip6addr)
00804         urlstr += "[" + host + "]";
00805       else
00806         urlstr += host;
00807     }
00808 
00809     if (port != -1)
00810       urlstr += ':' + tostring(port);
00811 
00812     return urlstr;
00813   }
00814 
00815   bool URL::operator<(const URL& url) const {
00816     return (str() < url.str());
00817   }
00818 
00819   bool URL::operator==(const URL& url) const {
00820     return (str() == url.str());
00821   }
00822 
00823   std::string URL::BaseDN2Path(const std::string& basedn) {
00824 
00825     std::string::size_type pos, pos2;
00826     // mds-vo-name=local, o=grid --> o=grid/mds-vo-name=local
00827     std::string newpath;
00828 
00829     pos = basedn.size();
00830     while ((pos2 = basedn.rfind(",", pos - 1)) != std::string::npos) {
00831       std::string tmppath = basedn.substr(pos2 + 1, pos - pos2 - 1);
00832       tmppath = tmppath.substr(tmppath.find_first_not_of(' '));
00833       newpath += tmppath + '/';
00834       pos = pos2;
00835     }
00836 
00837     newpath += basedn.substr(0, pos);
00838 
00839     return newpath;
00840   }
00841 
00842   std::string URL::Path2BaseDN(const std::string& newpath) {
00843 
00844     if (newpath.empty())
00845       return "";
00846 
00847     std::string basedn;
00848     std::string::size_type pos, pos2;
00849 
00850     pos = newpath.size();
00851     while ((pos2 = newpath.rfind("/", pos - 1)) != std::string::npos) {
00852       if (pos2 == 0) break;
00853       basedn += newpath.substr(pos2 + 1, pos - pos2 - 1) + ", ";
00854       pos = pos2;
00855     }
00856 
00857     if (pos2 == std::string::npos)
00858       basedn += newpath.substr(0, pos);
00859     else
00860       basedn += newpath.substr(pos2 + 1, pos - pos2 - 1);
00861 
00862     return basedn;
00863   }
00864 
00865   URL::operator bool() const {
00866     return valid;
00867   }
00868 
00869   bool URL::operator!() const {
00870     return !valid;
00871   }
00872 
00873   std::string URL::OptionString(const std::map<std::string,
00874                                                std::string>& options, char separator) {
00875 
00876     std::string optstring;
00877 
00878     if (options.empty())
00879       return optstring;
00880 
00881     for (std::map<std::string, std::string>::const_iterator
00882          it = options.begin(); it != options.end(); it++) {
00883       if (it != options.begin())
00884         optstring += separator;
00885       optstring += it->first + '=' + it->second;
00886     }
00887     return optstring;
00888   }
00889 
00890 
00891   std::ostream& operator<<(std::ostream& out, const URL& url) {
00892     return (out << url.str());
00893   }
00894 
00895 
00896   URLLocation::URLLocation(const std::string& url)
00897     : URL(url) {}
00898 
00899   URLLocation::URLLocation(const std::string& url,
00900                            const std::string& name)
00901     : URL(url),
00902       name(name) {}
00903 
00904   URLLocation::URLLocation(const URL& url)
00905     : URL(url) {}
00906 
00907   URLLocation::URLLocation(const URL& url,
00908                            const std::string& name)
00909     : URL(url),
00910       name(name) {}
00911 
00912   URLLocation::URLLocation(const std::map<std::string, std::string>& options,
00913                            const std::string& name)
00914     : URL(),
00915       name(name) {
00916     urloptions = options;
00917   }
00918 
00919   const std::string& URLLocation::Name() const {
00920     return name;
00921   }
00922 
00923   URLLocation::~URLLocation() {}
00924 
00925   std::string URLLocation::str() const {
00926 
00927     if (*this)
00928       return URL::str();
00929     else
00930       return name;
00931   }
00932 
00933   std::string URLLocation::fullstr() const {
00934 
00935     if (*this)
00936       return URL::fullstr();
00937     else if (urloptions.empty())
00938       return name;
00939     else
00940       return name + ';' + OptionString(urloptions, ';');
00941   }
00942 
00943 
00944   PathIterator::PathIterator(const std::string& path, bool end)
00945     : path(path),
00946       pos(std::string::npos),
00947       end(end) {
00948     if (end)
00949       operator--();
00950     else
00951       operator++();
00952   }
00953 
00954   PathIterator::~PathIterator() {}
00955 
00956   PathIterator& PathIterator::operator++() {
00957     done = false;
00958     if (pos != std::string::npos)
00959       pos = path.find('/', pos + 1);
00960     else if (!end && !path.empty())
00961       pos = path.find('/',(path[0] == '/')?1:0);
00962     else
00963       done = true;
00964     end = true;
00965     return *this;
00966   }
00967 
00968   PathIterator& PathIterator::operator--() {
00969     done = false;
00970     if (pos != std::string::npos) {
00971       pos = pos ? path.rfind('/', pos - 1) : std::string::npos;
00972     } else if (end && !path.empty()) {
00973       if((pos = path.rfind('/')) == 0) pos = std::string::npos;
00974     } else {
00975       done = true;
00976     }
00977     end = false;
00978     return *this;
00979   }
00980 
00981   PathIterator::operator bool() const {
00982     return !done;
00983   }
00984 
00985   std::string PathIterator::operator*() const {
00986     if (pos != std::string::npos)
00987       return path.substr(0, pos);
00988     else if (end)
00989       return path;
00990     else
00991       return "";
00992   }
00993 
00994   std::string PathIterator::Rest() const {
00995     if (pos != std::string::npos)
00996       return path.substr(pos + 1);
00997     else if (!end)
00998       return path;
00999     else
01000       return "";
01001   }
01002 
01003 
01004   std::list<URL> ReadURLList(const URL& url) {
01005 
01006     std::list<URL> urllist;
01007     if (url.Protocol() == "urllist") {
01008       std::ifstream f(url.Path().c_str());
01009       std::string line;
01010       while (getline(f, line)) {
01011         URL url(line);
01012         if (url)
01013           urllist.push_back(url);
01014         else
01015           URLLogger.msg(ERROR, "urllist %s contains invalid URL: %s",
01016                         url.Path(), line);
01017       }
01018     }
01019     else
01020       URLLogger.msg(ERROR, "URL protocol is not urllist: %s", url.str());
01021     return urllist;
01022   }
01023 
01024 } // namespace Arc