=== modified file 'src/HelperReply.cc'
--- src/HelperReply.cc	2012-11-11 05:10:59 +0000
+++ src/HelperReply.cc	2012-11-27 10:19:52 +0000
@@ -54,11 +54,11 @@
                 MemBuf authToken;
                 authToken.init();
                 authToken.append(w1, strlen(w1));
-                responseKeys.add("token",authToken.content());
+                notes.add("token",authToken.content());
             } else {
                 // token field is mandatory on this response code
                 result = HelperReply::BrokenHelper;
-                responseKeys.add("message","Missing 'token' data");
+                notes.add("message","Missing 'token' data");
             }
 
         } else if (!strncmp(p,"AF ",3)) {
@@ -75,19 +75,19 @@
                 MemBuf authToken;
                 authToken.init();
                 authToken.append(w1, strlen(w1));
-                responseKeys.add("token",authToken.content());
+                notes.add("token",authToken.content());
 
                 MemBuf user;
                 user.init();
                 user.append(w2,strlen(w2));
-                responseKeys.add("user",user.content());
+                notes.add("user",user.content());
 
             } else if (w1 != NULL) {
                 // NTLM "user"
                 MemBuf user;
                 user.init();
                 user.append(w1,strlen(w1));
-                responseKeys.add("user",user.content());
+                notes.add("user",user.content());
             }
         } else if (!strncmp(p,"NA ",3)) {
             // NTLM fail-closed ERR response
@@ -109,7 +109,7 @@
 
     // Hack for backward-compatibility: BH used to be a text message...
     if (other().hasContent() && result == HelperReply::BrokenHelper) {
-        responseKeys.add("message",other().content());
+        notes.add("message",other().content());
         modifiableOther().clean();
     }
 }
@@ -139,9 +139,9 @@
         char *v = strwordtok(NULL, &p);
         if (v != NULL && urlDecode && (p-v) > 2) // 1-octet %-escaped requires 3 bytes
             rfc1738_unescape(v);
-        String value = v;
+        const String value(v?v:""); // value can be empty, but must not be NULL
 
-        responseKeys.add(key, value);
+        notes.add(key, value);
 
         modifiableOther().consume(p - other().content());
         modifiableOther().consumeWhitespacePrefix();
@@ -171,9 +171,9 @@
     }
 
     // dump the helper key=pair "notes" list
