home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / sunbird / js / calWcapSession.js < prev    next >
Encoding:
JavaScript  |  2007-05-23  |  49.4 KB  |  1,243 lines

  1. /* -*- Mode: javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public
  6.  * License Version 1.1 (the "License"); you may not use this file
  7.  * except in compliance with the License. You may obtain a copy of
  8.  * the License at http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS
  11.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12.  * implied. See the License for the specific language governing
  13.  * rights and limitations under the License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is Sun Microsystems, Inc.
  18.  * Portions created by Sun Microsystems are Copyright (C) 2006 Sun
  19.  * Microsystems, Inc. All Rights Reserved.
  20.  *
  21.  * Original Author: Daniel Boelzle (daniel.boelzle@sun.com)
  22.  *
  23.  * Contributor(s):
  24.  *
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the NPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the NPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. function calWcapSession() {
  41.     this.wrappedJSObject = this;
  42.     this.m_observers = [];
  43.     this.m_loginQueue = [];
  44.     this.m_subscribedCals = {};
  45. }
  46. calWcapSession.prototype = {
  47.     m_ifaces: [ calIWcapSession,
  48.                 Components.interfaces.calICalendarManagerObserver,
  49.                 Components.interfaces.nsIInterfaceRequestor,
  50.                 Components.interfaces.nsIClassInfo,
  51.                 Components.interfaces.nsISupports ],
  52.     
  53.     // nsISupports:
  54.     QueryInterface: function calWcapSession_QueryInterface(iid) {
  55.         qiface(this.m_ifaces, iid);
  56.         return this;
  57.     },
  58.     
  59.     // nsIClassInfo:
  60.     getInterfaces: function calWcapSession_getInterfaces(count)
  61.     {
  62.         count.value = this.m_ifaces.length;
  63.         return this.m_ifaces;
  64.     },
  65.     get classDescription() {
  66.         return calWcapCalendarModule.WcapSessionInfo.classDescription;
  67.     },
  68.     get contractID() {
  69.         return calWcapCalendarModule.WcapSessionInfo.contractID;
  70.     },
  71.     get classID() {
  72.         return calWcapCalendarModule.WcapSessionInfo.classID;
  73.     },
  74.     getHelperForLanguage:
  75.     function calWcapSession_getHelperForLanguage(language) { return null; },
  76.     implementationLanguage:
  77.     Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
  78.     flags: 0,
  79.     
  80.     // nsIInterfaceRequestor:
  81.     getInterface: function calWcapSession_getInterface(iid, instance) {
  82.         if (iid.equals(Components.interfaces.nsIAuthPrompt)) {
  83.             // use the window watcher service to get a nsIAuthPrompt impl
  84.             return getWindowWatcher().getNewAuthPrompter(null);
  85.         }
  86.         else if (iid.equals(Components.interfaces.nsIPrompt)) {
  87.             // use the window watcher service to get a nsIPrompt impl
  88.             return getWindowWatcher().getNewPrompter(null);
  89.         }
  90.         Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  91.         return null;
  92.     },
  93.     
  94.     toString: function calWcapSession_toString(msg)
  95.     {
  96.         var str = (this.uri ? this.uri.spec : "no uri");
  97.         if (this.credentials.userId) {
  98.             str += (", userId=" + this.credentials.userId);
  99.         }
  100.         if (!this.m_sessionId) {
  101.             str += (getIoService().offline ? ", offline" : ", not logged in");
  102.         }
  103.         return str;
  104.     },
  105.     notifyError: function calWcapSession_notifyError(err, suppressOnError)
  106.     {
  107.         debugger;
  108.         var msg = logError(err, this);
  109.         if (!suppressOnError) {
  110.             this.notifyObservers(
  111.                 "onError",
  112.                 err instanceof Components.interfaces.nsIException
  113.                 ? [err.result, err.message] : [isNaN(err) ? -1 : err, msg]);
  114.         }
  115.     },
  116.     
  117.     m_lastOnErrorTime: 0,
  118.     m_lastOnErrorNo: 0,
  119.     m_lastOnErrorMsg: null,
  120.     
  121.     m_observers: null,
  122.     notifyObservers: function calWcapSession_notifyObservers(func, args)
  123.     {
  124.         if (g_bShutdown)
  125.             return;
  126.         
  127.         // xxx todo: hack
  128.         // suppress identical error bursts when multiple similar calls eg on getItems() fail.
  129.         if (func == "onError") {
  130.             var now = (new Date()).getTime();
  131.             if ((now - this.m_lastOnError) < 2000 &&
  132.                 (args[0] == this.m_lastOnErrorNo) &&
  133.                 (args[1] == this.m_lastOnErrorMsg)) {
  134.                 log("suppressing calIObserver::onError.", this);
  135.                 return;
  136.             }
  137.             this.m_lastOnError = now;
  138.             this.m_lastOnErrorNo = args[0];
  139.             this.m_lastOnErrorMsg = args[1];
  140.         }
  141.         
  142.         this.m_observers.forEach(
  143.             function notifyFunc(entry) {
  144.                 var obj = entry.obj;
  145.                 try {
  146.                     obj[func].apply(obj, args);
  147.                 }
  148.                 catch (exc) {
  149.                     // don't call notifyError() here:
  150.                     Components.utils.reportError(exc);
  151.                 }
  152.             });
  153.     },
  154.     
  155.     addObserver: function calWcapSession_addObserver(observer)
  156.     {
  157.         for each (var entry in this.m_observers) {
  158.             if (entry.obj == observer) {
  159.                 ++entry.count;
  160.                 return;
  161.             }
  162.         }
  163.         this.m_observers.push( { obj: observer, count: 1 } );
  164.     },
  165.     
  166.     removeObserver: function calWcapSession_removeObserver(observer)
  167.     {
  168.         function filterFunc(entry) {
  169.             if (entry.obj == observer) {
  170.                 --entry.count;
  171.                 if (entry.count == 0)
  172.                     return false;
  173.             }
  174.             return true;
  175.         }
  176.         this.m_observers = this.m_observers.filter(filterFunc);
  177.     },
  178.     
  179.     m_serverTimezones: null,
  180.     isSupportedTimezone: function calWcapSession_isSupportedTimezone(tzid)
  181.     {
  182.         if (!this.m_serverTimezones) {
  183.             throw new Components.Exception(
  184.                 "early run into getSupportedTimezones()!",
  185.                 Components.results.NS_ERROR_NOT_AVAILABLE);
  186.         }
  187.         return this.m_serverTimezones.some(
  188.             function someFunc(id) { return tzid == id; } );
  189.     },
  190.     
  191.     m_serverTimeDiff: null,
  192.     getServerTime: function calWcapSession_getServerTime(localTime)
  193.     {
  194.         if (this.m_serverTimeDiff === null) {
  195.             throw new Components.Exception(
  196.                 "early run into getServerTime()!",
  197.                 Components.results.NS_ERROR_NOT_AVAILABLE);
  198.         }
  199.         var ret = (localTime ? localTime.clone() : getTime());
  200.         ret.addDuration(this.m_serverTimeDiff);
  201.         return ret;
  202.     },
  203.     
  204.     m_sessionId: null,
  205.     m_loginQueue: null,
  206.     m_loginLock: false,
  207.     m_subscribedCals: null,
  208.     
  209.     getSessionId:
  210.     function calWcapSession_getSessionId(request, respFunc, timedOutSessionId)
  211.     {
  212.         if (getIoService().offline) {
  213.             log("in offline mode.", this);
  214.             respFunc(new Components.Exception(
  215.                          "The requested action could not be completed while the " +
  216.                          "networking library is in the offline state.",
  217.                          NS_ERROR_OFFLINE));
  218.             return;
  219.         }
  220.         
  221.         log("login queue lock: " + this.m_loginLock +
  222.             ", length: " + this.m_loginQueue.length, this);
  223.         
  224.         if (this.m_loginLock) {
  225.             var entry = {
  226.                 request: request,
  227.                 respFunc: respFunc
  228.             };
  229.             this.m_loginQueue.push(entry);
  230.             log("login queue: " + this.m_loginQueue.length);
  231.         }
  232.         else {
  233.             if (this.m_sessionId && this.m_sessionId != timedOutSessionId) {
  234.                 respFunc(null, this.m_sessionId);
  235.                 return;
  236.             }
  237.             
  238.             this.m_loginLock = true;
  239.             log("locked login queue.", this);
  240.             this.m_sessionId = null; // invalidate for relogin
  241.             this.assureInstalledLogoutObservers();
  242.             
  243.             if (timedOutSessionId)
  244.                 log("reconnecting due to session timeout...", this);
  245.             
  246.             var this_ = this;
  247.             this.getSessionId_(
  248.                 request,
  249.                 function getSessionId_resp(err, sessionId) {
  250.                     log("getSessionId_resp(): " + sessionId, this_);
  251.                     if (err) {
  252.                         this_.notifyError(err, request.suppressOnError);
  253.                     }
  254.                     else {
  255.                         this_.m_sessionId = sessionId;
  256.                     }
  257.                     
  258.                     var queue = this_.m_loginQueue;
  259.                     this_.m_loginLock = false;
  260.                     this_.m_loginQueue = [];
  261.                     log("unlocked login queue.", this_);
  262.                     
  263.                     if (request.isPending) {
  264.                         // answere first request:
  265.                         respFunc(err, sessionId);
  266.                     }
  267.                     // and any remaining:
  268.                     while (queue.length > 0) {
  269.                         var entry = queue.shift();
  270.                         if (entry.request.isPending)
  271.                             entry.respFunc(err, sessionId);
  272.                     }
  273.                 });
  274.         }
  275.     },
  276.     
  277.     getSessionId_: function calWcapSession_getSessionId_(request, respFunc)
  278.     {
  279.         var this_ = this;
  280.         this.getLoginText(
  281.             request,
  282.             // probe whether server is accessible and responds login text:
  283.             function getLoginText_resp(err, loginText) {
  284.                 if (err) {
  285.                     respFunc(err);
  286.                     return;
  287.                 }
  288.                 // lookup password manager, then try login or prompt/login:
  289.                 log("attempting to get a session id for " + this_.sessionUri.spec, this_);
  290.                 
  291.                 if (!this_.sessionUri.schemeIs("https") &&
  292.                     !confirmInsecureLogin(this_.sessionUri)) {
  293.                     log("user rejected insecure login on " + this_.sessionUri.spec, this_);
  294.                     respFunc(new Components.Exception(
  295.                                  "Login failed. Invalid session ID.",
  296.                                  calIWcapErrors.WCAP_LOGIN_FAILED));
  297.                     return;
  298.                 }
  299.                 
  300.                 var outUser = { value: this_.credentials.userId };
  301.                 var outPW = { value: this_.credentials.pw };
  302.                 var outSavePW = { value: false };
  303.                 
  304.                 // pw mgr host names must not have a trailing slash
  305.                 var passwordManager =
  306.                     Components.classes["@mozilla.org/passwordmanager;1"]
  307.                               .getService(Components.interfaces.nsIPasswordManager);
  308.                 var pwHost = this_.uri.spec;
  309.                 if (pwHost[pwHost.length - 1] == '/')
  310.                     pwHost = pwHost.substr(0, pwHost.length - 1);
  311.                 
  312.                 if (!outPW.value) { // lookup pw manager
  313.                     log("looking in pw db for: " + pwHost, this_);
  314.                     try {
  315.                         var enumerator = passwordManager.enumerator;
  316.                         while (enumerator.hasMoreElements()) {
  317.                             var pwEntry = enumerator.getNext().QueryInterface(
  318.                                 Components.interfaces.nsIPassword);
  319.                             if (LOG_LEVEL > 1) {
  320.                                 log("pw entry:\n\thost=" + pwEntry.host +
  321.                                     "\n\tuser=" + pwEntry.user, this_);
  322.                             }
  323.                             if (pwEntry.host == pwHost) {
  324.                                 // found an entry matching URI:
  325.                                 outUser.value = pwEntry.user;
  326.                                 outPW.value = pwEntry.password;
  327.                                 log("password entry found for host " + pwHost +
  328.                                     "\nuser is " + outUser.value, this_);
  329.                                 break;
  330.                             }
  331.                         }
  332.                     }
  333.                     catch (exc) { // just log error
  334.                         logError("[password manager lookup] " + errorToString(exc), this_);
  335.                     }
  336.                 }
  337.                 
  338.                 function promptAndLoginLoop_resp(err, sessionId) {
  339.                     if (getResultCode(err) == calIWcapErrors.WCAP_LOGIN_FAILED) {
  340.                         log("prompting for user/pw...", this_);
  341.                         var prompt = getWindowWatcher().getNewPrompter(null);
  342.                         if (prompt.promptUsernameAndPassword(
  343.                                 getWcapBundle().GetStringFromName("loginDialog.label"),
  344.                                 loginText, outUser, outPW,
  345.                                 getPref("signon.rememberSignons", true)
  346.                                 ? getWcapBundle().GetStringFromName("loginDialog.check.text")
  347.                                 : null, outSavePW)) {
  348.                             this_.login(request, promptAndLoginLoop_resp,
  349.                                         outUser.value, outPW.value);
  350.                         }
  351.                         else {
  352.                             log("login prompt cancelled.", this_);
  353.                             respFunc(new Components.Exception(
  354.                                          "Login failed. Invalid session ID.",
  355.                                          calIWcapErrors.WCAP_LOGIN_FAILED));
  356.                         }
  357.                     }
  358.                     else if (err)
  359.                         respFunc(err);
  360.                     else {
  361.                         if (outSavePW.value) {
  362.                             // so try to remove old pw from db first:
  363.                             try {
  364.                                 passwordManager.removeUser(pwHost, outUser.value);
  365.                                 log("removed from pw db: " + pwHost, this_);
  366.                             }
  367.                             catch (exc) {
  368.                             }
  369.                             try { // to save pw under session uri:
  370.                                 passwordManager.addUser(pwHost, outUser.value, outPW.value);
  371.                                 log("added to pw db: " + pwHost, this_);
  372.                             }
  373.                             catch (exc) {
  374.                                 logError("[adding pw to db] " + errorToString(exc), this_);
  375.                             }
  376.                         }
  377.                         this_.credentials.userId = outUser.value;
  378.                         this_.credentials.pw = outPW.value;
  379.                         this_.setupSession(sessionId,
  380.                                            request,
  381.                                            function setupSession_resp(err) {
  382.                                                respFunc(err, sessionId);
  383.                                            });
  384.                     }
  385.                 }
  386.                     
  387.                 if (outPW.value) {
  388.                     this_.login(request, promptAndLoginLoop_resp,
  389.                                 outUser.value, outPW.value);
  390.                 }
  391.                 else {
  392.                     promptAndLoginLoop_resp(calIWcapErrors.WCAP_LOGIN_FAILED);
  393.                 }
  394.             });
  395.     },
  396.     
  397.     login: function calWcapSession_login(request, respFunc, user, pw)
  398.     {
  399.         var this_ = this;
  400.         issueNetworkRequest(
  401.             request,
  402.             function netResp(err, str) {
  403.                 var sessionId;
  404.                 try {
  405.                     if (err)
  406.                         throw err;
  407.                     // currently, xml parsing at an early stage during
  408.                     // process startup does not work reliably, so use
  409.                     // libical parsing for now:
  410.                     var icalRootComp = stringToIcal(str);
  411.                     var prop = icalRootComp.getFirstProperty("X-NSCP-WCAP-SESSION-ID");
  412.                     if (!prop) {
  413.                         throw new Components.Exception(
  414.                             "missing X-NSCP-WCAP-SESSION-ID in\n" + str);
  415.                     }
  416.                     sessionId = prop.value;       
  417.                     log("login succeeded: " + sessionId, this_);
  418.                 }
  419.                 catch (exc) {
  420.                     err = exc;
  421.                     var rc = getResultCode(exc);
  422.                     if (rc == calIWcapErrors.WCAP_LOGIN_FAILED) {
  423.                         logError(exc, this_); // log login failure
  424.                     }
  425.                     else if (getErrorModule(rc) == NS_ERROR_MODULE_NETWORK) {
  426.                         // server seems unavailable:
  427.                         err = new Components.Exception(
  428.                             getWcapBundle().formatStringFromName(
  429.                                 "accessingServerFailedError.text",
  430.                                 [this_.sessionUri.hostPort], 1),
  431.                             exc);
  432.                     }
  433.                 }
  434.                 respFunc(err, sessionId);
  435.             },
  436.             this_.sessionUri.spec + "login.wcap?fmt-out=text%2Fcalendar&user=" +
  437.             encodeURIComponent(user) + "&password=" + encodeURIComponent(pw),
  438.             false /* no logging */);
  439.     },
  440.     
  441.     logout: function calWcapSession_logout(listener)
  442.     {
  443.         this.unregisterSubscribedCals();
  444.         
  445.         var this_ = this;
  446.         var request = new calWcapRequest(
  447.             function logout_resp(request, err) {
  448.                 if (err)
  449.                     logError(err, this_);
  450.                 else
  451.                     log("logout succeeded.", this_);
  452.                 if (listener)
  453.                     listener.onRequestResult(request, err);
  454.             },
  455.             log("logout", this));
  456.         
  457.         var url = null;
  458.         if (this.m_sessionId) {
  459.             log("attempting to log out...", this);
  460.             // although io service's offline flag is already
  461.             // set BEFORE notification
  462.             // (about to go offline, nsIOService.cpp).
  463.             // WTF.
  464.             url = (this.sessionUri.spec + "logout.wcap?fmt-out=text%2Fxml&id=" +
  465.                    encodeURIComponent(this.m_sessionId));
  466.             this.m_sessionId = null;
  467.         }
  468.         this.m_credentials = null;
  469.         
  470.         if (url) {
  471.             issueNetworkRequest(
  472.                 request,
  473.                 function netResp(err, str) {
  474.                     if (err)
  475.                         throw err;
  476.                     stringToXml(str, -1 /* logout successfull */);
  477.                 }, url);
  478.         }
  479.         else {
  480.             request.execRespFunc();
  481.         }
  482.         return request;
  483.     },
  484.     
  485.     getLoginText: function calWcapSession_getLoginText(request, respFunc)
  486.     {
  487.         // currently, xml parsing at an early stage during process startup
  488.         // does not work reliably, so use libical:
  489.         var this_ = this;
  490.         issueNetworkRequest(
  491.             request,
  492.             function netResp(err, str) {
  493.                 var loginText;
  494.                 try {
  495.                     var icalRootComp;
  496.                     if (!err) {
  497.                         try {
  498.                             icalRootComp = stringToIcal(str);
  499.                         }
  500.                         catch (exc) {
  501.                             err = exc;
  502.                         }
  503.                     }
  504.                     if (err) { // soft error; request denied etc.
  505.                                // map into localized message:
  506.                         throw new Components.Exception(
  507.                             getWcapBundle().formatStringFromName(
  508.                                 "accessingServerFailedError.text",
  509.                                 [this_.sessionUri.hostPort], 1),
  510.                             calIWcapErrors.WCAP_LOGIN_FAILED);
  511.                     }
  512.                     var prop = icalRootComp.getFirstProperty("X-NSCP-WCAPVERSION");
  513.                     if (!prop)
  514.                         throw new Components.Exception("missing X-NSCP-WCAPVERSION!");
  515.                     var wcapVersion = parseInt(prop.value);
  516.                     if (wcapVersion < 3) {
  517.                         var strVers = prop.value;
  518.                         var vars = [this_.sessionUri.hostPort];
  519.                         prop = icalRootComp.getFirstProperty("PRODID");
  520.                         vars.push(prop ? prop.value : "<unknown>");
  521.                         prop = icalRootComp.getFirstProperty("X-NSCP-SERVERVERSION");
  522.                         vars.push(prop ? prop.value : "<unknown>");
  523.                         vars.push(strVers);
  524.                         
  525.                         var prompt = getWindowWatcher().getNewPrompter(null);
  526.                         var bundle = getWcapBundle();
  527.                         var labelText = bundle.GetStringFromName(
  528.                             "insufficientWcapVersionConfirmation.label");
  529.                         if (!prompt.confirm(
  530.                                 labelText,
  531.                                 bundle.formatStringFromName(
  532.                                     "insufficientWcapVersionConfirmation.text",
  533.                                     vars, vars.length))) {
  534.                             throw new Components.Exception(
  535.                                 labelText, calIWcapErrors.WCAP_LOGIN_FAILED);
  536.                         }
  537.                     }
  538.                     loginText = getWcapBundle().formatStringFromName(
  539.                         "loginDialog.text", [this_.sessionUri.hostPort], 1);
  540.                 }
  541.                 catch (exc) {
  542.                     err = exc;
  543.                 }
  544.                 respFunc(err, loginText);
  545.             },
  546.             this_.sessionUri.spec + "version.wcap?fmt-out=text%2Fcalendar");
  547.     },
  548.     
  549.     setupSession:
  550.     function calWcapSession_setupSession(sessionId, request_, respFunc)
  551.     {
  552.         var this_ = this;
  553.         var request = new calWcapRequest(
  554.             function setupSession_resp(request_, err) {
  555.                 log("setupSession_resp finished: " + errorToString(err), this_);
  556.                 respFunc(err);
  557.             },
  558.             log("setupSession", this));
  559.         request_.attachSubRequest(request);
  560.         
  561.         request.lockPending();
  562.         try {
  563.             var this_ = this;
  564.             this.issueNetworkRequest_(
  565.                 request,
  566.                 function userprefs_resp(err, data) {
  567.                     if (err)
  568.                         throw err;
  569.                     this_.credentials.userPrefs = data;
  570.                     log("installed user prefs.", this_);
  571.                     
  572.                     this_.issueNetworkRequest_(
  573.                         request,
  574.                         function calprops_resp(err, data) {
  575.                             if (err)
  576.                                 throw err;
  577.                             // string to xml converter func without WCAP errno check:
  578.                             if (!data || data.length == 0) { // assuming time-out
  579.                                 throw new Components.Exception(
  580.                                     "Login failed. Invalid session ID.",
  581.                                     calIWcapErrors.WCAP_LOGIN_FAILED);
  582.                             }
  583.                             var xml = getDomParser().parseFromString(data, "text/xml");
  584.                             var nodeList = xml.getElementsByTagName("iCal");
  585.                             for (var i = 0; i < nodeList.length; ++i) {
  586.                                 var node = nodeList.item(i);
  587.                                 var ar = filterXmlNodes("X-NSCP-CALPROPS-RELATIVE-CALID", node);
  588.                                 if ((ar.length > 0) && (ar[0] == this_.defaultCalId)) {
  589.                                     checkWcapXmlErrno(node);
  590.                                     this_.defaultCalendar.m_calProps = node;
  591.                                     log("installed default cal props.", this_);
  592.                                     break;
  593.                                 }
  594.                             }
  595.                             if (!this_.defaultCalendar.m_calProps) {
  596.                                 throw new Components.Exception(
  597.                                     "Login failed. Invalid session ID.",
  598.                                     calIWcapErrors.WCAP_LOGIN_FAILED);
  599.                             }
  600.                         },
  601.                         null, "search_calprops",
  602.                         "&fmt-out=text%2Fxml&searchOpts=3&calid=1&search-string=" +
  603.                         encodeURIComponent(this_.defaultCalId),
  604.                         sessionId);
  605.                     if (getPref("calendar.wcap.subscriptions", false))
  606.                         this_.installSubscribedCals(sessionId, request);
  607.                 },
  608.                 stringToXml, "get_userprefs",
  609.                 "&fmt-out=text%2Fxml&userid=" + encodeURIComponent(this.credentials.userId),
  610.                 sessionId);
  611.             this.installServerTimeDiff(sessionId, request);
  612.             this.installServerTimezones(sessionId, request);
  613.         }
  614.         finally {
  615.             request.unlockPending();
  616.         }
  617.     },
  618.     
  619.     installServerTimeDiff:
  620.     function calWcapSession_installServerTimeDiff(sessionId, request)
  621.     {
  622.         var this_ = this;
  623.         this.issueNetworkRequest_(
  624.             request,
  625.             function netResp(err, data) {
  626.                 if (err)
  627.                     throw err;
  628.                 // xxx todo: think about
  629.                 // assure that locally calculated server time is smaller
  630.                 // than the current (real) server time:
  631.                 var localTime = getTime();
  632.                 var serverTime = getDatetimeFromIcalProp(
  633.                     data.getFirstProperty("X-NSCP-WCAPTIME"));
  634.                 this_.m_serverTimeDiff = serverTime.subtractDate(localTime);
  635.                 log("server time diff is: " + this_.m_serverTimeDiff, this_);
  636.             },
  637.             stringToIcal, "gettime", "&fmt-out=text%2Fcalendar",
  638.             sessionId);
  639.     },
  640.     
  641.     installServerTimezones:
  642.     function calWcapSession_installServerTimezones(sessionId, request)
  643.     {
  644.         this.m_serverTimezones = [];
  645.         var this_ = this;
  646.         this_.issueNetworkRequest_(
  647.             request,
  648.             function netResp(err, data) {
  649.                 if (err)
  650.                     throw err;
  651.                 var tzids = [];
  652.                 var icsService = getIcsService();
  653.                 forEachIcalComponent(
  654.                     data, "VTIMEZONE",
  655.                     function eachComp(subComp) {
  656.                         try {
  657.                             var tzCal = icsService.createIcalComponent("VCALENDAR");
  658.                             subComp = subComp.clone();
  659.                             tzCal.addSubcomponent(subComp);
  660.                             icsService.addTimezone(tzCal, "", "");
  661.                             this_.m_serverTimezones.push(
  662.                                 subComp.getFirstProperty("TZID").value);
  663.                         }
  664.                         catch (exc) { // ignore but errors:
  665.                             logError(exc, this_);
  666.                         }
  667.                     });
  668.                 log("installed timezones.", this_);
  669.             },
  670.             stringToIcal, "get_all_timezones", "&fmt-out=text%2Fcalendar",
  671.             sessionId);
  672.     },
  673.     
  674.     installSubscribedCals:
  675.     function calWcapSession_installSubscribedCals(sessionId, request)
  676.     {
  677.         var this_ = this;
  678.         // user may have dangling users referred in his subscription list, so
  679.         // retrieve each by each, don't break:
  680.         var list = this.getUserPreferences("X-NSCP-WCAP-PREF-icsSubscribed");
  681.         var calIds = {};
  682.         for each (var item in list) {
  683.             var ar = item.split(',');
  684.             // ',', '$' are not encoded. ',' can be handled here. WTF.
  685.             for each (var a in ar) {
  686.                 var dollar = a.indexOf('$');
  687.                 if (dollar >= 0) {
  688.                     var calId = a.substring(0, dollar);
  689.                     if (calId != this.defaultCalId)
  690.                         calIds[calId] = true;
  691.                 }
  692.             }
  693.         }
  694.         var issuedSearchRequests = {};
  695.         for (var calId in calIds) {
  696.             if (!this_.m_subscribedCals[calId]) {
  697.                 var listener = {
  698.                     onRequestResult: function search_onRequestResult(request, result) {
  699.                         try {
  700.                             if (!request.succeeded)
  701.                                 throw request.status;
  702.                             if (result.length < 1)
  703.                                 throw Components.results.NS_ERROR_UNEXPECTED;
  704.                             for each (var cal in result) {
  705.                                 try {
  706.                                     var calId = cal.calId;
  707.                                     if (calIds[calId] && !this_.m_subscribedCals[calId]) {
  708.                                         this_.m_subscribedCals[calId] = cal;
  709.                                         getCalendarManager().registerCalendar(cal);
  710.                                         log("installed subscribed calendar: " + calId, this_);
  711.                                     }
  712.                                 }
  713.                                 catch (exc) { // ignore but log any errors on subscribed calendars:
  714.                                     logError(exc, this_);
  715.                                 }
  716.                             }
  717.                         }
  718.                         catch (exc) { // ignore but log any errors on subscribed calendars:
  719.                             logError(exc, this_);
  720.                         }
  721.                     }
  722.                 };
  723.                 
  724.                 var colon = calId.indexOf(':');
  725.                 if (colon >= 0) // searching for secondary calendars doesn't work. WTF.
  726.                     calId = calId.substring(0, colon);
  727.                 if (!issuedSearchRequests[calId]) {
  728.                     issuedSearchRequests[calId] = true;
  729.                     this.searchForCalendars(
  730.                         calId,
  731.                         calIWcapSession.SEARCH_STRING_EXACT |
  732.                         calIWcapSession.SEARCH_INCLUDE_CALID |
  733.                         // else searching for secondary calendars doesn't work:
  734.                         calIWcapSession.SEARCH_INCLUDE_OWNER,
  735.                         20, listener);
  736.                 }
  737.             }
  738.         }
  739.     },
  740.     
  741.     unregisterSubscribedCals:
  742.     function calWcapSession_unregisterSubscribedCals() {
  743.         // unregister all subscribed cals, don't modify subscriptions upon unregisterCalendar:
  744.         var subscribedCals = this.m_subscribedCals;
  745.         this.m_subscribedCals = {};
  746.         // tag al to limit network traffic, listener chain will cause getItems calls:
  747.         for each (var cal in subscribedCals) {
  748.             cal.aboutToBeUnregistered = true;
  749.         }
  750.         if (!g_bShutdown) {
  751.             for each (var cal in subscribedCals) {
  752.                 try {
  753.                     getCalendarManager().unregisterCalendar(cal);
  754.                 }
  755.                 catch (exc) {
  756.                     this.notifyError(exc);
  757.                 }
  758.             }
  759.         }
  760.     },
  761.     
  762.     getCommandUrl: function calWcapSession_getCommandUrl(wcapCommand, params, sessionId)
  763.     {
  764.         var url = this.sessionUri.spec;
  765.         url += (wcapCommand + ".wcap?appid=mozilla-calendar");
  766.         url += params;
  767.         url += ("&id=" + encodeURIComponent(sessionId));
  768.         return url;
  769.     },
  770.  
  771.     issueNetworkRequest: function calWcapSession_issueNetworkRequest(
  772.         request, respFunc, dataConvFunc, wcapCommand, params)
  773.     {
  774.         var this_ = this;
  775.         function getSessionId_resp(err, sessionId) {
  776.             if (err)
  777.                 respFunc(err);
  778.             else {
  779.                 // else have session uri and id:
  780.                 this_.issueNetworkRequest_(
  781.                     request,
  782.                     function issueNetworkRequest_resp(err, data) {
  783.                         // timeout?
  784.                         if (getResultCode(err) == calIWcapErrors.WCAP_LOGIN_FAILED) {
  785.                             // try again:
  786.                             this_.getSessionId(
  787.                                 request,
  788.                                 getSessionId_resp,
  789.                                 sessionId/* (old) timed-out session */);
  790.                             return;
  791.                         }
  792.                         respFunc(err, data);
  793.                     },
  794.                     dataConvFunc, wcapCommand, params, sessionId);
  795.             }
  796.         }
  797.         this.getSessionId(request, getSessionId_resp);
  798.     },
  799.     
  800.     issueNetworkRequest_: function calWcapSession_issueNetworkRequest_(
  801.         request, respFunc, dataConvFunc, wcapCommand, params, sessionId)
  802.     {
  803.         var url = this.getCommandUrl(wcapCommand, params, sessionId);
  804.         issueNetworkRequest(
  805.             request,
  806.             function netResp(err, str) {
  807.                 var data;
  808.                 if (!err) {
  809.                     try {
  810.                         if (dataConvFunc)
  811.                             data = dataConvFunc(str);
  812.                         else
  813.                             data = str;
  814.                     }
  815.                     catch (exc) {
  816.                         err = exc;
  817.                     }
  818.                 }
  819.                 respFunc(err, data);
  820.             }, url);
  821.     },
  822.     
  823.     m_credentials: null,
  824.     get credentials() {
  825.         if (!this.m_credentials)
  826.             this.m_credentials = {
  827.                 userId: "",
  828.                 pw: "",
  829.                 userPrefs: null
  830.             };
  831.         return this.m_credentials;
  832.     },
  833.     
  834.     // calIWcapSession:
  835.     
  836.     m_uri: null,
  837.     m_sessionUri: null,
  838.     get uri() { return this.m_uri; },
  839.     get sessionUri() { return this.m_sessionUri; },
  840.     set uri(thatUri) {
  841.         if (!this.m_uri || !thatUri || !this.m_uri.equals(thatUri)) {
  842.             this.logout(null);
  843.             this.m_uri = null;
  844.             this.m_sessionUri = null;
  845.             if (thatUri) {
  846.                 this.m_uri = thatUri.clone();
  847.                 this.m_sessionUri = thatUri.clone();
  848.                 this.m_sessionUri.userPass = "";
  849.                 // sensible default for user id login:
  850.                 var username = decodeURIComponent(thatUri.username);
  851.                 if (username.length > 0)
  852.                     this.credentials.userId = username;
  853.                 log("set uri: " + this.uri.spec, this);
  854.             }
  855.         }
  856.         return thatUri;
  857.     },
  858.     
  859.     get userId() { return this.credentials.userId; },
  860.     
  861.     get defaultCalId() {
  862.         var list = this.getUserPreferences("X-NSCP-WCAP-PREF-icsCalendar");
  863.         return (list.length > 0 ? list[0] : this.credentials.userId);
  864.     },
  865.     
  866.     get isLoggedIn() {
  867.         return (this.m_sessionId != null);
  868.     },
  869.     
  870.     m_defaultCalendar: null,
  871.     get defaultCalendar() {
  872.         if (!this.m_defaultCalendar)
  873.             this.m_defaultCalendar = createWcapCalendar(this);
  874.         return this.m_defaultCalendar;
  875.     },
  876.     
  877.     getUserPreferences: function calWcapSession_getUserPreferences(prefName) {
  878.         var prefs = filterXmlNodes(prefName, this.credentials.userPrefs);
  879.         return prefs;
  880.     },
  881.     
  882.     get defaultAlarmStart() {
  883.         var alarmStart = null;
  884.         var ar = this.getUserPreferences("X-NSCP-WCAP-PREF-ceDefaultAlarmStart");
  885.         if (ar.length > 0 && ar[0].length > 0) {
  886.             // workarounding cs duration bug, missing "T":
  887.             var dur = ar[0].replace(/(^P)(\d+[HMS]$)/, "$1T$2");
  888.             alarmStart = new CalDuration();
  889.             alarmStart.icalString = dur;
  890.             alarmStart.isNegative = !alarmStart.isNegative;
  891.         }
  892.         return alarmStart;
  893.     },
  894.     
  895.     getDefaultAlarmEmails: function calWcapSession_getDefaultAlarmEmails(out_count)
  896.     {
  897.         var ret = [];
  898.         var ar = this.getUserPreferences("X-NSCP-WCAP-PREF-ceDefaultAlarmEmail");
  899.         if (ar.length > 0 && ar[0].length > 0) {
  900.             for each (var i in ar) {
  901.                 ret = ret.concat( i.split(/[;,]/).map(trimString) );
  902.             }
  903.         }
  904.         out_count.value = ret.length;
  905.         return ret;
  906.     },
  907.     
  908.     searchForCalendars:
  909.     function calWcapSession_searchForCalendars(searchString, searchOptions, maxResults, listener)
  910.     {
  911.         var this_ = this;
  912.         var request = new calWcapRequest(
  913.             function searchForCalendars_resp(request, err, data) {
  914.                 if (err)
  915.                     this_.notifyError(err);
  916.                 if (listener)
  917.                     listener.onRequestResult(request, data);
  918.             },
  919.             log("searchForCalendars, searchString=" + searchString, this));
  920.         
  921.         try {
  922.             var params = ("&fmt-out=text%2Fxml&search-string=" +
  923.                           encodeURIComponent(searchString));
  924.             params += ("&searchOpts=" + (searchOptions & 3).toString(10));
  925.             if (maxResults > 0)
  926.                 params += ("&maxResults=" + maxResults);
  927.             if (searchOptions & calIWcapSession.SEARCH_INCLUDE_CALID)
  928.                 params += "&calid=1";
  929.             if (searchOptions & calIWcapSession.SEARCH_INCLUDE_NAME)
  930.                 params += "&name=1";
  931.             if (searchOptions & calIWcapSession.SEARCH_INCLUDE_OWNER)
  932.                 params += "&primaryOwner=1";
  933.             
  934.             this.issueNetworkRequest(
  935.                 request,
  936.                 function searchForCalendars_netResp(err, data) {
  937.                     if (err)
  938.                         throw err;
  939.                     // string to xml converter func without WCAP errno check:
  940.                     if (!data || data.length == 0) { // assuming time-out
  941.                         throw new Components.Exception(
  942.                             "Login failed. Invalid session ID.",
  943.                             calIWcapErrors.WCAP_LOGIN_FAILED);
  944.                     }
  945.                     var xml = getDomParser().parseFromString(data, "text/xml");
  946.                     var ret = [];
  947.                     var nodeList = xml.getElementsByTagName("iCal");
  948.                     for ( var i = 0; i < nodeList.length; ++i ) {
  949.                         var node = nodeList.item(i);
  950.                         try {
  951.                             checkWcapXmlErrno(node);
  952.                             var ar = filterXmlNodes("X-NSCP-CALPROPS-RELATIVE-CALID", node);
  953.                             if (ar.length > 0) {
  954.                                 var calId = ar[0];
  955.                                 var cal = this_.m_subscribedCals[calId];
  956.                                 if (!cal) {
  957.                                     if (calId == this_.defaultCalId)
  958.                                         cal = this_.defaultCalendar;
  959.                                     else
  960.                                         cal = createWcapCalendar(this_, node);
  961.                                 }
  962.                                 ret.push(cal);
  963.                             }
  964.                         }
  965.                         catch (exc) {
  966.                             switch (getResultCode(exc)) {
  967.                             case calIWcapErrors.WCAP_NO_ERRNO: // workaround
  968.                             case calIWcapErrors.WCAP_ACCESS_DENIED_TO_CALENDAR:
  969.                                 log("searchForCalendars_netResp() ignored error: " +
  970.                                     errorToString(exc), this_);
  971.                                 break;
  972.                             default:
  973.                                 this_.notifyError(exc);
  974.                                 break;
  975.                             }
  976.                         }
  977.                     }
  978.                     log("search done. number of found calendars: " + ret.length, this_);
  979.                     request.execRespFunc(null, ret);
  980.                 },
  981.                 null, "search_calprops", params);
  982.         }
  983.         catch (exc) {
  984.             request.execRespFunc(exc);
  985.         }
  986.         return request;
  987.     },
  988.     
  989.     getFreeBusyTimes: function calWcapCalendar_getFreeBusyTimes(
  990.         calId, rangeStart, rangeEnd, bBusy, listener)
  991.     {
  992.         // assure DATETIMEs:
  993.         if (rangeStart && rangeStart.isDate) {
  994.             rangeStart = rangeStart.clone();
  995.             rangeStart.isDate = false;
  996.         }
  997.         if (rangeEnd && rangeEnd.isDate) {
  998.             rangeEnd = rangeEnd.clone();
  999.             rangeEnd.isDate = false;
  1000.         }
  1001.         var zRangeStart = getIcalUTC(rangeStart);
  1002.         var zRangeEnd = getIcalUTC(rangeEnd);
  1003.         
  1004.         var this_ = this;
  1005.         var request = new calWcapRequest(
  1006.             function getFreeBusyTimes_resp(request, err, data) {
  1007.                 var rc = getResultCode(err);
  1008.                 switch (rc) {
  1009.                 case calIWcapErrors.WCAP_NO_ERRNO: // workaround
  1010.                 case calIWcapErrors.WCAP_ACCESS_DENIED_TO_CALENDAR:
  1011.                 case calIWcapErrors.WCAP_CALENDAR_DOES_NOT_EXIST:
  1012.                     log("getFreeBusyTimes_resp() error: " + errorToString(err), this_);
  1013.                     break;
  1014.                 default:
  1015.                     if (!Components.isSuccessCode(rc))
  1016.                         this_.notifyError(err);
  1017.                     break;
  1018.                 }
  1019.                 if (listener)
  1020.                     listener.onRequestResult(request, data);
  1021.             },
  1022.             log("getFreeBusyTimes():\n\tcalId=" + calId +
  1023.                 "\n\trangeStart=" + zRangeStart + ",\n\trangeEnd=" + zRangeEnd, this));
  1024.         
  1025.         try {
  1026.             var params = ("&calid=" + encodeURIComponent(calId));
  1027.             params += ("&busyonly=" + (bBusy ? "1" : "0"));
  1028.             params += ("&dtstart=" + zRangeStart);
  1029.             params += ("&dtend=" + zRangeEnd);
  1030.             params += "&fmt-out=text%2Fxml";
  1031.             
  1032.             this.issueNetworkRequest(
  1033.                 request,
  1034.                 function getFreeBusyTimes_resp(err, xml) {
  1035.                     if (err)
  1036.                         throw err;
  1037.                     if (LOG_LEVEL > 0) {
  1038.                         log("getFreeBusyTimes_resp(): " +
  1039.                             getWcapRequestStatusString(xml), this_);
  1040.                     }
  1041.                     if (listener) {
  1042.                         var ret = [];
  1043.                         var nodeList = xml.getElementsByTagName("FB");
  1044.                         for ( var i = 0; i < nodeList.length; ++i ) {
  1045.                             var node = nodeList.item(i);
  1046.                             if ((node.attributes.getNamedItem("FBTYPE").nodeValue
  1047.                                  == "BUSY") != bBusy) {
  1048.                                 continue;
  1049.                             }
  1050.                             var str = node.textContent;
  1051.                             var slash = str.indexOf('/');
  1052.                             var period = new CalPeriod();
  1053.                             period.start = getDatetimeFromIcalString(str.substr(0, slash));
  1054.                             period.end = getDatetimeFromIcalString(str.substr(slash + 1));
  1055.                             period.makeImmutable();
  1056.                             ret.push(period);
  1057.                         }
  1058.                         request.execRespFunc(null, ret);
  1059.                     }
  1060.                 },
  1061.                 stringToXml, "get_freebusy", params);
  1062.         }
  1063.         catch (exc) {
  1064.             request.execRespFunc(exc);
  1065.         }
  1066.         return request;
  1067.     },
  1068.     
  1069.     m_bInstalledLogoutObservers: false,
  1070.     assureInstalledLogoutObservers:
  1071.     function calWcapSession_assureInstalledLogoutObservers()
  1072.     {
  1073.         // don't do this in ctor, calendar manager calls back to all
  1074.         // registered calendars!
  1075.         if (!this.m_bInstalledLogoutObservers) {
  1076.             this.m_bInstalledLogoutObservers = true;
  1077.             // listen for shutdown, being logged out:
  1078.             var observerService = Components.classes["@mozilla.org/observer-service;1"]
  1079.                                             .getService(Components.interfaces.nsIObserverService);
  1080.             observerService.addObserver(this, "quit-application", false /* don't hold weakly */);
  1081.             getCalendarManager().addObserver(this);
  1082.         }
  1083.     },
  1084.     
  1085.     // nsIObserver:
  1086.     observe: function calWcapSession_observer(subject, topic, data)
  1087.     {
  1088.         log("observing: " + topic + ", data: " + data, this);
  1089.         if (topic == "quit-application") {
  1090.             g_bShutdown = true;
  1091.             this.logout(null);
  1092.             // xxx todo: valid upon notification?
  1093.             getCalendarManager().removeObserver(this);
  1094.             var observerService = Components.classes["@mozilla.org/observer-service;1"]
  1095.                                             .getService(Components.interfaces.nsIObserverService);
  1096.             observerService.removeObserver(this, "quit-application");
  1097.         }
  1098.     },
  1099.     
  1100.     modifySubscriptions: function calWcapSession_modifySubscriptions(cal, bSubscribe)
  1101.     {
  1102.         var wcapCommand = ((bSubscribe ? "" : "un") + "subscribe_calendars");
  1103.         var this_ = this;
  1104.         var request = new calWcapRequest(
  1105.             function subscr_resp(request, err) {
  1106.                 if (err)
  1107.                     this_.notifyError(err);
  1108.             },
  1109.             log(wcapCommand + ": " + cal.calId));
  1110.         this.issueNetworkRequest(
  1111.             request,
  1112.             function netResp(err, xml) {
  1113.                 if (err)
  1114.                     throw err;
  1115.             },
  1116.             stringToXml, wcapCommand,
  1117.             "&fmt-out=text%2Fxml&calid=" + encodeURIComponent(cal.calId));
  1118.         return request;
  1119.     },
  1120.     
  1121.     // calICalendarManagerObserver:
  1122.     
  1123.     // called after the calendar is registered
  1124.     onCalendarRegistered: function calWcapSession_onCalendarRegistered(cal)
  1125.     {
  1126.         try {
  1127.             cal = cal.QueryInterface(calIWcapCalendar);
  1128.         }
  1129.         catch (exc) {
  1130.             cal = null;
  1131.         }
  1132.         try {
  1133.             // make sure the calendar belongs to this session and is a subscription:
  1134.             if (cal && cal.session.uri.equals(this.uri) && !cal.isDefaultCalendar) {
  1135.                 var calId = cal.calId;
  1136.                 if (!this.m_subscribedCals[calId])
  1137.                     this.modifySubscriptions(cal, true/*bSubscibe*/);
  1138.                 this.m_subscribedCals[calId] = cal.wrappedJSObject;
  1139.             }
  1140.         }
  1141.         catch (exc) { // never break the listener chain
  1142.             this.notifyError(exc);
  1143.         }
  1144.     },
  1145.     
  1146.     // called before the unregister actually takes place
  1147.     onCalendarUnregistering: function calWcapSession_onCalendarUnregistering(cal)
  1148.     {
  1149.         try {
  1150.             cal = cal.QueryInterface(calIWcapCalendar);
  1151.         }
  1152.         catch (exc) {
  1153.             cal = null;
  1154.         }
  1155.         try {
  1156.             // don't logout here (even if this is the default calendar):
  1157.             // upcoming calls may occur.
  1158.             // make sure the calendar belongs to this session and is a subscription:
  1159.             if (cal && cal.session.uri.equals(this.uri)) {
  1160.                 if (cal.isDefaultCalendar) {
  1161.                     this.unregisterSubscribedCals();
  1162.                 }
  1163.                 else {
  1164.                     var calId = cal.calId;
  1165.                     if (this.m_subscribedCals[calId]) {
  1166.                         delete this.m_subscribedCals[calId];
  1167.                         this.modifySubscriptions(cal, false/*bSubscibe*/);
  1168.                     }
  1169.                 }
  1170.             }
  1171.         }
  1172.         catch (exc) { // never break the listener chain
  1173.             this.notifyError(exc);
  1174.         }
  1175.     },
  1176.     
  1177.     // called before the delete actually takes place
  1178.     onCalendarDeleting: function calWcapSession_onCalendarDeleting(cal)
  1179.     {
  1180.     },
  1181.     
  1182.     // called after the pref is set
  1183.     onCalendarPrefSet: function calWcapSession_onCalendarPrefSet(cal, name, value)
  1184.     {
  1185.     },
  1186.     
  1187.     // called before the pref is deleted
  1188.     onCalendarPrefDeleting: function calWcapSession_onCalendarPrefDeleting(cal, name)
  1189.     {
  1190.     }
  1191. };
  1192.  
  1193. var g_confirmedHttpLogins = null;
  1194. function confirmInsecureLogin(uri)
  1195. {
  1196.     if (!g_confirmedHttpLogins) {
  1197.         g_confirmedHttpLogins = {};
  1198.         var confirmedHttpLogins = getPref(
  1199.             "calendar.wcap.confirmed_http_logins", "");
  1200.         var tuples = confirmedHttpLogins.split(',');
  1201.         for each ( var tuple in tuples ) {
  1202.             var ar = tuple.split(':');
  1203.             g_confirmedHttpLogins[ar[0]] = ar[1];
  1204.         }
  1205.     }
  1206.     
  1207.     var bConfirmed = false;
  1208.     
  1209.     var host = uri.hostPort;
  1210.     var encodedHost = encodeURIComponent(host);
  1211.     var confirmedEntry = g_confirmedHttpLogins[encodedHost];
  1212.     if (confirmedEntry) {
  1213.         bConfirmed = (confirmedEntry == "1");
  1214.     }
  1215.     else {
  1216.         var prompt = getWindowWatcher().getNewPrompter(null);
  1217.         var bundle = getWcapBundle();
  1218.         var out_dontAskAgain = { value: false };
  1219.         var bConfirmed = prompt.confirmCheck(
  1220.             bundle.GetStringFromName("noHttpsConfirmation.label"),
  1221.             bundle.formatStringFromName("noHttpsConfirmation.text", [host], 1),
  1222.             bundle.GetStringFromName("noHttpsConfirmation.check.text"),
  1223.             out_dontAskAgain);
  1224.         
  1225.         if (out_dontAskAgain.value) {
  1226.             // save decision for all running calendars and
  1227.             // all future confirmations:
  1228.             var confirmedHttpLogins = getPref(
  1229.                 "calendar.wcap.confirmed_http_logins", "");
  1230.             if (confirmedHttpLogins.length > 0)
  1231.                 confirmedHttpLogins += ",";
  1232.             confirmedEntry = (bConfirmed ? "1" : "0");
  1233.             confirmedHttpLogins += (encodedHost + ":" + confirmedEntry);
  1234.             setPref("calendar.wcap.confirmed_http_logins", confirmedHttpLogins);
  1235.             g_confirmedHttpLogins[encodedHost] = confirmedEntry;
  1236.         }
  1237.     }
  1238.     
  1239.     log("returned: " + bConfirmed, "confirmInsecureLogin(" + host + ")");
  1240.     return bConfirmed;
  1241. }
  1242.  
  1243.