/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sts=2 sw=2 et cin: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsIServiceManager.h" #include "nsIAuthPromptWrapper.h" #include "nsIAuthInformation.h" #include "nsPrompt.h" #include "nsReadableUtils.h" #include "nsDependentString.h" #include "nsIStringBundle.h" #include "nsIChannel.h" #include "nsIURI.h" #include "nsEmbedCID.h" #include "nsNetCID.h" #include "nsPIDOMWindow.h" #include "nsIPromptFactory.h" #include "nsIProxiedChannel.h" #include "nsIProxyInfo.h" #include "nsIIDNService.h" #include "nsNetUtil.h" #include "nsPromptUtils.h" #include "nsIPrefService.h" #include "nsIPrefLocalizedString.h" nsresult NS_NewPrompter(nsIPrompt **result, nsIDOMWindow *aParent) { nsresult rv; *result = 0; nsPrompt *prompter = new nsPrompt(aParent); if (!prompter) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(prompter); rv = prompter->Init(); if (NS_FAILED(rv)) { NS_RELEASE(prompter); return rv; } *result = prompter; return NS_OK; } nsresult NS_NewAuthPrompter(nsIAuthPrompt **result, nsIDOMWindow *aParent) { nsresult rv; nsCOMPtr factory = do_GetService(NS_PWMGR_AUTHPROMPTFACTORY); if (factory) { // We just delegate everything to the pw mgr if we can rv = factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt), reinterpret_cast(result)); // If the password manager doesn't support the interface, fall back to the // old way of doing things. This will allow older apps that haven't updated // to work still. if (rv != NS_NOINTERFACE) return rv; } *result = 0; nsPrompt *prompter = new nsPrompt(aParent); if (!prompter) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(prompter); rv = prompter->Init(); if (NS_FAILED(rv)) { NS_RELEASE(prompter); return rv; } *result = prompter; // wrap the base prompt in an nsIAuthPromptWrapper, if available // the impl used here persists prompt data and pre-fills the dialogs nsCOMPtr siPrompt = do_CreateInstance("@mozilla.org/wallet/single-sign-on-prompt;1"); if (siPrompt) { // then single sign-on is installed rv = siPrompt->SetPromptDialogs(prompter); if (NS_SUCCEEDED(rv)) { *result = siPrompt; NS_RELEASE(prompter); // siPrompt is a strong owner NS_ADDREF(*result); } } return NS_OK; } nsresult NS_NewAuthPrompter2(nsIAuthPrompt2 **result, nsIDOMWindow *aParent) { nsresult rv; nsCOMPtr factory = do_GetService(NS_PWMGR_AUTHPROMPTFACTORY); if (factory) { // We just delegate everything to the pw mgr. rv = factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt2), reinterpret_cast(result)); // Bug 403115. Don't suppress error if interface isn't supported. if (NS_SUCCEEDED(rv) || rv == NS_NOINTERFACE) return rv; } *result = 0; nsPrompt *prompter = new nsPrompt(aParent); if (!prompter) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(prompter); rv = prompter->Init(); if (NS_FAILED(rv)) { NS_RELEASE(prompter); return rv; } *result = prompter; return NS_OK; } NS_IMPL_THREADSAFE_ISUPPORTS3(nsPrompt, nsIPrompt, nsIAuthPrompt, nsIAuthPrompt2) nsPrompt::nsPrompt(nsIDOMWindow *aParent) : mParent(aParent) { #ifdef DEBUG { nsCOMPtr win(do_QueryInterface(aParent)); NS_ASSERTION(!win || win->IsOuterWindow(), "Inner window passed as nsPrompt parent!"); } #endif } nsresult nsPrompt::Init() { mPromptService = do_GetService(NS_PROMPTSERVICE_CONTRACTID); mPromptService2 = do_QueryInterface(mPromptService); // A null mPromptService2 is not fatal, we have to deal with that // (for compatibility with embeddors who only implement the old version) return mPromptService ? NS_OK : NS_ERROR_FAILURE; } //***************************************************************************** // nsPrompt::nsIPrompt //***************************************************************************** NS_IMETHODIMP nsPrompt::Alert(const PRUnichar* dialogTitle, const PRUnichar* text) { return mPromptService->Alert(mParent, dialogTitle, text); } NS_IMETHODIMP nsPrompt::AlertCheck(const PRUnichar* dialogTitle, const PRUnichar* text, const PRUnichar* checkMsg, PRBool *checkValue) { return mPromptService->AlertCheck(mParent, dialogTitle, text, checkMsg, checkValue); } NS_IMETHODIMP nsPrompt::Confirm(const PRUnichar* dialogTitle, const PRUnichar* text, PRBool *_retval) { return mPromptService->Confirm(mParent, dialogTitle, text, _retval); } NS_IMETHODIMP nsPrompt::ConfirmCheck(const PRUnichar* dialogTitle, const PRUnichar* text, const PRUnichar* checkMsg, PRBool *checkValue, PRBool *_retval) { return mPromptService->ConfirmCheck(mParent, dialogTitle, text, checkMsg, checkValue, _retval); } NS_IMETHODIMP nsPrompt::ConfirmEx(const PRUnichar *dialogTitle, const PRUnichar *text, PRUint32 buttonFlags, const PRUnichar *button0Title, const PRUnichar *button1Title, const PRUnichar *button2Title, const PRUnichar *checkMsg, PRBool *checkValue, PRInt32 *buttonPressed) { return mPromptService->ConfirmEx(mParent, dialogTitle, text, buttonFlags, button0Title, button1Title, button2Title, checkMsg, checkValue, buttonPressed); } NS_IMETHODIMP nsPrompt::Prompt(const PRUnichar *dialogTitle, const PRUnichar *text, PRUnichar **answer, const PRUnichar *checkMsg, PRBool *checkValue, PRBool *_retval) { return mPromptService->Prompt(mParent, dialogTitle, text, answer, checkMsg, checkValue, _retval); } NS_IMETHODIMP nsPrompt::PromptUsernameAndPassword(const PRUnichar *dialogTitle, const PRUnichar *text, PRUnichar **username, PRUnichar **password, const PRUnichar *checkMsg, PRBool *checkValue, PRBool *_retval) { return mPromptService->PromptUsernameAndPassword(mParent, dialogTitle, text, username, password, checkMsg, checkValue, _retval); } NS_IMETHODIMP nsPrompt::PromptPassword(const PRUnichar *dialogTitle, const PRUnichar *text, PRUnichar **password, const PRUnichar *checkMsg, PRBool *checkValue, PRBool *_retval) { return mPromptService->PromptPassword(mParent, dialogTitle, text, password, checkMsg, checkValue, _retval); } NS_IMETHODIMP nsPrompt::Select(const PRUnichar *dialogTitle, const PRUnichar* inMsg, PRUint32 inCount, const PRUnichar **inList, PRInt32 *outSelection, PRBool *_retval) { return mPromptService->Select(mParent, dialogTitle, inMsg, inCount, inList, outSelection, _retval); } //***************************************************************************** // nsPrompt::nsIAuthPrompt // This implementation serves as glue between nsIAuthPrompt and nsIPrompt // when a single-signon was not available. //***************************************************************************** NS_IMETHODIMP nsPrompt::Prompt(const PRUnichar* dialogTitle, const PRUnichar* text, const PRUnichar* passwordRealm, PRUint32 savePassword, const PRUnichar* defaultText, PRUnichar* *result, PRBool *_retval) { // Ignore passwordRealm and savePassword if (defaultText) { *result = ToNewUnicode(nsDependentString(defaultText)); if (!*result) { return NS_ERROR_OUT_OF_MEMORY; } } return mPromptService->Prompt(mParent, dialogTitle, text, result, nsnull, nsnull, _retval); } NS_IMETHODIMP nsPrompt::PromptUsernameAndPassword(const PRUnichar* dialogTitle, const PRUnichar* text, const PRUnichar* passwordRealm, PRUint32 savePassword, PRUnichar* *user, PRUnichar* *pwd, PRBool *_retval) { // Ignore passwordRealm and savePassword return mPromptService->PromptUsernameAndPassword(mParent, dialogTitle, text, user, pwd, nsnull, nsnull, _retval); } NS_IMETHODIMP nsPrompt::PromptPassword(const PRUnichar* dialogTitle, const PRUnichar* text, const PRUnichar* passwordRealm, PRUint32 savePassword, PRUnichar* *pwd, PRBool *_retval) { // Ignore passwordRealm and savePassword return mPromptService->PromptPassword(mParent, dialogTitle, text, pwd, nsnull, nsnull, _retval); } NS_IMETHODIMP nsPrompt::PromptAuth(nsIChannel* aChannel, PRUint32 aLevel, nsIAuthInformation* aAuthInfo, PRBool* retval) { if (mPromptService2) { return mPromptService2->PromptAuth(mParent, aChannel, aLevel, aAuthInfo, nsnull, nsnull, retval); } return PromptPasswordAdapter(mPromptService, mParent, aChannel, aLevel, aAuthInfo, nsnull, nsnull, retval); } NS_IMETHODIMP nsPrompt::AsyncPromptAuth(nsIChannel* aChannel, nsIAuthPromptCallback* aCallback, nsISupports* aContext, PRUint32 aLevel, nsIAuthInformation* aAuthInfo, nsICancelable** retval) { if (mPromptService2) { return mPromptService2->AsyncPromptAuth(mParent, aChannel, aCallback, aContext, aLevel, aAuthInfo, nsnull, nsnull, retval); } // Tell the caller to use the sync version return NS_ERROR_NOT_IMPLEMENTED; } static nsresult MakeDialogText(nsIChannel* aChannel, nsIAuthInformation* aAuthInfo, nsXPIDLString& message) { nsresult rv; nsCOMPtr bundleSvc = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr bundle; rv = bundleSvc->CreateBundle("chrome://global/locale/prompts.properties", getter_AddRefs(bundle)); NS_ENSURE_SUCCESS(rv, rv); // figure out what message to display... nsCAutoString host; PRInt32 port; NS_GetAuthHostPort(aChannel, aAuthInfo, PR_FALSE, host, &port); nsAutoString displayHost; CopyUTF8toUTF16(host, displayHost); nsCOMPtr uri; aChannel->GetURI(getter_AddRefs(uri)); nsCAutoString scheme; uri->GetScheme(scheme); nsAutoString username; aAuthInfo->GetUsername(username); PRUint32 flags; aAuthInfo->GetFlags(&flags); PRBool proxyAuth = (flags & nsIAuthInformation::AUTH_PROXY) != 0; nsAutoString realm; aAuthInfo->GetRealm(realm); // Trim obnoxiously long realms. if (realm.Length() > 150) { realm.Truncate(150); // Append "..." (or localized equivalent). Yay complexity. nsAutoString ellipsis; nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (prefs) { nsCOMPtr prefString; rv = prefs->GetComplexValue("intl.ellipsis", NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(prefString)); if (prefString) prefString->ToString(getter_Copies(ellipsis)); } if (ellipsis.IsEmpty()) ellipsis.AssignLiteral("..."); realm.Append(ellipsis); } // Append the port if it was specified if (port != -1) { displayHost.Append(PRUnichar(':')); displayHost.AppendInt(port); } NS_NAMED_LITERAL_STRING(proxyText, "EnterLoginForProxy"); NS_NAMED_LITERAL_STRING(originText, "EnterLoginForRealm"); NS_NAMED_LITERAL_STRING(noRealmText, "EnterUserPasswordFor"); NS_NAMED_LITERAL_STRING(passwordText, "EnterPasswordFor"); const PRUnichar *text; if (proxyAuth) { text = proxyText.get(); } else { text = originText.get(); // prepend "scheme://" nsAutoString schemeU; CopyASCIItoUTF16(scheme, schemeU); schemeU.AppendLiteral("://"); displayHost.Insert(schemeU, 0); } const PRUnichar *strings[] = { realm.get(), displayHost.get() }; PRUint32 count = NS_ARRAY_LENGTH(strings); if (flags & nsIAuthInformation::ONLY_PASSWORD) { text = passwordText.get(); strings[0] = username.get(); } else if (!proxyAuth && realm.IsEmpty()) { text = noRealmText.get(); count--; strings[0] = strings[1]; } rv = bundle->FormatStringFromName(text, strings, count, getter_Copies(message)); return rv; } /* static */ nsresult nsPrompt::PromptPasswordAdapter(nsIPromptService* aService, nsIDOMWindow* aParent, nsIChannel* aChannel, PRUint32 aLevel, nsIAuthInformation* aAuthInfo, const PRUnichar* aCheckLabel, PRBool* aCheckValue, PRBool* retval) { // construct the message string nsXPIDLString message; MakeDialogText(aChannel, aAuthInfo, message); nsAutoString defaultUser, defaultDomain, defaultPass; aAuthInfo->GetUsername(defaultUser); aAuthInfo->GetDomain(defaultDomain); aAuthInfo->GetPassword(defaultPass); PRUint32 flags; aAuthInfo->GetFlags(&flags); if ((flags & nsIAuthInformation::NEED_DOMAIN) && !defaultDomain.IsEmpty()) { defaultDomain.Append(PRUnichar('\\')); defaultUser.Insert(defaultDomain, 0); } // NOTE: Allocation failure is not fatal here (just default to empty string // if allocation fails) PRUnichar *user = ToNewUnicode(defaultUser), *pass = ToNewUnicode(defaultPass); nsresult rv; if (flags & nsIAuthInformation::ONLY_PASSWORD) rv = aService->PromptPassword(aParent, nsnull, message.get(), &pass, aCheckLabel, aCheckValue, retval); else rv = aService->PromptUsernameAndPassword(aParent, nsnull, message.get(), &user, &pass, aCheckLabel, aCheckValue, retval); nsAdoptingString userStr(user); nsAdoptingString passStr(pass); NS_SetAuthInfo(aAuthInfo, userStr, passStr); return rv; } NS_IMPL_THREADSAFE_ISUPPORTS1(AuthPromptWrapper, nsIAuthPrompt2) NS_IMETHODIMP AuthPromptWrapper::PromptAuth(nsIChannel* aChannel, PRUint32 aLevel, nsIAuthInformation* aAuthInfo, PRBool* retval) { nsCAutoString keyUTF8; NS_GetAuthKey(aChannel, aAuthInfo, keyUTF8); NS_ConvertUTF8toUTF16 key(keyUTF8); nsXPIDLString text; MakeDialogText(aChannel, aAuthInfo, text); PRUint32 flags; aAuthInfo->GetFlags(&flags); nsresult rv; nsXPIDLString user, password; if (flags & nsIAuthInformation::ONLY_PASSWORD) { rv = mAuthPrompt->PromptPassword(nsnull, text.get(), key.get(), nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, getter_Copies(password), retval); if (NS_SUCCEEDED(rv) && *retval) { NS_ASSERTION(password, "password must not be null if retval is true"); aAuthInfo->SetPassword(password); } } else { rv = mAuthPrompt->PromptUsernameAndPassword(nsnull, text.get(), key.get(), nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, getter_Copies(user), getter_Copies(password), retval); if (NS_SUCCEEDED(rv) && *retval) { NS_ASSERTION(user && password, "out params must be nonnull"); NS_SetAuthInfo(aAuthInfo, user, password); } } return rv; } NS_IMETHODIMP AuthPromptWrapper::AsyncPromptAuth(nsIChannel*, nsIAuthPromptCallback*, nsISupports*, PRUint32, nsIAuthInformation*, nsICancelable**) { // There is no way to implement this here. Just tell the caller // to fall back to the synchronous version. return NS_ERROR_NOT_IMPLEMENTED; }