/* * Copyright (C) 2001 Matt Aubury, Philip Langdale * Copyright (C) 2004 Crispin Flowerday * Copyright (C) 2005 Christian Persch * Copyright (C) 2005 Adam Hooper * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ #include "mozilla-config.h" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "EphyProtocolHandler.h" #include "EphyUtils.h" #include "ephy-debug.h" #include static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID); static NS_DEFINE_CID(kInputStreamChannelCID, NS_INPUTSTREAMCHANNEL_CID); EphyProtocolHandler::EphyProtocolHandler() { LOG ("EphyProtocolHandler ctor [%p]\n", this); } EphyProtocolHandler::~EphyProtocolHandler() { LOG ("EphyProtocolHandler dtor [%p]\n", this); } NS_IMPL_ISUPPORTS2 (EphyProtocolHandler, nsIProtocolHandler, nsIAboutModule) /* readonly attribute string scheme; */ NS_IMETHODIMP EphyProtocolHandler::GetScheme(nsACString &aScheme) { aScheme.Assign("epiphany"); return NS_OK; } /* readonly attribute long defaultPort; */ NS_IMETHODIMP EphyProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort) { NS_ENSURE_ARG_POINTER (aDefaultPort); *aDefaultPort = -1; return NS_OK; } /* readonly attribute short protocolFlags; */ NS_IMETHODIMP EphyProtocolHandler::GetProtocolFlags(PRUint32 *aProtocolFlags) { NS_ENSURE_ARG_POINTER (aProtocolFlags); *aProtocolFlags = nsIProtocolHandler::URI_NORELATIVE | nsIProtocolHandler::URI_NOAUTH; return NS_OK; } /* nsIURI newURI (in string aSpec, in nsIURI aBaseURI); */ NS_IMETHODIMP EphyProtocolHandler::NewURI(const nsACString &aSpec, const char *aOriginCharset, nsIURI *aBaseURI, nsIURI **_retval) { nsresult rv; nsCOMPtr uri (do_CreateInstance(kSimpleURICID, &rv)); NS_ENSURE_SUCCESS (rv, rv); rv = uri->SetSpec (aSpec); NS_ENSURE_SUCCESS (rv, rv); NS_ADDREF(*_retval = uri); return NS_OK; } /* nsIChannel newChannel (in nsIURI aURI); */ NS_IMETHODIMP EphyProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval) { NS_ENSURE_ARG(aURI); PRBool isEpiphany = PR_FALSE; if (NS_SUCCEEDED (aURI->SchemeIs ("epiphany", &isEpiphany)) && isEpiphany) { return HandleEpiphany (aURI, _retval); } PRBool isAbout = PR_FALSE; if (NS_SUCCEEDED (aURI->SchemeIs ("about", &isAbout)) && isAbout) { return Redirect (nsDependentCString ("file://" SHARE_DIR "/epiphany.xhtml"), _retval); } return NS_ERROR_ILLEGAL_VALUE; } /* boolean allowPort (in long port, in string scheme); */ NS_IMETHODIMP EphyProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval) { *_retval = PR_FALSE; return NS_OK; } /* private functions */ nsresult EphyProtocolHandler::Redirect(const nsACString &aURL, nsIChannel **_retval) { nsresult rv; nsCOMPtr ioService; rv = EphyUtils::GetIOService (getter_AddRefs (ioService)); NS_ENSURE_SUCCESS (rv, rv); nsCOMPtr tempChannel; rv = ioService->NewChannel(aURL, nsnull, nsnull, getter_AddRefs(tempChannel)); NS_ENSURE_SUCCESS (rv, rv); nsCOMPtr uri; rv = ioService->NewURI(aURL, nsnull, nsnull, getter_AddRefs(uri)); NS_ENSURE_SUCCESS (rv, rv); tempChannel->SetOriginalURI (uri); nsCOMPtr securityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS (rv, rv); nsCOMPtr principal; rv = securityManager->GetCodebasePrincipal(uri, getter_AddRefs(principal)); NS_ENSURE_SUCCESS (rv, rv); rv = tempChannel->SetOwner(principal); NS_ENSURE_SUCCESS (rv, rv); NS_ADDREF(*_retval = tempChannel); return rv; } nsresult EphyProtocolHandler::ParseErrorURL(const char *aURL, nsACString &aError, nsACString &aOriginURL, nsACString &aOriginCharset) { /* The error page URL is of the form "epiphany:error?e=&u=&c=&d=" */ const char *query = strstr (aURL, "?"); if (!query) return NS_ERROR_FAILURE; /* skip the '?' */ ++query; char **params = g_strsplit (query, "&", -1); if (!params) return NS_ERROR_FAILURE; for (PRUint32 i = 0; params[i] != NULL; ++i) { char *param = params[i]; if (strlen (param) <= 2) continue; switch (param[0]) { case 'e': aError.Assign (nsUnescape (param + 2)); break; case 'u': aOriginURL.Assign (nsUnescape (param + 2)); break; case 'c': aOriginCharset.Assign (nsUnescape (param + 2)); break; case 'd': /* we don't need mozilla's description parameter */ default: break; } } g_strfreev (params); return NS_OK; } nsresult EphyProtocolHandler::GetErrorMessage(nsIURI *aURI, const char *aError, char **aPrimary, char **aSecondary) { if (strcmp (aError, "protocolNotFound") == 0) { nsCAutoString scheme; aURI->GetScheme (scheme); *aPrimary = g_strdup (_("Unknown protocol")); /* FIXME: get the list of supported protocols from necko */ *aSecondary = g_strdup_printf (_("The \"%s\" protocol could not be recognised. " "Epiphany supports \"http\", \"https\", \"ftp\", " "\"smb\" and \"sftp\" protocols."), scheme.get()); } else if (strcmp (aError, "fileNotFound") == 0) { nsCAutoString path; aURI->GetPath (path); *aPrimary = g_strdup (_("File not found")); *aSecondary = g_strdup_printf (_("The file \"%s\" could not be found. " "Check the location of the file and try again."), path.get()); } else if (strcmp (aError, "dnsNotFound") == 0) { nsCAutoString host; aURI->GetHost (host); *aPrimary = g_strdup (_("Address not found")); *aSecondary = g_strdup_printf (_("\"%s\" could not be found. " "Check the address and try again."), host.get()); } else if (strcmp (aError, "connectionFailure") == 0) { nsCAutoString hostport; aURI->GetHostPort (hostport); *aPrimary = g_strdup_printf (_("Could not connect to \"%s\""), hostport.get()); *aSecondary = g_strdup (_("The server refused the connection. " "Check the address and port and try again.")); } else if (strcmp (aError, "netInterrupt") == 0) { nsCAutoString hostport; aURI->GetHostPort (hostport); *aPrimary = g_strdup (_("Connection interrupted")); *aSecondary = g_strdup_printf (_(" The connection to \"%s\" was interrupted. " "The server may be very busy or you may have a " "network connection problem."), hostport.get()); } else if (strcmp (aError, "netTimeout") == 0) { nsCAutoString host; aURI->GetHost (host); *aPrimary = g_strdup (_("Connection timed out")); *aSecondary = g_strdup_printf (_("The connection to \"%s\" was lost because the " "server took too long to respond. The server " "may be very busy or you may have a network"), host.get()); } else if (strcmp (aError, "malformedURI") == 0) { *aPrimary = g_strdup (_("Invalid address")); *aSecondary = g_strdup (_("The address you entered is not valid.")); } else if (strcmp (aError, "redirectLoop") == 0) { *aPrimary = g_strdup (_("Too many redirects")); *aSecondary = g_strdup_printf (_("The server redirected too many times. " "The redirection has been stopped for " "security reasons.")); } else if (strcmp (aError, "unknownSocketType") == 0) { *aPrimary = g_strdup (_("The document could not be loaded")); *aSecondary = g_strdup (_("This document requires \"Personal " "Security Manager\" to be installed.")); } else if (strcmp (aError, "netReset") == 0) { *aPrimary = g_strdup (_("Connection dropped by server")); *aSecondary = g_strdup (_("Epiphany connected to the server, " "but the server dropped the connection " "before any data could be read.")); } else if (strcmp (aError, "netOffline") == 0) { *aPrimary = g_strdup (_("Cannot load document in offline mode")); *aSecondary = g_strdup (_("This document cannot be viewed in offline " "mode. Set Epiphany to \"online\" and try " "again.")); } else if (strcmp (aError, "deniedPortAccess") == 0) { *aPrimary = g_strdup (_("FIXME")); *aSecondary = g_strdup (_("FIXME")); } else if (strcmp (aError, "proxyResolveFailure") == 0 || strcmp (aError, "proxyConnectFailure") == 0) { *aPrimary = g_strdup (_("Could not connect to proxy server")); *aSecondary = g_strdup (_("Check you system wide proxy server settings. " "If the connection still fails, there may be " "a problem with your proxy server or your " "network connection.")); } else { return NS_ERROR_ILLEGAL_VALUE; } return NS_OK; } nsresult EphyProtocolHandler::HandleEpiphany(nsIURI *aErrorURI, nsIChannel **_retval) { NS_ENSURE_ARG (aErrorURI); nsCAutoString spec; aErrorURI->GetSpec (spec); if (g_str_has_prefix (spec.get(), "epiphany:error?")) { return CreateErrorPage (aErrorURI, _retval); } return NS_ERROR_ILLEGAL_VALUE; } nsresult EphyProtocolHandler::CreateErrorPage(nsIURI *aErrorURI, nsIChannel **_retval) { /* First parse the arguments */ nsresult rv = NS_ERROR_ILLEGAL_VALUE; nsCAutoString spec; rv = aErrorURI->GetSpec (spec); NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && !spec.IsEmpty(), rv); nsCAutoString error, url, charset; rv = ParseErrorURL (spec.get (), error, url, charset); if (NS_FAILED (rv) || error.IsEmpty () || url.IsEmpty()) return rv; nsCOMPtr uri; rv = EphyUtils::NewURI(getter_AddRefs (uri), url, charset.get()); /* FIXME can uri be NULL if the original url was invalid? */ NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && uri, rv); char *primary = NULL, *secondary = NULL; rv = GetErrorMessage (uri, error.get(), &primary, &secondary); /* we don't know about this error code; forward to mozilla's impl */ if (rv == NS_ERROR_ILLEGAL_VALUE) { nsCAutoString url(spec); /* remove "epiphany:error" part and insert mozilla's error page url */ url.Cut(0, strlen ("epiphany:error")); url.Insert("chrome://global/content/netError.xhtml", 0); return Redirect (url, _retval); } NS_ENSURE_TRUE (primary && secondary, NS_ERROR_FAILURE); /* open the rendering stream */ nsCOMPtr storageStream; rv = NS_NewStorageStream(16384, (PRUint32) -1, getter_AddRefs (storageStream)); NS_ENSURE_SUCCESS (rv, rv); nsCOMPtr stream; rv = storageStream->GetOutputStream(0, getter_AddRefs (stream)); NS_ENSURE_SUCCESS (rv, rv); char *language = g_strdup (pango_language_to_string (gtk_get_default_language ())); g_strdelimit (language, "_-@", '\0'); Write (stream, "\n" "\n" "\n" "\n" ""); WriteHTMLEscape (stream, primary); /* no favicon for now, it would pollute the favicon cache */ /* "<link rel=\"icon\" type=\"image/png\" href=\"moz-icon://stock/gtk-dialog-error?size=16\" />\n" */ Write (stream, "\n" "\n" "\n" "\n" "
\"");
\n" "
\n" "