-    if (r.responseKeys.notes.size() > 0) {
+    if (r.notes.notes.size() > 0) {
         os << ", notes={";
-        for (Notes::NotesList::const_iterator m = r.responseKeys.notes.begin(); m != r.responseKeys.notes.end(); ++m) {
+        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);
             }

=== modified file 'src/HelperReply.h'
--- src/HelperReply.h	2012-11-10 06:40:21 +0000
+++ src/HelperReply.h	2012-11-27 10:25:27 +0000
@@ -24,7 +24,7 @@
     HelperReply &operator =(const HelperReply &r);
 
 public:
-    HelperReply() : result(HelperReply::Unknown), responseKeys(), whichServer(NULL) {
+    HelperReply() : result(HelperReply::Unknown), notes(), whichServer(NULL) {
         other_.init(1,1);
         other_.terminate();
     }
@@ -42,8 +42,8 @@
     MemBuf &modifiableOther() const { return *const_cast<MemBuf*>(&other_); }
 
     /** parse a helper response line format:
-     *   line     := [ result ] *#( key-pair )
-     *   key-pair := OWS token '=' ( quoted-string | token )
+     *   line     := [ result ] *#( kv-pair )
+     *   kv-pair := OWS token '=' ( quoted-string | token )
      *
      * token are URL-decoded.
      * quoted-string are \-escape decoded and the quotes are stripped.
@@ -60,12 +60,12 @@
         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 key-pair parameters
+        // TODO: migrate to a variant of the above results with kv-pair parameters
         TT
     } result;
 
     // list of key=value pairs the helper produced
-    Notes responseKeys;
+    Notes notes;
 
     /// for stateful replies the responding helper 'server' needs to be preserved across callbacks
     CbcPointer<helper_stateful_server> whichServer;

=== modified file 'src/Notes.cc'
--- src/Notes.cc	2012-11-06 22:32:56 +0000
+++ src/Notes.cc	2012-11-27 11:42:55 +0000
@@ -73,7 +73,7 @@
 }
 
 Note::Pointer
-Notes::findByName(const String &noteKey) const
+Notes::find(const String &noteKey) const
 {
     typedef Notes::NotesList::const_iterator AMLI;
     for (AMLI i = notes.begin(); i != notes.end(); ++i) {
@@ -94,7 +94,7 @@
 Note::Pointer
 Notes::add(const String &noteKey)
 {
-    Note::Pointer note = findByName(noteKey);
+    Note::Pointer note = find(noteKey);
     if (note == NULL) {
         note = new Note(noteKey);
         notes.push_back(note);

=== modified file 'src/Notes.h'
--- src/Notes.h	2012-11-06 22:32:56 +0000
+++ src/Notes.h	2012-11-27 10:24:27 +0000
@@ -48,6 +48,12 @@
      * 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
 };
@@ -84,9 +90,21 @@
     /// 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);
+
+    /**
+     * 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
@@ -95,17 +113,6 @@
      */
     Note::Pointer add(const String &noteKey);
 
-public:
-    /**
-     * 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);
-
-    /**
-     * Looks up a note by key name and returns a Note::Pointer to it
-     */
-    Note::Pointer findByName(const String &noteKey) const;
 };
 
 class NotePairs : public HttpHeader

=== modified file 'src/auth/digest/UserRequest.cc'
--- src/auth/digest/UserRequest.cc	2012-11-09 04:23:08 +0000
+++ src/auth/digest/UserRequest.cc	2012-11-27 11:15:21 +0000
@@ -304,9 +304,9 @@
         Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
         assert(digest_user != NULL);
 
-        Note::Pointer ha1Note = reply.responseKeys.findByName("ha1");
+        Note::Pointer ha1Note = reply.notes.find("ha1");
         if (ha1Note != NULL) {
-            CvtBin(ha1Note->values[0]->value.termedBuf(), digest_user->HA1);
+            CvtBin(ha1Note->firstValue(), 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);
@@ -330,9 +330,9 @@
         digest_request->user()->credentials(Auth::Failed);
         digest_request->flags.invalid_password = 1;
 
-        Note::Pointer msgNote = reply.responseKeys.findByName("message");
+        Note::Pointer msgNote = reply.notes.find("message");
         if (msgNote != NULL) {
-            digest_request->setDenyMessage(msgNote->values[0]->value.termedBuf());
+            digest_request->setDenyMessage(msgNote->firstValue());
         } 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());

=== modified file 'src/auth/negotiate/UserRequest.cc'
--- src/auth/negotiate/UserRequest.cc	2012-11-07 13:13:47 +0000
+++ src/auth/negotiate/UserRequest.cc	2012-11-27 11:11:48 +0000
@@ -268,11 +268,11 @@
         safe_free(lm_request->server_blob);
         lm_request->request->flags.mustKeepalive = 1;
         if (lm_request->request->flags.proxyKeepalive) {
-            Note::Pointer tokenNote = reply.responseKeys.findByName("token");
-            lm_request->server_blob = xstrdup(tokenNote->values[0]->value.termedBuf());
+            Note::Pointer tokenNote = reply.notes.find("token");
+            lm_request->server_blob = xstrdup(tokenNote->firstValue());
             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->values[0]->value << "'");
+            debugs(29, 4, HERE << "Need to challenge the client with a server token: '" << tokenNote->firstValue() << "'");
         } else {
             auth_user_request->user()->credentials(Auth::Failed);
             auth_user_request->denyMessage("Negotiate authentication requires a persistent connection");
@@ -280,8 +280,8 @@
         break;
 
     case HelperReply::Okay: {
-        Note::Pointer userNote = reply.responseKeys.findByName("user");
-        Note::Pointer tokenNote = reply.responseKeys.findByName("token");
+        Note::Pointer userNote = reply.notes.find("user");
+        Note::Pointer tokenNote = reply.notes.find("token");
         if (userNote == NULL || tokenNote == NULL) {
             // XXX: handle a success with no username better
             /* protocol error */
@@ -290,10 +290,10 @@
         }
 
         /* we're finished, release the helper */
-        auth_user_request->user()->username(userNote->values[0]->value.termedBuf());
+        auth_user_request->user()->username(userNote->firstValue());
         auth_user_request->denyMessage("Login successful");
         safe_free(lm_request->server_blob);
-        lm_request->server_blob = xstrdup(tokenNote->values[0]->value.termedBuf());
+        lm_request->server_blob = xstrdup(tokenNote->firstValue());
         lm_request->releaseAuthServer();
 
         /* connection is authenticated */
@@ -327,8 +327,8 @@
     break;
 
     case HelperReply::Error: {
-        Note::Pointer messageNote = reply.responseKeys.findByName("message");
-        Note::Pointer tokenNote = reply.responseKeys.findByName("token");
+        Note::Pointer messageNote = reply.notes.find("message");
+        Note::Pointer tokenNote = reply.notes.find("token");
         if (tokenNote == NULL) {
             /* protocol error */
             fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply.other().content());
@@ -336,10 +336,10 @@
         }
 
         /* authentication failure (wrong password, etc.) */
-        auth_user_request->denyMessage(messageNote->values[0]->value.termedBuf());
+        auth_user_request->denyMessage(messageNote->firstValue());
         auth_user_request->user()->credentials(Auth::Failed);
         safe_free(lm_request->server_blob);
-        lm_request->server_blob = xstrdup(tokenNote->values[0]->value.termedBuf());
+        lm_request->server_blob = xstrdup(tokenNote->firstValue());
         lm_request->releaseAuthServer();
         debugs(29, 4, HERE << "Failed validating user via Negotiate. Error returned '" << reply << "'");
     }
@@ -355,11 +355,11 @@
          * 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.responseKeys.findByName("message");
+        Note::Pointer 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->values[0]->value.termedBuf());
+            auth_user_request->denyMessage(errNote->firstValue());
         else
             auth_user_request->denyMessage("Negotiate Authentication failed with no reason given");
         auth_user_request->user()->credentials(Auth::Failed);

