| Bug ID (本家/日本) |
Plan | Summary/Comment |
|---|---|---|
| 180299/ 2814 |
- | ★★★Support 'In-Reply-To: Your message of "Date" <Message-ID>' |
| In-Reply-To: Your message of "Date" <Message-ID>の形式(obs-in-reply-to)のときに親メッセージを認識できない。 |
In-Reply-Toによる検索結果
/mailnews/compose/src/nsMsgCompUtils.cpp, line 682
-- PUSH_STRING ("In-Reply-To: ");
/mailnews/imap/src/nsImapServerResponseParser.cpp, line 1332
-- {"In-reply-to", envelopeString},
/mailnews/imap/src/nsImapServerResponseParser.cpp, line 1339
-- // reply-to, to, cc, bcc, in-reply-to, and message-id.
/mailnews/imap/src/nsImapServerResponseParser.cpp, line 1340
-- // The date, subject, in-reply-to, and message-id
/mailnews/imap/src/nsImapProtocol.cpp, line 122
-- #define IMAP_DB_HEADERS "Priority X-Priority References Newsgroups In-Reply-To"
/mailnews/local/src/nsParseMailbox.cpp, line 908
-- if (!nsCRT::strncasecmp ("In-Reply-To", buf, end - buf))
/mailnews/local/src/nsParseMailbox.cpp, line 1383
-- // use in-reply-to header as references, if there's no references header
mozilla/ mailnews/ local/ src/ nsParseMailbox.cpp 1383 // use in-reply-to header as references, if there's no references header 1384 if (references != nsnull) 1385 m_newMsgHdr->SetReferences(references->value); 1386 else if (inReplyTo != nsnull) 1387 m_newMsgHdr->SetReferences(inReplyTo->value);
ここが親メッセージのIDを得る部分と思われる。まずReferencesヘッダからIDを取得し、無ければIn-Reply-Toヘッダから取得する。
mozilla/ mailnews/ db/ msgdb/ src/ nsMsgHdr.cpp
389 NS_IMETHODIMP nsMsgHdr::SetReferences(const char *references)
390 {
391 if (*references == '\0') {
392 m_numReferences = 0;
393 }
394 else {
395 ParseReferences(references);
396 }
397
398 SetUInt32Column(m_numReferences, m_mdb->m_numReferencesColumnToken);
399 m_initedValues |= REFERENCES_INITED;
400
401 return SetStringColumn(references, m_mdb->m_referencesColumnToken);
402 }
323 nsresult nsMsgHdr::ParseReferences(const char *references)
324 {
325 const char *startNextRef = references;
326 nsCAutoString resultReference;
327
328 while (startNextRef && *startNextRef)
329 {
330 startNextRef = GetNextReference(startNextRef, resultReference);
331 m_references.AppendCString(resultReference);
332 }
333 m_numReferences = m_references.Count();
334 return NS_OK;
335 }
74 typedef struct message_header
75 {
76 const char *value; /* The contents of a header (after ": ") */
77 PRInt32 length; /* The length of the data (it is not NULL-terminated.) */
78 } message_header;
118 struct message_header m_references;
739 const char *nsMsgHdr::GetNextReference(const char *startNextRef, nsCString &reference)
740 {
741 const char *ptr = startNextRef;
742
743 reference.Truncate(0);
744 while ((*ptr == '<' || *ptr == ' ' || *ptr == nsCRT::CR || *ptr == nsCRT::LF || *ptr == '\t') && *ptr)
745 ptr++;
746
747 for (int i = 0; *ptr && *ptr != '>'; i++)
748 reference += *ptr++;
749
750 if (*ptr == '>')
751 ptr++;
752 return ptr;
753 }
この関数は「Bug 2804 References:ヘッダの区切りにTABが含まれているとスレッドの繋がりが正しくならない」でも問題になった部分だ。
ここでは'<'、スペース、CR、LF、TABを読み飛ばし、'>'までの文字列を取得している。これを、'<'までを読み飛ばし、'>'までの文字列を取得するようにすればよいと思う。
744 while (*ptr!='<' && *ptr) ptr++; 745 if (*ptr) ptr++; //skip '<'
↑こんな感じ。厳密にはRFC2822のobs-in-reply-toで許されるphraseの中にクオートした'<'が許されるのであればその処理をちゃんとする必要がある。
RFC2822ではIn-Reply-ToとReferencesは以下のように定義されている。
in-reply-to := "In-Reply-To:" 1*msg-id CRLF references := "References:" 1*msg-id CRLF obs-in-reply-to := "In-Reply-To" *WSP ":" *(phrase / msg-id) CRLF obs-references := "References" *WSP ":" *(phrase / msg-id) CRLF
ここで、問題になるのはphraseにどのような文字列が許されるかだ。
phrase := 1*word / obs-phrase word := atom / quoted-string atom := [CFWS] 1*atext [CFWS] atext := ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "/" / "=" / "?" "^" / "_" / "`" / "{" / "|" / "}" / "~" ; Any character except controls, SP, and specials. Used for atoms quoted-string := [CFWS] DQUOTE *([FWS] qcontent) [FWS] DQUOTE [CFWS] qcontent := qtext / quoted-pair qtext := NO-WS-CTL / %d33 / %d35-91 / %d93-126 ; Non white space controls and The rest of the US-ASCII characters not including "\" or the quote character quoted-pair := ("\" text) / obs-qp obs-qp := "\" (%d0-127) obs-phrase := word *(word / "." / CFWS)> text := %d1-9 / %d11 / %d12 / %d14-127 / obs-text ; Characters excluding CR and LF
ASCIIコード表: '<' = %d60, '>' = %d62
上の定義によると
phrase->word->quoted-string->"qcontent"
であるので、'<'がphraseの中に現れるのは、"<foo>"のようなケースだ。従って"..."の中を無条件にスキップし、かつ\"をも考慮(例:"<foo>
\"bar\"")すると以下のようなコードで良さそうだ。
while (*ptr && *ptr != '<') { //skip phrase
if (*ptr == '"') { //skip quoted-string
ptr++;
while (*ptr && *ptr != '"') {
if (*ptr == '\\' && *(ptr+1)) ptr += 2; //skip quoted-pair
else ptr++;
}
if (*ptr) ptr++; //skip '"'
} else ptr++;
}
if (*ptr) ptr++; //skip '<'
上のコードだと、Message-IDの後ろにゴミがあるとうまくいかないようだ。例えば"<foo@bar.com>
baz"。文字列の最後がちょうどMessage-IDで終わっている場合、それ以上この関数はコールされないが、後ろにゴミがついているとこの関数がもう一度呼ばれ、reference.Truncate(0)が実行されてしまうのが良くないようだ。ということで以下のように修正してみたら、これで期待通りの動作をするようになった。
ところで、C++素人なのでreference.Truncate(0)の動作がわからない。
このパッチにより、In-Reply-Toヘッダと、ReferencesヘッダからMessage-IDを抽出する際に、<Message-ID>以外の部分はすべて無視される。これにより、Referenceヘッダを間違って','区切りにしているケースでも正しく親メッセージを認識できるようになる。
Thread構築の際のIn-Reply-ToヘッダとReferencesヘッダはまったく同じように扱われるようなので(両方ある場合はReferences優先)、In-Reply-Toに複数のMessage-IDがセットされている場合、右端のIDが親のIDとなる。
Index: mailnews/db/msgdb/src/nsMsgHdr.cpp
===================================================================
RCS file: /cvsroot/mozilla/mailnews/db/msgdb/src/nsMsgHdr.cpp,v
retrieving revision 1.105
diff -u -r1.105 nsMsgHdr.cpp
--- mailnews/db/msgdb/src/nsMsgHdr.cpp 12 Nov 2002 19:22:09 -0000 1.105
+++ mailnews/db/msgdb/src/nsMsgHdr.cpp 1 Dec 2002 13:03:49 -0000
@@ -735,20 +735,36 @@
return m_mdb->RowCellColumnToUInt32(GetMDBRow(), token, pvalue, defaultValue);
}
-// get the next <> delimited reference from nextRef and copy it into reference,
+// get the next <> delimited reference from nextRef and copy it into reference.
+// Called for parsing both References and In-Reply-To headers.
+// Now, any characters outside of <> are ignored.
const char *nsMsgHdr::GetNextReference(const char *startNextRef, nsCString &reference)
{
const char *ptr = startNextRef;
- reference.Truncate(0);
- while ((*ptr == '<' || *ptr == ' ' || *ptr == nsCRT::CR || *ptr == nsCRT::LF || *ptr == '\t') && *ptr)
- ptr++;
-
- for (int i = 0; *ptr && *ptr != '>'; i++)
- reference += *ptr++;
-
- if (*ptr == '>')
- ptr++;
+ while (*ptr && *ptr != '<') //skip phrase
+ {
+ if (*ptr == '"') //skip quoted-string
+ {
+ ptr++;
+ while (*ptr && *ptr != '"')
+ {
+ if (*ptr == '\\' && *(ptr+1)) ptr += 2; //skip quoted-pair
+ else ptr++;
+ }
+ if (*ptr) ptr++; //skip '"'
+ } else ptr++;
+ }
+ if (*ptr)
+ {
+ ptr++; //skip '<'
+ reference.Truncate(0); //execute only if message-id exists
+ }
+
+ while (*ptr && *ptr != '>') reference += *ptr++;
+
+ if (*ptr) ptr++; //skip '>'
+
return ptr;
}
// Get previous <> delimited reference - used to go backwards through the