=== modified file 'src/HttpRequest.cc'
--- src/HttpRequest.cc	2014-04-27 07:59:17 +0000
+++ src/HttpRequest.cc	2014-04-27 13:20:32 +0000
@@ -75,41 +75,40 @@
 HttpRequest::~HttpRequest()
 {
     clean();
     debugs(93,7, HERE << "destructed, this=" << this);
 }
 
 void
 HttpRequest::initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath)
 {
     method = aMethod;
     url.setScheme(aProtocol);
     urlpath = aUrlpath;
 }
 
 void
 HttpRequest::init()
 {
     method = Http::METHOD_NONE;
     url.clear();
     urlpath = NULL;
-    login[0] = '\0';
     host[0] = '\0';
     host_is_numeric = -1;
 #if USE_AUTH
     auth_user_request = NULL;
 #endif
     port = 0;
     canonical = NULL;
     memset(&flags, '\0', sizeof(flags));
     range = NULL;
     ims = -1;
     imslen = 0;
     lastmod = -1;
     client_addr.setEmpty();
     my_addr.setEmpty();
     body_pipe = NULL;
     // hier
     dnsWait = -1;
     errType = ERR_NONE;
     errDetail = ERR_DETAIL_NONE;
     peer_login = NULL;		// not allocated/deallocated by this class
@@ -190,41 +189,41 @@
 
 void
 HttpRequest::reset()
 {
     clean();
     init();
 }
 
 HttpRequest *
 HttpRequest::clone() const
 {
     HttpRequest *copy = new HttpRequest(method, url.getScheme(), urlpath.termedBuf());
     // TODO: move common cloning clone to Msg::copyTo() or copy ctor
     copy->header.append(&header);
     copy->hdrCacheInit();
     copy->hdr_sz = hdr_sz;
     copy->http_ver = http_ver;
     copy->pstate = pstate; // TODO: should we assert a specific state here?
     copy->body_pipe = body_pipe;
 
-    strncpy(copy->login, login, sizeof(login)); // MAX_LOGIN_SZ
+    copy->url.userInfo(url.userInfo());
     strncpy(copy->host, host, sizeof(host)); // SQUIDHOSTNAMELEN
     copy->host_addr = host_addr;
 
     copy->port = port;
     // urlPath handled in ctor
     copy->canonical = canonical ? xstrdup(canonical) : NULL;
 
     // range handled in hdrCacheInit()
     copy->ims = ims;
     copy->imslen = imslen;
     copy->hier = hier; // Is it safe to copy? Should we?
 
     copy->errType = errType;
 
     // XXX: what to do with copy->peer_login?
 
     copy->lastmod = lastmod;
     copy->etag = etag;
     copy->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL;
     // XXX: what to do with copy->peer_domain?

=== modified file 'src/HttpRequest.h'
--- src/HttpRequest.h	2014-04-27 07:59:17 +0000
+++ src/HttpRequest.h	2014-04-27 08:49:32 +0000
@@ -121,43 +121,41 @@
     /// Returns possibly nil history, creating it if icap logging is enabled
     Adaptation::Icap::History::Pointer icapHistory() const;
 #endif
 
     void recordLookup(const DnsLookupDetails &detail);
 
     /// sets error detail if no earlier detail was available
     void detailError(err_type aType, int aDetail);
     /// clear error details, useful for retries/repeats
     void clearError();
 
 protected:
     void clean();
 
     void init();
 
 public:
     HttpRequestMethod method;
 
     // TODO expand to include all URI parts
-    URL url; ///< the request URI (scheme only)
-
-    char login[MAX_LOGIN_SZ];
+    URL url; ///< the request URI (scheme and userinfo only)
 
 private:
     char host[SQUIDHOSTNAMELEN];
     int host_is_numeric;
 
 #if USE_ADAPTATION
     mutable Adaptation::History::Pointer adaptHistory_; ///< per-HTTP transaction info
 #endif
 #if ICAP_CLIENT
     mutable Adaptation::Icap::History::Pointer icapHistory_; ///< per-HTTP transaction info
 #endif
 
 public:
     Ip::Address host_addr;
 #if USE_AUTH
     Auth::UserRequest::Pointer auth_user_request;
 #endif
     unsigned short port;
 
     String urlpath;

=== modified file 'src/URL.h'
--- src/URL.h	2014-04-27 07:59:17 +0000
+++ src/URL.h	2014-04-27 13:21:53 +0000
@@ -16,84 +16,88 @@
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 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, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
 
 #ifndef SQUID_SRC_URL_H
 #define SQUID_SRC_URL_H
 
 #include "anyp/UriScheme.h"
 #include "MemPool.h"
+#include "SBuf.h"
 
 /**
- \ingroup POD
- *
  * The URL class represents a Uniform Resource Location
  */
 class URL
 {
 public:
     MEMPROXY_CLASS(URL);
     URL() : scheme_() {}
     URL(AnyP::UriScheme const &aScheme) : scheme_(aScheme) {}
 
     void clear() {
         scheme_=AnyP::PROTO_NONE;
     }
 
     AnyP::UriScheme const & getScheme() const {return scheme_;}
 
     /// convert the URL scheme to that given
     void setScheme(const AnyP::ProtocolType &p) {scheme_=p;}
 
+    void userInfo(const SBuf &s) {userInfo_=s;}
+    const SBuf &userInfo(void) const {return userInfo_;}
+
 private:
     /**
      \par
      * The scheme of this URL. This has the 'type code' smell about it.
      * In future we may want to make the methods that dispatch based on
      * the scheme virtual and have a class per protocol.
      \par
      * On the other hand, having Protocol as an explicit concept is useful,
      * see for instance the ACLProtocol acl type. One way to represent this
      * is to have one prototype URL with no host etc for each scheme,
      * another is to have an explicit scheme class, and then each URL class
      * could be a subclass of the scheme. Another way is one instance of
      * a AnyP::UriScheme class instance for each URL scheme we support, and one URL
      * class for each manner of treating the scheme : a Hierarchical URL, a
      * non-hierarchical URL etc.
      \par
      * Deferring the decision, its a type code for now. RBC 20060507.
      \par
      * In order to make taking any of these routes easy, scheme is private
      * and immutable, only settable at construction time,
      */
     AnyP::UriScheme scheme_;
+
+    SBuf userInfo_; // aka 'URL-login'
 };
 
 MEMPROXY_CLASS_INLINE(URL);
 
 class HttpRequest;
 class HttpRequestMethod;
 
 AnyP::ProtocolType urlParseProtocol(const char *, const char *e = NULL);
 void urlInitialize(void);
 HttpRequest *urlParse(const HttpRequestMethod&, char *, HttpRequest *request = NULL);
 const char *urlCanonical(HttpRequest *);
 char *urlCanonicalClean(const HttpRequest *);
 const char *urlCanonicalFakeHttps(const HttpRequest * request);
 bool urlIsRelative(const char *);
 char *urlMakeAbsolute(const HttpRequest *, const char *);
 char *urlRInternal(const char *host, unsigned short port, const char *dir, const char *name);
 char *urlInternal(const char *dir, const char *name);
 int matchDomainName(const char *host, const char *domain);
 int urlCheckRequest(const HttpRequest *);
 int urlDefaultPort(AnyP::ProtocolType p);

=== modified file 'src/acl/UrlLogin.cc'
--- src/acl/UrlLogin.cc	2013-10-25 00:13:46 +0000
+++ src/acl/UrlLogin.cc	2014-04-27 14:36:25 +0000
@@ -21,36 +21,36 @@
  *
  *  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, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
 
 #include "squid.h"
 #include "acl/Checklist.h"
 #include "acl/RegexData.h"
 #include "acl/UrlLogin.h"
 #include "HttpRequest.h"
 #include "rfc1738.h"
 
 int
-ACLUrlLoginStrategy::match (ACLData<char const *> * &data, ACLFilledChecklist *checklist, ACLFlags &)
+ACLUrlLoginStrategy::match(ACLData<char const *> * &data, ACLFilledChecklist *checklist, ACLFlags &)
 {
-    char *esc_buf = xstrdup(checklist->request->login);
-    rfc1738_unescape(esc_buf);
-    int result = data->match(esc_buf);
-    safe_free(esc_buf);
+    SBuf tmp = checklist->request->url.userInfo();
+    // NOTE: unescape works in-place and may decrease the c-string length, never increase.
+    rfc1738_unescape(const_cast<char*>(tmp.c_str()));
+    int result = data->match(tmp.c_str());
     return result;
 }
 
 ACLUrlLoginStrategy *
 ACLUrlLoginStrategy::Instance()
 {
     return &Instance_;
 }
 
 ACLUrlLoginStrategy ACLUrlLoginStrategy::Instance_;

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2014-04-27 07:59:17 +0000
+++ src/client_side.cc	2014-04-27 14:36:51 +0000
@@ -2718,43 +2718,40 @@
         } else
             request->flags.spoofClientIp = false;
     }
 
     if (internalCheck(request->urlpath.termedBuf())) {
         if (internalHostnameIs(request->GetHost()) && request->port == getMyPort()) {
             debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->GetHost() <<
                    ':' << request->port);
             http->flags.internal = true;
         } else if (Config.onoff.global_internal_static && internalStaticCheck(request->urlpath.termedBuf())) {
             debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->GetHost() <<
                    ':' << request->port << " (global_internal_static on)");
             request->SetHost(internalHostname());
             request->port = getMyPort();
             http->flags.internal = true;
         } else
             debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->GetHost() <<
                    ':' << request->port << " (not this proxy)");
     }
 
