/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /* ***** 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 edsintegration. * * The Initial Developer of the Original Code is * Mozilla Corp. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Mike Conley * * 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 ***** */ var EXPORTED_SYMBOLS = [ "AuthHelper" ]; const Cu = Components.utils; const Cc = Components.classes; const Ci = Components.interfaces; Cu.import("resource://gre/modules/ctypes.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://edsintegration/libedataserver-ctypes.jsm"); Cu.import("resource://edsintegration/LibGLib.jsm"); Cu.import("resource://edsintegration/nsAbEDSCommon.jsm"); Cu.import("resource://edsintegration/LibEBookClient.jsm"); Cu.import("resource://edsintegration/LibESource.jsm"); Cu.import("resource://edsintegration/LibGCancellable.jsm"); Cu.import("resource://edsintegration/ReferenceService.jsm"); Cu.import("resource://edsintegration/LibGAsyncResult.jsm"); Cu.import("resource://edsintegration/LibEClient.jsm"); Cu.import("resource://edsintegration/LibECredentials.jsm"); var gAuthLookup = {}; var gAuthAnchors = {}; function openNewAsyncCb(aGObject, aAsyncResult, aData) { LOG("Entered openNewAsyncCb"); if (!aData || aData.isNull()) { ERROR("Could not open EClient - aData was null is openNewAsyncCb"); return; } try { var opData = AuthHelper.getOpData(aData); } catch(e) { LOG("Could not get opData (openNewAsyncCb) - this is fine if the" + " EBookClient does not require authentication."); return; } opData.openFinished = true; let client = ctypes.cast(aGObject, LibEClient.EClient.ptr); let errPtr = new LibGLib.GError.ptr(); if (!LibEClient.openFinish(client, aAsyncResult, errPtr.address() || LibGCancellable.setErrorIfCancelled(opData.cancellable, errPtr.address()))) { LOG("Calling finishOrRetryOpen from openNewAsyncCb"); AuthHelper.finishOrRetryOpen(opData, errPtr); LibGLib.g_error_free(errPtr); errPtr = null; return; } if (opData.openedCbError) { AuthHelper.finishOrRetryOpen(opData, opData.openedCbError); return; } if (LibEClient.isOpened(client)) { LOG("Huzzah! Apparently an EClient has been opened (uri: " + opData.asyncCbData + ")"); AuthHelper.openNewDone(opData); return; } opData.refs['openNewCancelledCbPtr'] = LibGLib .GCallback(AuthHelper .openNewCancelledCb); opData.refs.signal_connect(opData.cancellable, "cancelled", opData.refs['openNewCancelledCbPtr'], opData.uriPtr); return; } var openNewAsyncCbPtr = LibGAsyncResult.GAsyncReadyCallback(openNewAsyncCb); var AuthHelper = { ADDRESSBOOK: "addressbook", newClient: function AH_newClient(aESource, aClientType, aErrPtr) { if (aClientType == AuthHelper.ADDRESSBOOK) { return LibEBookClient.newFromSource(aESource, aErrPtr); } return null; }, openAndAuthESource: function AH_openAndAuthESource(aESource, aType, aOnlyIfExists, aCancellable, aAuthHandler, aAuthHandlerData, aAsyncCb, aAsyncCbData, aURI) { if (gAuthLookup[aURI]) { WARN("There's already an auth helper trying to open an EClient with" + " URI: " + aURI + " - cancelling this attempt."); return false; } if (!aESource || aESource.isNull()) { ERROR("Tried to openAndAuth an ESource that was null!"); return false; } let errPtr = new LibGLib.GError.ptr(); let client = AuthHelper.newClient(aESource, aType, errPtr.address()); if (!errPtr.isNull()) { ERROR("Could not create a client of type " + aType + " from ESource."); ERROR("Message was: " + errPtr.contents.message.readString()); return false; } let opData = new AsyncOpData(); LOG("Registering some opData at uri: " + aURI); gAuthLookup[aURI] = opData; let data = LibGLib.g_object_ref(aESource); opData.uriPtr = LibGLib.gchar.array()(aURI); opData.source = ctypes.cast(data, LibESource.ESource.ptr); opData.client = client; opData.openFinished = false; opData.retryOpenId = 0; opData.authHandler = aAuthHandler; opData.authHandlerData = aAuthHandlerData; opData.asyncCb = aAsyncCb; opData.asyncCbData = aAsyncCbData; if (aCancellable && !aCancellable.isNull()) opData.cancellable = aCancellable; else opData.cancellable = LibGCancellable.newGCancellable(); opData.onlyIfExists = aOnlyIfExists; opData.refs = ReferenceService.register("auth:" + aURI); // Register the authentication callback if one exists if (aAuthHandler) { opData.refs['openNewAuthCbPtr'] = LibGLib .GBooleanCallback(AuthHelper .openNewAuthCb); opData.refs.signal_connect(client, "authenticate", opData.refs['openNewAuthCbPtr'], opData.uriPtr); } opData.refs['openedCbPtr'] = LibGLib.GCallback(AuthHelper.openedCb); opData.refs.signal_connect(client, "opened", opData.refs['openedCbPtr'], opData.uriPtr); let eclient = ctypes.cast(client, LibEClient.EClient.ptr); LibEClient.open(eclient, aOnlyIfExists, aCancellable, openNewAsyncCbPtr, opData.uriPtr); }, openedCb: function AH_openedCb(aEClient, aGError, aData) { LOG("Entered openedCb"); if (!aEClient || aEClient.isNull()) { ERROR("In openedCb and aEClient is null"); return; } try { var opData = AuthHelper.getOpData(aData); } catch(e) { ERROR("Could not complete openedCb:", e); return; } if (!opData.openFinished) { if (!aGError.isNull()) { aGError = ctypes.cast(aGError, LibGLib.GError.ptr); opData.openedCbError = LibGLib.g_error_copy(aGError); } } else { LOG("Calling finishOrRetryOpen from openedCb"); AuthHelper.finishOrRetryOpen(opData, aGError); } LOG("OpenedCb exited"); return; }, openNewCancelledCb: function AH_openNewCancelledCb(arg1, arg2, arg3) { return; }, openNewAuthCb: function AH_openNewAuthCb(aEClient, aECredentials, aData) { LOG("Within openNewAuthCb"); if (!aEClient || aEClient.isNull()) { ERROR("The EClient passed to openNewAuthCb was null."); return LibGLib.FALSE; } if (!aECredentials || aECredentials.isNull()) { ERROR("The ECredentials passed to openNewAuthCb was null."); return LibGLib.FALSE; } try { var opData = AuthHelper.getOpData(aData); } catch(e) { ERROR("Could not retrieve opData (openNewAuthCb): " + e); return LibGLib.FALSE; } if (!opData.authHandler) { ERROR("There was no authHandler for EClient"); return LibGLib.FALSE; } aECredentials = ctypes.cast(aECredentials, LibECredentials.ECredentials.ptr); if (opData.usedCreds) { let reason = LibECredentials.peek(opData.usedCreds, LibECredentials.E_CREDENTIALS_KEY_PROMPT_REASON); if (reason) { LibECredentials.set(aECredentials, LibECredentials.E_CREDENTIALS_KEY_PROMPT_TEXT, null); LibECredentials.set(aECredentials, LibECredentials.E_CREDENTIALS_KEY_PROMPT_REASON, reason); } } aEClient = ctypes.cast(aEClient, LibEClient.EClient.ptr); let handled = opData.authHandler(aEClient, aECredentials, opData.authHandlerData); if (handled == LibGLib.TRUE) { if (opData.usedCreds) { LibECredentials.free(opData.usedCreds); opData.usedCreds = null; } opData.usedCreds = LibECredentials.newClone(aECredentials); } return handled; }, finishOrRetryOpen: function AH_finishOrRetryOpen(aOpData, aErrPtr) { LOG("Entered finishOrRetryOpen"); aErrPtr = ctypes.cast(aErrPtr, LibGLib.GError.ptr); if (aOpData.authHandler && !aErrPtr.isNull() && LibGLib.g_error_matches(aErrPtr, LibEClient.errorQuark(), LibEClient.getEnum("E_CLIENT_ERROR_AUTHENTICATION_FAILED"))) { if (aOpData.usedCreds && !aOpData.usedCreds.isNull()) { // TODO: Password remembering? Forgetting? LibECredentials.set(aOpData.usedCreds, LibECredentials.E_CREDENTIALS_KEY_PROMPT_FLAGS, LibECredentials.E_CREDENTIALS_USED); LibECredentials.set(aOpData.usedCreds, LibECredentials.E_CREDENTIALS_KEY_PROMPT_REASON, aErrPtr.contents.message.readString()); LOG("Old credentials have been used."); } let eclient = ctypes.cast(aOpData.client, LibEClient.EClient.ptr); LOG("Calling LibEClient.processAuth"); LibEClient.processAuth(eclient, aOpData.usedCreds); } else if (!aErrPtr.isNull()) { LOG("Calling asyncCb"); aOpData.asyncCb(aOpData.client, aErrPtr, aOpData.asyncCbData); AuthHelper.freeOpData(aOpData); } else { LOG("Calling openNewDone"); AuthHelper.openNewDone(aOpData); } }, openNewDone: function AH_openNewDone(aOpData) { if (!aOpData.asyncCb) { ERROR("Entered openNewDone, but opData.asyncCb was null!"); return; } // The callback should probably reconnect auth handler // directly. aOpData.asyncCb(aOpData.client, aOpData.openedCbError, aOpData.asyncCbData); AuthHelper.freeOpData(aOpData); }, getOpData: function AH_getOpData(aData) { if (!aData || aData.isNull()) throw("aData was null in getOpData"); let uri = ctypes.cast(aData, LibGLib.gchar.ptr).readString(); if (!gAuthLookup[uri]) throw("There was no opData in gAuthLookup for URI: " + uri); return gAuthLookup[uri]; }, freeOpData: function AH_freeOpData(aOpData) { if (!aOpData) { WARN("Could not free opData, since it was null."); return; } if (!aOpData.client || aOpData.client.isNull()) { WARN("Could not free opData, since it didn't have a client."); return; } if (!aOpData.cancellable || aOpData.cancellable.isNull()) { WARN("Could not free opData, since it didn't have a cancellable."); return; } // Free signal handlers; LOG("Freeing signal handlers"); aOpData.refs.dispose(); LOG("Signal handlers free'd for uri:" + aOpData.uriPtr.readString()); if (aOpData.usedCreds) { LibECredentials.free(aOpData.usedCreds); aOpData.usedCreds = null; } if (aOpData.openError) { LibGLib.g_error_free(aOpData.openError); aOpData.openError = null; } LibGLib.g_object_unref(aOpData.cancellable); LibGLib.g_object_unref(aOpData.client); LibGLib.g_object_unref(aOpData.source); let uri = aOpData.uriPtr.readString(); gAuthLookup[uri] = null; delete opData; } } var AsyncOpData = function() { this.uriPtr = null; this.authHandler = null; this.authHandlerData = null; this.client = null; this.source = null; this.creds = null; this.usedCreds = null; this.asyncCb = null; this.asyncCbData = null; this.cancellable = null; this.openFinished = false; this.openError = null; this.onlyIfExists = false; this.retryOpenId = null; this.openedCbError = null; }