=== modified file 'src/HelperReply.cc'
--- src/HelperReply.cc	2013-02-14 05:42:57 +0000
+++ src/HelperReply.cc	2013-03-14 16:13:31 +0000
@@ -126,76 +126,75 @@
 }
 
 void
 HelperReply::parseResponseKeys()
 {
     // parse a "key=value" pair off the 'other()' buffer.
     while (other().hasContent()) {
         char *p = modifiableOther().content();
         while (*p && *p != '=' && *p != ' ') ++p;
         if (*p != '=')
             return; // done. Not a key.
 
         // whitespace between key and value is prohibited.
         // workaround strwordtok() which skips whitespace prefix.
         if (xisspace(*(p+1)))
             return; // done. Not a key.
 
         *p = '\0';
         ++p;
 
-        const String key(other().content());
+        const char *key = other().content();
 
         // the value may be a quoted string or a token
         const bool urlDecode = (*p != '"'); // check before moving p.
         char *v = strwordtok(NULL, &p);
         if (v != NULL && urlDecode && (p-v) > 2) // 1-octet %-escaped requires 3 bytes
             rfc1738_unescape(v);
-        const String value(v?v:""); // value can be empty, but must not be NULL
 
-        notes.add(key, value);
+        notes.add(key, v ? v : ""); // value can be empty, but must not be NULL
 
         modifiableOther().consume(p - other().content());
         modifiableOther().consumeWhitespacePrefix();
     }
 }
 
 std::ostream &
 operator <<(std::ostream &os, const HelperReply &r)
 {
     os << "{result=";
     switch (r.result) {
     case HelperReply::Okay:
         os << "OK";
         break;
     case HelperReply::Error:
         os << "ERR";
         break;
     case HelperReply::BrokenHelper:
         os << "BH";
         break;
     case HelperReply::TT:
         os << "TT";
         break;
     case HelperReply::Unknown:
         os << "Unknown";
         break;
     }
 
     // dump the helper key=pair "notes" list
-    if (r.notes.notes.size() > 0) {
+    if (r.notes.entries.size() > 0) {
         os << ", notes={";
-        for (Notes::NotesList::const_iterator m = r.notes.notes.begin(); m != r.notes.notes.end(); ++m) {
-            for (Note::Values::iterator v = (*m)->values.begin(); v != (*m)->values.end(); ++v) {
-                os << ',' << (*m)->key << '=' << ConfigParser::QuoteString((*v)->value);
-            }
+        NotePairs::Pos pos = NotePairs::InitPos;
+        while (NotePairs::Entry *e = r.notes.getEntry(&pos)) {
+            os << ',' << e->name << '=' << ConfigParser::QuoteString(e->value);
         }
         os << "}";
     }
 
     if (r.other().hasContent())
         os << ", other: \"" << r.other().content() << '\"';
 
     os << '}';
 
     return os;
 }
+

=== modified file 'src/HelperReply.h'
--- src/HelperReply.h	2012-11-27 21:19:46 +0000
+++ src/HelperReply.h	2013-03-08 15:14:52 +0000
@@ -48,35 +48,35 @@
      * token are URL-decoded.
      * quoted-string are \-escape decoded and the quotes are stripped.
      */
     // XXX: buf should be const but we may need strwordtok() and rfc1738_unescape()
     void parse(char *buf, size_t len);
 
 public:
     /// The helper response 'result' field.
     enum Result_ {
         Unknown,      // no result code received, or unknown result code
         Okay,         // "OK" indicating success/positive result
         Error,        // "ERR" indicating success/negative result
         BrokenHelper, // "BH" indicating failure due to helper internal problems.
 
         // result codes for backward compatibility with NTLM/Negotiate
         // TODO: migrate to a variant of the above results with kv-pair parameters
         TT
     } result;
 
     // list of key=value pairs the helper produced
-    Notes notes;
+    NotePairs notes;
 
     /// for stateful replies the responding helper 'server' needs to be preserved across callbacks
     CbcPointer<helper_stateful_server> whichServer;
 
 private:
     void parseResponseKeys();
 
     /// the remainder of the line
     MemBuf other_;
 };
 
 std::ostream &operator <<(std::ostream &os, const HelperReply &r);
 
 #endif /* _SQUID_SRC_HELPERREPLY_H */

=== modified file 'src/HttpRequest.cc'
--- src/HttpRequest.cc	2013-02-17 02:24:54 +0000
+++ src/HttpRequest.cc	2013-03-08 16:46:59 +0000
@@ -215,42 +215,42 @@
 
     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->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL;
     // XXX: what to do with copy->peer_domain?
 
     copy->myportname = myportname;
     if (helperNotes) {
-        copy->helperNotes = new Notes;
-        copy->helperNotes->notes = helperNotes->notes;
+        copy->helperNotes = new NotePairs;
+        copy->helperNotes->append(helperNotes);
     }
     copy->tag = tag;
 #if USE_AUTH
     copy->extacl_user = extacl_user;
     copy->extacl_passwd = extacl_passwd;
 #endif
     copy->extacl_log = extacl_log;
     copy->extacl_message = extacl_message;
 
     assert(copy->inheritProperties(this));
 
     return copy;
 }
 
 bool
 HttpRequest::inheritProperties(const HttpMsg *aMsg)
 {
     const HttpRequest* aReq = dynamic_cast<const HttpRequest*>(aMsg);
     if (!aReq)
         return false;

=== modified file 'src/HttpRequest.h'
--- src/HttpRequest.h	2013-02-12 11:34:35 +0000
+++ src/HttpRequest.h	2013-03-08 16:12:22 +0000
@@ -186,41 +186,41 @@
 
     HierarchyLogEntry hier;
 
     int dnsWait; ///< sum of DNS lookup delays in milliseconds, for %dt
 
     err_type errType;
     int errDetail; ///< errType-specific detail about the transaction error
 
     char *peer_login;		/* Configured peer login:password */
 
     char *peer_host;           /* Selected peer host*/
 
     time_t lastmod;		/* Used on refreshes */
 
     const char *vary_headers;	/* Used when varying entities are detected. Changes how the store key is calculated */
 
     char *peer_domain;		/* Configured peer forceddomain */
 
     String myportname; // Internal tag name= value from port this requests arrived in.
 
-    Notes *helperNotes;         ///< collection of meta notes associated with this request by helper lookups.
+    NotePairs *helperNotes;         ///< collection of meta notes associated with this request by helper lookups.
 
     String tag;			/* Internal tag for this request */
 
     String extacl_user;		/* User name returned by extacl lookup */
 
     String extacl_passwd;	/* Password returned by extacl lookup */
 
     String extacl_log;		/* String to be used for access.log purposes */
 
     String extacl_message;	/* String to be used for error page purposes */
 
 #if FOLLOW_X_FORWARDED_FOR
     String x_forwarded_for_iterator; /* XXX a list of IP addresses */
 #endif /* FOLLOW_X_FORWARDED_FOR */
 
 public:
     bool multipartRangeRequest() const;
 
     bool parseFirstLine(const char *start, const char *end);
 

=== modified file 'src/Notes.cc'
--- src/Notes.cc	2013-02-12 11:34:35 +0000
+++ src/Notes.cc	2013-03-14 16:39:33 +0000
@@ -23,127 +23,89 @@
  *
  *  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 "acl/FilledChecklist.h"
 #include "acl/Gadgets.h"
 #include "ConfigParser.h"
 #include "HttpRequest.h"
 #include "HttpReply.h"
 #include "SquidConfig.h"
 #include "Store.h"
 
 #include <algorithm>
 #include <string>
 
+const int NotePairs::InitPos = HttpHeaderInitPos;
+
 Note::Value::~Value()
 {
     aclDestroyAclList(&aclList);
 }
 
 Note::Value::Pointer
 Note::addValue(const String &value)
 {
     Value::Pointer v = new Value(value);
     values.push_back(v);
     return v;
 }
 
 const char *
 Note::match(HttpRequest *request, HttpReply *reply)
 {
 
     typedef Values::iterator VLI;
     ACLFilledChecklist ch(NULL, request, NULL);
     ch.reply = reply;
     if (reply)
         HTTPMSGLOCK(ch.reply);
 
     for (VLI i = values.begin(); i != values.end(); ++i ) {
         const int ret= ch.fastCheck((*i)->aclList);
         debugs(93, 5, HERE << "Check for header name: " << key << ": " << (*i)->value
                <<", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
         if (ret == ACCESS_ALLOWED)
             return (*i)->value.termedBuf();
     }
     return NULL;
 }
 
 Note::Pointer
-Notes::find(const String &noteKey) const
+Notes::add(const String &noteKey)
 {
-    typedef Notes::NotesList::const_iterator AMLI;
+    typedef Notes::NotesList::iterator AMLI;
     for (AMLI i = notes.begin(); i != notes.end(); ++i) {
         if ((*i)->key == noteKey)
             return (*i);
     }
 
-    return Note::Pointer();
-}
-
-void
-Notes::add(const String &noteKey, const String &noteValue)
-{
-    Note::Pointer key = add(noteKey);
-    key->addValue(noteValue);
-}
-
-Note::Pointer
-Notes::add(const String &noteKey)
-{
-    Note::Pointer note = find(noteKey);
-    if (note == NULL) {
-        note = new Note(noteKey);
-        notes.push_back(note);
-    }
+    Note::Pointer note = new Note(noteKey);
+    notes.push_back(note);
     return note;
 }
 
-void
-Notes::add(const Notes &src)
-{
-    typedef Notes::NotesList::const_iterator AMLI;
-    typedef Note::Values::iterator VLI;
-
-    for (AMLI i = src.notes.begin(); i != src.notes.end(); ++i) {
-
-        // ensure we have a key by that name to fill out values for...
-        // NP: not sharing pointers at the key level since merging other helpers
-        // details later would affect this src objects keys, which is a bad idea.
-        Note::Pointer ourKey = add((*i)->key);
-
-        // known key names, merge the values lists...
-        for (VLI v = (*i)->values.begin(); v != (*i)->values.end(); ++v ) {
-            // 2012-11-29: values are read-only and Pointer can safely be shared
-            // for now we share pointers to save memory and gain speed.
-            // If that ever ceases to be true, convert this to a full copy.
-            ourKey->values.push_back(*v);
-            // TODO: prune/skip duplicates ?
-        }
-    }
-}
-
 Note::Pointer
 Notes::parse(ConfigParser &parser)
 {
     String key, value;
     ConfigParser::ParseString(&key);
     ConfigParser::ParseQuotedString(&value);
     Note::Pointer note = add(key);
     Note::Value::Pointer noteValue = note->addValue(value);
     aclParseAclList(parser, &noteValue->aclList);
 
     if (blacklisted) {
         for (int i = 0; blacklisted[i] != NULL; ++i) {
             if (note->key.caseCmp(blacklisted[i]) == 0) {
                 fatalf("%s:%d: meta key \"%s\" is a reserved %s name",
                        cfg_filename, config_lineno, note->key.termedBuf(),
                        descr ? descr : "");
             }
         }
     }
 
@@ -153,20 +115,47 @@
 void
 Notes::dump(StoreEntry *entry, const char *key)
 {
     typedef Notes::NotesList::iterator AMLI;
     for (AMLI m = notes.begin(); m != notes.end(); ++m) {
         typedef Note::Values::iterator VLI;
         for (VLI v =(*m)->values.begin(); v != (*m)->values.end(); ++v ) {
             storeAppendPrintf(entry, "%s " SQUIDSTRINGPH " %s",
                               key, SQUIDSTRINGPRINT((*m)->key), ConfigParser::QuoteString((*v)->value));
             dump_acl_list(entry, (*v)->aclList);
             storeAppendPrintf(entry, "\n");
         }
     }
 }
 
 void
 Notes::clean()
 {
     notes.clean();
 }
+
+const char *
+NotePairs::find(const char *noteKey) const
+{
+    static String value;
+    if (getByNameIfPresent(noteKey, value))
+        return value.termedBuf();
+
+    return NULL;
+}
+
+const char *
+NotePairs::findFirst(const char *noteKey) const
+{
+    NotePairs::Pos pos = NotePairs::InitPos;
+    while (const NotePairs::Entry *e = getEntry(&pos)) {
+        if (e->name.cmp(noteKey) == 0)
+            e->value.termedBuf();
+    }
+    return NULL;
+}
+
+void
+NotePairs::add(const char *key, const char *note)
+{
+    addEntry(new HttpHeaderEntry(HDR_OTHER, key, note));
+}

=== modified file 'src/Notes.h'
--- src/Notes.h	2012-11-30 11:08:47 +0000
+++ src/Notes.h	2013-03-14 16:30:51 +0000
@@ -32,116 +32,94 @@
         explicit Value(const String &aVal) : value(aVal), aclList(NULL) {}
         ~Value();
     };
     typedef Vector<Value::Pointer> Values;
 
     explicit Note(const String &aKey): key(aKey) {}
 
     /**
      * Adds a value to the note and returns a  pointer to the
      * related Value object.
      */
     Value::Pointer addValue(const String &value);
 
     /**
      * Walks through the  possible values list of the note and selects
      * the first value which matches the given HttpRequest and HttpReply
      * or NULL if none matches.
      */
     const char *match(HttpRequest *request, HttpReply *reply);
 
-    /**
-     * Returns the first value for this key or an empty string.
-     */
-    const char *firstValue() const { return (values.size()>0&&values[0]->value.defined()?values[0]->value.termedBuf():""); }
-
     String key; ///< The note key
     Values values; ///< The possible values list for the note
 };
 
 class ConfigParser;
 /**
  * Used to store a notes list.
  */
 class Notes
 {
 public:
     typedef Vector<Note::Pointer> NotesList;
     typedef NotesList::iterator iterator; ///< iterates over the notes list
     typedef NotesList::const_iterator const_iterator; ///< iterates over the notes list
 
     Notes(const char *aDescr, const char **metasBlacklist): descr(aDescr), blacklisted(metasBlacklist) {}
     Notes(): descr(NULL), blacklisted(NULL) {}
     ~Notes() { notes.clean(); }
     /**
      * Parse a notes line and returns a pointer to the
      * parsed Note object.
      */
     Note::Pointer parse(ConfigParser &parser);
     /**
      * Dump the notes list to the given StoreEntry object.
      */
     void dump(StoreEntry *entry, const char *name);
     void clean(); /// clean the notes list
 
     /// points to the first argument
     iterator begin() { return notes.begin(); }
     /// points to the end of list
     iterator end() { return notes.end(); }
     /// return true if the notes list is empty
     bool empty() { return notes.empty(); }
 
-    /**
-     * Adds a note key and value to the notes list.
-     * If the key name already exists in list, add the given value to its set of values.
-     */
-    void add(const String &noteKey, const String &noteValue);
-
-    /**
-     * Adds a set of notes from another notes list to this set.
-     * Creating entries for any new keys needed.
-     * If the key name already exists in list, add the given value to its set of values.
-     *
-     * WARNING:
-     * The list entries are all of shared Pointer type. Altering the src object(s) after
-     * using this function will update both Notes lists. Likewise, altering this
-     * destination NotesList will affect any relevant copies of src still in use.
-     */
-    void add(const Notes &src);
-
-    /**
-     * Returns a pointer to an existing Note with given key name or nil.
-     */
-    Note::Pointer find(const String &noteKey) const;
-
     NotesList notes; ///< The Note::Pointer objects array list
     const char *descr; ///< A short description for notes list
     const char **blacklisted; ///< Null terminated list of blacklisted note keys
 
 private:
     /**
      * Adds a note to the notes list and returns a pointer to the
      * related Note object. If the note key already exists in list,
      * returns a pointer to the existing object.
      */
     Note::Pointer add(const String &noteKey);
 };
 
 class NotePairs : public HttpHeader
 {
 public:
+    typedef HttpHeaderPos Pos;
+    typedef HttpHeaderEntry Entry;
+
     NotePairs() : HttpHeader(hoNote) {}
 
-    /// convert a NotesList into a NotesPairs
-    /// appending to any existing entries already present
-    void append(const Notes::NotesList &src) {
-        for (Notes::NotesList::const_iterator m = src.begin(); m != src.end(); ++m)
-            for (Note::Values::iterator v =(*m)->values.begin(); v != (*m)->values.end(); ++v)
-                putExt((*m)->key.termedBuf(), (*v)->value.termedBuf());
-    }
-
-    void append(const NotePairs *src) {
-        HttpHeader::append(dynamic_cast<const HttpHeader*>(src));
-    }
+    /**
+     * Returns the first value for this key or an empty string.
+     */
+    const char *findFirst(const char *noteKey) const;
+    /**
+     * Returns a comma separated list of notes with key 'noteKey'.
+     */
+    const char *find(const char *noteKey) const;
+    /**
+     * Adds the key/note note pair.
+     */
+    void add(const char *key, const char *note);
+
+    static const int InitPos;
 };
 
 #endif

=== modified file 'src/auth/digest/UserRequest.cc'
--- src/auth/digest/UserRequest.cc	2013-01-25 16:53:16 +0000
+++ src/auth/digest/UserRequest.cc	2013-03-08 15:51:21 +0000
@@ -290,67 +290,67 @@
         // the HA1 will be found in content() for these responses.
         if (!oldHelperWarningDone) {
             debugs(29, DBG_IMPORTANT, "WARNING: Digest auth helper returned old format HA1 response. It needs to be upgraded.");
             oldHelperWarningDone=true;
         }
 
         /* allow this because the digest_request pointer is purely local */
         Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
         assert(digest_user != NULL);
 
         CvtBin(reply.other().content(), digest_user->HA1);
         digest_user->HA1created = 1;
     }
     break;
 
     case HelperReply::Okay: {
         /* allow this because the digest_request pointer is purely local */
         Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
         assert(digest_user != NULL);
 
-        Note::Pointer ha1Note = reply.notes.find("ha1");
+        const char *ha1Note = reply.notes.findFirst("ha1");
         if (ha1Note != NULL) {
-            CvtBin(ha1Note->firstValue(), digest_user->HA1);
+            CvtBin(ha1Note, digest_user->HA1);
             digest_user->HA1created = 1;
         } else {
             debugs(29, DBG_IMPORTANT, "ERROR: Digest auth helper did not produce a HA1. Using the wrong helper program? received: " << reply);
         }
     }
     break;
 
     case HelperReply::TT:
         debugs(29, DBG_IMPORTANT, "ERROR: Digest auth does not support the result code received. Using the wrong helper program? received: " << reply);
         // fall through to next case. Handle this as an ERR response.
 
     case HelperReply::BrokenHelper:
         // TODO retry the broken lookup on another helper?
         // fall through to next case for now. Handle this as an ERR response silently.
 
     case HelperReply::Error: {
         /* allow this because the digest_request pointer is purely local */
         Auth::Digest::UserRequest *digest_request = dynamic_cast<Auth::Digest::UserRequest *>(auth_user_request.getRaw());
         assert(digest_request);
 
         digest_request->user()->credentials(Auth::Failed);
         digest_request->flags.invalid_password = true;
 
-        Note::Pointer msgNote = reply.notes.find("message");
+        const char *msgNote = reply.notes.find("message");
         if (msgNote != NULL) {
-            digest_request->setDenyMessage(msgNote->firstValue());
+            digest_request->setDenyMessage(msgNote);
         } else if (reply.other().hasContent()) {
             // old helpers did send ERR result but a bare message string instead of message= key name.
             digest_request->setDenyMessage(reply.other().content());
             if (!oldHelperWarningDone) {
                 debugs(29, DBG_IMPORTANT, "WARNING: Digest auth helper returned old format ERR response. It needs to be upgraded.");
                 oldHelperWarningDone=true;
             }
         }
     }
     break;
     }
 
     void *cbdata = NULL;
     if (cbdataReferenceValidDone(replyData->data, &cbdata))
         replyData->handler(cbdata);
 
     delete replyData;
 }