-    if (http->flags.internal)
-        request->login[0] = '\0';
-
     request->flags.internal = http->flags.internal;
     setLogUri (http, urlCanonicalClean(request.getRaw()));
     request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member.
 #if FOLLOW_X_FORWARDED_FOR
     // indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
     // not a details about teh TCP connection itself
     request->indirect_client_addr = conn->clientConnection->remote;
 #endif /* FOLLOW_X_FORWARDED_FOR */
     request->my_addr = conn->clientConnection->local;
     request->myportname = conn->port->name;
     request->http_ver = http_ver;
 
     // Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it
     // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
     request->clientConnectionManager = conn;
 
     if (request->header.chunked()) {
         chunked = true;
     } else if (request->header.has(HDR_TRANSFER_ENCODING)) {
         const String te = request->header.getList(HDR_TRANSFER_ENCODING);

=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc	2014-04-27 07:59:17 +0000
+++ src/client_side_request.cc	2014-04-27 10:11:28 +0000
@@ -1141,41 +1141,41 @@
             http->range_iter.pos = request->range->begin();
             http->range_iter.end = request->range->end();
             http->range_iter.valid = true;
         }
     }
 
     /* Only HEAD and GET requests permit a Range or Request-Range header.
      * If these headers appear on any other type of request, delete them now.
      */
     else {
         req_hdr->delById(HDR_RANGE);
         req_hdr->delById(HDR_REQUEST_RANGE);
         request->ignoreRange("neither HEAD nor GET");
     }
 
     if (req_hdr->has(HDR_AUTHORIZATION))
         request->flags.auth = true;
 
     clientCheckPinning(http);
 
