/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /****************** Get publishing data methods *******************/ // Build an array of all publish site data obtained from prefs function GetPublishSiteData() { var publishBranch = GetPublishPrefsBranch(); if (!publishBranch) return null; // Array of site names - sorted, but don't put default name first var siteNameList = GetSiteNameList(true, false); if (!siteNameList) return null; // Array of all site data var siteArray = []; // We rewrite siteName prefs to eliminate names if data is bad // and to be sure order is the same as sorted name list try { publishBranch.deleteBranch("site_name."); } catch (e) {} // Get publish data using siteName as the key var index = 0; for (var i = 0; i < siteNameList.length; i++) { // Associated data uses site name as key var publishData = GetPublishData_internal(publishBranch, siteNameList[i]); if (publishData) { siteArray[index] = publishData; SetPublishStringPref(publishBranch, "site_name."+index, siteNameList[i]); index++; } else { try { // Remove bad site prefs now publishBranch.deleteBranch("site_data." + siteNameList[i] + "."); } catch (e) {} } } SavePrefFile(); if (index == 0) // No Valid pref records found! return null; return siteArray; } function GetDefaultPublishSiteName() { var publishBranch = GetPublishPrefsBranch(); var name = ""; if (publishBranch) name = GetPublishStringPref(publishBranch, "default_site"); return name; } // Return object with all info needed to publish // from database of sites previously published to. function CreatePublishDataFromUrl(docUrl) { if (!docUrl || IsUrlAboutBlank(docUrl) || GetScheme(docUrl) == "file") return null; var pubSiteData = GetPublishSiteData(); if (pubSiteData) { var dirObj = {}; var index = FindSiteIndexAndDocDir(pubSiteData, docUrl, dirObj); var publishData; if (index != -1) { publishData = pubSiteData[index]; publishData.docDir = FormatDirForPublishing(dirObj.value) //XXX Problem: OtherDir: How do we decide when to use the dir in // publishSiteData (default DocDir) or docDir from current filepath? publishData.otherDir = FormatDirForPublishing(pubSiteData[index].otherDir); publishData.filename = GetFilename(docUrl); publishData.notInSiteData = false; return publishData; } } // Document wasn't found in publish site database // Create data just from URL // Extract username and password from docUrl var userObj = {}; var passObj = {}; var pubUrl = StripUsernamePassword(docUrl, userObj, passObj); // Strip off filename var lastSlash = pubUrl.lastIndexOf("\/"); //XXX Look for "?", "=", and "&" ? pubUrl = pubUrl.slice(0, lastSlash+1); var siteName = CreateSiteNameFromUrl(pubUrl, pubSiteData); publishData = { siteName : siteName, previousSiteName : siteName, filename : GetFilename(docUrl), username : userObj.value, password : passObj.value, savePassword : false, publishUrl : pubUrl, browseUrl : pubUrl, docDir : "", otherDir : "", publishOtherFiles : true, dirList : [""], saveDirs : false, notInSiteData : true } return publishData; } function CreateSiteNameFromUrl(url, publishSiteData) { var host = GetHost(url); var schemePostfix = " (" + GetScheme(url) + ")"; var siteName = host + schemePostfix; if (publishSiteData) { // Look for duplicates. Append "-1" etc until unique name found var i = 1; var exists = false; do { exists = PublishSiteNameExists(siteName, publishSiteData, -1) if (exists) siteName = host + "-" + i + schemePostfix; i++; } while (exists); } return siteName; } // Similar to above, but in param is a site profile name // Note that this is more efficient than getting from a URL, // since we don't have to get all the sitedata but can key off of sitename. // Caller must supply the current docUrl or just a filename // If doc URL is supplied, we find the publish subdirectory if publishUrl is part of docUrl function GetPublishDataFromSiteName(siteName, docUrlOrFilename) { var publishBranch = GetPublishPrefsBranch(); if (!publishBranch) return null; var siteNameList = GetSiteNameList(false, false); if (!siteNameList) return null; for (var i = 0; i < siteNameList.length; i++) { if (siteNameList[i] == siteName) { var publishData = GetPublishData_internal(publishBranch, siteName); if (GetScheme(docUrlOrFilename)) FillInMatchingPublishData(publishData, docUrlOrFilename); else publishData.filename = docUrlOrFilename; return publishData; } } return null; } function GetDefaultPublishData() { var publishBranch = GetPublishPrefsBranch(); if (!publishBranch) return null; var siteName = GetPublishStringPref(publishBranch, "default_site"); if (!siteName) return null; return GetPublishData_internal(publishBranch, siteName); } function GetPublishData_internal(publishBranch, siteName) { if (!publishBranch || !siteName) return null; var prefPrefix = "site_data." + siteName + "."; // We must have a publish url, else we ignore this site // (siteData and siteNames for sites with incomplete data // will get deleted by SavePublishSiteDataToPrefs) var publishUrl = GetPublishStringPref(publishBranch, prefPrefix+"url"); if (!publishUrl) return null; var savePassword = false; var publishOtherFiles = true; try { savePassword = publishBranch.getBoolPref(prefPrefix+"save_password"); publishOtherFiles = publishBranch.getBoolPref(prefPrefix+"publish_other_files"); } catch (e) {} var publishData = { siteName : siteName, previousSiteName : siteName, filename : "", username : GetPublishStringPref(publishBranch, prefPrefix+"username"), savePassword : savePassword, publishUrl : publishUrl, browseUrl : GetPublishStringPref(publishBranch, prefPrefix+"browse_url"), docDir : FormatDirForPublishing(GetPublishStringPref(publishBranch, prefPrefix+"doc_dir")), otherDir : FormatDirForPublishing(GetPublishStringPref(publishBranch, prefPrefix+"other_dir")), publishOtherFiles : publishOtherFiles, saveDirs : false } // Get password from PasswordManager publishData.password = GetSavedPassword(publishData); // If password was found, user must have checked "Save password" // checkbox in prompt outside of publishing, so override the pref we stored if (publishData.password) { if (!savePassword) { try { publishPrefsBranch.setBoolPref(prefPrefix+"save_password", true); } catch (e) {} } publishData.savePassword = true; } // Build history list of directories // Always supply the root dir publishData.dirList = [""]; // Get the rest from prefs var dirPrefs; try { dirPrefs = publishBranch.getChildList(prefPrefix+"dir."); } catch (e) {} if (dirPrefs && dirPrefs.length > 0) { if (dirPrefs.length > 1) dirPrefs.sort(); for (var j = 0; j < dirPrefs.length; j++) { var dirName = GetPublishStringPref(publishBranch, dirPrefs[j]); if (dirName) publishData.dirList[j+1] = dirName; } } return publishData; } /****************** Save publishing data methods *********************/ // Save the siteArray containing all current publish site data function SavePublishSiteDataToPrefs(siteArray, defaultName) { var publishBranch = GetPublishPrefsBranch(); if (!publishBranch) return false; try { if (siteArray) { var defaultFound = false; // Clear existing names and data -- rebuild all site prefs publishBranch.deleteBranch("site_name."); publishBranch.deleteBranch("site_data."); for (var i = 0; i < siteArray.length; i++) { SavePublishData_Internal(publishBranch, siteArray[i], i); if (!defaultFound) defaultFound = defaultName == siteArray[i].siteName; } // Assure that we have a default name if (siteArray.length && !defaultFound) defaultName = siteArray[0].siteName; } // Save default site name SetPublishStringPref(publishBranch, "default_site", defaultName); // Force saving to file so next page edited finds these values SavePrefFile(); } catch (ex) { return false; } return true; } // Update prefs if publish site already exists // or add prefs for a new site function SavePublishDataToPrefs(publishData) { if (!publishData || !publishData.publishUrl) return false; var publishBranch = GetPublishPrefsBranch(); if (!publishBranch) return false; // Create name from URL if no site name is provided if (!publishData.siteName) publishData.siteName = CreateSiteNameFromUrl(publishData.publishUrl, publishData); var siteNamePrefs; try { siteNamePrefs = publishBranch.getChildList("site_name."); } catch (e) {} if (!siteNamePrefs || siteNamePrefs.length == 0) { // We currently have no site prefs, so create them var siteData = [publishData]; return SavePublishSiteDataToPrefs(siteData, publishData.siteName); } // Use "previous" name if available in case it was changed var previousSiteName = ("previousSiteName" in publishData && publishData.previousSiteName) ? publishData.previousSiteName : publishData.siteName; // Find site number of existing site or fall through at next available one // (Number is arbitrary; needed to construct unique "site_name.x" pref string) for (var i = 0; i < siteNamePrefs.length; i++) { var siteName = GetPublishStringPref(publishBranch, "site_name."+i); if (siteName == previousSiteName) { // Delete prefs for an existing site try { publishBranch.deleteBranch("site_data." + siteName + "."); } catch (e) {} break; } } // We've taken care of finding old duplicate, so be sure 'previous name' is current publishData.previousSiteName = publishData.siteName; var ret = SavePublishData_Internal(publishBranch, publishData, i); if (ret) { // Check if siteName was the default and we need to update that var defaultSiteName = GetPublishStringPref(publishBranch, "default_site"); if (previousSiteName == defaultSiteName && publishData.siteName != defaultSiteName) SetPublishStringPref(publishBranch, "default_site", publishData.siteName); SavePrefFile(); // Clear signal to save these data if ("notInSiteData" in publishData && publishData.notInSiteData) publishData.notInSiteData = false; } return ret; } // Save data at a particular site number function SavePublishData_Internal(publishPrefsBranch, publishData, siteIndex) { if (!publishPrefsBranch || !publishData) return false; SetPublishStringPref(publishPrefsBranch, "site_name."+siteIndex, publishData.siteName); FixupUsernamePasswordInPublishData(publishData); var prefPrefix = "site_data." + publishData.siteName + "." SetPublishStringPref(publishPrefsBranch, prefPrefix+"url", publishData.publishUrl); SetPublishStringPref(publishPrefsBranch, prefPrefix+"browse_url", publishData.browseUrl); SetPublishStringPref(publishPrefsBranch, prefPrefix+"username", publishData.username); try { publishPrefsBranch.setBoolPref(prefPrefix+"save_password", publishData.savePassword); publishPrefsBranch.setBoolPref(prefPrefix+"publish_other_files", publishData.publishOtherFiles); } catch (e) {} // Save password using PasswordManager // (If publishData.savePassword = false, this clears existing password) SavePassword(publishData); SetPublishStringPref(publishPrefsBranch, prefPrefix+"doc_dir", FormatDirForPublishing(publishData.docDir)); if (publishData.publishOtherFiles && publishData.otherDir) SetPublishStringPref(publishPrefsBranch, prefPrefix+"other_dir", FormatDirForPublishing(publishData.otherDir)); if ("saveDirs" in publishData && publishData.saveDirs) { if (publishData.docDir) AppendNewDirToList(publishData, publishData.docDir); if (publishData.publishOtherFiles && publishData.otherDir && publishData.otherDir != publishData.docDir) AppendNewDirToList(publishData, publishData.otherDir); } // Save array of subdirectories with site if (publishData.dirList.length) { publishData.dirList.sort(); var dirIndex = 0; for (var j = 0; j < publishData.dirList.length; j++) { var dir = publishData.dirList[j]; // Don't store the root dir if (dir && dir != "/") { SetPublishStringPref(publishPrefsBranch, prefPrefix + "dir." + dirIndex, dir); dirIndex++; } } } return true; } function AppendNewDirToList(publishData, newDir) { newDir = FormatDirForPublishing(newDir); if (!publishData || !newDir) return; if (!publishData.dirList) { publishData.dirList = [newDir]; return; } // Check if already in the list for (var i = 0; i < publishData.dirList.length; i++) { // Don't add if already in the list if (newDir == publishData.dirList[i]) return; } // Add to end of list publishData.dirList[publishData.dirList.length] = newDir; } function RemovePublishSubdirectoryFromPrefs(publishData, removeDir) { removeDir = FormatDirForPublishing(removeDir); if (!publishData || !publishData.siteName || !removeDir) return false; var publishBranch = GetPublishPrefsBranch(); if (!publishBranch) return false; var prefPrefix = "site_data." + publishData.siteName + "."; // Remove dir from the default dir prefs if (publishData.docDir == removeDir) { publishData.docDir = ""; SetPublishStringPref(publishBranch, prefPrefix+"doc_dir", ""); } if (publishData.otherDir == removeDir) { publishData.otherDir = ""; SetPublishStringPref(publishBranch, prefPrefix+"other_dir", ""); } prefPrefix += "dir."; // Delete entire subdir list try { publishBranch.deleteBranch(prefPrefix); } catch (e) {} // Rebuild prefs, skipping over site to remove if (publishData.dirList.length) { var dirIndex = 0; var docDirInList = false; var otherDirInList = false; for (var i = 0; i < publishData.dirList.length; i++) { var dir = publishData.dirList[i]; if (dir == removeDir) { // Remove item from the dirList array publishData.dirList.splice(i, 1); --i; } else if (dir && dir != "/") // skip empty or root dir { // Save to prefs SetPublishStringPref(publishBranch, prefPrefix + dirIndex, dir); dirIndex++; } } } SavePrefFile(); return true; } function SetDefaultSiteName(name) { if (name) { var publishBranch = GetPublishPrefsBranch(); if (publishBranch) SetPublishStringPref(publishBranch, "default_site", name); SavePrefFile(); } } function SavePrefFile() { try { Services.prefs.savePrefFile(null); } catch (e) {} } /***************** Helper / utility methods ********************/ function GetPublishPrefsBranch() { return Services.prefs.getBranch("editor.publish."); } function GetSiteNameList(doSort, defaultFirst) { var publishBranch = GetPublishPrefsBranch(); if (!publishBranch) return null; var siteNamePrefs; try { siteNamePrefs = publishBranch.getChildList("site_name."); } catch (e) {} if (!siteNamePrefs || siteNamePrefs.length == 0) return null; // Array of site names var siteNameList = []; var index = 0; var defaultName = ""; if (defaultFirst) { defaultName = GetPublishStringPref(publishBranch, "default_site"); // This always sorts to top -- replace with real string below siteNameList[0] = ""; index++; } for (var i = 0; i < siteNamePrefs.length; i++) { var siteName = GetPublishStringPref(publishBranch, siteNamePrefs[i]); // Skip if siteName pref is empty or is default name if (siteName && siteName != defaultName) { siteNameList[index] = siteName; index++; } } if (siteNameList.length && doSort) siteNameList.sort(); if (defaultName) { siteNameList[0] = defaultName; index++; } return siteNameList.length? siteNameList : null; } function PublishSiteNameExists(name, publishSiteData, skipSiteIndex) { if (!name) return false; if (!publishSiteData) { publishSiteData = GetPublishSiteData(); skipSiteIndex = -1; } if (!publishSiteData) return false; // Array of site names - sorted, but don't put default name first for (var i = 0; i < publishSiteData.length; i++) { if (i != skipSiteIndex && name == publishSiteData[i].siteName) return true; } return false; } // Find index of a site record in supplied publish site database // docUrl: Document URL with or without filename // (Must end in "/" if no filename) // dirObj.value = the directory of the document URL // relative to the base publishing URL, using "" if none // // XXX: Currently finds the site with the longest-matching url; // should we look for the shortest instead? Or match just the host portion? function FindSiteIndexAndDocDir(publishSiteData, docUrl, dirObj) { if (dirObj) dirObj.value = ""; if (!publishSiteData || !docUrl || GetScheme(docUrl) == "file") return -1; var siteIndex = -1; var siteUrlLen = 0; for (var i = 0; i < publishSiteData.length; i++) { // Site publish or browse url needs to be contained in document URL, // but that may also have a directory after the site base URL // So we must examine all records to find the site URL that best // matches the document URL: the longest-matching substring (XXX is this right?) var lenObj = {value:0}; var tempData = Clone(publishSiteData[i]); // Check if this site matches docUrl (returns length of match if found) var len = FillInMatchingPublishData(tempData, docUrl); if (len > siteUrlLen) { siteIndex = i; siteUrlLen = len; if (dirObj) dirObj.value = tempData.docDir; // Continue to find the site with longest-matching publishUrl } } return siteIndex; } // Look for a matching publish url within the document url // (We need to look at both "publishUrl" and "browseUrl" in case we are editing // an http: document but using ftp: to publish.) // If match is found: // Fill in the filename and subdirectory based on the docUrl and // return the length of the docUrl with username+password stripped out function FillInMatchingPublishData(publishData, docUrl) { if (!publishData || !docUrl) return 0; // Separate docUrl into the base url and filename var lastSlash = docUrl.lastIndexOf("\/"); var baseUrl = docUrl.slice(0, lastSlash+1); var filename = docUrl.slice(lastSlash+1); // Strip username+password from docUrl because these // are stored separately in publishData, never embedded in the publishUrl // If both docUrl and publishData contain usernames, // we must match that as well as the url var username = {value:""}; baseUrl = StripUsernamePassword(baseUrl, username); username = username.value; var matchedLength = 0; let pubUrlFound = publishData.publishUrl && baseUrl.startsWith(publishData.publishUrl); let browseUrlFound = publishData.browseUrl && baseUrl.startsWith(publishData.browseUrl); if ((pubUrlFound || browseUrlFound) && (!username || !publishData.username || username == publishData.username)) { // We found a match matchedLength = pubUrlFound ? publishData.publishUrl.length : publishData.browseUrl.length; if (matchedLength > 0) { publishData.filename = filename; // Subdirectory within the site is what's left in baseUrl after the matched portion publishData.docDir = FormatDirForPublishing(baseUrl.slice(matchedLength)); } } return matchedLength; } // Prefs that don't exist will through an exception, // so just return an empty string function GetPublishStringPref(prefBranch, name) { if (prefBranch && name) { try { return prefBranch.getStringPref(name); } catch (e) {} } return ""; } function SetPublishStringPref(prefBranch, name, value) { if (prefBranch && name) { try { prefBranch.setStringPref(name, value); } catch (e) {} } } // Assure that a publishing URL ends in "/", "=", "&" or "?" // Username and password should always be extracted as separate fields // and are not allowed to remain embedded in publishing URL function FormatUrlForPublishing(url) { url = TrimString(StripUsernamePassword(url)); if (url) { var lastChar = url.charAt(url.length-1); if (lastChar != "/" && lastChar != "=" && lastChar != "&" && lastChar != "?") return (url + "/"); } return url; } // Username and password present in publish url are // extracted into the separate "username" and "password" fields // of the publishData object // Returns true if we did change the publishData function FixupUsernamePasswordInPublishData(publishData) { var ret = false; if (publishData && publishData.publishUrl) { var userObj = {value:""}; var passObj = {value:""}; publishData.publishUrl = FormatUrlForPublishing(StripUsernamePassword(publishData.publishUrl, userObj, passObj)); if (userObj.value) { publishData.username = userObj.value; ret = true; } if (passObj.value) { publishData.password = passObj.value; ret = true; } // While we're at it, be sure browse URL is proper format publishData.browseUrl = FormatUrlForPublishing(publishData.browseUrl); } return ret; } // Assure that a publishing directory ends with "/" and does not begin with "/" // Input dir is assumed to be a subdirectory string, not a full URL or pathname function FormatDirForPublishing(dir) { dir = TrimString(dir); // The "//" case is an expected "typo" filter // that simplifies code below! if (!dir || dir == "/" || dir == "//") return ""; // Remove leading "/" if (dir.startsWith("/")) dir = dir.slice(1); // Append "/" at the end if necessary var dirLen = dir.length; var lastChar = dir.charAt(dirLen-1); if (dirLen > 1 && ["/", "=", "&", "?"].indexOf(lastChar) == -1) dir += "/"; return dir; } function GetSavedPassword(publishData) { if (!publishData || !publishData.publishUrl) return ""; let url = GetUrlForPasswordManager(publishData); let logins = Services.logins.findLogins(url, null, url); for (let i = 0; i < logins.length; i++) { if (logins[i].username == publishData.username) return logins[i].password; } return ""; } function SavePassword(publishData) { if (!publishData || !publishData.publishUrl || !publishData.username) return false; let url = GetUrlForPasswordManager(publishData); // Remove existing entry by finding all logins that match. let logins = Services.logins.findLogins(url, null, url); for (let i = 0; i < logins.length; i++) { if (logins[i].username == publishData.username) { Services.logins.removeLogin(logins[i]); break; } } // If SavePassword is true, add new password. if (publishData.savePassword) { let authInfo = Cc["@mozilla.org/login-manager/loginInfo;1"] .createInstance(Ci.nsILoginInfo); authInfo.init(url, null, url, publishData.username, publishData.password, "", ""); Services.logins.addLogin(authInfo); } return true; } function GetUrlForPasswordManager(publishData) { if (!publishData || !publishData.publishUrl) return false; let url = Services.io.newURI(publishData.publishUrl); if (url.scheme == "ftp" && publishData.username) // Include username in the URL so we can handle multiple users per server // in the password manager url = url.scheme + "://" + publishData.username + "@" + url.hostPort; else url = url.scheme + "://" + url.hostPort; return url; }