=== modified file 'src/auth/negotiate/UserRequest.cc'
--- src/auth/negotiate/UserRequest.cc	2013-02-14 06:31:38 +0000
+++ src/auth/negotiate/UserRequest.cc	2013-03-08 15:49:47 +0000
@@ -251,131 +251,131 @@
     assert(lm_request != NULL);
     assert(lm_request->waiting);
 
     lm_request->waiting = 0;
     safe_free(lm_request->client_blob);
 
     assert(auth_user_request->user() != NULL);
     assert(auth_user_request->user()->auth_type == Auth::AUTH_NEGOTIATE);
 
     if (lm_request->authserver == NULL)
         lm_request->authserver = reply.whichServer.get(); // XXX: no locking?
     else
         assert(reply.whichServer == lm_request->authserver);
 
     switch (reply.result) {
     case HelperReply::TT:
         /* we have been given a blob to send to the client */
         safe_free(lm_request->server_blob);
         lm_request->request->flags.mustKeepalive = true;
         if (lm_request->request->flags.proxyKeepalive) {
-            Note::Pointer tokenNote = reply.notes.find("token");
-            lm_request->server_blob = xstrdup(tokenNote->firstValue());
+            const char *tokenNote = reply.notes.findFirst("token");
+            lm_request->server_blob = xstrdup(tokenNote);
             auth_user_request->user()->credentials(Auth::Handshake);
             auth_user_request->denyMessage("Authentication in progress");
-            debugs(29, 4, HERE << "Need to challenge the client with a server token: '" << tokenNote->firstValue() << "'");
+            debugs(29, 4, HERE << "Need to challenge the client with a server token: '" << tokenNote << "'");
         } else {
             auth_user_request->user()->credentials(Auth::Failed);
             auth_user_request->denyMessage("Negotiate authentication requires a persistent connection");
         }
         break;
 
     case HelperReply::Okay: {
-        Note::Pointer userNote = reply.notes.find("user");
-        Note::Pointer tokenNote = reply.notes.find("token");
+        const char *userNote = reply.notes.findFirst("user");
+        const char *tokenNote = reply.notes.findFirst("token");
         if (userNote == NULL || tokenNote == NULL) {
             // XXX: handle a success with no username better
             /* protocol error */
             fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply.other().content());
             break;
         }
 
         /* we're finished, release the helper */
-        auth_user_request->user()->username(userNote->firstValue());
+        auth_user_request->user()->username(userNote);
         auth_user_request->denyMessage("Login successful");
         safe_free(lm_request->server_blob);
-        lm_request->server_blob = xstrdup(tokenNote->firstValue());
+        lm_request->server_blob = xstrdup(tokenNote);
         lm_request->releaseAuthServer();
 
         /* connection is authenticated */
         debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
         /* see if this is an existing user with a different proxy_auth
          * string */
         AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username()));
         Auth::User::Pointer local_auth_user = lm_request->user();
         while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NEGOTIATE ||
                                 strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0))
             usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
         if (usernamehash) {
             /* we can't seamlessly recheck the username due to the
              * challenge-response nature of the protocol.
              * Just free the temporary auth_user after merging as
              * much of it new state into the existing one as possible */
             usernamehash->user()->absorb(local_auth_user);
             /* from here on we are working with the original cached credentials. */
             local_auth_user = usernamehash->user();
             auth_user_request->user(local_auth_user);
         } else {
             /* store user in hash's */
             local_auth_user->addToNameCache();
         }
         /* set these to now because this is either a new login from an
          * existing user or a new user */
         local_auth_user->expiretime = current_time.tv_sec;
         auth_user_request->user()->credentials(Auth::Ok);
         debugs(29, 4, HERE << "Successfully validated user via Negotiate. Username '" << auth_user_request->user()->username() << "'");
     }
     break;
 
     case HelperReply::Error: {
-        Note::Pointer messageNote = reply.notes.find("message");
-        Note::Pointer tokenNote = reply.notes.find("token");
+        const char *messageNote = reply.notes.find("message");
+        const char *tokenNote = reply.notes.findFirst("token");
 
         /* authentication failure (wrong password, etc.) */
         if (messageNote != NULL)
-            auth_user_request->denyMessage(messageNote->firstValue());
+            auth_user_request->denyMessage(messageNote);
         else
             auth_user_request->denyMessage("Negotiate Authentication denied with no reason given");
         auth_user_request->user()->credentials(Auth::Failed);
         safe_free(lm_request->server_blob);
         if (tokenNote != NULL)
-            lm_request->server_blob = xstrdup(tokenNote->firstValue());
+            lm_request->server_blob = xstrdup(tokenNote);
         lm_request->releaseAuthServer();
         debugs(29, 4, HERE << "Failed validating user via Negotiate. Error returned '" << reply << "'");
     }
     break;
 
     case HelperReply::Unknown:
         debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication Helper '" << reply.whichServer << "' crashed!.");
         /* continue to the next case */
 
     case HelperReply::BrokenHelper: {
         /* TODO kick off a refresh process. This can occur after a YR or after
          * a KK. If after a YR release the helper and resubmit the request via
          * Authenticate Negotiate start.
          * If after a KK deny the user's request w/ 407 and mark the helper as
          * Needing YR. */
-        Note::Pointer errNote = reply.notes.find("message");
+        const char *errNote = reply.notes.find("message");
         if (reply.result == HelperReply::Unknown)
             auth_user_request->denyMessage("Internal Error");
         else if (errNote != NULL)
-            auth_user_request->denyMessage(errNote->firstValue());
+            auth_user_request->denyMessage(errNote);
         else
             auth_user_request->denyMessage("Negotiate Authentication failed with no reason given");
         auth_user_request->user()->credentials(Auth::Failed);
         safe_free(lm_request->server_blob);
         lm_request->releaseAuthServer();
         debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication validating user. Error returned " << reply);
     } // break;
     }
 
     if (lm_request->request) {
         HTTPMSGUNLOCK(lm_request->request);
         lm_request->request = NULL;
     }
     r->handler(r->data);
     delete r;
 }
 
 void
 Auth::Negotiate::UserRequest::addAuthenticationInfoHeader(HttpReply * rep, int accel)
 {

=== modified file 'src/auth/ntlm/UserRequest.cc'
--- src/auth/ntlm/UserRequest.cc	2013-01-28 16:56:05 +0000
+++ src/auth/ntlm/UserRequest.cc	2013-03-08 15:45:40 +0000
@@ -244,116 +244,116 @@
     assert(lm_request != NULL);
     assert(lm_request->waiting);
 
     lm_request->waiting = 0;
     safe_free(lm_request->client_blob);
 
     assert(auth_user_request->user() != NULL);
     assert(auth_user_request->user()->auth_type == Auth::AUTH_NTLM);
 
     if (lm_request->authserver == NULL)
         lm_request->authserver = reply.whichServer.get(); // XXX: no locking?
     else
         assert(reply.whichServer == lm_request->authserver);
 
     switch (reply.result) {
     case HelperReply::TT:
         /* we have been given a blob to send to the client */
         safe_free(lm_request->server_blob);
         lm_request->request->flags.mustKeepalive = true;
         if (lm_request->request->flags.proxyKeepalive) {
-            Note::Pointer serverBlob = reply.notes.find("token");
-            lm_request->server_blob = xstrdup(serverBlob->firstValue());
+            const char *serverBlob = reply.notes.findFirst("token");
+            lm_request->server_blob = xstrdup(serverBlob);
             auth_user_request->user()->credentials(Auth::Handshake);
             auth_user_request->denyMessage("Authentication in progress");
-            debugs(29, 4, HERE << "Need to challenge the client with a server token: '" << serverBlob->firstValue() << "'");
+            debugs(29, 4, HERE << "Need to challenge the client with a server token: '" << serverBlob << "'");
         } else {
             auth_user_request->user()->credentials(Auth::Failed);
             auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
         }
         break;
 
     case HelperReply::Okay: {
         /* we're finished, release the helper */
-        Note::Pointer userLabel = reply.notes.find("user");
-        auth_user_request->user()->username(userLabel->firstValue());
+        const char *userLabel = reply.notes.findFirst("user");
+        auth_user_request->user()->username(userLabel);
         auth_user_request->denyMessage("Login successful");
         safe_free(lm_request->server_blob);
         lm_request->releaseAuthServer();
 
-        debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << userLabel->firstValue() << "'");
+        debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << userLabel << "'");
         /* connection is authenticated */
         debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
         /* see if this is an existing user with a different proxy_auth
          * string */
         AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username()));
         Auth::User::Pointer local_auth_user = lm_request->user();
         while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NTLM ||
                                 strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0))
             usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
         if (usernamehash) {
             /* we can't seamlessly recheck the username due to the
              * challenge-response nature of the protocol.
              * Just free the temporary auth_user after merging as
              * much of it new state into the existing one as possible */
             usernamehash->user()->absorb(local_auth_user);
             /* from here on we are working with the original cached credentials. */
             local_auth_user = usernamehash->user();
             auth_user_request->user(local_auth_user);
         } else {
             /* store user in hash's */
             local_auth_user->addToNameCache();
         }
         /* set these to now because this is either a new login from an
          * existing user or a new user */
         local_auth_user->expiretime = current_time.tv_sec;
         auth_user_request->user()->credentials(Auth::Ok);
         debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << auth_user_request->user()->username() << "'");
     }
     break;
 
     case HelperReply::Error: {
         /* authentication failure (wrong password, etc.) */
-        Note::Pointer errNote = reply.notes.find("message");
+        const char *errNote = reply.notes.find("message");
         if (errNote != NULL)
-            auth_user_request->denyMessage(errNote->firstValue());
+            auth_user_request->denyMessage(errNote);
         else
             auth_user_request->denyMessage("NTLM Authentication denied with no reason given");
         auth_user_request->user()->credentials(Auth::Failed);
         safe_free(lm_request->server_blob);
         lm_request->releaseAuthServer();
-        debugs(29, 4, HERE << "Failed validating user via NTLM. Error returned '" << errNote->firstValue() << "'");
+        debugs(29, 4, HERE << "Failed validating user via NTLM. Error returned '" << errNote << "'");
     }
     break;
 
     case HelperReply::Unknown:
         debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication Helper '" << reply.whichServer << "' crashed!.");
         /* continue to the next case */
 
     case HelperReply::BrokenHelper: {
         /* TODO kick off a refresh process. This can occur after a YR or after
          * a KK. If after a YR release the helper and resubmit the request via
          * Authenticate NTLM start.
          * If after a KK deny the user's request w/ 407 and mark the helper as
          * Needing YR. */
-        Note::Pointer errNote = reply.notes.find("message");
+        const char *errNote = reply.notes.find("message");
         if (reply.result == HelperReply::Unknown)
             auth_user_request->denyMessage("Internal Error");
         else if (errNote != NULL)
-            auth_user_request->denyMessage(errNote->firstValue());
+            auth_user_request->denyMessage(errNote);
         else
             auth_user_request->denyMessage("NTLM Authentication failed with no reason given");
         auth_user_request->user()->credentials(Auth::Failed);
         safe_free(lm_request->server_blob);
         lm_request->releaseAuthServer();
         debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication validating user. Error returned '" << reply << "'");
     }
     break;
     }
 
     if (lm_request->request) {
         HTTPMSGUNLOCK(lm_request->request);
         lm_request->request = NULL;
     }
     r->handler(r->data);
     delete r;
 }

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2013-02-12 11:34:35 +0000
+++ src/client_side.cc	2013-03-08 16:18:46 +0000
@@ -599,41 +599,41 @@
             ah->lastMeta.packInto(&p);
             aLogEntry->adapt.last_meta = xstrdup(mb.buf);
             aLogEntry->notes.append(&ah->metaHeaders);
         }
 #endif
 
         packerClean(&p);
         mb.clean();
     }
 
 #if ICAP_CLIENT
     const Adaptation::Icap::History::Pointer ih = request->icapHistory();
     if (ih != NULL)
         aLogEntry->icap.processingTime = ih->processingTime();
 #endif
 
     aLogEntry->http.method = request->method;
     aLogEntry->http.version = request->http_ver;
     aLogEntry->hier = request->hier;
     if (request->helperNotes)
