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);
}
Beispiel #4
0
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("&#8203;");
        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);
}