=== modified file 'src/auth/ntlm/UserRequest.cc'
--- src/auth/ntlm/UserRequest.cc	2012-11-07 13:13:47 +0000
+++ src/auth/ntlm/UserRequest.cc	2012-11-27 11:11:29 +0000
@@ -261,11 +261,11 @@
         safe_free(lm_request->server_blob);
         lm_request->request->flags.mustKeepalive = 1;
         if (lm_request->request->flags.proxyKeepalive) {
-            Note::Pointer serverBlob = reply.responseKeys.findByName("token");
-            lm_request->server_blob = xstrdup(serverBlob->values[0]->value.termedBuf());
+            Note::Pointer serverBlob = reply.notes.find("token");
+            lm_request->server_blob = xstrdup(serverBlob->firstValue());
             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->values[0]->value << "'");
+            debugs(29, 4, HERE << "Need to challenge the client with a server token: '" << serverBlob->firstValue() << "'");
         } else {
             auth_user_request->user()->credentials(Auth::Failed);
             auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
@@ -274,13 +274,13 @@
 
     case HelperReply::Okay: {
         /* we're finished, release the helper */
-        Note::Pointer userLabel = reply.responseKeys.findByName("user");
-        auth_user_request->user()->username(userLabel->values[0]->value.termedBuf());
+        Note::Pointer userLabel = reply.notes.find("user");
+        auth_user_request->user()->username(userLabel->firstValue());
         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->values[0]->value << "'");
+        debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << userLabel->firstValue() << "'");
         /* 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
@@ -313,15 +313,15 @@
 
     case HelperReply::Error: {
         /* authentication failure (wrong password, etc.) */
-        Note::Pointer errNote = reply.responseKeys.findByName("message");
+        Note::Pointer errNote = reply.notes.find("message");
         if (errNote != NULL)
-            auth_user_request->denyMessage(errNote->values[0]->value.termedBuf());
+            auth_user_request->denyMessage(errNote->firstValue());
         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->values[0]->value << "'");
+        debugs(29, 4, HERE << "Failed validating user via NTLM. Error returned '" << errNote->firstValue() << "'");
     }
     break;
 
@@ -335,11 +335,11 @@
          * 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.responseKeys.findByName("message");