-        aLogEntry->notes.append(request->helperNotes->notes);
+        aLogEntry->notes.append(request->helperNotes);
     if (request->content_length > 0) // negative when no body or unknown length
         aLogEntry->cache.requestSize += request->content_length;
     aLogEntry->cache.extuser = request->extacl_user.termedBuf();
 
 #if USE_AUTH
     if (request->auth_user_request != NULL) {
         if (request->auth_user_request->username())
             aLogEntry->cache.authuser = xstrdup(request->auth_user_request->username());
     }
 #endif
 
     // Adapted request, if any, inherits and then collects all the stats, but
     // the virgin request gets logged instead; copy the stats to log them.
     // TODO: avoid losses by keeping these stats in a shared history object?
     if (aLogEntry->request) {
         aLogEntry->request->dnsWait = request->dnsWait;
         aLogEntry->request->errType = request->errType;
         aLogEntry->request->errDetail = request->errDetail;
     }
 }

=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc	2013-02-12 11:34:35 +0000
+++ src/client_side_request.cc	2013-03-08 16:26:59 +0000
@@ -1244,194 +1244,192 @@
     ClientRequestContext *calloutContext = (ClientRequestContext *)data;
 
     if (!calloutContext->httpStateIsValid())
         return;
 
     calloutContext->clientStoreIdDone(result);
 }
 
 void
 ClientRequestContext::clientRedirectDone(const HelperReply &reply)
 {
     HttpRequest *old_request = http->request;
     debugs(85, 5, HERE << "'" << http->uri << "' result=" << reply);
     assert(redirect_state == REDIRECT_PENDING);
     redirect_state = REDIRECT_DONE;
 
     // copy the URL rewriter response Notes to the HTTP request for logging
     // do it early to ensure that no matter what the outcome the notes are present.
     // TODO put them straight into the transaction state record (ALE?) eventually
     if (!old_request->helperNotes)
-        old_request->helperNotes = new Notes;
-    old_request->helperNotes->add(reply.notes);
+        old_request->helperNotes = new NotePairs;
+    old_request->helperNotes->append(&reply.notes);
 
     switch (reply.result) {
     case HelperReply::Unknown:
     case HelperReply::TT:
         // Handler in redirect.cc should have already mapped Unknown
         // IF it contained valid entry for the old URL-rewrite helper protocol
         debugs(85, DBG_IMPORTANT, "ERROR: URL rewrite helper returned invalid result code. Wrong helper? " << reply);
         break;
 
     case HelperReply::BrokenHelper:
         debugs(85, DBG_IMPORTANT, "ERROR: URL rewrite helper: " << reply << ", attempt #" << (redirect_fail_count+1) << " of 2");
         if (redirect_fail_count < 2) { // XXX: make this configurable ?
             ++redirect_fail_count;
             // reset state flag to try redirector again from scratch.
             redirect_done = false;
         }
         break;
 
     case HelperReply::Error:
         // no change to be done.
         break;
 
     case HelperReply::Okay: {
         // #1: redirect with a specific status code    OK status=NNN url="..."
         // #2: redirect with a default status code     OK url="..."
         // #3: re-write the URL                        OK rewrite-url="..."
 
-        Note::Pointer statusNote = reply.notes.find("status");
-        Note::Pointer urlNote = reply.notes.find("url");
+        const char *statusNote = reply.notes.findFirst("status");
+        const char *urlNote = reply.notes.findFirst("url");
 
         if (urlNote != NULL) {
             // HTTP protocol redirect to be done.
 
             // TODO: change default redirect status for appropriate requests
             // Squid defaults to 302 status for now for better compatibility with old clients.
             // HTTP/1.0 client should get 302 (HTTP_MOVED_TEMPORARILY)
             // HTTP/1.1 client contacting reverse-proxy should get 307 (HTTP_TEMPORARY_REDIRECT)
             // HTTP/1.1 client being diverted by forward-proxy should get 303 (HTTP_SEE_OTHER)
             http_status status = HTTP_MOVED_TEMPORARILY;
-            if (statusNote != NULL) {
-                const char * result = statusNote->firstValue();
-                status = (http_status) atoi(result);
-            }
+            if (statusNote != NULL)
+                status = (http_status) atoi(statusNote);
 
             if (status == HTTP_MOVED_PERMANENTLY
                     || status == HTTP_MOVED_TEMPORARILY
                     || status == HTTP_SEE_OTHER
                     || status == HTTP_PERMANENT_REDIRECT
                     || status == HTTP_TEMPORARY_REDIRECT) {
                 http->redirect.status = status;
-                http->redirect.location = xstrdup(urlNote->firstValue());
+                http->redirect.location = xstrdup(urlNote);
                 // TODO: validate the URL produced here is RFC 2616 compliant absolute URI
             } else {
-                debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid " << status << " redirect Location: " << urlNote->firstValue());
+                debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid " << status << " redirect Location: " << urlNote);
             }
         } else {
             // URL-rewrite wanted. Ew.
-            urlNote = reply.notes.find("rewrite-url");
+            urlNote = reply.notes.findFirst("rewrite-url");
 
             // prevent broken helpers causing too much damage. If old URL == new URL skip the re-write.
-            if (urlNote != NULL && strcmp(urlNote->firstValue(), http->uri)) {
+            if (urlNote != NULL && strcmp(urlNote, http->uri)) {
                 // XXX: validate the URL properly *without* generating a whole new request object right here.
                 // XXX: the clone() should be done only AFTER we know the new URL is valid.
                 HttpRequest *new_request = old_request->clone();
-                if (urlParse(old_request->method, const_cast<char*>(urlNote->firstValue()), new_request)) {
+                if (urlParse(old_request->method, const_cast<char*>(urlNote), new_request)) {
                     debugs(61,2, HERE << "URL-rewriter diverts URL from " << urlCanonical(old_request) << " to " << urlCanonical(new_request));
 
                     // update the new request to flag the re-writing was done on it
                     new_request->flags.redirected = true;
 
                     // unlink bodypipe from the old request. Not needed there any longer.
                     if (old_request->body_pipe != NULL) {
                         old_request->body_pipe = NULL;
                         debugs(61,2, HERE << "URL-rewriter diverts body_pipe " << new_request->body_pipe <<
                                " from request " << old_request << " to " << new_request);
                     }
 
                     // update the current working ClientHttpRequest fields
                     safe_free(http->uri);
                     http->uri = xstrdup(urlCanonical(new_request));
                     HTTPMSGUNLOCK(old_request);
                     http->request = new_request;
                     HTTPMSGLOCK(http->request);
                 } else {
                     debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid request: " <<
-                           old_request->method << " " << urlNote->firstValue() << " " << old_request->http_ver);
+                           old_request->method << " " << urlNote << " " << old_request->http_ver);
                     delete new_request;
                 }
             }
         }
     }
     break;
     }
 
     /* FIXME PIPELINE: This is innacurate during pipelining */
 
     if (http->getConn() != NULL && Comm::IsConnOpen(http->getConn()->clientConnection))
         fd_note(http->getConn()->clientConnection->fd, http->uri);
 
     assert(http->uri);
 
     http->doCallouts();
 }
 
 /**
  * This method handles the different replies from StoreID helper.
  */
 void
 ClientRequestContext::clientStoreIdDone(const HelperReply &reply)
 {
     HttpRequest *old_request = http->request;
     debugs(85, 5, "'" << http->uri << "' result=" << reply);
     assert(store_id_state == REDIRECT_PENDING);
     store_id_state = REDIRECT_DONE;
 
     // copy the helper response Notes to the HTTP request for logging
     // do it early to ensure that no matter what the outcome the notes are present.
     // TODO put them straight into the transaction state record (ALE?) eventually
     if (!old_request->helperNotes)
-        old_request->helperNotes = new Notes;
-    old_request->helperNotes->add(reply.notes);
+        old_request->helperNotes = new NotePairs;
+    old_request->helperNotes->append(&reply.notes);
 
     switch (reply.result) {
     case HelperReply::Unknown:
     case HelperReply::TT:
         // Handler in redirect.cc should have already mapped Unknown
         // IF it contained valid entry for the old helper protocol
         debugs(85, DBG_IMPORTANT, "ERROR: storeID helper returned invalid result code. Wrong helper? " << reply);
         break;
 
     case HelperReply::BrokenHelper:
         debugs(85, DBG_IMPORTANT, "ERROR: storeID helper: " << reply << ", attempt #" << (store_id_fail_count+1) << " of 2");
         if (store_id_fail_count < 2) { // XXX: make this configurable ?
             ++store_id_fail_count;
             // reset state flag to try StoreID again from scratch.
             store_id_done = false;
         }
         break;
 
     case HelperReply::Error:
         // no change to be done.
         break;
 
     case HelperReply::Okay: {
-        Note::Pointer urlNote = reply.notes.find("store-id");
+        const char *urlNote = reply.notes.findFirst("store-id");
 
         // prevent broken helpers causing too much damage. If old URL == new URL skip the re-write.
-        if (urlNote != NULL && strcmp(urlNote->firstValue(), http->uri) ) {
+        if (urlNote != NULL && strcmp(urlNote, http->uri) ) {
             // Debug section required for some very specific cases.
-            debugs(85, 9, "Setting storeID with: " << urlNote->firstValue() );
-            http->request->store_id = urlNote->firstValue();
-            http->store_id = urlNote->firstValue();
+            debugs(85, 9, "Setting storeID with: " << urlNote );
+            http->request->store_id = urlNote;
+            http->store_id = urlNote;
         }
     }
     break;
     }
 
     http->doCallouts();
 }
 
 /** Test cache allow/deny configuration
  *  Sets flags.cachable=1 if caching is not denied.
  */
 void
 ClientRequestContext::checkNoCache()
 {
     if (Config.accessList.noCache) {
         acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
         acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, this);
     } else {
         /* unless otherwise specified, we try to cache. */
         checkNoCacheDone(ACCESS_ALLOWED);

=== modified file 'src/external_acl.cc'
--- src/external_acl.cc	2013-01-27 17:35:07 +0000
+++ src/external_acl.cc	2013-03-08 16:33:26 +0000
@@ -1311,60 +1311,60 @@
  * value needs to be URL-encoded or enclosed in double quotes (")
  * with \-escaping on any whitespace, quotes, or slashes (\).
  */
 static void
 externalAclHandleReply(void *data, const HelperReply &reply)
 {
     externalAclState *state = static_cast<externalAclState *>(data);
     externalAclState *next;
     ExternalACLEntryData entryData;
     entryData.result = ACCESS_DENIED;
     external_acl_entry *entry = NULL;
 
     debugs(82, 2, HERE << "reply=" << reply);
 
     if (reply.result == HelperReply::Okay)
         entryData.result = ACCESS_ALLOWED;
     // XXX: handle other non-DENIED results better
 
     // XXX: make entryData store a proper HelperReply object instead of copying.
 
-    Note::Pointer label = reply.notes.find("tag");
-    if (label != NULL && label->values[0]->value.size() > 0)
-        entryData.tag = label->values[0]->value;
-
-    label = reply.notes.find("message");
-    if (label != NULL && label->values[0]->value.size() > 0)
-        entryData.message = label->values[0]->value;
-
-    label = reply.notes.find("log");
-    if (label != NULL && label->values[0]->value.size() > 0)
-        entryData.log = label->values[0]->value;
+    const char *label = reply.notes.findFirst("tag");
+    if (label != NULL && *label != '\0')
+        entryData.tag = label;
+
+    label = reply.notes.findFirst("message");
+    if (label != NULL && *label != '\0')
+        entryData.message = label;
+
+    label = reply.notes.findFirst("log");
+    if (label != NULL && *label != '\0')
+        entryData.log = label;
 
 #if USE_AUTH
-    label = reply.notes.find("user");
-    if (label != NULL && label->values[0]->value.size() > 0)
-        entryData.user = label->values[0]->value;
-
-    label = reply.notes.find("password");
-    if (label != NULL && label->values[0]->value.size() > 0)
-        entryData.password = label->values[0]->value;
+    label = reply.notes.findFirst("user");
+    if (label != NULL && *label != '\0')
+        entryData.user = label;
+
+    label = reply.notes.findFirst("password");
+    if (label != NULL && *label != '\0')
+        entryData.password = label;
 #endif
 
     dlinkDelete(&state->list, &state->def->queue);
 
     if (cbdataReferenceValid(state->def)) {
         // only cache OK and ERR results.
         if (reply.result == HelperReply::Okay || reply.result == HelperReply::Error)
             entry = external_acl_cache_add(state->def, state->key, entryData);
         else {
             external_acl_entry *oldentry = (external_acl_entry *)hash_lookup(state->def->cache, state->key);
 
             if (oldentry)
                 external_acl_cache_delete(state->def, oldentry);
         }
     }
 
     do {
         void *cbdata;
         cbdataReferenceDone(state->def);
 

=== modified file 'src/format/Format.cc'
--- src/format/Format.cc	2013-02-11 23:11:12 +0000
+++ src/format/Format.cc	2013-03-14 16:20:56 +0000
@@ -1029,42 +1029,42 @@
                     out = tmp;
                 }
             }
             break;
 
         case LFT_SSL_USER_CERT_ISSUER:
             if (X509 *cert = al->cache.sslClientCert.get()) {
                 if (X509_NAME *issuer = X509_get_issuer_name(cert)) {
                     X509_NAME_oneline(issuer, tmp, sizeof(tmp));
                     out = tmp;
                 }
             }
             break;
 #endif
         case LFT_NOTE:
             if (fmt->data.string) {
                 sb = al->notes.getByName(fmt->data.string);
                 out = sb.termedBuf();
                 quote = 1;
             } else {
-                HttpHeaderPos pos = HttpHeaderInitPos;
-                while (const HttpHeaderEntry *e = al->notes.getEntry(&pos)) {
+                NotePairs::Pos pos = NotePairs::InitPos;
+                while (const NotePairs::Entry *e = al->notes.getEntry(&pos)) {
                     sb.append(e->name);
                     sb.append(": ");
                     sb.append(e->value);
                     sb.append("\r\n");
                 }
                 out = sb.termedBuf();
                 quote = 1;
             }
             break;
 
         case LFT_PERCENT:
             out = "%";
 
             break;
         }
 
         if (dooff) {
             snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
             out = tmp;
 


