nsresult nsGopherContentStream::ParseTypeAndSelector(char &type, nsCString &selector) { nsCAutoString buffer; nsresult rv = mChannel->URI()->GetPath(buffer); // unescaped down below if (NS_FAILED(rv)) return rv; // No path given if (buffer[0] == '\0' || (buffer[0] == '/' && buffer[1] == '\0')) { type = '1'; selector.Truncate(); } else { NS_ENSURE_STATE(buffer[1] != '\0'); type = buffer[1]; // Ignore leading '/' // Do it this way in case selector contains embedded nulls after // unescaping. char *sel = buffer.BeginWriting() + 2; PRInt32 count = nsUnescapeCount(sel); selector.Assign(sel, count); // NOTE: FindCharInSet cannot be used to search for a null byte. if (selector.FindCharInSet("\t\n\r") != kNotFound || selector.FindChar('\0') != kNotFound) { // gopher selectors cannot containt tab, cr, lf, or \0 return NS_ERROR_MALFORMED_URI; } } return NS_OK; }
nsresult nsChromeRegistry::GetProviderAndPath(nsIURL* aChromeURL, nsACString& aProvider, nsACString& aPath) { nsresult rv; #ifdef DEBUG PRBool isChrome; aChromeURL->SchemeIs("chrome", &isChrome); NS_ASSERTION(isChrome, "Non-chrome URI?"); #endif nsCAutoString path; rv = aChromeURL->GetPath(path); NS_ENSURE_SUCCESS(rv, rv); if (path.Length() < 3) { LogMessage("Invalid chrome URI: %s", path.get()); return NS_ERROR_FAILURE; } path.SetLength(nsUnescapeCount(path.BeginWriting())); NS_ASSERTION(path.First() == '/', "Path should always begin with a slash!"); PRInt32 slash = path.FindChar('/', 1); if (slash == 1) { LogMessage("Invalid chrome URI: %s", path.get()); return NS_ERROR_FAILURE; } if (slash == -1) { aPath.Truncate(); } else { if (slash == (PRInt32) path.Length() - 1) aPath.Truncate(); else aPath.Assign(path.get() + slash + 1, path.Length() - slash - 1); --slash; } aProvider.Assign(path.get() + 1, slash); return NS_OK; }
NS_IMETHODIMP nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest, nsISupports *aCtxt, nsIDirIndex *aIndex) { nsresult rv; if (!aIndex) return NS_ERROR_NULL_POINTER; nsCString pushBuffer; pushBuffer.AppendLiteral("<tr"); // We don't know the file's character set yet, so retrieve the raw bytes // which will be decoded by the HTML parser. nsXPIDLCString loc; aIndex->GetLocation(getter_Copies(loc)); // Adjust the length in case unescaping shortened the string. loc.Truncate(nsUnescapeCount(loc.BeginWriting())); if (loc.First() == char16_t('.')) pushBuffer.AppendLiteral(" class=\"hidden-object\""); pushBuffer.AppendLiteral(">\n <td sortable-data=\""); // The sort key is the name of the item, prepended by either 0, 1 or 2 // in order to group items. uint32_t type; aIndex->GetType(&type); switch (type) { case nsIDirIndex::TYPE_SYMLINK: pushBuffer.Append('0'); break; case nsIDirIndex::TYPE_DIRECTORY: pushBuffer.Append('1'); break; default: pushBuffer.Append('2'); break; } nsAdoptingCString escaped(nsEscapeHTML(loc)); pushBuffer.Append(escaped); pushBuffer.AppendLiteral("\"><table class=\"ellipsis\"><tbody><tr><td><a class=\""); switch (type) { case nsIDirIndex::TYPE_DIRECTORY: pushBuffer.AppendLiteral("dir"); break; case nsIDirIndex::TYPE_SYMLINK: pushBuffer.AppendLiteral("symlink"); break; default: pushBuffer.AppendLiteral("file"); break; } pushBuffer.AppendLiteral("\" href=\""); // need to escape links nsAutoCString locEscaped; // Adding trailing slash helps to recognize whether the URL points to a file // or a directory (bug #214405). if ((type == nsIDirIndex::TYPE_DIRECTORY) && (loc.Last() != '/')) { loc.Append('/'); } // now minimally re-escape the location... uint32_t escFlags; // for some protocols, we expect the location to be absolute. // if so, and if the location indeed appears to be a valid URI, then go // ahead and treat it like one. nsAutoCString scheme; if (mExpectAbsLoc && NS_SUCCEEDED(net_ExtractURLScheme(loc, scheme))) { // escape as absolute escFlags = esc_Forced | esc_AlwaysCopy | esc_Minimal; } else { // escape as relative // esc_Directory is needed because directories have a trailing slash. // Without it, the trailing '/' will be escaped, and links from within // that directory will be incorrect escFlags = esc_Forced | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory; } NS_EscapeURL(loc.get(), loc.Length(), escFlags, locEscaped); // esc_Directory does not escape the semicolons, so if a filename // contains semicolons we need to manually escape them. // This replacement should be removed in bug #473280 locEscaped.ReplaceSubstring(";", "%3b"); nsAdoptingCString htmlEscapedURL(nsEscapeHTML(locEscaped.get())); pushBuffer.Append(htmlEscapedURL); pushBuffer.AppendLiteral("\">"); if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) { pushBuffer.AppendLiteral("<img src=\"moz-icon://"); int32_t lastDot = locEscaped.RFindChar('.'); if (lastDot != kNotFound) { locEscaped.Cut(0, lastDot); nsAdoptingCString htmlFileExt(nsEscapeHTML(locEscaped.get())); pushBuffer.Append(htmlFileExt); } else { pushBuffer.AppendLiteral("unknown"); } pushBuffer.AppendLiteral("?size=16\" alt=\""); nsXPIDLString altText; rv = mBundle->GetStringFromName(MOZ_UTF16("DirFileLabel"), getter_Copies(altText)); if (NS_FAILED(rv)) return rv; AppendNonAsciiToNCR(altText, pushBuffer); pushBuffer.AppendLiteral("\">"); } pushBuffer.Append(escaped); pushBuffer.AppendLiteral("</a></td></tr></tbody></table></td>\n <td"); if (type == nsIDirIndex::TYPE_DIRECTORY || type == nsIDirIndex::TYPE_SYMLINK) { pushBuffer.Append('>'); } else { int64_t size; aIndex->GetSize(&size); if (uint64_t(size) != UINT64_MAX) { pushBuffer.AppendLiteral(" sortable-data=\""); pushBuffer.AppendInt(size); pushBuffer.AppendLiteral("\">"); nsAutoCString sizeString; FormatSizeString(size, sizeString); pushBuffer.Append(sizeString); } else { pushBuffer.Append('>'); } } pushBuffer.AppendLiteral("</td>\n <td"); PRTime t; aIndex->GetLastModified(&t); if (t == -1LL) { pushBuffer.AppendLiteral("></td>\n <td>"); } else { pushBuffer.AppendLiteral(" sortable-data=\""); pushBuffer.AppendInt(static_cast<int64_t>(t)); pushBuffer.AppendLiteral("\">"); nsAutoString formatted; mDateTime->FormatPRTime(nullptr, kDateFormatShort, kTimeFormatNone, t, formatted); AppendNonAsciiToNCR(formatted, pushBuffer); pushBuffer.AppendLiteral("</td>\n <td>"); mDateTime->FormatPRTime(nullptr, kDateFormatNone, kTimeFormatSeconds, t, formatted); // use NCR to show date in any doc charset AppendNonAsciiToNCR(formatted, pushBuffer); } pushBuffer.AppendLiteral("</td>\n</tr>"); return SendToListener(aRequest, aCtxt, pushBuffer); }
nsresult nsGopherChannel::Init(nsIURI* uri, nsIProxyInfo* proxyInfo) { nsresult rv; nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv); if (NS_FAILED(rv)) return NS_ERROR_MALFORMED_URI; mUrl = uri; mProxyInfo = proxyInfo; nsCAutoString buffer; rv = url->GetPath(buffer); // unescaped down below if (NS_FAILED(rv)) return rv; rv = url->GetAsciiHost(mHost); if (NS_FAILED(rv)) return rv; rv = url->GetPort(&mPort); if (NS_FAILED(rv)) return rv; // For security reasons, don't allow anything expect the default // gopher port (70). See bug 71916 - [email protected] /* if (mPort==-1) mPort=GOPHER_PORT; */ mPort=GOPHER_PORT; // No path given if (buffer[0]=='\0' || (buffer[0]=='/' && buffer[1]=='\0')) { mType = '1'; mSelector.Truncate(); } else { mType = buffer[1]; // Ignore leading '/' // Do it this way in case selector contains embedded nulls after // unescaping char* selector = nsCRT::strdup(buffer.get()+2); PRInt32 count = nsUnescapeCount(selector); mSelector.Assign(selector,count); nsCRT::free(selector); if (mSelector.FindCharInSet(nsCString("\t\n\r\0",4)) != -1) { // gopher selectors cannot containt tab, cr, lf, or \0 return NS_ERROR_MALFORMED_URI; } } PR_LOG(gGopherLog, PR_LOG_DEBUG, ("Host: mHost = %s, mPort = %d\n", mHost.get(), mPort)); PR_LOG(gGopherLog, PR_LOG_DEBUG, ("Status: mType = %c, mSelector = %s\n", mType, mSelector.get())); return NS_OK; }
nsresult nsDirIndexParser::ParseFormat(const char* aFormatStr) { // Parse a "200" format line, and remember the fields and their // ordering in mFormat. Multiple 200 lines stomp on each other. delete[] mFormat; // Lets find out how many elements we have. // easier to do this then realloc const char* pos = aFormatStr; unsigned int num = 0; do { while (*pos && nsCRT::IsAsciiSpace(PRUnichar(*pos))) ++pos; ++num; // There are a maximum of six allowed header fields (doubled plus // terminator, just in case) -- Bug 443299 if (num > (2 * NS_ARRAY_LENGTH(gFieldTable))) return NS_ERROR_UNEXPECTED; if (! *pos) break; while (*pos && !nsCRT::IsAsciiSpace(PRUnichar(*pos))) ++pos; } while (*pos); mFormat = new int[num+1]; // Prevent NULL Deref - Bug 443299 if (mFormat == nsnull) return NS_ERROR_OUT_OF_MEMORY; mFormat[num] = -1; int formatNum=0; do { while (*aFormatStr && nsCRT::IsAsciiSpace(PRUnichar(*aFormatStr))) ++aFormatStr; if (! *aFormatStr) break; nsCAutoString name; PRInt32 len = 0; while (aFormatStr[len] && !nsCRT::IsAsciiSpace(PRUnichar(aFormatStr[len]))) ++len; name.SetCapacity(len + 1); name.Append(aFormatStr, len); aFormatStr += len; // Okay, we're gonna monkey with the nsStr. Bold! name.SetLength(nsUnescapeCount(name.BeginWriting())); // All tokens are case-insensitive - http://www.mozilla.org/projects/netlib/dirindexformat.html if (name.LowerCaseEqualsLiteral("description")) mHasDescription = PR_TRUE; for (Field* i = gFieldTable; i->mName; ++i) { if (name.EqualsIgnoreCase(i->mName)) { mFormat[formatNum] = i->mType; ++formatNum; break; } } } while (*aFormatStr); return NS_OK; }
NS_IMETHODIMP nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest, nsISupports *aCtxt, nsIDirIndex *aIndex) { nsresult rv; if (!aIndex) return NS_ERROR_NULL_POINTER; nsString pushBuffer; pushBuffer.AppendLiteral("<tr"); nsXPIDLString description; aIndex->GetDescription(getter_Copies(description)); if (description.First() == char16_t('.')) pushBuffer.AppendLiteral(" class=\"hidden-object\""); pushBuffer.AppendLiteral(">\n <td sortable-data=\""); // The sort key is the name of the item, prepended by either 0, 1 or 2 // in order to group items. uint32_t type; aIndex->GetType(&type); switch (type) { case nsIDirIndex::TYPE_SYMLINK: pushBuffer.AppendInt(0); break; case nsIDirIndex::TYPE_DIRECTORY: pushBuffer.AppendInt(1); break; case nsIDirIndex::TYPE_FILE: case nsIDirIndex::TYPE_UNKNOWN: pushBuffer.AppendInt(2); break; } char16_t* escaped = nsEscapeHTML2(description.get(), description.Length()); pushBuffer.Append(escaped); pushBuffer.AppendLiteral("\"><a class=\""); switch (type) { case nsIDirIndex::TYPE_DIRECTORY: pushBuffer.AppendLiteral("dir"); break; case nsIDirIndex::TYPE_SYMLINK: pushBuffer.AppendLiteral("symlink"); break; case nsIDirIndex::TYPE_FILE: case nsIDirIndex::TYPE_UNKNOWN: pushBuffer.AppendLiteral("file"); break; } pushBuffer.AppendLiteral("\""); // Truncate long names to not stretch the table //XXX this should be left to the stylesheet (bug 391471) nsString escapedShort; if (description.Length() > 71) { nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); nsCOMPtr<nsIURI> uri; rv = channel->GetURI(getter_AddRefs(uri)); if (NS_FAILED(rv)) return rv; //XXX this potentially truncates after a combining char (bug 391472) nsXPIDLString descriptionAffix; descriptionAffix.Assign(description); descriptionAffix.Cut(0, descriptionAffix.Length() - 25); if (NS_IS_LOW_SURROGATE(descriptionAffix.First())) descriptionAffix.Cut(0, 1); description.Truncate(std::min<uint32_t>(71, description.Length() - 28)); if (NS_IS_HIGH_SURROGATE(description.Last())) description.Truncate(description.Length() - 1); escapedShort.Adopt(nsEscapeHTML2(description.get(), description.Length())); escapedShort.Append(mEscapedEllipsis); // add ZERO WIDTH SPACE (U+200B) for wrapping escapedShort.AppendLiteral("​"); nsString tmp; tmp.Adopt(nsEscapeHTML2(descriptionAffix.get(), descriptionAffix.Length())); escapedShort.Append(tmp); pushBuffer.AppendLiteral(" title=\""); pushBuffer.Append(escaped); pushBuffer.AppendLiteral("\""); } if (escapedShort.IsEmpty()) escapedShort.Assign(escaped); nsMemory::Free(escaped); pushBuffer.AppendLiteral(" href=\""); nsXPIDLCString loc; aIndex->GetLocation(getter_Copies(loc)); nsXPIDLCString encoding; rv = mParser->GetEncoding(getter_Copies(encoding)); if (NS_FAILED(rv)) return rv; // Don't byte-to-Unicode conversion here, it is lossy. loc.SetLength(nsUnescapeCount(loc.BeginWriting())); // need to escape links nsAutoCString locEscaped; // Adding trailing slash helps to recognize whether the URL points to a file // or a directory (bug #214405). if ((type == nsIDirIndex::TYPE_DIRECTORY) && (loc.Last() != '/')) { loc.Append('/'); } // now minimally re-escape the location... uint32_t escFlags; // for some protocols, we expect the location to be absolute. // if so, and if the location indeed appears to be a valid URI, then go // ahead and treat it like one. if (mExpectAbsLoc && NS_SUCCEEDED(net_ExtractURLScheme(loc, nullptr, nullptr, nullptr))) { // escape as absolute escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_Minimal; } else { // escape as relative // esc_Directory is needed because directories have a trailing slash. // Without it, the trailing '/' will be escaped, and links from within // that directory will be incorrect escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory; } NS_EscapeURL(loc.get(), loc.Length(), escFlags, locEscaped); // esc_Directory does not escape the semicolons, so if a filename // contains semicolons we need to manually escape them. // This replacement should be removed in bug #473280 locEscaped.ReplaceSubstring(";", "%3b"); nsAutoString utf16URI; if (encoding.EqualsLiteral("UTF-8")) { // Try to convert non-ASCII bytes to Unicode using UTF-8 decoder. nsCOMPtr<nsIUnicodeDecoder> decoder = mozilla::dom::EncodingUtils::DecoderForEncoding("UTF-8"); decoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal); int32_t len = locEscaped.Length(); int32_t outlen = 0; rv = decoder->GetMaxLength(locEscaped.get(), len, &outlen); if (NS_FAILED(rv)) { return rv; } nsAutoArrayPtr<char16_t> outbuf(new char16_t[outlen]); rv = decoder->Convert(locEscaped.get(), &len, outbuf, &outlen); // Use the result only if the sequence is valid as UTF-8. if (rv == NS_OK) { utf16URI.Append(outbuf, outlen); } } if (utf16URI.IsEmpty()) { // Escape all non-ASCII bytes to preserve the raw value. nsAutoCString outstr; NS_EscapeURL(locEscaped, esc_AlwaysCopy | esc_OnlyNonASCII, outstr); CopyASCIItoUTF16(outstr, utf16URI); } nsString htmlEscapedURL; htmlEscapedURL.Adopt(nsEscapeHTML2(utf16URI.get(), utf16URI.Length())); pushBuffer.Append(htmlEscapedURL); pushBuffer.AppendLiteral("\">"); if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) { pushBuffer.AppendLiteral("<img src=\"moz-icon://"); int32_t lastDot = locEscaped.RFindChar('.'); if (lastDot != kNotFound) { locEscaped.Cut(0, lastDot); NS_ConvertUTF8toUTF16 utf16LocEscaped(locEscaped); nsString htmlFileExt; htmlFileExt.Adopt(nsEscapeHTML2(utf16LocEscaped.get(), utf16LocEscaped.Length())); pushBuffer.Append(htmlFileExt); } else { pushBuffer.AppendLiteral("unknown"); } pushBuffer.AppendLiteral("?size=16\" alt=\""); nsXPIDLString altText; rv = mBundle->GetStringFromName(MOZ_UTF16("DirFileLabel"), getter_Copies(altText)); if (NS_FAILED(rv)) return rv; AppendNonAsciiToNCR(altText, pushBuffer); pushBuffer.AppendLiteral("\">"); } pushBuffer.Append(escapedShort); pushBuffer.AppendLiteral("</a></td>\n <td"); if (type == nsIDirIndex::TYPE_DIRECTORY || type == nsIDirIndex::TYPE_SYMLINK) { pushBuffer.AppendLiteral(">"); } else { int64_t size; aIndex->GetSize(&size); if (uint64_t(size) != UINT64_MAX) { pushBuffer.AppendLiteral(" sortable-data=\""); pushBuffer.AppendInt(size); pushBuffer.AppendLiteral("\">"); nsAutoString sizeString; FormatSizeString(size, sizeString); pushBuffer.Append(sizeString); } else { pushBuffer.AppendLiteral(">"); } } pushBuffer.AppendLiteral("</td>\n <td"); PRTime t; aIndex->GetLastModified(&t); if (t == -1) { pushBuffer.AppendLiteral("></td>\n <td>"); } else { pushBuffer.AppendLiteral(" sortable-data=\""); pushBuffer.AppendInt(static_cast<int64_t>(t)); pushBuffer.AppendLiteral("\">"); nsAutoString formatted; mDateTime->FormatPRTime(nullptr, kDateFormatShort, kTimeFormatNone, t, formatted); AppendNonAsciiToNCR(formatted, pushBuffer); pushBuffer.AppendLiteral("</td>\n <td>"); mDateTime->FormatPRTime(nullptr, kDateFormatNone, kTimeFormatSeconds, t, formatted); // use NCR to show date in any doc charset AppendNonAsciiToNCR(formatted, pushBuffer); } pushBuffer.AppendLiteral("</td>\n</tr>"); return FormatInputStream(aRequest, aCtxt, pushBuffer); }