"); WriteHTMLEscape (stream, primary); Write (stream, "

\n" "

"); WriteHTMLEscape (stream, secondary); Write (stream, "

\n" "
\n" "\n" "\n"); g_free (language); g_free (primary); g_free (secondary); /* finish the rendering */ nsCOMPtr inputStream; rv = storageStream->NewInputStream(0, getter_AddRefs (inputStream)); NS_ENSURE_SUCCESS (rv, rv); nsCOMPtr channel (do_CreateInstance (kInputStreamChannelCID, &rv)); NS_ENSURE_SUCCESS (rv, rv); rv |= channel->SetURI (aErrorURI); rv |= channel->SetContentStream (inputStream); rv |= channel->SetContentType (NS_LITERAL_CSTRING ("application/xhtml+xml")); rv |= channel->SetContentCharset (NS_LITERAL_CSTRING ("utf-8")); NS_ENSURE_SUCCESS (rv, rv); nsCOMPtr securityManager (do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); NS_ENSURE_SUCCESS (rv, rv); nsCOMPtr principal; rv = securityManager->GetCodebasePrincipal (aErrorURI, getter_AddRefs (principal)); NS_ENSURE_SUCCESS (rv, rv); rv = channel->SetOwner(principal); NS_ENSURE_SUCCESS (rv, rv); NS_ADDREF(*_retval = channel); return rv; } nsresult EphyProtocolHandler::Write(nsIOutputStream *aStream, const char *aText) { PRUint32 bytesWritten; return aStream->Write (aText, strlen (aText), &bytesWritten); } nsresult EphyProtocolHandler::WriteHTMLEscape(nsIOutputStream *aStream, const char *aText) { char *text = g_markup_escape_text (aText, strlen (aText)); nsresult rv = Write (aStream, text); g_free (text); return rv; }