#include "Xpclucene.h" #include "nsLuceneChannel.h" #include "nsLuceneUtils.h" #include "nsIServiceManager.h" #include "nsNetUtil.h" #include "nsInt64.h" #include "nsILoadGroup.h" #include "plbase64.h" #include "prmem.h" #include "prtime.h" #include "nsIPipe.h" #include "nsIInputStream.h" #include "nsIOutputStream.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" #include "nsCRT.h" #include "nsEscape.h" #include "nsNetCID.h" #include "nsStringStream.h" #include "nsILuceneFactory.h" #include "nsILuceneService.h" #include "nsILuceneIndexReader.h" #include "nsILuceneIndexSearcher.h" #include "nsILuceneDocument.h" #include "nsILuceneAnalyzer.h" #include "nsILuceneQuery.h" #include "nsILuceneHits.h" #include "nsILuceneHighlighter.h" #include "nsIFile.h" #include "nsICharsetConverterManager.h" #include "nsLayoutCID.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsIDOMElement.h" #include "nsIDOMProcessingInstruction.h" #include "nsIDOMText.h" #include "nsIDOMNode.h" #include "nsIPipe.h" #include "nsIDocumentEncoder.h" #include "nsIURL.h" #include "nsTextFormatter.h" #include "nsIFileStreams.h" #include "nsContentCID.h" static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID); #define ATOM_NAMESPACE_URI "http://www.w3.org/2005/Atom" #define kXHTMLNamespace NS_LITERAL_STRING("http://www.w3.org/1999/xhtml") #define kHighlightDelimiter NS_LITERAL_STRING("\01") /* * The length of the string to show from the document content if the * highlighter does not pick anything out */ #define PREVIEW_LENGTH 300 // nsLuceneChannel methods nsLuceneChannel::nsLuceneChannel() : mContentLength(-1), mOpened(PR_FALSE) { kAtomNamespace.Assign(NS_LITERAL_STRING(ATOM_NAMESPACE_URI)); } nsLuceneChannel::~nsLuceneChannel() { } NS_IMPL_ISUPPORTS5(nsLuceneChannel, nsILuceneChannel, nsIChannel, nsIRequest, nsIRequestObserver, nsIStreamListener) nsresult nsLuceneChannel::Init(nsIURI* uri) { nsresult rv; mUri = uri; nsCAutoString path; mUri->GetPath(path); nsCAutoString indexName; mUri->GetHost(indexName); mLuceneService = do_GetService(XPCLUCENESERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); LOG(("nsLuceneChannel Init with indexName = '%s' path = '%s'\n", indexName.get(), path.get())); if(path.FindChar('f') == 1) { rv = DoFile(); } else { /* * Get an index reader and open up the index */ mLuceneFactory = do_GetService(XPCLUCENEFACTORY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr indexFile; rv = mLuceneService->IndexLocation(NS_ConvertUTF8toUTF16(indexName), getter_AddRefs(indexFile)); NS_ENSURE_SUCCESS(rv, rv); rv = mLuceneFactory->GetIndexReader(indexFile, getter_AddRefs(mLuceneIndexReader)); if(NS_FAILED(rv)) { // TOOD: Check if this is a filenot found for reals rv = DoEmptyIndex(); return rv; } if(path.FindChar('s') == 1) { rv = DoSearch(); } else { if(path.FindChar('d') == 1) { rv = DoGetDocument(); } else { rv = NS_ERROR_MALFORMED_URI; } } /* * Clean up, but don't clobber result of action */ nsresult rvClose = mLuceneIndexReader->Close(); NS_ENSURE_SUCCESS(rvClose, rvClose); } return rv; } nsresult nsLuceneChannel::DoGetDocument() { nsresult rv; /* * Documents in lucene are always html */ mContentType.AssignLiteral("text/plain"); nsCAutoString path; mUri->GetPath(path); if(path.Length() < 11) { return NS_ERROR_MALFORMED_URI; } nsCAutoString id; path.Right(id, path.Length() - 11); PRInt32 err; PRInt32 num = id.ToInteger(&err); if(err) { return NS_ERROR_MALFORMED_URI; } LOG(("Getting document with id = '%d'\n", num)); /* * Retrive the specified document from the index and grab the contents * out of the "contents" field and stick it into an nsIStringInputStream */ nsCOMPtr document; rv = mLuceneIndexReader->Document(num, getter_AddRefs(document)); if(NS_FAILED(rv)) { // TODO: Try to serve some kind of 404? return rv; } nsAutoString content; rv = document->Get(NS_LITERAL_STRING("content"), content); NS_ENSURE_SUCCESS(rv, rv); /* * Convert the content string into a char * following what is done here */ nsCOMPtr sis; rv = GetStreamForWString(content.get(), nsCRT::strlen(content.get()), getter_AddRefs(sis)); NS_ENSURE_SUCCESS(rv, rv); rv = sis->QueryInterface(NS_GET_IID(nsIInputStream), getter_AddRefs(mDataStream)); NS_ENSURE_SUCCESS(rv, rv); mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); return rv; } nsresult nsLuceneChannel::DoFile() { nsresult rv; nsCOMPtr path; rv = mLuceneService->GetInstallLocation(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); rv = path->Append(NS_LITERAL_STRING("stylesheets")); NS_ENSURE_SUCCESS(rv, rv); rv = path->Append(NS_LITERAL_STRING("query.xsl")); NS_ENSURE_SUCCESS(rv, rv); mContentType.AssignLiteral("application/xml"); nsAutoString s; path->GetPath(s); nsCOMPtr fileStream = do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = fileStream->Init(path, PR_RDONLY, 0664, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); mDataStream = do_QueryInterface(fileStream); mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult nsLuceneChannel::DoSearch() { nsresult rv; nsCAutoString indexName; mUri->GetHost(indexName); /* * Parse the querystring. Extract the q parameter value and decode */ nsCOMPtr url(do_QueryInterface(mUri)); nsCAutoString queryString; url->GetQuery(queryString); nsACString::const_iterator start, end; queryString.BeginReading(start); queryString.EndReading(end); nsXPIDLString query; if(FindInReadable(NS_LITERAL_CSTRING("q="), start, end)) { nsACString::const_iterator start2(start), end2; queryString.EndReading(end2); if(FindInReadable(NS_LITERAL_CSTRING("&"), start2, end2)) { rv = nsLuceneUtils::urlDecode(Substring(end, start2), getter_Copies(query)); NS_ENSURE_SUCCESS(rv, rv); } else { queryString.EndReading(end2); rv = nsLuceneUtils::urlDecode(Substring(end, end2), getter_Copies(query)); NS_ENSURE_SUCCESS(rv, rv); } } LOG(("Searching with query = '%s'\n", NS_ConvertUTF16toUTF8(query).get())); /* * Parse the query */ nsCOMPtr hits; nsCOMPtr rewrittenQuery; nsCOMPtr highlighter; nsCOMPtr analyzer; rv = mLuceneFactory->GetAnalyzer(0, getter_AddRefs(analyzer)); NS_ENSURE_SUCCESS(rv, rv); if(!query.IsEmpty()) { nsCOMPtr luceneQuery; rv = mLuceneFactory->ParseQuery(query, NS_LITERAL_STRING("content"), analyzer, getter_AddRefs(luceneQuery)); if(NS_SUCCEEDED(rv)) { /* * Prepare the highlighter */ rv = luceneQuery->Rewrite(mLuceneIndexReader, getter_AddRefs(rewrittenQuery)); NS_ENSURE_SUCCESS(rv, rv); rv = mLuceneFactory->CreateHighlighter(rewrittenQuery, NS_LITERAL_STRING("..."), 2, kHighlightDelimiter, kHighlightDelimiter, getter_AddRefs(highlighter)); NS_ENSURE_SUCCESS(rv, rv); /* * Run the query and get the result hits */ nsCOMPtr searcher; rv = mLuceneFactory->GetIndexSearcher(mLuceneIndexReader, getter_AddRefs(searcher)); NS_ENSURE_SUCCESS(rv, rv); rv = searcher->Search(luceneQuery, getter_AddRefs(hits)); NS_ENSURE_SUCCESS(rv, rv); } } /* * Set up the result document */ nsCOMPtr doc; rv = CreateResultDocument(indexName, getter_AddRefs(doc)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr documentNode(do_QueryInterface(doc, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr document(do_QueryInterface(doc, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr junkNode; nsCOMPtr junkElement; nsAutoString value; /* * Add the root node of the feed */ nsCOMPtr root; rv = AppendAtomElement(doc, documentNode, NS_LITERAL_STRING("feed"), getter_AddRefs(root)); NS_ENSURE_SUCCESS(rv, rv); /* * atom:id */ value.Assign(NS_LITERAL_STRING("http://logborg.com/")); rv = AppendAtomElement(doc, root, NS_LITERAL_STRING("id"), value, getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); /* * atom:link is the URL */ /* value.Assign(NS_ConvertUTF8toUTF16(indexName)); value.Append(NS_LITERAL_STRING(":")); value.Append(query); */ nsCAutoString spec; mUri->GetSpec(spec); value.Assign(NS_ConvertUTF8toUTF16(spec)); rv = AppendAtomElement(doc, root, NS_LITERAL_STRING("link"), getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); junkElement = do_QueryInterface(junkNode, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = junkElement->SetAttribute(NS_LITERAL_STRING("href"), value); NS_ENSURE_SUCCESS(rv, rv); rv = junkElement->SetAttribute(NS_LITERAL_STRING("rel"), NS_LITERAL_STRING("self")); NS_ENSURE_SUCCESS(rv, rv); /* * atom:title */ value.Assign(NS_LITERAL_STRING("Search: ")); value.Append(query); rv = AppendAtomElement(doc, root, NS_LITERAL_STRING("title"), value, getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); /* * atom:updated */ rv = nsLuceneUtils::FormatRFC3339Local(PR_Now(), value); NS_ENSURE_SUCCESS(rv, rv); rv = AppendAtomElement(doc, root, NS_LITERAL_STRING("updated"), value, getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); /* * Add each hit */ if(hits) { PRInt16 count; hits->Length(&count); for(PRInt16 i = 0; i < count; i++) { nsCOMPtr entry; rv = AppendAtomElement(doc, root, NS_LITERAL_STRING("entry"), getter_AddRefs(entry)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr luceneDocument; rv = hits->Doc(i, getter_AddRefs(luceneDocument)); NS_ENSURE_SUCCESS(rv, rv); /* * atom:id */ PRInt16 id; rv = hits->Id(i, &id); NS_ENSURE_SUCCESS(rv, rv); nsTextFormatter::ssprintf(value, NS_LITERAL_STRING("lucene://%s/document/%d").get(), indexName.get(), id); /* * Workaround for bug #323376 */ nsAutoString value2; value2.Assign(value.get()); rv = AppendAtomElement(doc, entry, NS_LITERAL_STRING("id"), value2, getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); /* * atom:title */ rv = luceneDocument->Get(NS_LITERAL_STRING("title"), value); NS_ENSURE_SUCCESS(rv, rv); rv = AppendAtomElement(doc, entry, NS_LITERAL_STRING("title"), value, getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); /* * atom:link */ rv = luceneDocument->Get(NS_LITERAL_STRING("link"), value); NS_ENSURE_SUCCESS(rv, rv); rv = AppendAtomElement(doc, entry, NS_LITERAL_STRING("link"), getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); junkElement = do_QueryInterface(junkNode, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = junkElement->SetAttribute(NS_LITERAL_STRING("href"), value); NS_ENSURE_SUCCESS(rv, rv); rv = junkElement->SetAttribute(NS_LITERAL_STRING("rel"), NS_LITERAL_STRING("self")); NS_ENSURE_SUCCESS(rv, rv); /* * atom:updated */ rv = luceneDocument->Get(NS_LITERAL_STRING("updated"), value); NS_ENSURE_SUCCESS(rv, rv); PRTime t; rv = nsLuceneUtils::ParseIndexDateGMT(value, &t); NS_ENSURE_SUCCESS(rv, rv); /* * Format the date to something human readable */ PRExplodedTime exploded; PR_ExplodeTime(t, PR_LocalTimeParameters, &exploded); char timeString[256]; // XXX was PR_FormatTime, which spit out non-UTF-8 strings PR_FormatTimeUSEnglish(timeString, 256, "%b %d, %Y %X", &exploded); nsCAutoString formattedTime(timeString); value.Assign(NS_ConvertUTF8toUTF16(formattedTime)); rv = AppendAtomElement(doc, entry, NS_LITERAL_STRING("updated"), value, getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); /* * atom:category */ rv = luceneDocument->Get(NS_LITERAL_STRING("tag"), value); NS_ENSURE_SUCCESS(rv, rv); if(!value.IsEmpty()) { /* * Create a category element for each space delimited substring */ nsAString::const_iterator startFound, endFound, pos; value.BeginReading(startFound); value.EndReading(endFound); pos = startFound; while(FindInReadable(NS_LITERAL_STRING(" "), startFound, endFound)) { rv = AppendAtomElement(doc, entry, NS_LITERAL_STRING("category"), getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); junkElement = do_QueryInterface(junkNode, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = junkElement->SetAttribute(NS_LITERAL_STRING("term"), Substring(pos, startFound)); NS_ENSURE_SUCCESS(rv, rv); startFound = endFound; pos = endFound; value.EndReading(endFound); } rv = AppendAtomElement(doc, entry, NS_LITERAL_STRING("category"), getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); junkElement = do_QueryInterface(junkNode, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = junkElement->SetAttribute(NS_LITERAL_STRING("term"), Substring(pos, startFound)); NS_ENSURE_SUCCESS(rv, rv); } /* * atom:content. Grab the document content, highlight it, and parse * the result into a DOM */ nsAutoString content; rv = luceneDocument->Get(NS_LITERAL_STRING("content"), content); NS_ENSURE_SUCCESS(rv, rv); nsAutoString highlighted; if(!content.IsEmpty()) { rv = highlighter->Highlight(analyzer, NS_LITERAL_STRING("content"), content, highlighted); } /* * If the highlighter returned nothing, grab the first PREVIEW_LENGTH * chars from the document */ if(highlighted.IsEmpty()) { highlighted.Assign(Substring(content, 0, 300)); if(highlighted.Length() == PREVIEW_LENGTH) { highlighted.Append(NS_LITERAL_STRING("...")); } } rv = AppendAtomElement(doc, entry, NS_LITERAL_STRING("content"), getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); rv = ParseHighlightString(doc, highlighted, junkNode); NS_ENSURE_SUCCESS(rv, rv); } } rv = SerializeResultDocument(doc); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult nsLuceneChannel::DoEmptyIndex() { nsresult rv; /* * Set up the result document */ nsCAutoString indexName; mUri->GetHost(indexName); nsCOMPtr doc; rv = CreateResultDocument(indexName, getter_AddRefs(doc)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr documentNode(do_QueryInterface(doc, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr document(do_QueryInterface(doc, &rv)); NS_ENSURE_SUCCESS(rv, rv); /* * Create an atom:feed with no entries -- this tells the stylesheet * to display the empty index message */ nsCOMPtr root; rv = AppendAtomElement(doc, documentNode, NS_LITERAL_STRING("feed"), getter_AddRefs(root)); NS_ENSURE_SUCCESS(rv, rv); rv = SerializeResultDocument(doc); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult nsLuceneChannel::AppendAtomElement(nsIDOMDocument* document, nsIDOMNode* parent, const nsAString& elementName, nsIDOMNode **result) { nsresult rv; nsCOMPtr junk; nsCOMPtr element; rv = document->CreateElementNS(kAtomNamespace, elementName, getter_AddRefs(element)); NS_ENSURE_SUCCESS(rv, rv); rv = parent->AppendChild(element, getter_AddRefs(junk)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr node = do_QueryInterface(element, &rv); NS_ENSURE_SUCCESS(rv, rv); *result = node.get(); NS_ADDREF(*result); return NS_OK; } nsresult nsLuceneChannel::AppendAtomElement(nsIDOMDocument* document, nsIDOMNode* parent, const nsAString& elementName, nsAString& elementValue, nsIDOMNode **result) { nsresult rv; nsCOMPtr junk; nsCOMPtr text; nsCOMPtr node; rv = AppendAtomElement(document, parent, elementName, getter_AddRefs(node)); NS_ENSURE_SUCCESS(rv, rv); rv = document->CreateTextNode(elementValue, getter_AddRefs(text)); NS_ENSURE_SUCCESS(rv, rv); rv = node->AppendChild(text, getter_AddRefs(junk)); NS_ENSURE_SUCCESS(rv, rv); *result = node.get(); NS_ADDREF(*result); return NS_OK; } nsresult nsLuceneChannel::ParseHighlightString(nsIDOMDocument* document, const nsAString& string, nsIDOMNode* root) { nsresult rv; nsCOMPtr container; nsCOMPtr junk; rv = document->CreateElementNS(kXHTMLNamespace, NS_LITERAL_STRING("div"), getter_AddRefs(container)); NS_ENSURE_SUCCESS(rv, rv); rv = root->AppendChild(container, getter_AddRefs(junk)); NS_ENSURE_SUCCESS(rv, rv); nsAString::const_iterator delimStart, delimEnd, end, chunkStart; string.BeginReading(delimStart); string.EndReading(end); chunkStart = delimStart; delimEnd = end; PRBool inHighlight = PR_FALSE; while(FindInReadable(kHighlightDelimiter, delimStart, delimEnd)) { nsCOMPtr parent; /* * If we're in a highlight, this piece of text needs to be surrounded * by bold tags */ if(inHighlight) { rv = document->CreateElementNS(kXHTMLNamespace, NS_LITERAL_STRING("b"), getter_AddRefs(parent)); NS_ENSURE_SUCCESS(rv, rv); container->AppendChild(parent, getter_AddRefs(junk)); NS_ENSURE_SUCCESS(rv, rv); } else { parent = container; } nsCOMPtr text; rv = document->CreateTextNode(Substring(chunkStart, delimStart), getter_AddRefs(text)); NS_ENSURE_SUCCESS(rv, rv); rv = parent->AppendChild(text, getter_AddRefs(junk)); chunkStart = ++delimStart; delimStart = delimEnd; delimEnd = end; inHighlight = !inHighlight; } /* * If anything remiains, tack on the rest of the string */ if(chunkStart != end) { nsCOMPtr text; rv = document->CreateTextNode(Substring(chunkStart, end), getter_AddRefs(text)); NS_ENSURE_SUCCESS(rv, rv); rv = container->AppendChild(text, getter_AddRefs(junk)); } return NS_OK; } nsresult nsLuceneChannel::CreateResultDocument(const nsACString& indexName, nsIDOMDocument** _retval) { nsresult rv; mContentType.AssignLiteral("text/xml"); nsCOMPtr doc(do_CreateInstance(kXMLDocumentCID, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr document(do_QueryInterface(doc)); nsAutoString emptyString; document->SetXMLDeclaration(NS_LITERAL_STRING("1.0").get(), emptyString.get(), -1); document->SetDocumentURI(mUri); /* * Add XSLT processing instruction */ nsAutoString piData; piData.Assign(NS_LITERAL_STRING("href=\"lucene://")); piData.Append(NS_ConvertUTF8toUTF16(indexName)); piData.Append(NS_LITERAL_STRING("/file\" type=\"text/xml\"")); nsCOMPtr pi; rv = doc->CreateProcessingInstruction( NS_LITERAL_STRING("xml-stylesheet"), piData, getter_AddRefs(pi)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr junkNode; rv = doc->AppendChild(pi, getter_AddRefs(junkNode)); NS_ENSURE_SUCCESS(rv, rv); *_retval = doc; NS_ADDREF(*_retval); return NS_OK; } nsresult nsLuceneChannel::SerializeResultDocument(nsIDOMDocument* document) { nsresult rv; nsCOMPtr encoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/xml", &rv); NS_ENSURE_SUCCESS(rv, rv); // bug 330517 made nsIDocumentEncoder::Init scriptable, changing the type of // the document parameter from nsIDocument to nsIDOMDocument. Luckily we can // get the former by QIing the dom document. #ifndef NS_IDOCUMENTENCODER_IID_STR nsCOMPtr doc = do_QueryInterface(document); NS_ENSURE_TRUE(doc != nsnull, NS_ERROR_FAILURE); rv = encoder->Init(doc, NS_LITERAL_STRING("text/xml"), nsIDocumentEncoder::OutputEncodeBasicEntities); #else rv = encoder->Init(document, NS_LITERAL_STRING("text/xml"), nsIDocumentEncoder::OutputEncodeBasicEntities); #endif NS_ENSURE_SUCCESS(rv, rv); rv = encoder->SetCharset(NS_LITERAL_CSTRING("UTF-8")); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr bufOutStream; rv = NS_NewPipe(getter_AddRefs(mDataStream), getter_AddRefs(bufOutStream), 0, PR_UINT32_MAX, PR_TRUE, PR_TRUE); NS_ENSURE_SUCCESS(rv, rv); rv = encoder->EncodeToStream(bufOutStream); NS_ENSURE_SUCCESS(rv, rv); mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); return rv; } /* * Copied from * http://lxr.mozilla.org/mozilla1.8/source/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp#1059 */ nsresult nsLuceneChannel::GetStreamForWString(const PRUnichar* aStr, PRInt32 aLength, nsIInputStream** aStream) { nsresult rv; nsCOMPtr encoder; char* postData; // We want to encode the string as utf-8, so get the right encoder nsCOMPtr charsetConv = do_GetService(kCharsetConverterManagerCID, &rv); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); rv = charsetConv->GetUnicodeEncoderRaw("UTF-8", getter_AddRefs(encoder)); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); // Convert to utf-8 PRInt32 charLength; const PRUnichar* unicodeBuf = aStr; PRInt32 unicodeLength = aLength; rv = encoder->GetMaxLength(unicodeBuf, unicodeLength, &charLength); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; // Allocate extra space for the null-terminator postData = (char*)nsMemory::Alloc(charLength + 1); if (!postData) { return NS_ERROR_OUT_OF_MEMORY; } rv = encoder->Convert(unicodeBuf, &unicodeLength, postData, &charLength); if (NS_FAILED(rv)) { nsMemory::Free(postData); return NS_ERROR_FAILURE; } // Null-terminate postData[charLength] = '\0'; nsCOMPtr inputStream( do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv)); if (NS_SUCCEEDED(rv)) { rv = inputStream->AdoptData(postData, charLength); if (NS_SUCCEEDED(rv)) { return CallQueryInterface(inputStream, aStream); } } // If we got here then something went wrong before the stream // adopted the buffer. nsMemory::Free(postData); return NS_ERROR_FAILURE; } NS_METHOD nsLuceneChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) { nsLuceneChannel* dc = new nsLuceneChannel(); if (dc == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(dc); nsresult rv = dc->QueryInterface(aIID, aResult); NS_RELEASE(dc); return rv; } //////////////////////////////////////////////////////////////////////////////// // nsIRequest methods: NS_IMETHODIMP nsLuceneChannel::GetName(nsACString &result) { if (mUri) return mUri->GetSpec(result); result.Truncate(); return NS_OK; } NS_IMETHODIMP nsLuceneChannel::IsPending(PRBool *result) { return mPump->IsPending(result); } NS_IMETHODIMP nsLuceneChannel::GetStatus(nsresult *status) { return mPump->GetStatus(status); } NS_IMETHODIMP nsLuceneChannel::Cancel(nsresult status) { NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code"); return mPump->Cancel(status); } NS_IMETHODIMP nsLuceneChannel::Suspend(void) { return mPump->Suspend(); } NS_IMETHODIMP nsLuceneChannel::Resume(void) { return mPump->Resume(); } NS_IMETHODIMP nsLuceneChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup) { *aLoadGroup = mLoadGroup; NS_IF_ADDREF(*aLoadGroup); return NS_OK; } NS_IMETHODIMP nsLuceneChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) { mLoadGroup = aLoadGroup; return NS_OK; } NS_IMETHODIMP nsLuceneChannel::GetLoadFlags(PRUint32 *aLoadFlags) { return mPump->GetLoadFlags(aLoadFlags); } NS_IMETHODIMP nsLuceneChannel::SetLoadFlags(PRUint32 aLoadFlags) { return mPump->SetLoadFlags(aLoadFlags); } //////////////////////////////////////////////////////////////////////////////// // nsIChannel methods: NS_IMETHODIMP nsLuceneChannel::GetOriginalURI(nsIURI* *aURI) { *aURI = mOriginalURI ? mOriginalURI : mUri; NS_ADDREF(*aURI); return NS_OK; } NS_IMETHODIMP nsLuceneChannel::SetOriginalURI(nsIURI* aURI) { mOriginalURI = aURI; return NS_OK; } NS_IMETHODIMP nsLuceneChannel::GetURI(nsIURI* *aURI) { *aURI = mUri; NS_IF_ADDREF(*aURI); return NS_OK; } // This class NS_IMETHODIMP nsLuceneChannel::Open(nsIInputStream **_retval) { NS_ENSURE_ARG_POINTER(_retval); NS_ADDREF(*_retval = mDataStream); mOpened = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsLuceneChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt) { // Hold onto the real consumer... mListener = aListener; mOpened = PR_TRUE; nsresult rv = mPump->Init(mDataStream, nsInt64(-1), nsInt64(-1), 0, 0, PR_FALSE); if (NS_FAILED(rv)) return rv; // Add to load group if (mLoadGroup) mLoadGroup->AddRequest(this, nsnull); // Start the read return mPump->AsyncRead(this, ctxt); // These notifications will be processed when control returns to the // message pump and the PLEvents are processed... } NS_IMETHODIMP nsLuceneChannel::GetContentType(nsACString &aContentType) { aContentType = mContentType; return NS_OK; } NS_IMETHODIMP nsLuceneChannel::SetContentType(const nsACString &aContentType) { /* * I think I know what my content type is, ignore hints */ return NS_OK; } NS_IMETHODIMP nsLuceneChannel::GetContentCharset(nsACString &aContentCharset) { aContentCharset.Assign("UTF-8"); return NS_OK; } NS_IMETHODIMP nsLuceneChannel::SetContentCharset(const nsACString &aContentCharset) { /* * I think I know what my content charset is, ignore hints */ return NS_OK; } NS_IMETHODIMP nsLuceneChannel::GetContentLength(PRInt32 *aContentLength) { *aContentLength = mContentLength; return NS_OK; } NS_IMETHODIMP nsLuceneChannel::SetContentLength(PRInt32 aContentLength) { mContentLength = aContentLength; return NS_OK; } NS_IMETHODIMP nsLuceneChannel::GetOwner(nsISupports* *aOwner) { *aOwner = mOwner.get(); NS_IF_ADDREF(*aOwner); return NS_OK; } NS_IMETHODIMP nsLuceneChannel::SetOwner(nsISupports* aOwner) { mOwner = aOwner; return NS_OK; } NS_IMETHODIMP nsLuceneChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks) { NS_ENSURE_ARG_POINTER(aNotificationCallbacks); *aNotificationCallbacks = mCallbacks.get(); NS_IF_ADDREF(*aNotificationCallbacks); return NS_OK; } NS_IMETHODIMP nsLuceneChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks) { mCallbacks = aNotificationCallbacks; return NS_OK; } NS_IMETHODIMP nsLuceneChannel::GetSecurityInfo(nsISupports **sec) { NS_ENSURE_ARG_POINTER(sec); *sec = nsnull; return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsIRequestObserver methods: NS_IMETHODIMP nsLuceneChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { if (mListener) { return mListener->OnStartRequest(this, ctxt); } return NS_OK; } NS_IMETHODIMP nsLuceneChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status) { if (mListener) { mListener->OnStopRequest(this, ctxt, status); // Drop the reference to the stream listener -- it is no longer needed. mListener = nsnull; } // Remove from Loadgroup if (mLoadGroup) mLoadGroup->RemoveRequest(this, nsnull, status); // Drop notification callbacks to prevent cycles. mCallbacks = nsnull; return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsIStreamListener methods: NS_IMETHODIMP nsLuceneChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *input, PRUint32 offset, PRUint32 count) { if (mListener) { return mListener->OnDataAvailable(this, ctxt, input, offset, count); } return NS_OK; }