-    if (request->login[0] != '\0')
+    if (!request->url.userInfo().isEmpty())
         request->flags.auth = true;
 
     if (req_hdr->has(HDR_VIA)) {
         String s = req_hdr->getList(HDR_VIA);
         /*
          * ThisCache cannot be a member of Via header, "1.1 ThisCache" can.
          * Note ThisCache2 has a space prepended to the hostname so we don't
          * accidentally match super-domains.
          */
 
         if (strListIsSubstr(&s, ThisCache2, ',')) {
             debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
                      request, (ObjPackMethod) & httpRequestPack);
             request->flags.loopDetected = true;
         }
 
 #if USE_FORW_VIA_DB
         fvdbCountVia(s.termedBuf());
 
 #endif

=== modified file 'src/ftp.cc'
--- src/ftp.cc	2014-04-27 07:59:17 +0000
+++ src/ftp.cc	2014-04-27 12:50:27 +0000
@@ -43,41 +43,40 @@
 #include "fde.h"
 #include "FwdState.h"
 #include "html_quote.h"
 #include "HttpHdrContRange.h"
 #include "HttpHeader.h"
 #include "HttpHeaderRange.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "ip/tools.h"
 #include "Mem.h"
 #include "MemBuf.h"
 #include "mime.h"
 #include "rfc1738.h"
 #include "Server.h"
 #include "SquidConfig.h"
 #include "SquidString.h"
 #include "SquidTime.h"
 #include "StatCounters.h"
 #include "Store.h"
 #include "tools.h"
-#include "URL.h"
 #include "wordlist.h"
 
 #if USE_DELAY_POOLS
 #include "DelayPools.h"
 #include "MemObject.h"
 #endif
 
 #if HAVE_ERRNO_H
 #include <errno.h>
 #endif
 
 /**
  \defgroup ServerProtocolFTPInternal Server-Side FTP Internals
  \ingroup ServerProtocolFTPAPI
  */
 
 /// \ingroup ServerProtocolFTPInternal
 static const char *const crlf = "\r\n";
 
 #define CTRL_BUFLEN 1024
@@ -219,41 +218,40 @@
         size_t offset;
         wordlist *message;
         char *last_command;
         char *last_reply;
         int replycode;
     } ctrl;
 
     /// FTP data channel info; the channel may be opened/closed a few times
     struct DataChannel: public FtpChannel {
         MemBuf *readBuf;
         char *host;
         unsigned short port;
         bool read_pending;
     } data;
 
     struct _ftp_flags flags;
 
 public:
     // these should all be private
     virtual void start();
