bool net_IsValidHostName(const nsCSubstring &host) { const char *end = host.EndReading(); // Use explicit whitelists to select which characters we are // willing to send to lower-level DNS logic. This is more // self-documenting, and can also be slightly faster than the // blacklist approach, since DNS names are the common case, and // the commonest characters will tend to be near the start of // the list. // Whitelist for DNS names (RFC 1035) with extra characters added // for pragmatic reasons "$+_" // see https://bugzilla.mozilla.org/show_bug.cgi?id=355181#c2 if (net_FindCharNotInSet(host.BeginReading(), end, "abcdefghijklmnopqrstuvwxyz" ".-0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ$+_") == end) return true; // Might be a valid IPv6 link-local address containing a percent sign nsCAutoString strhost(host); PRNetAddr addr; return PR_StringToNetAddr(strhost.get(), &addr) == PR_SUCCESS; }
nsresult CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey, bool aDiskStorage, nsICacheEntryDoomCallback* aCallback) { mLock.AssertCurrentThreadOwns(); NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED); nsAutoCString memoryStorageID(aContextKey); AppendMemoryStorageID(memoryStorageID); nsAutoPtr<CacheEntryTable> entries; if (aDiskStorage) { LOG((" dooming disk+memory storage of %s", aContextKey.BeginReading())); // Grab all entries in this storage sGlobalEntryTables->RemoveAndForget(aContextKey, entries); // Just remove the memory-only records table sGlobalEntryTables->Remove(memoryStorageID); } else { LOG((" dooming memory-only storage of %s", aContextKey.BeginReading())); // Grab the memory-only records table, EvictionRunnable will safely remove // entries one by one from the master hashtable on the background management // thread. Code at AddStorageEntry ensures a new entry will always replace // memory only entries that EvictionRunnable yet didn't manage to remove. sGlobalEntryTables->RemoveAndForget(memoryStorageID, entries); } nsRefPtr<EvictionRunnable> evict = new EvictionRunnable( aContextKey, entries.forget(), aDiskStorage, aCallback); return Dispatch(evict); }
void ToLowerCase( nsCSubstring& aCString ) { ConvertToLowerCase converter; char* start; converter.write(aCString.BeginWriting(start), aCString.Length()); }
static void MorkUnescape(const nsCSubstring &aString, nsCString &aResult) { PRUint32 len = aString.Length(); // We optimize for speed over space here -- size the result buffer to // the size of the source, which is an upper bound on the size of the // unescaped string. // FIXME: Mork assume there will never be errors if (!EnsureStringLength(aResult, len)) { aResult.Truncate(); return; // out of memory. } char *result = aResult.BeginWriting(); const char *source = aString.BeginReading(); const char *sourceEnd = source + len; const char *startPos = nsnull; PRUint32 bytes; for (; source < sourceEnd; ++source) { char c = *source; if (c == '\\') { if (startPos) { bytes = source - startPos; memcpy(result, startPos, bytes); result += bytes; startPos = nsnull; } if (source < sourceEnd - 1) { *(result++) = *(++source); } } else if (c == '$') { if (startPos) { bytes = source - startPos; memcpy(result, startPos, bytes); result += bytes; startPos = nsnull; } if (source < sourceEnd - 2) { // Would be nice to use ToInteger() here, but it currently // requires a null-terminated string. char c2 = *(++source); char c3 = *(++source); if (ConvertChar(&c2) && ConvertChar(&c3)) { *(result++) = ((c2 << 4) | c3); } } } else if (!startPos) { startPos = source; } } if (startPos) { bytes = source - startPos; memcpy(result, startPos, bytes); result += bytes; } aResult.SetLength(result - aResult.BeginReading()); }
bool nsHttpNegotiateAuth::MatchesBaseURI(const nsCSubstring &matchScheme, const nsCSubstring &matchHost, PRInt32 matchPort, const char *baseStart, const char *baseEnd) { // check if scheme://host:port matches baseURI // parse the base URI const char *hostStart, *schemeEnd = strstr(baseStart, "://"); if (schemeEnd) { // the given scheme must match the parsed scheme exactly if (!matchScheme.Equals(Substring(baseStart, schemeEnd))) return false; hostStart = schemeEnd + 3; } else hostStart = baseStart; // XXX this does not work for IPv6-literals const char *hostEnd = strchr(hostStart, ':'); if (hostEnd && hostEnd < baseEnd) { // the given port must match the parsed port exactly int port = atoi(hostEnd + 1); if (matchPort != (PRInt32) port) return false; } else hostEnd = baseEnd; // if we didn't parse out a host, then assume we got a match. if (hostStart == hostEnd) return true; PRUint32 hostLen = hostEnd - hostStart; // matchHost must either equal host or be a subdomain of host if (matchHost.Length() < hostLen) return false; const char *end = matchHost.EndReading(); if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) { // if matchHost ends with host from the base URI, then make sure it is // either an exact match, or prefixed with a dot. we don't want // "foobar.com" to match "bar.com" if (matchHost.Length() == hostLen || *(end - hostLen) == '.' || *(end - hostLen - 1) == '.') return true; } return false; }
void ReverseString(const nsCSubstring& source, nsCSubstring& result) { nsACString::const_iterator sourceBegin, sourceEnd; source.BeginReading(sourceBegin); source.EndReading(sourceEnd); result.SetLength(source.Length()); nsACString::iterator destEnd; result.EndWriting(destEnd); while (sourceBegin != sourceEnd) { *(--destEnd) = *sourceBegin; ++sourceBegin; } }
nsresult nsFtpControlConnection::Write(const nsCSubstring& command) { NS_ENSURE_STATE(mSocketOutput); PRUint32 len = command.Length(); PRUint32 cnt; nsresult rv = mSocketOutput->Write(command.Data(), len, &cnt); if (NS_FAILED(rv)) return rv; if (len != cnt) return NS_ERROR_FAILURE; return NS_OK; }
void AppendTagWithValue(nsACString & aTarget, char const aTag, nsCSubstring const & aValue) { aTarget.Append(aTag); // First check the value string to save some memory copying // for cases we don't need to escape at all (most likely). if (!aValue.IsEmpty()) { if (!aValue.Contains(',')) { // No need to escape aTarget.Append(aValue); } else { nsAutoCString escapedValue(aValue); escapedValue.ReplaceSubstring( NS_LITERAL_CSTRING(","), NS_LITERAL_CSTRING(",,")); aTarget.Append(escapedValue); } } aTarget.Append(','); }
PRBool IsValidHTTPToken(const nsCSubstring& aToken) { if (aToken.IsEmpty()) { return PR_FALSE; } nsCSubstring::const_char_iterator iter, end; aToken.BeginReading(iter); aToken.EndReading(end); while (iter != end) { if (*iter <= 32 || *iter >= 127 || *iter == '(' || *iter == ')' || *iter == '<' || *iter == '>' || *iter == '@' || *iter == ',' || *iter == ';' || *iter == ':' || *iter == '\\' || *iter == '\"' || *iter == '/' || *iter == '[' || *iter == ']' || *iter == '?' || *iter == '=' || *iter == '{' || *iter == '}') { return PR_FALSE; } ++iter; } return PR_TRUE; }
nsresult nsBase64Encoder::Finish(nsCSubstring& result) { char* b64 = PL_Base64Encode(mData.get(), mData.Length(), nsnull); if (!b64) return NS_ERROR_OUT_OF_MEMORY; result.Assign(b64); PR_Free(b64); // Free unneeded memory and allow reusing the object mData.Truncate(); return NS_OK; }
nsresult NS_DeserializeObject(const nsCSubstring& str, nsISupports** obj) { // Base64 maps 3 binary bytes -> 4 ASCII bytes, so this calculation gives us // the right size. Compare also the comment in plbase64.h. PRUint32 size = (str.Length() * 3) / 4; char* buf = PL_Base64Decode(str.BeginReading(), str.Length(), nsnull); if (!buf) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr<nsIInputStream> stream; nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), Substring(buf, buf + size)); PR_Free(buf); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIObjectInputStream> objstream = do_CreateInstance("@mozilla.org/binaryinputstream;1"); if (!objstream) return NS_ERROR_OUT_OF_MEMORY; objstream->SetInputStream(stream); return objstream->ReadObject(PR_TRUE, obj); }
nsresult NS_DeserializeObject(const nsCSubstring& str, nsISupports** obj) { // Base64 maps 3 binary bytes -> 4 ASCII bytes. If the original byte array // does not have length 0 mod 3, the input is padded with zeros and the // output is padded with a corresponding number of trailing '=' (which are // then sometimes dropped). To compute the correct length of the original // byte array, we have to subtract the number of trailing '=' and then // multiply by 3 and then divide by 4 (making sure this is an integer // division). PRUint32 size = str.Length(); if (size > 0 && str[size-1] == '=') { if (size > 1 && str[size-2] == '=') { size -= 2; } else { size -= 1; } } size = (size * 3) / 4; char* buf = PL_Base64Decode(str.BeginReading(), str.Length(), nullptr); if (!buf) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr<nsIInputStream> stream; nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), Substring(buf, size)); PR_Free(buf); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIObjectInputStream> objstream = do_CreateInstance("@mozilla.org/binaryinputstream;1"); if (!objstream) return NS_ERROR_OUT_OF_MEMORY; objstream->SetInputStream(stream); return objstream->ReadObject(true, obj); }
nsresult rdf_MakeRelativeRef(const nsCSubstring& aBaseURI, nsCString& aURI) { // This implementation is extremely simple: e.g., it can't compute // relative paths, or anything fancy like that. If the context URI // is not a prefix of the URI in question, we'll just bail. PRUint32 prefixLen = aBaseURI.Length(); if (prefixLen != 0 && StringBeginsWith(aURI, aBaseURI)) { if (prefixLen < aURI.Length() && aURI.CharAt(prefixLen) == '/') ++prefixLen; // chop the leading slash so it's not `absolute' aURI.Cut(0, prefixLen); } return NS_OK; }
nsresult nsHttpChannelAuthProvider::GetAuthorizationMembers(bool proxyAuth, nsCSubstring& scheme, const char*& host, int32_t& port, nsCSubstring& path, nsHttpAuthIdentity*& ident, nsISupports**& continuationState) { if (proxyAuth) { NS_ASSERTION (UsingHttpProxy(), "proxyAuth is true, but no HTTP proxy is configured!"); host = ProxyHost(); port = ProxyPort(); ident = &mProxyIdent; scheme.AssignLiteral("http"); continuationState = &mProxyAuthContinuationState; } else { host = Host(); port = Port(); ident = &mIdent; nsresult rv; rv = GetCurrentPath(path); if (NS_FAILED(rv)) return rv; rv = mURI->GetScheme(scheme); if (NS_FAILED(rv)) return rv; continuationState = &mAuthContinuationState; } return NS_OK; }
// Find the storage ID for the storage area that contains aPath. MtpStorageID MozMtpDatabase::FindStorageIDFor(const nsACString& aPath, nsCSubstring& aRemainder) { MutexAutoLock lock(mMutex); aRemainder.Truncate(); StorageArray::size_type numStorages = mStorage.Length(); StorageArray::index_type storageIndex; for (storageIndex = 0; storageIndex < numStorages; storageIndex++) { RefPtr<StorageEntry> storage = mStorage[storageIndex]; if (StringHead(aPath, storage->mStoragePath.Length()).Equals(storage->mStoragePath)) { if (aPath.Length() == storage->mStoragePath.Length()) { return storage->mStorageID; } if (aPath[storage->mStoragePath.Length()] == '/') { aRemainder = Substring(aPath, storage->mStoragePath.Length() + 1); return storage->mStorageID; } } } return 0; }
nsresult CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey, nsILoadContextInfo* aContext, bool aDiskStorage, nsICacheEntryDoomCallback* aCallback) { mLock.AssertCurrentThreadOwns(); NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED); nsAutoCString memoryStorageID(aContextKey); AppendMemoryStorageID(memoryStorageID); if (aDiskStorage) { LOG((" dooming disk+memory storage of %s", aContextKey.BeginReading())); // Just remove all entries, CacheFileIOManager will take care of the files. sGlobalEntryTables->Remove(aContextKey); sGlobalEntryTables->Remove(memoryStorageID); if (aContext && !aContext->IsPrivate()) { LOG((" dooming disk entries")); CacheFileIOManager::EvictByContext(aContext); } } else { LOG((" dooming memory-only storage of %s", aContextKey.BeginReading())); class MemoryEntriesRemoval { public: static PLDHashOperator EvictEntry(const nsACString& aKey, CacheEntry* aEntry, void* aClosure) { CacheEntryTable* entries = static_cast<CacheEntryTable*>(aClosure); nsCString key(aKey); RemoveExactEntry(entries, key, aEntry, false); return PL_DHASH_NEXT; } }; // Remove the memory entries table from the global tables. // Since we store memory entries also in the disk entries table // we need to remove the memory entries from the disk table one // by one manually. nsAutoPtr<CacheEntryTable> memoryEntries; sGlobalEntryTables->RemoveAndForget(memoryStorageID, memoryEntries); CacheEntryTable* entries; sGlobalEntryTables->Get(aContextKey, &entries); if (memoryEntries && entries) memoryEntries->EnumerateRead(&MemoryEntriesRemoval::EvictEntry, entries); } // An artificial callback. This is a candidate for removal tho. In the new // cache any 'doom' or 'evict' function ensures that the entry or entries // being doomed is/are not accessible after the function returns. So there is // probably no need for a callback - has no meaning. But for compatibility // with the old cache that is still in the tree we keep the API similar to be // able to make tests as well as other consumers work for now. class Callback : public nsRunnable { public: Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { } NS_IMETHODIMP Run() { mCallback->OnCacheEntryDoomed(NS_OK); return NS_OK; } nsCOMPtr<nsICacheEntryDoomCallback> mCallback; }; if (aCallback) { nsRefPtr<nsRunnable> callback = new Callback(aCallback); return NS_DispatchToCurrentThread(callback); } return NS_OK; }
nsresult CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey, nsIURI* aURI, const nsACString & aIdExtension, bool aWriteToDisk, bool aCreateIfNotExist, bool aReplace, CacheEntryHandle** aResult) { NS_ENSURE_ARG(aURI); nsresult rv; nsAutoCString entryKey; rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey); NS_ENSURE_SUCCESS(rv, rv); LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]", entryKey.get(), aContextKey.BeginReading())); nsRefPtr<CacheEntry> entry; nsRefPtr<CacheEntryHandle> handle; { mozilla::MutexAutoLock lock(mLock); NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED); // Ensure storage table CacheEntryTable* entries; if (!sGlobalEntryTables->Get(aContextKey, &entries)) { entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES); sGlobalEntryTables->Put(aContextKey, entries); LOG((" new storage entries table for context %s", aContextKey.BeginReading())); } bool entryExists = entries->Get(entryKey, getter_AddRefs(entry)); // check whether the file is already doomed if (entryExists && entry->IsFileDoomed() && !aReplace) { aReplace = true; } // If truncate is demanded, delete and doom the current entry if (entryExists && aReplace) { entries->Remove(entryKey); LOG((" dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get())); // On purpose called under the lock to prevent races of doom and open on I/O thread // No need to remove from both memory-only and all-entries tables. The new entry // will overwrite the shadow entry in its ctor. entry->DoomAlreadyRemoved(); entry = nullptr; entryExists = false; } if (entryExists && entry->SetUsingDisk(aWriteToDisk)) { RecordMemoryOnlyEntry(entry, !aWriteToDisk, true /* overwrite */); } // Ensure entry for the particular URL, if not read/only if (!entryExists && (aCreateIfNotExist || aReplace)) { // Entry is not in the hashtable or has just been truncated... entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk); entries->Put(entryKey, entry); LOG((" new entry %p for %s", entry.get(), entryKey.get())); } if (entry) { // Here, if this entry was not for a long time referenced by any consumer, // gets again first 'handles count' reference. handle = entry->NewHandle(); } } handle.forget(aResult); return NS_OK; }
static nsresult rdf_BlockingWrite(nsIOutputStream* stream, const nsCSubstring& s) { return rdf_BlockingWrite(stream, s.BeginReading(), s.Length()); }
bool MatchesBaseURI(const nsCSubstring &matchScheme, const nsCSubstring &matchHost, int32_t matchPort, nsDependentCSubstring const& url) { // check if scheme://host:port matches baseURI // parse the base URI mozilla::Tokenizer t(url); mozilla::Tokenizer::Token token; t.SkipWhites(); // We don't know if the url to check against starts with scheme // or a host name. Start recording here. t.Record(); mozilla::Unused << t.Next(token); // The ipv6 literals MUST be enclosed with [] in the preference. bool ipv6 = false; if (token.Equals(mozilla::Tokenizer::Token::Char('['))) { nsDependentCSubstring ipv6BareLiteral; if (!t.ReadUntil(mozilla::Tokenizer::Token::Char(']'), ipv6BareLiteral)) { // Broken ipv6 literal return false; } nsDependentCSubstring ipv6Literal; t.Claim(ipv6Literal, mozilla::Tokenizer::INCLUDE_LAST); if (!matchHost.Equals(ipv6Literal, nsCaseInsensitiveUTF8StringComparator()) && !matchHost.Equals(ipv6BareLiteral, nsCaseInsensitiveUTF8StringComparator())) { return false; } ipv6 = true; } else if (t.CheckChar(':') && t.CheckChar('/') && t.CheckChar('/')) { if (!matchScheme.Equals(token.Fragment())) { return false; } // Re-start recording the hostname from the point after scheme://. t.Record(); } while (t.Next(token)) { bool eof = token.Equals(mozilla::Tokenizer::Token::EndOfFile()); bool port = token.Equals(mozilla::Tokenizer::Token::Char(':')); if (eof || port) { if (!ipv6) { // Match already performed above. nsDependentCSubstring hostName; t.Claim(hostName); // An empty hostname means to accept everything for the schema if (!hostName.IsEmpty()) { /* host: bar.com foo.bar.com foobar.com foo.bar.com bar.com pref: bar.com bar.com bar.com .bar.com .bar.com result: accept accept reject accept reject */ if (!StringEndsWith(matchHost, hostName, nsCaseInsensitiveUTF8StringComparator())) { return false; } if (matchHost.Length() > hostName.Length() && matchHost[matchHost.Length() - hostName.Length() - 1] != '.' && hostName[0] != '.') { return false; } } } if (port) { uint16_t portNumber; if (!t.ReadInteger(&portNumber)) { // Missing port number return false; } if (matchPort != portNumber) { return false; } if (!t.CheckEOF()) { return false; } } } else if (ipv6) { // After an ipv6 literal there can only be EOF or :port. Everything else // must be treated as non-match/broken input. return false; } } // All negative checks has passed positively. return true; }
nsresult CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey, nsIURI* aURI, const nsACString & aIdExtension, bool aWriteToDisk, bool aCreateIfNotExist, bool aReplace, CacheEntry** aResult) { NS_ENSURE_ARG(aURI); nsresult rv; nsAutoCString entryKey; rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey); NS_ENSURE_SUCCESS(rv, rv); LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]", entryKey.get(), aContextKey.BeginReading())); nsRefPtr<CacheEntry> entry; { mozilla::MutexAutoLock lock(mLock); NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED); // Ensure storage table CacheEntryTable* entries; if (!sGlobalEntryTables->Get(aContextKey, &entries)) { entries = new CacheEntryTable(); sGlobalEntryTables->Put(aContextKey, entries); LOG((" new storage entries table for context %s", aContextKey.BeginReading())); } bool entryExists = entries->Get(entryKey, getter_AddRefs(entry)); // Check entry that is memory-only is also in related memory-only hashtable. // If not, it has been evicted and we will truncate it ; doom is pending for it, // this consumer just made it sooner then the entry has actually been removed // from the master hash table. // (This can be bypassed when entry is about to be replaced anyway.) if (entryExists && !entry->UsingDisk() && !aReplace) { nsAutoCString memoryStorageID(aContextKey); AppendMemoryStorageID(memoryStorageID); CacheEntryTable* memoryEntries; aReplace = sGlobalEntryTables->Get(memoryStorageID, &memoryEntries) && memoryEntries->GetWeak(entryKey) != entry; #ifdef MOZ_LOGGING if (aReplace) { LOG((" memory-only entry %p for %s already doomed, replacing", entry.get(), entryKey.get())); } #endif } // If truncate is demanded, delete and doom the current entry if (entryExists && aReplace) { entries->Remove(entryKey); LOG((" dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get())); // On purpose called under the lock to prevent races of doom and open on I/O thread entry->DoomAlreadyRemoved(); entry = nullptr; entryExists = false; } if (entryExists && entry->SetUsingDisk(aWriteToDisk)) { RecordMemoryOnlyEntry(entry, !aWriteToDisk, true /* overwrite */); } // Ensure entry for the particular URL, if not read/only if (!entryExists && (aCreateIfNotExist || aReplace)) { // Entry is not in the hashtable or has just been truncated... entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk); entries->Put(entryKey, entry); LOG((" new entry %p for %s", entry.get(), entryKey.get())); } } entry.forget(aResult); return NS_OK; }