nsresult nsJARInputStream::ReadDirectory(char* aBuffer, uint32_t aCount, uint32_t *aBytesRead) { // No need to check the args, ::Read did that, but assert them at least NS_ASSERTION(aBuffer,"aBuffer parameter must not be null"); NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null"); // If the buffer contains data, copy what's there up to the desired amount uint32_t numRead = CopyDataToBuffer(aBuffer, aCount); if (aCount > 0) { // empty the buffer and start writing directory entry lines to it mBuffer.Truncate(); mCurPos = 0; const uint32_t arrayLen = mArray.Length(); for ( ;aCount > mBuffer.Length(); mArrPos++) { // have we consumed all the directory contents? if (arrayLen <= mArrPos) break; const char * entryName = mArray[mArrPos].get(); uint32_t entryNameLen = mArray[mArrPos].Length(); nsZipItem* ze = mJar->mZip->GetItem(entryName); NS_ENSURE_TRUE(ze, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST); // Last Modified Time PRExplodedTime tm; PR_ExplodeTime(ze->LastModTime(), PR_GMTParameters, &tm); char itemLastModTime[65]; PR_FormatTimeUSEnglish(itemLastModTime, sizeof(itemLastModTime), " %a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm); // write a 201: line to the buffer for this item // 200: filename content-length last-modified file-type mBuffer.AppendLiteral("201: "); // Names must be escaped and relative, so use the pre-calculated length // of the directory name as the offset into the string // NS_EscapeURL adds the escaped URL to the give string buffer NS_EscapeURL(entryName + mNameLen, entryNameLen - mNameLen, esc_Minimal | esc_AlwaysCopy, mBuffer); mBuffer.Append(' '); mBuffer.AppendInt(ze->RealSize(), 10); mBuffer.Append(itemLastModTime); // starts/ends with ' ' if (ze->IsDirectory()) mBuffer.AppendLiteral("DIRECTORY\n"); else mBuffer.AppendLiteral("FILE\n"); } // Copy up to the desired amount of data to buffer numRead += CopyDataToBuffer(aBuffer, aCount); } *aBytesRead = numRead; return NS_OK; }
char * nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCString &aString) { char *line = aBuffer; char *eol; bool cr = false; list_state state; state.magic = 0; // while we have new lines, parse 'em into application/http-index-format. while ( line && (eol = PL_strchr(line, nsCRT::LF)) ) { // yank any carriage returns too. if (eol > line && *(eol-1) == nsCRT::CR) { eol--; *eol = '\0'; cr = PR_TRUE; } else { *eol = '\0'; cr = PR_FALSE; } list_result result; int type = ParseFTPList(line, &state, &result ); // if it is other than a directory, file, or link -OR- if it is a // directory named . or .., skip over this line. if ((type != 'd' && type != 'f' && type != 'l') || (result.fe_type == 'd' && result.fe_fname[0] == '.' && (result.fe_fnlen == 1 || (result.fe_fnlen == 2 && result.fe_fname[1] == '.'))) ) { if (cr) line = eol+2; else line = eol+1; continue; } // blast the index entry into the indexFormat buffer as a 201: line. aString.AppendLiteral("201: "); // FILENAME // parsers for styles 'U' and 'W' handle sequence " -> " themself if (state.lstyle != 'U' && state.lstyle != 'W') { const char* offset = strstr(result.fe_fname, " -> "); if (offset) { result.fe_fnlen = offset - result.fe_fname; } } nsCAutoString buf; aString.Append('\"'); aString.Append(NS_EscapeURL(Substring(result.fe_fname, result.fe_fname+result.fe_fnlen), esc_Minimal|esc_OnlyASCII|esc_Forced,buf)); aString.AppendLiteral("\" "); // CONTENT LENGTH if (type != 'd') { for (int i = 0; i < int(sizeof(result.fe_size)); ++i) { if (result.fe_size[i] != '\0') aString.Append((const char*)&result.fe_size[i], 1); } aString.Append(' '); } else aString.AppendLiteral("0 "); // MODIFIED DATE char buffer[256] = ""; // Note: The below is the RFC822/1123 format, as required by // the application/http-index-format specs // viewers of such a format can then reformat this into the // current locale (or anything else they choose) PR_FormatTimeUSEnglish(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S", &result.fe_time ); char *escapedDate = nsEscape(buffer, url_Path); aString.Append(escapedDate); nsMemory::Free(escapedDate); aString.Append(' '); // ENTRY TYPE if (type == 'd') aString.AppendLiteral("DIRECTORY"); else if (type == 'l') aString.AppendLiteral("SYMBOLIC-LINK"); else aString.AppendLiteral("FILE"); aString.Append(' '); aString.Append(char(nsCRT::LF)); // complete this line // END 201: if (cr) line = eol+2; else line = eol+1; } // end while(eol) return line; }
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() == PRUnichar('.')) 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; } PRUnichar* 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(NS_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)); if (!mTextToSubURI) { mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; } nsXPIDLCString encoding; rv = mParser->GetEncoding(getter_Copies(encoding)); if (NS_FAILED(rv)) return rv; nsXPIDLString unEscapeSpec; rv = mTextToSubURI->UnEscapeAndConvert(encoding, loc, getter_Copies(unEscapeSpec)); if (NS_FAILED(rv)) return rv; // need to escape links nsAutoCString escapeBuf; NS_ConvertUTF16toUTF8 utf8UnEscapeSpec(unEscapeSpec); // Adding trailing slash helps to recognize whether the URL points to a file // or a directory (bug #214405). if ((type == nsIDirIndex::TYPE_DIRECTORY) && (utf8UnEscapeSpec.Last() != '/')) { utf8UnEscapeSpec.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(utf8UnEscapeSpec, 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(utf8UnEscapeSpec.get(), utf8UnEscapeSpec.Length(), escFlags, escapeBuf); // 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 escapeBuf.ReplaceSubstring(";", "%3b"); NS_ConvertUTF8toUTF16 utf16URI(escapeBuf); 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 = escapeBuf.RFindChar('.'); if (lastDot != kNotFound) { escapeBuf.Cut(0, lastDot); NS_ConvertUTF8toUTF16 utf16EscapeBuf(escapeBuf); nsString htmlFileExt; htmlFileExt.Adopt(nsEscapeHTML2(utf16EscapeBuf.get(), utf16EscapeBuf.Length())); pushBuffer.Append(htmlFileExt); } else { pushBuffer.AppendLiteral("unknown"); } pushBuffer.AppendLiteral("?size=16\" alt=\""); nsXPIDLString altText; rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DirFileLabel").get(), 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) != LL_MAXUINT) { 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); }
nsresult net_GetFileFromURLSpec(const nsACString &aURL, nsIFile **result) { nsresult rv; nsCOMPtr<nsILocalFile> localFile( do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); if (NS_FAILED(rv)) { NS_ERROR("Only nsILocalFile supported right now"); return rv; } localFile->SetFollowLinks(true); const nsACString *specPtr; nsCAutoString buf; if (net_NormalizeFileURL(aURL, buf)) specPtr = &buf; else specPtr = &aURL; nsCAutoString directory, fileBaseName, fileExtension; rv = net_ParseFileURL(*specPtr, directory, fileBaseName, fileExtension); if (NS_FAILED(rv)) return rv; nsCAutoString path; if (!directory.IsEmpty()) { NS_EscapeURL(directory, esc_Directory|esc_AlwaysCopy, path); if (path.Length() > 2 && path.CharAt(2) == '|') path.SetCharAt(':', 2); path.ReplaceChar('/', '\\'); } if (!fileBaseName.IsEmpty()) NS_EscapeURL(fileBaseName, esc_FileBaseName|esc_AlwaysCopy, path); if (!fileExtension.IsEmpty()) { path += '.'; NS_EscapeURL(fileExtension, esc_FileExtension|esc_AlwaysCopy, path); } NS_UnescapeURL(path); if (path.Length() != strlen(path.get())) return NS_ERROR_FILE_INVALID_PATH; // remove leading '\' if (path.CharAt(0) == '\\') path.Cut(0, 1); if (IsUTF8(path)) rv = localFile->InitWithPath(NS_ConvertUTF8toUTF16(path)); // XXX In rare cases, a valid UTF-8 string can be valid as a native // encoding (e.g. 0xC5 0x83 is valid both as UTF-8 and Windows-125x). // However, the chance is very low that a meaningful word in a legacy // encoding is valid as UTF-8. else // if path is not in UTF-8, assume it is encoded in the native charset rv = localFile->InitWithNativePath(path); if (NS_FAILED(rv)) return rv; NS_ADDREF(*result = localFile); 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); }
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. if (mExpectAbsLoc && NS_SUCCEEDED(net_ExtractURLScheme(loc, nullptr, nullptr, nullptr))) { // 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 == -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 SendToListener(aRequest, aCtxt, pushBuffer); }
nsresult net_GetFileFromURLSpec(const nsACString &aURL, nsIFile **result) { // NOTE: See also the implementation in nsURLHelperUnix.cpp // This matches it except for the HFS path handling. nsresult rv; nsCOMPtr<nsILocalFile> localFile; rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(localFile)); if (NS_FAILED(rv)) return rv; nsCAutoString directory, fileBaseName, fileExtension, path; bool bHFSPath = false; rv = net_ParseFileURL(aURL, directory, fileBaseName, fileExtension); if (NS_FAILED(rv)) return rv; if (!directory.IsEmpty()) { NS_EscapeURL(directory, esc_Directory|esc_AlwaysCopy, path); // The canonical form of file URLs on OSX use POSIX paths: // file:///path-name. // But, we still encounter file URLs that use HFS paths: // file:///volume-name/path-name // Determine that here and normalize HFS paths to POSIX. nsCAutoString possibleVolName; if (pathBeginsWithVolName(directory, possibleVolName)) { // Though we know it begins with a volume name, it could still // be a valid POSIX path if the boot drive is named "Mac HD" // and there is a directory "Mac HD" at its root. If such a // directory doesn't exist, we'll assume this is an HFS path. FSRef testRef; possibleVolName.Insert("/", 0); if (::FSPathMakeRef((UInt8*)possibleVolName.get(), &testRef, nsnull) != noErr) bHFSPath = true; } if (bHFSPath) { // "%2F"s need to become slashes, while all other slashes need to // become colons. If we start out by changing "%2F"s to colons, we // can reply on SwapSlashColon() to do what we need path.ReplaceSubstring("%2F", ":"); path.Cut(0, 1); // directory begins with '/' SwapSlashColon((char *)path.get()); // At this point, path is an HFS path made using the same // algorithm as nsURLHelperMac. We'll convert to POSIX below. } } if (!fileBaseName.IsEmpty()) NS_EscapeURL(fileBaseName, esc_FileBaseName|esc_AlwaysCopy, path); if (!fileExtension.IsEmpty()) { path += '.'; NS_EscapeURL(fileExtension, esc_FileExtension|esc_AlwaysCopy, path); } NS_UnescapeURL(path); if (path.Length() != strlen(path.get())) return NS_ERROR_FILE_INVALID_PATH; if (bHFSPath) convertHFSPathtoPOSIX(path, path); // assuming path is encoded in the native charset rv = localFile->InitWithNativePath(path); if (NS_FAILED(rv)) return rv; NS_ADDREF(*result = localFile); return NS_OK; }
NS_IMETHODIMP DOMStorageObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { nsresult rv; // Start the thread that opens the database. if (!strcmp(aTopic, kStartupTopic)) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); obs->RemoveObserver(this, kStartupTopic); mDBThreadStartDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID); if (!mDBThreadStartDelayTimer) { return NS_ERROR_UNEXPECTED; } mDBThreadStartDelayTimer->Init(this, nsITimer::TYPE_ONE_SHOT, kStartupDelay); return NS_OK; } // Timer callback used to start the database a short timer after startup if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject); if (!timer) { return NS_ERROR_UNEXPECTED; } if (timer == mDBThreadStartDelayTimer) { mDBThreadStartDelayTimer = nullptr; DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); } return NS_OK; } // Clear everything, caches + database if (!strcmp(aTopic, "cookie-changed")) { if (!NS_LITERAL_STRING("cleared").Equals(aData)) { return NS_OK; } DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); db->AsyncClearAll(); Notify("cookie-cleared"); return NS_OK; } // Clear from caches everything that has been stored // while in session-only mode if (!strcmp(aTopic, "perm-changed")) { // Check for cookie permission change nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject)); if (!perm) { return NS_OK; } nsAutoCString type; perm->GetType(type); if (type != NS_LITERAL_CSTRING("cookie")) { return NS_OK; } uint32_t cap = 0; perm->GetCapability(&cap); if (!(cap & nsICookiePermission::ACCESS_SESSION) || !NS_LITERAL_STRING("deleted").Equals(nsDependentString(aData))) { return NS_OK; } nsCOMPtr<nsIPrincipal> principal; perm->GetPrincipal(getter_AddRefs(principal)); if (!principal) { return NS_OK; } nsAutoCString originSuffix; BasePrincipal::Cast(principal)->OriginAttributesRef().CreateSuffix(originSuffix); nsCOMPtr<nsIURI> origin; principal->GetURI(getter_AddRefs(origin)); if (!origin) { return NS_OK; } nsAutoCString host; origin->GetHost(host); if (host.IsEmpty()) { return NS_OK; } nsAutoCString originScope; rv = CreateReversedDomain(host, originScope); NS_ENSURE_SUCCESS(rv, rv); Notify("session-only-cleared", NS_ConvertUTF8toUTF16(originSuffix), originScope); return NS_OK; } // Clear everything (including so and pb data) from caches and database // for the gived domain and subdomains. if (!strcmp(aTopic, "browser:purge-domain-data")) { // Convert the domain name to the ACE format nsAutoCString aceDomain; nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID); if (converter) { rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain); NS_ENSURE_SUCCESS(rv, rv); } else { // In case the IDN service is not available, this is the best we can come up with! rv = NS_EscapeURL(NS_ConvertUTF16toUTF8(aData), esc_OnlyNonASCII | esc_AlwaysCopy, aceDomain, fallible); NS_ENSURE_SUCCESS(rv, rv); } nsAutoCString originScope; rv = CreateReversedDomain(aceDomain, originScope); NS_ENSURE_SUCCESS(rv, rv); DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); db->AsyncClearMatchingOrigin(originScope); Notify("domain-data-cleared", EmptyString(), originScope); return NS_OK; } // Clear all private-browsing caches if (!strcmp(aTopic, "last-pb-context-exited")) { Notify("private-browsing-data-cleared"); return NS_OK; } // Clear data of the origins whose prefixes will match the suffix. if (!strcmp(aTopic, "clear-origin-attributes-data")) { OriginAttributesPattern pattern; if (!pattern.Init(nsDependentString(aData))) { NS_ERROR("Cannot parse origin attributes pattern"); return NS_ERROR_FAILURE; } DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); db->AsyncClearMatchingOriginAttributes(pattern); Notify("origin-attr-pattern-cleared", nsDependentString(aData)); return NS_OK; } if (!strcmp(aTopic, "profile-after-change")) { Notify("profile-change"); return NS_OK; } if (!strcmp(aTopic, "profile-before-change") || !strcmp(aTopic, "xpcom-shutdown")) { rv = DOMStorageCache::StopDatabase(); if (NS_FAILED(rv)) { NS_WARNING("Error while stopping DOMStorage DB background thread"); } return NS_OK; } if (!strcmp(aTopic, "disk-space-watcher")) { if (NS_LITERAL_STRING("full").Equals(aData)) { Notify("low-disk-space"); } else if (NS_LITERAL_STRING("free").Equals(aData)) { Notify("no-low-disk-space"); } return NS_OK; } #ifdef DOM_STORAGE_TESTS if (!strcmp(aTopic, "domstorage-test-flush-force")) { DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); if (db) { db->AsyncFlush(); } return NS_OK; } if (!strcmp(aTopic, "domstorage-test-flushed")) { // Only used to propagate to IPC children Notify("test-flushed"); return NS_OK; } if (!strcmp(aTopic, "domstorage-test-reload")) { Notify("test-reload"); return NS_OK; } #endif NS_ERROR("Unexpected topic"); return NS_ERROR_UNEXPECTED; }
NS_IMETHODIMP DOMStorageObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { nsresult rv; // Start the thread that opens the database. if (!strcmp(aTopic, kStartupTopic)) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); obs->RemoveObserver(this, kStartupTopic); mDBThreadStartDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID); if (!mDBThreadStartDelayTimer) { return NS_ERROR_UNEXPECTED; } mDBThreadStartDelayTimer->Init(this, nsITimer::TYPE_ONE_SHOT, kStartupDelay); return NS_OK; } // Timer callback used to start the database a short timer after startup if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject); if (!timer) { return NS_ERROR_UNEXPECTED; } if (timer == mDBThreadStartDelayTimer) { mDBThreadStartDelayTimer = nullptr; DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); } return NS_OK; } // Clear everything, caches + database if (!strcmp(aTopic, "cookie-changed")) { if (!NS_LITERAL_STRING("cleared").Equals(aData)) { return NS_OK; } DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); db->AsyncClearAll(); Notify("cookie-cleared"); return NS_OK; } // Clear from caches everything that has been stored // while in session-only mode if (!strcmp(aTopic, "perm-changed")) { // Check for cookie permission change nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject)); if (!perm) { return NS_OK; } nsAutoCString type; perm->GetType(type); if (type != NS_LITERAL_CSTRING("cookie")) { return NS_OK; } uint32_t cap = 0; perm->GetCapability(&cap); if (!(cap & nsICookiePermission::ACCESS_SESSION) || !NS_LITERAL_STRING("deleted").Equals(nsDependentString(aData))) { return NS_OK; } nsAutoCString host; perm->GetHost(host); if (host.IsEmpty()) { return NS_OK; } nsAutoCString scope; rv = CreateReversedDomain(host, scope); NS_ENSURE_SUCCESS(rv, rv); Notify("session-only-cleared", scope); return NS_OK; } // Clear everything (including so and pb data) from caches and database // for the gived domain and subdomains. if (!strcmp(aTopic, "browser:purge-domain-data")) { // Convert the domain name to the ACE format nsAutoCString aceDomain; nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID); if (converter) { rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain); NS_ENSURE_SUCCESS(rv, rv); } else { // In case the IDN service is not available, this is the best we can come up with! NS_EscapeURL(NS_ConvertUTF16toUTF8(aData), esc_OnlyNonASCII | esc_AlwaysCopy, aceDomain); } nsAutoCString scopePrefix; rv = CreateReversedDomain(aceDomain, scopePrefix); NS_ENSURE_SUCCESS(rv, rv); DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); db->AsyncClearMatchingScope(scopePrefix); Notify("domain-data-cleared", scopePrefix); return NS_OK; } // Clear all private-browsing caches if (!strcmp(aTopic, "last-pb-context-exited")) { Notify("private-browsing-data-cleared"); return NS_OK; } // Clear data beloging to an app. if (!strcmp(aTopic, "webapps-clear-data")) { nsCOMPtr<mozIApplicationClearPrivateDataParams> params = do_QueryInterface(aSubject); if (!params) { NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); return NS_ERROR_UNEXPECTED; } uint32_t appId; bool browserOnly; rv = params->GetAppId(&appId); NS_ENSURE_SUCCESS(rv, rv); rv = params->GetBrowserOnly(&browserOnly); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID); DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); nsAutoCString scope; scope.AppendInt(appId); scope.AppendLiteral(":t:"); db->AsyncClearMatchingScope(scope); Notify("app-data-cleared", scope); if (!browserOnly) { scope.Truncate(); scope.AppendInt(appId); scope.AppendLiteral(":f:"); db->AsyncClearMatchingScope(scope); Notify("app-data-cleared", scope); } return NS_OK; } if (!strcmp(aTopic, "profile-after-change")) { Notify("profile-change"); return NS_OK; } if (!strcmp(aTopic, "profile-before-change") || !strcmp(aTopic, "xpcom-shutdown")) { rv = DOMStorageCache::StopDatabase(); if (NS_FAILED(rv)) { NS_WARNING("Error while stopping DOMStorage DB background thread"); } return NS_OK; } if (!strcmp(aTopic, "disk-space-watcher")) { if (NS_LITERAL_STRING("full").Equals(aData)) { Notify("low-disk-space"); } else if (NS_LITERAL_STRING("free").Equals(aData)) { Notify("no-low-disk-space"); } return NS_OK; } #ifdef DOM_STORAGE_TESTS if (!strcmp(aTopic, "domstorage-test-flush-force")) { DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); if (db) { db->AsyncFlush(); } return NS_OK; } if (!strcmp(aTopic, "domstorage-test-flushed")) { // Only used to propagate to IPC children Notify("test-flushed"); return NS_OK; } if (!strcmp(aTopic, "domstorage-test-reload")) { Notify("test-reload"); return NS_OK; } #endif NS_ERROR("Unexpected topic"); return NS_ERROR_UNEXPECTED; }