-    void loginParser(const char *, int escaped);
     int restartable();
     void appendSuccessHeader();
     void hackShortcut(FTPSM * nextState);
     void failed(err_type, int xerrno);
     void failedErrorMessage(err_type, int xerrno);
     void unhack();
     void scheduleReadControlReply(int);
     void handleControlReply();
     void readStor();
     void parseListing();
     MemBuf *htmlifyListEntry(const char *line);
     void completedListing(void);
     void dataComplete();
     void dataRead(const CommIoCbParams &io);
 
     /// ignore timeout on CTRL channel. set read timeout on DATA channel.
     void switchTimeoutToDataChannel();
     /// create a data channel acceptor and start listening.
     void listenForDataChannel(const Comm::ConnectionPointer &conn, const char *note);
 
@@ -284,62 +282,57 @@
 
     static HttpReply *ftpAuthRequired(HttpRequest * request, const char *realm);
     const char *ftpRealm(void);
     void loginFailed(void);
     static wordlist *ftpParseControlReply(char *, size_t, int *, size_t *);
 
     // sending of the request body to the server
     virtual void sentRequestBody(const CommIoCbParams&);
     virtual void doneSendingRequestBody();
 
     virtual void haveParsedReplyHeaders();
 
     virtual bool doneWithServer() const;
     virtual bool haveControlChannel(const char *caller_name) const;
     AsyncCall::Pointer dataCloser(); /// creates a Comm close callback
     AsyncCall::Pointer dataOpener(); /// creates a Comm connect callback
 
 private:
     // BodyConsumer for HTTP: consume request body.
     virtual void handleRequestBodyProducerAborted();
+    void loginParser(const SBuf &login, bool escaped);
 
     CBDATA_CLASS2(FtpStateData);
 };
 
 CBDATA_CLASS_INIT(FtpStateData);
 
 /// \ingroup ServerProtocolFTPInternal
 typedef struct {
     char type;
     int64_t size;
     char *date;
     char *name;
     char *showname;
     char *link;
 } ftpListParts;
 
-/// \ingroup ServerProtocolFTPInternal
-#define FTP_LOGIN_ESCAPED	1
-
-/// \ingroup ServerProtocolFTPInternal
-#define FTP_LOGIN_NOT_ESCAPED	0
-
 /*
  * State machine functions
  * send == state transition
  * read == wait for response, and select next state transition
  * other == Transition logic
  */
 static FTPSM ftpReadWelcome;
 static FTPSM ftpSendUser;
 static FTPSM ftpReadUser;
 static FTPSM ftpSendPass;
 static FTPSM ftpReadPass;
 static FTPSM ftpSendType;
 static FTPSM ftpReadType;
 static FTPSM ftpSendMdtm;
 static FTPSM ftpReadMdtm;
 static FTPSM ftpSendSize;
 static FTPSM ftpReadSize;
 static FTPSM ftpSendEPRT;
 static FTPSM ftpReadEPRT;
 static FTPSM ftpSendPORT;
@@ -539,92 +532,84 @@
     safe_free(old_reply);
 
     safe_free(old_filepath);
 
     title_url.clean();
 
     base_href.clean();
 
     safe_free(filepath);
 
     safe_free(dirpath);
 
     safe_free(data.host);
 
     fwd = NULL;	// refcounted
 }
 
 /**
  * Parse a possible login username:password pair.
  * Produces filled member variables user, password, password_url if anything found.
+ *
+ * \param login  a decoded Basic authentication credential token or URI user-info token.
+ * \param escaped  whether to URL-decode the token after extracting user and password
  */
 void