+        Note::Pointer 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->values[0]->value.termedBuf());
+            auth_user_request->denyMessage(errNote->firstValue());
         else
             auth_user_request->denyMessage("NTLM Authentication failed with no reason given");
         auth_user_request->user()->credentials(Auth::Failed);

=== modified file 'src/dns.cc'
--- src/dns.cc	2012-11-08 08:49:33 +0000
+++ src/dns.cc	2012-11-27 09:34:42 +0000
@@ -134,8 +134,8 @@
         HelperReply failReply;
         /* XXX: upgrade the ipcache and fqdn cache handlers to new syntax
         failReply.result= HelperReply::BrokenHelper;
-        failReply.responseKeys.add("message","Temporary network problem, please retry later");
-        failReply.responseKeys.add("message","DNS lookup queue overloaded");
+        failReply.notes.add("message","Temporary network problem, please retry later");
+        failReply.notes.add("message","DNS lookup queue overloaded");
         */
         failReply.modifiableOther().append(t, strlen(t));
         callback(data, failReply);

=== modified file 'src/external_acl.cc'
--- src/external_acl.cc	2012-11-10 11:03:49 +0000
+++ src/external_acl.cc	2012-11-27 11:34:30 +0000
@@ -1326,26 +1326,26 @@
         entryData.result = ACCESS_ALLOWED;
     // XXX: handle other non-DENIED results better
 
-    // XXX: make entryData store a proper helperReply object.
+    // XXX: make entryData store a proper HelperReply object instead of copying.
 
-    Note::Pointer label = reply.responseKeys.findByName("tag");
+    Note::Pointer label = reply.notes.find("tag");
     if (label != NULL && label->values[0]->value.size() > 0)
         entryData.tag = label->values[0]->value;
 
-    label = reply.responseKeys.findByName("message");
+    label = reply.notes.find("message");
     if (label != NULL && label->values[0]->value.size() > 0)
         entryData.message = label->values[0]->value;
 
-    label = reply.responseKeys.findByName("log");
+    label = reply.notes.find("log");
     if (label != NULL && label->values[0]->value.size() > 0)
         entryData.log = label->values[0]->value;
 
 #if USE_AUTH
-    label = reply.responseKeys.findByName("user");
+    label = reply.notes.find("user");
     if (label != NULL && label->values[0]->value.size() > 0)
         entryData.user = label->values[0]->value;
 
-    label = reply.responseKeys.findByName("password");
+    label = reply.notes.find("password");
     if (label != NULL && label->values[0]->value.size() > 0)
         entryData.password = label->values[0]->value;
 #endif

=== modified file 'src/redirect.cc'
--- src/redirect.cc	2012-11-08 08:49:33 +0000
+++ src/redirect.cc	2012-11-27 10:25:33 +0000
@@ -79,7 +79,7 @@
     debugs(61, 5, HERE << "reply=" << reply);
 
     // XXX: This funtion is now kept only to check for and display this garbage use-case
-    // it can be removed when the helpers are all updated to the normalized "OK/ERR key-pairs" format
+    // it can be removed when the helpers are all updated to the normalized "OK/ERR kv-pairs" format
 
     if (reply.result == HelperReply::Unknown) {
         // BACKWARD COMPATIBILITY 2012-06-15:
@@ -153,7 +153,7 @@
         ++n_bypassed;
         HelperReply bypassReply;
         bypassReply.result = HelperReply::Okay;
-        bypassReply.responseKeys.add("message","URL rewrite/redirect queue too long. Bypassed.");
+        bypassReply.notes.add("message","URL rewrite/redirect queue too long. Bypassed.");
         handler(data, bypassReply);
         return;
     }

=== modified file 'src/ssl/helper.cc'
--- src/ssl/helper.cc	2012-11-08 08:49:33 +0000
+++ src/ssl/helper.cc	2012-11-27 09:34:54 +0000
@@ -95,7 +95,7 @@
         debugs(34, DBG_IMPORTANT, HERE << "Queue overload, rejecting");
         HelperReply failReply;
         failReply.result = HelperReply::BrokenHelper;
-        failReply.responseKeys.add("message", "error 45 Temporary network problem, please retry later");
+        failReply.notes.add("message", "error 45 Temporary network problem, please retry later");
         callback(data, failReply);
         return;
     }