-FtpStateData::loginParser(const char *login, int escaped)
+FtpStateData::loginParser(const SBuf &login, bool escaped)
 {
-    const char *u = NULL; // end of the username sub-string
-    int len;              // length of the current sub-string to handle.
+    debugs(9, 4, "login=" << login << ", escaped=" << escaped);
+    debugs(9, 9, "IN : login=" << login << ", escaped=" << escaped << ", user=" << user << ", password=" << password);
 
-    int total_len = strlen(login);
+    if (login.isEmpty())
+        return;
 
-    debugs(9, 4, HERE << ": login='" << login << "', escaped=" << escaped);
-    debugs(9, 9, HERE << ": IN : login='" << login << "', escaped=" << escaped << ", user=" << user << ", password=" << password);
-
-    if ((u = strchr(login, ':'))) {
-
-        /* if there was a username part */
-        if (u > login) {
-            len = u - login;
-            ++u; // jump off the delimiter.
-            if (len > MAX_URL)
-                len = MAX_URL-1;
-            xstrncpy(user, login, len +1);
-            debugs(9, 9, HERE << ": found user='" << user << "'(" << len <<"), escaped=" << escaped);
-            if (escaped)
-                rfc1738_unescape(user);
-            debugs(9, 9, HERE << ": found user='" << user << "'(" << len <<") unescaped.");
-        }
+    const SBuf::size_type colonPos = login.find(':');
 
-        /* if there was a password part */
-        len = login + total_len - u;
-        if ( len > 0) {
-            if (len > MAX_URL)
-                len = MAX_URL -1;
-            xstrncpy(password, u, len +1);
-            debugs(9, 9, HERE << ": found password='" << password << "'(" << len <<"), escaped=" << escaped);
-            if (escaped) {
-                rfc1738_unescape(password);
-                password_url = 1;
-            }
-            debugs(9, 9, HERE << ": found password='" << password << "'(" << len <<") unescaped.");
-        }
-    } else if (login[0]) {
-        /* no password, just username */
-        if (total_len > MAX_URL)
-            total_len = MAX_URL -1;
-        xstrncpy(user, login, total_len +1);
-        debugs(9, 9, HERE << ": found user='" << user << "'(" << total_len <<"), escaped=" << escaped);
+    /* if there was a username part */
+    if (colonPos != 0) {
+        SBuf userName = login.substr(0, colonPos);
+        if (userName.length() >= sizeof(user))
+            userName.chop(0, sizeof(user)-1);
+        memcpy(user, userName.rawContent(), userName.length());
+        user[userName.length()] = '\0';
+        debugs(9, 9, "found user=" << userName << " (" << userName.length() << "), escaped=" << escaped);
         if (escaped)
             rfc1738_unescape(user);
-        debugs(9, 9, HERE << ": found user='" << user << "'(" << total_len <<") unescaped.");
+        debugs(9, 9, "found user=" << user << " (" << strlen(user) << ") unescaped.");
+    }
+
+    /* if there was a password part */
+    if (colonPos != SBuf::npos) {
+        SBuf pass = login.substr(colonPos+1, SBuf::npos);
+        if (pass.length() >= sizeof(pass))
+            pass.chop(0, sizeof(pass)-1);
+        memcpy(password, pass.rawContent(), pass.length());
+        password[pass.length()] = '\0';
+        debugs(9, 9, "found password=" << pass << " (" << pass.length() << "), escaped=" << escaped);
+        if (escaped) {
+            rfc1738_unescape(password);
+            password_url = 1;
+        }
+        debugs(9, 9, "found password=" << password << " (" << strlen(password) << ") unescaped.");
     }
 
-    debugs(9, 9, HERE << ": OUT: login='" << login << "', escaped=" << escaped << ", user=" << user << ", password=" << password);
+    debugs(9, 9, "OUT: login=" << login << ", escaped=" << escaped << ", user=" << user << ", password=" << password);
 }
 
 /**
  * Cancel the timeout on the Control socket and establish one
  * on the data socket
  */
 void
 FtpStateData::switchTimeoutToDataChannel()
 {
     commUnsetConnTimeout(ctrl.conn);
 
     typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
     AsyncCall::Pointer timeoutCall = JobCallback(9, 5, TimeoutDialer, this, FtpStateData::ftpTimeout);
     commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall);
 }
 
 void
 FtpStateData::listenForDataChannel(const Comm::ConnectionPointer &conn, const char *note)
 {
     assert(!Comm::IsConnOpen(data.conn));
@@ -1359,50 +1344,50 @@
  *  - Checks URL (ftp://user:pass@domain)
  *  - Authorization: Basic header
  *  - squid.conf anonymous-FTP settings (default: anonymous:Squid@).
  *
  * Special Case: A username-only may be provided in the URL and password in the HTTP headers.
  *
  * TODO: we might be able to do something about locating username from other sources:
  *       ie, external ACL user=* tag or ident lookup
  *
  \retval 1	if we have everything needed to complete this request.
  \retval 0	if something is missing.
  */
 int
 FtpStateData::checkAuth(const HttpHeader * req_hdr)
 {
     /* default username */
     xstrncpy(user, "anonymous", MAX_URL);
 
 #if HAVE_AUTH_MODULE_BASIC
     /* Check HTTP Authorization: headers (better than defaults, but less than URL) */
-    const char *auth;
-    if ( (auth = req_hdr->getAuth(HDR_AUTHORIZATION, "Basic")) ) {
+    const SBuf auth(req_hdr->getAuth(HDR_AUTHORIZATION, "Basic"));
+    if (!auth.isEmpty()) {
         flags.authenticated = 1;
-        loginParser(auth, FTP_LOGIN_NOT_ESCAPED);
+        loginParser(auth, false);
     }
     /* we fail with authorization-required error later IFF the FTP server requests it */
 #endif
 
     /* Test URL login syntax. Overrides any headers received. */
-    loginParser(request->login, FTP_LOGIN_ESCAPED);
+    loginParser(request->url.userInfo(), true);
 
     /* name is missing. thats fatal. */
     if (!user[0])
         fatal("FTP login parsing destroyed username info");
 
     /* name + password == success */
     if (password[0])
         return 1;
 
     /* Setup default FTP password settings */
     /* this has to be done last so that we can have a no-password case above. */
     if (!password[0]) {
         if (strcmp(user, "anonymous") == 0 && !flags.tried_auth_anonymous) {
             xstrncpy(password, Config.Ftp.anon_user, MAX_URL);
             flags.tried_auth_anonymous=1;
             return 1;
         } else if (!flags.tried_auth_nopass) {
             xstrncpy(password, null_string, MAX_URL);
             flags.tried_auth_nopass=1;
             return 1;

=== modified file 'src/http.cc'
--- src/http.cc	2014-04-27 07:59:17 +0000
+++ src/http.cc	2014-04-27 13:17:58 +0000
@@ -1802,43 +1802,44 @@
             hdr_out->putStr(HDR_X_FORWARDED_FOR, strFwd.termedBuf());
     }
     /** If set to DELETE - do not copy through. */
 
     /* append Host if not there already */
     if (!hdr_out->has(HDR_HOST)) {
         if (request->peer_domain) {
             hdr_out->putStr(HDR_HOST, request->peer_domain);
         } else if (request->port == urlDefaultPort(request->url.getScheme())) {
             /* use port# only if not default */
             hdr_out->putStr(HDR_HOST, request->GetHost());
         } else {
             httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d",
                               request->GetHost(),
                               (int) request->port);
         }
     }
 
     /* append Authorization if known in URL, not in header and going direct */
     if (!hdr_out->has(HDR_AUTHORIZATION)) {
-        if (!request->flags.proxying && request->login[0] != '\0') {
-            httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s",
-                              old_base64_encode(request->login));
+        if (!request->flags.proxying && !request->url.userInfo().isEmpty()) {
+            static char result[MAX_URL*2]; // should be big enough for a single URI segment
+            if (base64_encode_str(result, sizeof(result)-1, request->url.userInfo().rawContent(), request->url.userInfo().length()) > 0)
+                httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", result);
         }
     }
 
     /* Fixup (Proxy-)Authorization special cases. Plain relaying dealt with above */
     httpFixupAuthentication(request, hdr_in, hdr_out, flags);
 
     /* append Cache-Control, add max-age if not there already */
     {
         HttpHdrCc *cc = hdr_in->getCc();
 
         if (!cc)
             cc = new HttpHdrCc();
 
 #if 0 /* see bug 2330 */
         /* Set no-cache if determined needed but not found */
         if (request->flags.nocache)
             EBIT_SET(cc->mask, CC_NO_CACHE);
 #endif
 
         /* Add max-age only without no-cache */

=== modified file 'src/url.cc'
--- src/url.cc	2014-04-27 07:59:17 +0000
+++ src/url.cc	2014-04-27 14:14:27 +0000
@@ -26,41 +26,41 @@
  *  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, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
 
 #include "squid.h"
 #include "globals.h"
 #include "HttpRequest.h"
 #include "rfc1738.h"
 #include "SquidConfig.h"
 #include "SquidString.h"
 #include "URL.h"
 
 static HttpRequest *urlParseFinish(const HttpRequestMethod& method,
                                    const AnyP::ProtocolType protocol,
                                    const char *const urlpath,
                                    const char *const host,
-                                   const char *const login,
+                                   const SBuf &login,
                                    const int port,
                                    HttpRequest *request);
 static HttpRequest *urnParse(const HttpRequestMethod& method, char *urn, HttpRequest *request);
 static const char valid_hostname_chars_u[] =
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     "abcdefghijklmnopqrstuvwxyz"
     "0123456789-._"
     "[:]"
     ;
 static const char valid_hostname_chars[] =
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     "abcdefghijklmnopqrstuvwxyz"
     "0123456789-."
     "[:]"
     ;
 
 void
 urlInitialize(void)
 {
     debugs(23, 5, "urlInitialize: Initializing...");
@@ -224,41 +224,41 @@
     char *dst;
     proto[0] = host[0] = urlpath[0] = login[0] = '\0';
 
     if ((l = strlen(url)) + Config.appendDomainLen > (MAX_URL - 1)) {
         /* terminate so it doesn't overflow other buffers */
         *(url + (MAX_URL >> 1)) = '\0';
         debugs(23, DBG_IMPORTANT, "urlParse: URL too large (" << l << " bytes)");
         return NULL;
     }
     if (method == Http::METHOD_CONNECT) {
         port = CONNECT_PORT;
 
         if (sscanf(url, "[%[^]]]:%d", host, &port) < 1)
             if (sscanf(url, "%[^:]:%d", host, &port) < 1)
                 return NULL;
 
     } else if ((method == Http::METHOD_OPTIONS || method == Http::METHOD_TRACE) &&
                strcmp(url, "*") == 0) {
         protocol = AnyP::PROTO_HTTP;
         port = urlDefaultPort(protocol);
-        return urlParseFinish(method, protocol, url, host, login, port, request);
+        return urlParseFinish(method, protocol, url, host, SBuf(), port, request);
     } else if (!strncmp(url, "urn:", 4)) {
         return urnParse(method, url, request);
     } else {
         /* Parse the URL: */
         src = url;
         i = 0;
         /* Find first : - everything before is protocol */
         for (i = 0, dst = proto; i < l && *src != ':'; ++i, ++src, ++dst) {
             *dst = *src;
         }
         if (i >= l)
             return NULL;
         *dst = '\0';
 
         /* Then its :// */
         if ((i+3) > l || *src != ':' || *(src + 1) != '/' || *(src + 2) != '/')
             return NULL;
         i += 3;
         src += 3;
 
@@ -429,66 +429,66 @@
             break;
 
         case URI_WHITESPACE_CHOP:
             *(urlpath + strcspn(urlpath, w_space)) = '\0';
             break;
 
         case URI_WHITESPACE_STRIP:
         default:
             t = q = urlpath;
             while (*t) {
                 if (!xisspace(*t)) {
                     *q = *t;
                     ++q;
                 }
                 ++t;
             }
             *q = '\0';
         }
     }
 
-    return urlParseFinish(method, protocol, urlpath, host, login, port, request);
+    return urlParseFinish(method, protocol, urlpath, host, SBuf(login), port, request);
 }
 
 /**
  * Update request with parsed URI data.  If the request arg is
  * non-NULL, put parsed values there instead of allocating a new
  * HttpRequest.
  */
 static HttpRequest *
 urlParseFinish(const HttpRequestMethod& method,
                const AnyP::ProtocolType protocol,
                const char *const urlpath,
                const char *const host,
-               const char *const login,
+               const SBuf &login,
                const int port,
                HttpRequest *request)
 {
     if (NULL == request)
         request = new HttpRequest(method, protocol, urlpath);
     else {
         request->initHTTP(method, protocol, urlpath);
         safe_free(request->canonical);
     }
 
     request->SetHost(host);
-    xstrncpy(request->login, login, MAX_LOGIN_SZ);
+    request->url.userInfo(login);
     request->port = (unsigned short) port;
     return request;
 }
 
 static HttpRequest *
 urnParse(const HttpRequestMethod& method, char *urn, HttpRequest *request)
 {
     debugs(50, 5, "urnParse: " << urn);
     if (request) {
         request->initHTTP(method, AnyP::PROTO_URN, urn + 4);
         safe_free(request->canonical);
         return request;
     }
 
     return new HttpRequest(method, AnyP::PROTO_URN, urn + 4);
 }
 
 const char *
 urlCanonical(HttpRequest * request)
 {
@@ -498,93 +498,93 @@
     if (request->canonical)
         return request->canonical;
 
     if (request->url.getScheme() == AnyP::PROTO_URN) {
         snprintf(urlbuf, MAX_URL, "urn:" SQUIDSTRINGPH,
                  SQUIDSTRINGPRINT(request->urlpath));
     } else {
         switch (request->method.id()) {
 
         case Http::METHOD_CONNECT:
             snprintf(urlbuf, MAX_URL, "%s:%d", request->GetHost(), request->port);
             break;
 
         default:
             {
                 portbuf[0] = '\0';
 
                 if (request->port != urlDefaultPort(request->url.getScheme()))
                     snprintf(portbuf, 32, ":%d", request->port);
 
-                snprintf(urlbuf, MAX_URL, "%s://%s%s%s%s" SQUIDSTRINGPH,
+                snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s%s%s" SQUIDSTRINGPH,
                          request->url.getScheme().c_str(),
-                         request->login,
-                         *request->login ? "@" : null_string,
+                         SQUIDSBUFPRINT(request->url.userInfo()),
+                         !request->url.userInfo().isEmpty() ? "@" : "",
                          request->GetHost(),
                          portbuf,
                          SQUIDSTRINGPRINT(request->urlpath));
             }
         }
     }
 
     return (request->canonical = xstrdup(urlbuf));
 }
 
 /** \todo AYJ: Performance: This is an *almost* duplicate of urlCanonical. But elides the query-string.
  *        After copying it on in the first place! Would be less code to merge the two with a flag parameter.
  *        and never copy the query-string part in the first place
  */
 char *
 urlCanonicalClean(const HttpRequest * request)
 {
     LOCAL_ARRAY(char, buf, MAX_URL);
     LOCAL_ARRAY(char, portbuf, 32);
     LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1);
     char *t;
 
     if (request->url.getScheme() == AnyP::PROTO_URN) {
         snprintf(buf, MAX_URL, "urn:" SQUIDSTRINGPH,
                  SQUIDSTRINGPRINT(request->urlpath));
     } else {
         switch (request->method.id()) {
 
         case Http::METHOD_CONNECT:
             snprintf(buf, MAX_URL, "%s:%d", request->GetHost(), request->port);
             break;
 
         default:
             {
                 portbuf[0] = '\0';
 
                 if (request->port != urlDefaultPort(request->url.getScheme()))
                     snprintf(portbuf, 32, ":%d", request->port);
 
-                loginbuf[0] = '\0';
-
-                if ((int) strlen(request->login) > 0) {
-                    strcpy(loginbuf, request->login);
-
-                    if ((t = strchr(loginbuf, ':')))
-                        *t = '\0';
-
-                    strcat(loginbuf, "@");
-                }
+                if (!request->url.userInfo().isEmpty()) {
+                    SBuf::size_type len = request->url.userInfo().find(':');
+                    if (len == SBuf::npos)
+                        len = request->url.userInfo().length();
+
+                    memcpy(loginbuf, request->url.userInfo().rawContent(), len);
+                    loginbuf[len] = '@';
+                    loginbuf[len+1] = '\0';
+                } else
+                    loginbuf[0] = '\0';
 
                 snprintf(buf, MAX_URL, "%s://%s%s%s" SQUIDSTRINGPH,
                          request->url.getScheme().c_str(),
                          loginbuf,
                          request->GetHost(),
                          portbuf,
                          SQUIDSTRINGPRINT(request->urlpath));
 
                 // strip arguments AFTER a question-mark
                 if (Config.onoff.strip_query_terms)
                     if ((t = strchr(buf, '?')))
                         *(++t) = '\0';
             }
         }
     }
 
     if (stringHasCntl(buf))
         xstrncpy(buf, rfc1738_escape_unescaped(buf), MAX_URL);
 
     return buf;
@@ -652,52 +652,52 @@
  */
 char *
 urlMakeAbsolute(const HttpRequest * req, const char *relUrl)
 {
 
     if (req->method.id() == Http::METHOD_CONNECT) {
         return (NULL);
     }
 
     char *urlbuf = (char *)xmalloc(MAX_URL * sizeof(char));
 
     if (req->url.getScheme() == AnyP::PROTO_URN) {
         snprintf(urlbuf, MAX_URL, "urn:" SQUIDSTRINGPH,
                  SQUIDSTRINGPRINT(req->urlpath));
         return (urlbuf);
     }
 
     size_t urllen;
 
     if (req->port != urlDefaultPort(req->url.getScheme())) {
-        urllen = snprintf(urlbuf, MAX_URL, "%s://%s%s%s:%d",
+        urllen = snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s%s:%d",
                           req->url.getScheme().c_str(),
-                          req->login,
-                          *req->login ? "@" : null_string,
+                          SQUIDSBUFPRINT(req->url.userInfo()),
+                          !req->url.userInfo().isEmpty() ? "@" : "",
                           req->GetHost(),
                           req->port
                          );
     } else {
-        urllen = snprintf(urlbuf, MAX_URL, "%s://%s%s%s",
+        urllen = snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s%s",
                           req->url.getScheme().c_str(),
-                          req->login,
-                          *req->login ? "@" : null_string,
+                          SQUIDSBUFPRINT(req->url.userInfo()),
+                          !req->url.userInfo().isEmpty() ? "@" : "",
                           req->GetHost()
                          );
     }
 
     if (relUrl[0] == '/') {
         strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
     } else {
         const char *path = req->urlpath.termedBuf();
         const char *last_slash = strrchr(path, '/');
 
         if (last_slash == NULL) {
             urlbuf[urllen] = '/';
             ++urllen;
             strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
         } else {
             ++last_slash;
             size_t pathlen = last_slash - path;
             if (pathlen > MAX_URL - urllen - 1) {
                 pathlen = MAX_URL - urllen - 1;
             }


