mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-10 01:40:17 +01:00
1381 lines
46 KiB
JavaScript
1381 lines
46 KiB
JavaScript
/*-*- Mode: C++; 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 Mozilla Roaming code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Ben Bucksch <http://www.bucksch.org> of
|
|
* Beonex <http://www.beonex.com>
|
|
* Portions created by the Initial Developer are Copyright (C) 2002-2004
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Netscape Editor team, esp. Charles Manske <cmanske@netscape.com>
|
|
* ManyOne <http://www.manyone.net>
|
|
*
|
|
* 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 ***** */
|
|
|
|
/* Classes to transfer a list of files and to track the transfer progress.
|
|
It does not provide any UI.
|
|
|
|
You basically only need to create a Transfer object and call its transfer()
|
|
method. The callbacks will then be notified of the progress and possible
|
|
errors. In the callbacks, you can ask about the status (incl. errors)
|
|
of the files by reading the properties of the Transfer object
|
|
(see Transfer.dump() and TransferFile.dump() for a short explanation).
|
|
The functions in utility.js will help you with interpreting and
|
|
displaying possible errors.
|
|
|
|
The initial version/concept was stolen from Composer's publishing
|
|
functionality, but heavily modified (probably almost all lines changed). */
|
|
|
|
const nsIChannel = Components.interfaces.nsIChannel;
|
|
|
|
/*
|
|
Passes parameters for transfer.
|
|
|
|
@param download Bool download or upload
|
|
@param serial Bool true: transfer one file after the other
|
|
false: all files at once (parallel)
|
|
@param localDir String file:-URL of the local dir,
|
|
where the profile files are stored
|
|
@param remoteDir String URL of the dir on the server,
|
|
where the profile files are stored
|
|
@param password String Password for server in cleartext
|
|
@param savepw Bool Should password be stored by def. when asked?
|
|
@param files Array of Objects with properties
|
|
filename String path relative to
|
|
localDir/remoteDir, plus
|
|
filename (incl. possible
|
|
extension)
|
|
mimetype String undefined, if unknown
|
|
size int in bytes.
|
|
undefined, if unknown
|
|
@param progessCallback function(file) to be called when the progress
|
|
of a file transfer changed. can be used to e.g.
|
|
update the UI. parameter file is an entry in the
|
|
files array of this object, see makeFileVar()
|
|
and [this.]dump().
|
|
@param finishedCallback function(bool success) to be called when
|
|
we're done. |success|, if it seems that
|
|
all files have been transferred successfully
|
|
and completely.
|
|
@throws e.result==kMalformedURI bad base URI
|
|
@throws e.prop=="NoUsername" no username given
|
|
*/
|
|
function Transfer(download, serial,
|
|
localDir, remoteDir,
|
|
password, savepw,
|
|
files,
|
|
finishedCallback, progressCallback)
|
|
{
|
|
|
|
// from caller
|
|
|
|
this.download = download;
|
|
this.serial = serial;
|
|
this.localDir = localDir;
|
|
this.remoteDir = remoteDir;
|
|
|
|
var uri = GetIOService().newURI(remoteDir, null, null);
|
|
// this will throw an exception, if the user entered a bad URI.
|
|
// catch in caller
|
|
this.username = uri.username;
|
|
if (!this.username || this.username == "")
|
|
throw {prop:"NoUsername"};
|
|
this.password = password;
|
|
if (!savepw)
|
|
savepw = false;
|
|
this.savePassword = savepw; //savepw is when the user checked the checkbox
|
|
this.saveLogin = false; //savelogin is when we will need to write to disk
|
|
this.sitename = uri.host;
|
|
|
|
this.files = new Array();
|
|
for (var i = 0, l = files.length; i < l; i++)
|
|
{
|
|
this.files[i] = new TransferFile(this, i,
|
|
files[i].filename,
|
|
files[i].mimetype,
|
|
files[i].size);
|
|
}
|
|
|
|
|
|
// internal management
|
|
|
|
this.cancelled = false; // user pressed cancel. Don't proceed.
|
|
|
|
|
|
// progress
|
|
|
|
this.progressSizeAll = 0;
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
this.progressSizeAll += this.files[i].size;
|
|
// We fixed up this.files[].size, so files != this.files
|
|
this.progressSizeCompletedFiles = 0;
|
|
/* Cumulated size of the files which have been *completely* (not partially)
|
|
transferred. Used to speed up processing of incremental updates. */
|
|
|
|
|
|
// callbacks
|
|
|
|
this.finishedCallbacks = new Array();
|
|
this.progressCallbacks = new Array();
|
|
if (finishedCallback)
|
|
this.finishedCallbacks.push(finishedCallback);
|
|
if (progressCallback)
|
|
this.progressCallbacks.push(progressCallback);
|
|
/* I'm using arrays here, to allow the caller to hook up
|
|
additional callbacks. */
|
|
}
|
|
Transfer.prototype =
|
|
{
|
|
/* Called when all files finished
|
|
@param success bool no file failed
|
|
*/
|
|
done : function(success)
|
|
{
|
|
//ddump("Done(" + (success ? "without" : "with") + " errors)");
|
|
|
|
for (var i = 0, l = this.finishedCallbacks.length; i < l; i++)
|
|
this.finishedCallbacks[i](success);
|
|
|
|
/* The close dialog stuff (when all files are done successfully) is UI and
|
|
thus in sroamingProgress.js, called from SetProgressStatus(), which
|
|
should already be called. */
|
|
},
|
|
|
|
/* Initiate transfer of files
|
|
Note: when this function returns, the transfer is most likely *not*
|
|
finished yet.
|
|
@throws "transfer failed" (from transferFile)
|
|
*/
|
|
transfer : function()
|
|
{
|
|
//ddump("starting transfer");
|
|
this.dump();
|
|
|
|
if (this.files.length == 0)
|
|
this.done(true);
|
|
|
|
if (this.serial)
|
|
{
|
|
this.nextFile();
|
|
}
|
|
else
|
|
{
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
this.transferFile(i);
|
|
}
|
|
},
|
|
|
|
/* In serial mode, start the download of the next file, because all
|
|
previous downloads are finished.
|
|
@return 1: there are still files busy (something went wrong)
|
|
2: transfer of next file started
|
|
3: all files were already done, nothing left to do.
|
|
@throws "transfer failed" (from transferFile)
|
|
*/
|
|
nextFile : function()
|
|
{
|
|
// check that there are no downloads in progress
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
{
|
|
if (this.files[i].status == "busy")
|
|
{
|
|
dumpError("Programming error: nextFile() called, although there is "
|
|
+ "still a download in progress.");
|
|
this.dump();
|
|
//return 1;
|
|
}
|
|
}
|
|
|
|
// all halted?
|
|
if (this.cancelled)
|
|
return 3;
|
|
|
|
// find the next pending one
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
{
|
|
if (this.files[i].status == "pending")
|
|
{
|
|
this.transferFile(i);
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
return 3;
|
|
},
|
|
|
|
/* Transfer that file.
|
|
Implements the actual upload/download.
|
|
Independent of serial/parallel (caller is responsible for that).
|
|
|
|
@param id index of this.files array of the file to be transferred
|
|
@throws "transfer failed"
|
|
*/
|
|
transferFile : function(id)
|
|
{
|
|
var file = this.files[id];
|
|
//ddump("TransferFile(" + id + ")");
|
|
|
|
try
|
|
{
|
|
var ioserv = GetIOService();
|
|
var baseURL = ioserv.newURI(this.remoteDir, null, null);
|
|
if (this.savePassword && this.password && this.password != "")
|
|
baseURL.password = this.password;
|
|
var localURL = ioserv.newURI(this.localDir, null, null);
|
|
var channel = ioserv.newChannel(file.filename, null, baseURL);
|
|
file.channel = channel;
|
|
var progress = new TransferProgressListener(this, id);
|
|
file.progressListener = progress;
|
|
channel.notificationCallbacks = progress;
|
|
//ddumpCont("Trying to "+ (this.download ? "download from" : "upload to"));
|
|
//ddumpCont(" remote URL <" + channel.URI.spec + "> ");
|
|
|
|
if (this.download)
|
|
{
|
|
var fos = Components
|
|
.classes["@mozilla.org/network/file-output-stream;1"]
|
|
.createInstance(Components.interfaces.nsIFileOutputStream);
|
|
var bos = Components
|
|
.classes["@mozilla.org/network/buffered-output-stream;1"]
|
|
.createInstance(Components.interfaces
|
|
.nsIBufferedOutputStream);
|
|
var ssl = Components
|
|
.classes["@mozilla.org/network/simple-stream-listener;1"]
|
|
.createInstance(Components.interfaces
|
|
.nsISimpleStreamListener);
|
|
/* Download to temporary local location first, in case something
|
|
goes wrong, e.g. failed auth (in that case, necko overwrites
|
|
the file with nothing! :-( ). Moved to final location in
|
|
fileFinished(). */
|
|
var lf = ioserv.newURI(file.filename + ".tmp", null, localURL)
|
|
.QueryInterface(Components.interfaces.nsIFileURL)
|
|
.file.clone();
|
|
file.localFile = lf;
|
|
//ddump("to local file " + lf.path);
|
|
try
|
|
{
|
|
fos.init(lf, -1, -1, 0);//odd params from NS_NewLocalFileOutputStream
|
|
}
|
|
catch(e)
|
|
{
|
|
var dir = lf.parent;
|
|
if (e.result == kFileNotFound
|
|
// we get this error, if the directory does no exist yet locally
|
|
&& dir && !dir.exists())
|
|
{
|
|
//ddumpCont("Creating dir " + dir.path + " ... ");
|
|
dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
|
|
// also creates all higher-level dirs, if necessary
|
|
//ddump("done (hopefully).");
|
|
fos.init(lf, -1, -1, 0); // try again
|
|
}
|
|
else
|
|
throw e; // pass on all other errors
|
|
}
|
|
bos.init(fos, 32768);
|
|
ssl.init(bos, progress);
|
|
file.fos = fos; // for flush after finished
|
|
file.bos = bos; // dito
|
|
channel.asyncOpen(ssl, null);
|
|
}
|
|
else // upload
|
|
{
|
|
var uploadChannel = channel.QueryInterface(
|
|
Components.interfaces.nsIUploadChannel);
|
|
var lfURL = ioserv.newURI(file.filename, null, localURL);
|
|
//ddump("from local file <" + lfURL.spec + ">");
|
|
|
|
var fileChannel = ioserv.newChannelFromURI(lfURL);
|
|
var is = fileChannel.open(); // maybe async? WFM.
|
|
var bis = Components
|
|
.classes["@mozilla.org/network/buffered-input-stream;1"]
|
|
.createInstance(Components.interfaces
|
|
.nsIBufferedInputStream);
|
|
bis.init(is, 32768);
|
|
uploadChannel.setUploadStream(bis, file.mimetype, -1);
|
|
/* I *must* use a mimetype here. Passing null will cause the
|
|
HTTP protocol implementation to use POST instead of PUT. */
|
|
|
|
channel.asyncOpen(progress, null);
|
|
}
|
|
}
|
|
catch(e)
|
|
{
|
|
if (e.result) // XPCOM error, we can report that to the user and we
|
|
// partially even expect it (file not found in new profiles etc.)
|
|
{
|
|
//ddump("Transfer problem, will later report it to user:\n" + e);
|
|
file.setStatus("failed", e.result, e.message);
|
|
}
|
|
else
|
|
{
|
|
//ddump("Unexpected (non-XPCOM) transfer problem:\n" + e);
|
|
file.setStatus("failed", kErrorUnexpected, e);
|
|
}
|
|
}
|
|
},
|
|
|
|
// called when an individual file transfer completed or failed
|
|
fileFinished : function(filenr)
|
|
{
|
|
//ddump("file " + filenr + " finished");
|
|
|
|
var file = this.files[filenr];
|
|
|
|
// flush/close - only for download
|
|
if (file.bos && file.fos)
|
|
{
|
|
file.bos.flush();
|
|
file.fos.flush();
|
|
file.bos.close();
|
|
file.fos.close();
|
|
}
|
|
|
|
// move to final location
|
|
if (this.download && file.localFile && file.localFile.exists())
|
|
{
|
|
if (file.status == "done")
|
|
file.localFile.moveTo(null, file.filename)
|
|
else
|
|
file.localFile.remove(false);
|
|
}
|
|
|
|
// check, if we're done with all files
|
|
var done = true;
|
|
var failed = false;
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
{
|
|
var file = this.files[i];
|
|
if (file.status == "failed")
|
|
failed = true; // |done| stays true
|
|
else if (file.status != "done") // "failed" already tested above
|
|
done = false;
|
|
}
|
|
if (done)
|
|
this.done(!failed);
|
|
|
|
else if (this.serial)
|
|
this.nextFile();
|
|
},
|
|
|
|
// user wishes transfer to stop
|
|
cancel : function()
|
|
{
|
|
//ddump("Canceling transfer");
|
|
this.cancelled = true; /* needed so that file.setStatus doesn't start
|
|
a next transfer in serial mode */
|
|
|
|
// network stuff
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
{
|
|
if (this.files[i].status != "done" &&
|
|
this.files[i].status != "failed")
|
|
{
|
|
this.files[i].setStatus("failed", kErrorAbort);
|
|
var channel = this.files[i].channel;
|
|
if (channel && channel.isPending)
|
|
{
|
|
channel.cancel(kErrorAbort);
|
|
/* the above line sets all files in the UI to failed, but that does
|
|
not close the dialog. */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* finishedCallbacks called by done(), which will be called once all files
|
|
are finished/cancelled. */
|
|
},
|
|
|
|
/* Hack.
|
|
We need to change the list of files (add or remove files) after
|
|
the Transfer object was created. However, in that case, the internal
|
|
bookkeeping stuff (filenr of TransferFile) won't match anymore, so this
|
|
function fixes that.
|
|
Note: If you add files, create them using new TransferFile(). If you
|
|
remove files, do that best using splice or readd the remaining files to a
|
|
new array.
|
|
*/
|
|
filesChanged : function()
|
|
{
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
{
|
|
this.files[i].filenr = i;
|
|
}
|
|
},
|
|
|
|
// Progress
|
|
|
|
// functions dealing with tracking progress
|
|
// not sure, if that should be at the progress listener
|
|
|
|
progress : 0.0, // Gives the overall transfer progress of all files (0..1)
|
|
progressCalculate : true, // if false, don't update this.progress
|
|
|
|
/* The progress listener calls this function to notify it that the
|
|
transfer progress of a certain file changed.
|
|
This function will update the file's progress this.files[].progress
|
|
and the overall progress this.progress.
|
|
It will also call the owner's callbacks.
|
|
|
|
SLOW! :-(
|
|
|
|
@param filenr int index of the file whose progress changed
|
|
@param aProgress float 0..1 progress of that file
|
|
*/
|
|
progressChanged : function(filenr, aProgress)
|
|
{
|
|
if (!this.progressCalculate)
|
|
return;
|
|
|
|
//ddump("Transfer.progressChanged(" + filenr + ", " + aProgress + ")");
|
|
|
|
var file = this.files[filenr];
|
|
file.progress = aProgress;
|
|
|
|
if (this.progressSizeAll == 0) // undetermined mode
|
|
return;
|
|
|
|
// avoid too many updates
|
|
if (file.progress - file.progressPrevious < 0.01)
|
|
return;
|
|
else
|
|
file.progressPrevious = file.progress;
|
|
|
|
|
|
// calculate progress
|
|
var progressSize = 0;
|
|
if (file.status == "done")
|
|
{
|
|
//ddump(" file completed mode");
|
|
//ddump(" progressSizeAll: " + this.progressSizeAll);
|
|
//ddumpCont(" progressSizeCompletedFiles before: ");
|
|
//ddump(this.progressSizeCompletedFiles);
|
|
|
|
this.progressSizeCompletedFiles = 0;
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
{
|
|
var cur = this.files[i];
|
|
if (cur.status == "done")
|
|
this.progressSizeCompletedFiles += cur.size;
|
|
}
|
|
progressSize = this.progressSizeCompletedFiles;
|
|
|
|
//ddumpCont(" progressSizeCompletedFiles now: ");
|
|
//ddump(this.progressSizeCompletedFiles);
|
|
//ddump(" progressSize: " + progressSize);
|
|
}
|
|
else
|
|
{
|
|
/* It's probably just an update during the file download -
|
|
that happens often, so performance matters. */
|
|
if (this.serial)
|
|
{
|
|
//ddump(" update serial mode");
|
|
//ddump(" progressSizeAll: " + this.progressSizeAll);
|
|
//ddump(" progressSizeCompletedFiles: "+this.progressSizeCompletedFiles);
|
|
//ddump(" files[filenr].size: " + file.size);
|
|
//ddump(" files[filenr].progress: " + file.progress);
|
|
|
|
progressSize = this.progressSizeCompletedFiles
|
|
+ file.size * file.progress;
|
|
|
|
//ddump(" progressSize: " + progressSize);
|
|
}
|
|
else
|
|
{
|
|
//ddump(" update parallel mode");
|
|
//ddump(" progressSizeCompletedFiles: "+this.progressSizeCompletedFiles);
|
|
|
|
progressSize = this.progressSizeCompletedFiles;
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
{
|
|
var cur = this.files[i];
|
|
if (cur.status == "busy")
|
|
progressSize += cur.size * cur.progress;
|
|
}
|
|
|
|
//ddump(" progressSize: " + progressSize);
|
|
}
|
|
}
|
|
|
|
this.progress = progressSize / this.progressSizeAll;
|
|
// I protected at the very beginning against this.progressSizeAll == 0
|
|
|
|
for (var i = 0, l = this.progressCallbacks.length; i < l; i++)
|
|
this.progressCallbacks[i](filenr);
|
|
},
|
|
|
|
|
|
// functions dealing with conflict-(resolution), i.e. ensuring that
|
|
// we don't accidently overwrite newer files
|
|
|
|
/* Queries the last-modification-time and size of the roaming files,
|
|
from the OS and writes them all into a special local file.
|
|
This file will also be uploaded and later be used to compare the ...
|
|
*/
|
|
saveFileStats : function()
|
|
{
|
|
},
|
|
|
|
|
|
// Pass back info to dialog/c++.
|
|
|
|
/* returns int, really enum:
|
|
0 = do nothing, ignore all password/login changes
|
|
1 = Save password, set SavePW pref (user checked the box in the prompt)
|
|
2 = Set SavePassword pref to false and ignore Password/Username retval
|
|
*/
|
|
getSaveLogin : function()
|
|
{
|
|
if (this.saveLogin)
|
|
return this.savePassword ? 1 : 2;
|
|
else
|
|
return 0;
|
|
},
|
|
|
|
getPassword : function()
|
|
{
|
|
if (this.getSaveLogin() == 1)
|
|
return this.password;
|
|
else
|
|
return null;
|
|
},
|
|
|
|
getUsername : function()
|
|
{
|
|
if (this.getSaveLogin() == 1)
|
|
return this.username;
|
|
else
|
|
return null;
|
|
},
|
|
|
|
|
|
// helpful functions for callers
|
|
|
|
// returns an array of TransferFiles with all files with a certain status
|
|
filesWithStatus : function(status)
|
|
{
|
|
var result = new Array();
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
if (this.files[i].status == status)
|
|
result.push(this.files[i]);
|
|
return result;
|
|
},
|
|
|
|
// utility/debug
|
|
|
|
/*
|
|
Prints to the console the contents of this object.
|
|
*/
|
|
dump : function()
|
|
{
|
|
return;
|
|
//ddump("Transfer object:");
|
|
//ddump(" download: " + this.download);
|
|
// bool, see constructor
|
|
//ddump(" serial: " + this.serial);
|
|
// bool, see constructor
|
|
//ddump(" localDir: " + this.localDir);
|
|
// String, see constructor
|
|
//ddump(" remoteDir: " + this.remoteDir);
|
|
// String, see constructor
|
|
//ddump(" password: " + this.password);
|
|
// String, see constructor
|
|
//ddump(" savePassword: " + this.savePassword);
|
|
// bool, see constructor
|
|
//ddump(" sitename: " + this.sitename);
|
|
// String, to be displayed to the user identifying the remote server
|
|
|
|
//ddump(" progress: "+(this.progressCalculate ? this.progress : "disabled"));
|
|
// float, 0..1, how much of the files (measured by filesizes) has
|
|
// already been transfered
|
|
//ddump(" progressSizeAll: " + this.progressSizeAll);
|
|
// int, in bytes, cumulated filesizes as reported by the owner
|
|
|
|
//ddump(" Files: (" + this.files.length + " files)");
|
|
for (var i = 0, l = this.files.length; i < l; i++)
|
|
{
|
|
//ddump(" File " + i + ":");
|
|
this.files[i].dump();
|
|
}
|
|
//ddump(" finished callbacks: " + this.finishedCallbacks.length);
|
|
//ddump(" progress callbacks: " + this.progressCallbacks.length);
|
|
//for (var i = 0, l = this.finishedCallbacks.length; i < l; i++)
|
|
//dump(this.finishedCallbacks[i]);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
@param transfer Transfer the owner
|
|
@param filenr int index of this file in the transfer's files array
|
|
|
|
@param filename, mimetype, size see Transfer
|
|
*/
|
|
function TransferFile(transfer, filenr, // hooks to owner
|
|
filename, mimetype, size)
|
|
{
|
|
// sanitize input
|
|
if (mimetype == undefined || mimetype == "")
|
|
mimetype = "application/octet-stream";
|
|
if (size == undefined)
|
|
size = 0;
|
|
|
|
// for explanations, see this.dump()
|
|
this.filename = filename;
|
|
this.mimetype = mimetype;
|
|
this.size = size;
|
|
//ddump("got file " + filename + " with mimetype " + mimetype);
|
|
this.status = "pending";
|
|
this.statusCode = 0;
|
|
this.progress = 0.0;
|
|
this.channel = null;
|
|
this.progressListener = null;
|
|
this.fos = null;
|
|
this.bos = null;
|
|
this.localFile = null;
|
|
|
|
this.transfer = transfer;
|
|
this.filenr = filenr;
|
|
}
|
|
TransferFile.prototype =
|
|
{
|
|
dump : function()
|
|
{
|
|
//ddump(" filename: " + this.filename);
|
|
// String: relative pathname from profile root
|
|
//ddump(" mimetype: " + this.mimetype);
|
|
// String. might be undefined.
|
|
//ddump(" size: " + this.size);
|
|
// int, in bytes. might be 0 for undefined.
|
|
//ddump(" status: " + this.status);
|
|
// String: "pending" (not started), "busy", "done", "failed"
|
|
//ddumpCont(" statusCode: " + this.statusCode + " (");
|
|
//ddump(NameForStatusCode(this.statusCode) + ")");
|
|
// int: nsresult error code that we got from Necko
|
|
//ddump(" httpResponse: " + this.httpResponse);
|
|
// int: HTTP response code that we got from the server
|
|
//ddump(" statusText: " + this.statusText);
|
|
// String: Text corresponding to error (usually with httpResponse
|
|
// or fatal errors). Might be in English or the server's language.
|
|
// Text (usually translated) corresponding to statusCode can be
|
|
// gotten from ...utility.js; it may include this text.
|
|
//ddump(" progress: " + this.progress);
|
|
// float: 0 (nothing) .. 1 (complete)
|
|
},
|
|
|
|
/* Use this function to change this.status
|
|
|
|
@param aStatus String like this.status, i.e. "done", "pending" etc.
|
|
@param aStatusCode Mozilla error code
|
|
@param aMessage String Sets statusText, a long explanation specific
|
|
to this error case. Optional.
|
|
*/
|
|
setStatus : function(aStatus, aStatusCode, aMessage)
|
|
{
|
|
//ddumpCont("request to change "+this.filename+" from "+this.status+" (");
|
|
//ddumpCont(NameForStatusCode(this.statusCode)+") to "+aStatus+" (");
|
|
var undef = aStatusCode == undefined;
|
|
//ddump((undef ? "no new status code" : NameForStatusCode(aStatusCode))+")");
|
|
|
|
if (this.statusCode == aStatusCode && this.status == aStatus)
|
|
{
|
|
//ddump(" (no changes)");
|
|
return;
|
|
}
|
|
|
|
var was_failed = (this.status == "failed");
|
|
var was_done = (this.status == "done");
|
|
if (!was_failed) /* prevent overwriting older errors with newer, bogus
|
|
OK status codes or the real problem with
|
|
consequential problems. */
|
|
{
|
|
this.status = aStatus;
|
|
if (aStatusCode != undefined)
|
|
{
|
|
this.statusCode = aStatusCode;
|
|
//this.statusText = aMessage; // might be undefined, but it must match
|
|
// // this.statusCode
|
|
// WORKAROUND *sigh*, for FTP, we get the error msg before the error
|
|
}
|
|
if (aMessage)
|
|
this.statusText = aMessage;
|
|
|
|
for (var i = 0, l = this.transfer.progressCallbacks.length; i < l; i++)
|
|
this.transfer.progressCallbacks[i](this.filenr);
|
|
}
|
|
if (
|
|
!(was_done || was_failed)
|
|
&& (aStatus == "done" || aStatus == "failed")
|
|
) // file just completed or failed
|
|
this.transfer.fileFinished(this.filenr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Use one object per file to be downloaded.
|
|
@param filenr integer index of file in transfer.files
|
|
@param transfer Transfer the context
|
|
*/
|
|
function TransferProgressListener(transfer, filenr)
|
|
{
|
|
this.transfer = transfer; // this creates a cyclic reference :-(
|
|
this.filenr = filenr;
|
|
this.file = this.transfer.files[this.filenr];
|
|
|
|
var consumer = Components.classes["@mozilla.org/binaryoutputstream;1"]
|
|
.createInstance(Components.interfaces
|
|
.nsIBinaryOutputStream);
|
|
/* I don't care about that stream, and it's empty anyways, so
|
|
just use any outputstream that stores in RAM.
|
|
nsStorageStream would probably have been more suited, but I don't
|
|
see how I could instantiate that from JS, and binaryoutputstream
|
|
works just as well. */
|
|
var buffer = Components
|
|
.classes["@mozilla.org/network/buffered-output-stream;1"]
|
|
.createInstance(Components.interfaces
|
|
.nsIBufferedOutputStream);
|
|
this.dummy_ssl = Components
|
|
.classes["@mozilla.org/network/simple-stream-listener;1"]
|
|
.createInstance(Components.interfaces
|
|
.nsISimpleStreamListener);
|
|
buffer.init(consumer, 32768);
|
|
this.dummy_ssl.init(buffer, null);
|
|
}
|
|
TransferProgressListener.prototype =
|
|
{
|
|
|
|
// Progress
|
|
|
|
// nsIRequestObserver
|
|
|
|
onStartRequest : function(aRequest, aContext)
|
|
{
|
|
//ddump("onStartRequest: " + aRequest.name);
|
|
this.file.setStatus("busy");
|
|
},
|
|
|
|
onStopRequest : function(aRequest, aContext, aStatusCode)
|
|
{
|
|
//ddump("onStopRequest:");
|
|
//ddump(" Request: " + aRequest.name);
|
|
//ddump(" StatusCode: " + NameForStatusCode(aStatusCode));
|
|
|
|
if (aStatusCode == kNS_OK)
|
|
{
|
|
/* WORKAROUND
|
|
HTTP gives us NS_OK, although the request failed with an HTTP error
|
|
code, so check for that.
|
|
FTP gives us NS_OK, although the transfer is still ongoing.
|
|
I tried to work around that in onStatus(), see comment there. */
|
|
var scheme = this.file.channel.URI.scheme;
|
|
if (scheme == "http")
|
|
this.privateHTTPResponse();
|
|
//else if (scheme == "ftp")
|
|
// return;
|
|
else
|
|
// let's hope that the other protocol impl.s are saner
|
|
this.file.setStatus("done", aStatusCode);
|
|
}
|
|
else
|
|
this.privateStatusChange(aStatusCode);
|
|
},
|
|
|
|
// nsIProgressEventSink
|
|
|
|
onStatus : function(aRequest, aContext, aStatusCode, aStatusArg)
|
|
{
|
|
/* WORKAROUND
|
|
The status codes that are passed to us here in aStatusCode look like
|
|
nsresult error codes, but their numerical values overlap with other,
|
|
real errors. Darin said that real errors are never passed in here,
|
|
so we don't have a hard conflict. To make processing easier, I just
|
|
translate these status codes into made-up, unique error codes,
|
|
so we can later check them together with other status and error codes
|
|
and store them in the normal this.status field (which contains
|
|
normal XPCOM error codes) etc.. */
|
|
if (aStatusCode == kStatusResolvingHost_Status)
|
|
aStatusCode = kStatusResolvingHost;
|
|
else if (aStatusCode == kStatusConnectedTo_Status)
|
|
aStatusCode = kStatusConnectedTo;
|
|
else if (aStatusCode == kStatusSendingTo_Status)
|
|
aStatusCode = kStatusSendingTo;
|
|
else if (aStatusCode == kStatusReceivingFrom_Status)
|
|
aStatusCode = kStatusReceivingFrom;
|
|
else if (aStatusCode == kStatusConnectingTo_Status)
|
|
aStatusCode = kStatusConnectingTo;
|
|
else if (aStatusCode == kStatusWaitingFor_Status)
|
|
aStatusCode = kStatusWaitingFor;
|
|
else if (aStatusCode == kStatusReadFrom_Status)
|
|
aStatusCode = kStatusReadFrom;
|
|
else if (aStatusCode == kStatusWroteTo_Status)
|
|
aStatusCode = kStatusWroteTo;
|
|
|
|
//ddump("onStatus:");
|
|
//ddump(" Request: " + aRequest.name);
|
|
//ddump(" StatusCode: " + NameForStatusCode(aStatusCode));
|
|
//ddump(" StatusArg: " + aStatusArg);
|
|
|
|
/* WORKAROUND
|
|
We sometimes get onStopRequest *before* e.g. onStatus(SENDING_TO), so
|
|
ignore all status msgs after onstop. This is dangerous, because
|
|
IIRC I saw onStop also on the very beginning of a transfer, and
|
|
if that happens for the last file, we might close the dialog before
|
|
the transfer finished, but I don't know another quick and not too bad
|
|
workaround. */
|
|
if (this.file.status == "done")
|
|
return;
|
|
|
|
this.privateStatusChange(aStatusCode);
|
|
},
|
|
|
|
/* Use this function to interpret Mozilla status/error codes and
|
|
update |this| appropriately.
|
|
Don't use it for NS_OK - what this means depends on the situation and
|
|
thus can't be interpreted here.
|
|
@param aStatusCode Mozilla error code
|
|
*/
|
|
privateStatusChange : function(aStatusCode)
|
|
{
|
|
var status;
|
|
if (aStatusCode == kNS_OK)
|
|
// don't know what this means, only the caller knows what succeeded
|
|
return;
|
|
else if (aStatusCode == kStatusReadFrom)
|
|
status = "busy";
|
|
else if (aStatusCode == kStatusReceivingFrom)
|
|
status = "busy";
|
|
else if (aStatusCode == kStatusSendingTo)
|
|
status = "busy";
|
|
else if (aStatusCode == kStatusWaitingFor)
|
|
status = "busy";
|
|
else if (aStatusCode == kStatusResolvingHost)
|
|
status = "busy";
|
|
else if (aStatusCode == kStatusConnectedTo)
|
|
status = "busy";
|
|
else if (aStatusCode == kStatusConnectingTo)
|
|
status = "busy";
|
|
else if (aStatusCode == kErrorBindingRedirected)
|
|
status = "busy";
|
|
else if (aStatusCode == kErrorBindingRetargeted)
|
|
status = "busy";
|
|
else if (aStatusCode == kStatusBeginFTPTransaction)
|
|
status = "busy";
|
|
else if (aStatusCode == kStatusEndFTPTransaction)
|
|
status = "done";
|
|
else if (aStatusCode == kInProgress)
|
|
status = "busy";
|
|
else if (aStatusCode == kStatusFTPLogin)
|
|
status = "busy";
|
|
else if (aStatusCode == kErrorFTPAuthNeeded)
|
|
status = "busy";
|
|
else if (aStatusCode == kNetReset)
|
|
; // correct?
|
|
else
|
|
status = "failed";
|
|
|
|
this.file.setStatus(status, aStatusCode);
|
|
},
|
|
|
|
/* Use this function to get and interpret HTTP status/error codes and
|
|
update |this| appropriately.
|
|
*/
|
|
privateHTTPResponse : function()
|
|
{
|
|
//ddumpCont("privateHTTPResponse ");
|
|
|
|
// Get HTTP response code
|
|
var responseCode;
|
|
var respText;
|
|
try
|
|
{
|
|
var httpchannel = this.file.channel
|
|
.QueryInterface(Components.interfaces.nsIHttpChannel);
|
|
responseCode = httpchannel.responseStatus;
|
|
this.file.httpResponse = responseCode;
|
|
respText = httpchannel.responseStatusText;
|
|
//ddump(responseCode);
|
|
}
|
|
catch(e)
|
|
{
|
|
//ddump(" Error while trying to get HTTP response: " + e);
|
|
this.file.setStatus("failed", kErrorNoInterface);
|
|
return;
|
|
}
|
|
|
|
// Interpret HTTP response code
|
|
var status;
|
|
if (responseCode >= 100 && responseCode < 200)
|
|
status = "busy";
|
|
else if (responseCode >= 200 && responseCode < 300)
|
|
status = "done";
|
|
else if (responseCode >= 300 && responseCode < 400)
|
|
status = "busy";
|
|
else if (responseCode >= 400 && responseCode < 600)
|
|
status = "failed";
|
|
else // what?
|
|
return;
|
|
|
|
this.file.setStatus(status, kStatusHTTP, respText);
|
|
},
|
|
|
|
onProgress : function(aRequest, aContext, aProgress, aProgressMax)
|
|
{
|
|
//ddump("onProgress: " + aProgress + "/" + aProgressMax);
|
|
|
|
if (aProgressMax > 0 && aProgress > 0)
|
|
// WORKAROUND Necko sometimes sends crap like 397/0 or 0/4294967295
|
|
{
|
|
this.transfer.progressChanged(this.filenr, aProgress / aProgressMax);
|
|
}
|
|
},
|
|
|
|
|
|
// Dummies
|
|
|
|
// nsStreamListener
|
|
|
|
onDataAvailable : function(aRequest, aContext,
|
|
anInputStream, anOffset, aCount)
|
|
{
|
|
/* dummy, because it's only the down stream during an upload
|
|
and during download, we use the tee, which copies to the outstream. */
|
|
|
|
this.dummy_ssl.onDataAvailable(aRequest, aContext, anInputStream,
|
|
anOffset, aCount);
|
|
},
|
|
|
|
// nsIFTPEventSink, nsIHTTPEventSink
|
|
|
|
OnFTPControlLog : function(aServer, aMsg)
|
|
{
|
|
// dummy
|
|
},
|
|
|
|
onChannelRedirect : function(aOldChannel, aNewChannel, aFlags)
|
|
{
|
|
// dummy
|
|
},
|
|
|
|
/*
|
|
// nsIDocShellTreeItem (wtf?)
|
|
name : "dummy in transfer.js",
|
|
itemType : 0,
|
|
parent : null,
|
|
sameTypeParent : null,
|
|
rootTreeItem : null,
|
|
sameTypeRootTreeItem : null,
|
|
treeOwner : null,
|
|
childOffset : 0,
|
|
nameEquals : function(name)
|
|
{
|
|
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
|
},
|
|
findItemWithName : function(name, requestor)
|
|
{
|
|
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
|
},
|
|
*/
|
|
|
|
// Prompts
|
|
|
|
// nsIPrompt
|
|
alert : function(dlgTitle, text)
|
|
{
|
|
//ddump("alert " + text);
|
|
|
|
/* WORKAROUND
|
|
FTP sends us these in the case of an error *sigh*. Don't display dialog,
|
|
but redirect to file errors. */
|
|
this.file.statusText = text.replace(/\n/, "");
|
|
|
|
/*
|
|
if (!dlgTitle)
|
|
title = GetString("Alert");
|
|
GetPromptService().alert(window, dlgTitle, text);
|
|
*/
|
|
},
|
|
alertCheck : function(dlgTitle, text, checkBoxLabel, checkObj)
|
|
{
|
|
//ddump("alertCheck");
|
|
this.alert(dlgTitle, text);
|
|
},
|
|
confirm : function(dlgTitle, text)
|
|
{
|
|
//ddump("confirm");
|
|
return this.confirmCheck(dlgTitle, text, null, null);
|
|
},
|
|
confirmCheck : function(dlgTitle, text, checkBoxLabel, checkObj)
|
|
{
|
|
//ddump("confirmCheck");
|
|
var promptServ = GetPromptService();
|
|
if (!promptServ)
|
|
return;
|
|
|
|
promptServ.confirmEx(window, dlgTitle, text,
|
|
nsIPromptService.STD_OK_CANCEL_BUTTONS,
|
|
"", "", "", checkBoxLabel, checkObj);
|
|
},
|
|
confirmEx : function(dlgTitle, text, btnFlags,
|
|
btn0Title, btn1Title, btn2Title,
|
|
checkBoxLabel, checkVal)
|
|
{
|
|
//ddump("confirmEx");
|
|
var promptServ = GetPromptService();
|
|
if (!promptServ)
|
|
return 0;
|
|
|
|
return promptServ.confirmEx(window, dlgTitle, text, btnFlags,
|
|
btn0Title, btn1Title, btn2Title,
|
|
checkBoxLabel, checkVal);
|
|
},
|
|
select : function(dlgTitle, text, count, selectList, outSelection)
|
|
{
|
|
//ddump("select");
|
|
var promptServ = GetPromptService();
|
|
if (!promptServ)
|
|
return false;
|
|
|
|
return promptServ.select(window, dlgTitle, text, count,
|
|
selectList, outSelection);
|
|
},
|
|
prompt : function(dlgTitle, label, /*inout*/ inputvalueObj,
|
|
checkBoxLabel, /*inout*/ checkObj)
|
|
{
|
|
//ddump("nsIPrompt::prompt");
|
|
return this.privatePrompt(dlgTitle, label, null, inputvalueObj,
|
|
checkBoxLabel, checkObj);
|
|
},
|
|
/* Warning: these |promptPassword| and |promptUsernameAndPassword|
|
|
from nsIPrompt will never be invoked. If they are being called, the
|
|
nsIAuthPrompt versions will be invoked, with wrong arguments.
|
|
This is because we're overloading the functions with the same number
|
|
of parameters and JS can't distinguish between different parameter types.
|
|
This is a bug copied from Editor's Publishing feature; the bug exists
|
|
there as well. */
|
|
promptPassword : function(dlgTitle, label, /*inout*/ pwObj,
|
|
checkBoxLabel, /*inout*/ savePWObj)
|
|
{
|
|
//ddump("nsIPrompt::promptPassword:");
|
|
//ddump(" pwObj: " + pwObj.value);
|
|
//ddump(" checkBoxLabel: " + checkBoxLabel);
|
|
//ddump(" savePWObj: " + savePWObj.value + " (ignored)");
|
|
|
|
var ret = this.privatePromptPassword(dlgTitle, label, null,
|
|
pwObj, checkBoxLabel);
|
|
|
|
savePWObj.value = this.transfer.savePassword; // out param
|
|
return ret;
|
|
},
|
|
promptUsernameAndPassword : function(dlgTitle, label,
|
|
/*inout*/ userObj, /*inout*/ pwObj,
|
|
savePWLabel, /*inout*/ savePWObj)
|
|
{
|
|
//ddump("nsIPrompt::promptUsernameAndPassword:");
|
|
//ddump(" userObj: " + userObj.value);
|
|
//ddump(" pwObj: " + pwObj.value);
|
|
//ddump(" savePWLabel: " + savePWLabel);
|
|
//ddump(" savePWObj: " + savePWObj.value + " (ignored)");
|
|
|
|
var ret = this.privatePromptUsernameAndPassword(dlgTitle, label, null,
|
|
userObj, pwObj,
|
|
savePWLabel);
|
|
|
|
savePWObj.value = this.transfer.savePassword; // out param
|
|
return ret;
|
|
},
|
|
|
|
// nsIAuthPrompt
|
|
|
|
prompt : function(dlgTitle, label, pwrealm, /*in*/ savePW, defaultText,
|
|
/*out*/ resultObj)
|
|
{
|
|
//ddump("nsIAuthPrompt::prompt");
|
|
var savePWObj = {value:this.transfer.savePassword};
|
|
// I know it better than the caller.
|
|
var inputvalueObj = {value:defaultText};
|
|
var ret = this.privatePrompt(dlgTitle, label, pwrealm,
|
|
inputvalueObj, null, savePWObj);
|
|
resultObj.value = inputvalueObj.value;
|
|
return ret;
|
|
},
|
|
promptPassword : function(dlgTitle, label, pwrealm, /*in*/ savePW,
|
|
/*out*/ pwObj)
|
|
{
|
|
//ddump("nsIAuthPrompt::promptPassword");
|
|
//ddump(" pwrealm: " + pwrealm);
|
|
//ddump(" savePW: " + savePW + " (ignored)");
|
|
//ddump(" pwObj: " + pwObj.value);
|
|
|
|
return this.privatePromptPassword(dlgTitle, label, pwrealm,
|
|
pwObj, null);
|
|
},
|
|
promptUsernameAndPassword : function(dlgTitle, label, pwrealm, /*in*/savePW,
|
|
/*out*/ userObj, /*out*/ pwObj)
|
|
{
|
|
//ddump("nsIAuthPrompt::promptUsernameAndPassword:");
|
|
//ddump(" pwrealm: " + pwrealm);
|
|
//ddump(" savePW: " + savePW + " (ignored)");
|
|
//ddump(" userObj: " + userObj.value);
|
|
//ddump(" pwObj: " + pwObj.value);
|
|
|
|
return this.privatePromptUsernameAndPassword(dlgTitle, label, pwrealm,
|
|
userObj, pwObj, null);
|
|
},
|
|
|
|
// helper functions for prompt stuff
|
|
|
|
privatePrompt : function(dlgTitle, label, pwrealm, /*inout*/ inputvalueObj,
|
|
checkBoxLabel, /*inout*/ checkObj)
|
|
{
|
|
if (!dlgTitle)
|
|
dlgTitle = GetString("PasswordTitle");
|
|
if (!pwrealm)
|
|
pwrealm = this.transfer.sitename;
|
|
if (!label)
|
|
label = this.makePWLabel(userObj.value, pwrealm);
|
|
if (!checkBoxLabel)
|
|
checkBoxLabel = GetString("SavePassword");
|
|
|
|
var promptServ = GetPromptService();
|
|
if (!promptServ)
|
|
return false;
|
|
|
|
var ret = promptServ.prompt(window, dlgTitle, label,
|
|
inputvalueObj, checkBoxLabel, checkObj);
|
|
if (!ret)
|
|
this.transfer.cancel();
|
|
return ret;
|
|
},
|
|
privatePromptPassword : function(dlgTitle, label, pwrealm,
|
|
/*inout*/ pwObj, savePWLabel)
|
|
{
|
|
//ddump("privatePromptPassword()");
|
|
//ddump(" pwObj.value: " + pwObj.value);
|
|
//ddump(" savePWLabel: " + savePWLabel);
|
|
//ddump(" label: " + label);
|
|
//ddump(" pwrealm: " + pwrealm);
|
|
//ddump(" this.transfer.savePassword: " + this.transfer.savePassword);
|
|
//ddump(" this.transfer.username: " + this.transfer.username);
|
|
|
|
if (!dlgTitle)
|
|
dlgTitle = GetString("PasswordTitle");
|
|
if (!pwrealm)
|
|
pwrealm = this.transfer.sitename;
|
|
if (!label)
|
|
label = this.makePWLabel(userObj.value, pwrealm);
|
|
if (!savePWLabel)
|
|
savePWLabel = GetString("SavePassword");
|
|
var savePWObj = {value: this.transfer.savePassword};
|
|
|
|
//ddump(" pwObj.value: " + pwObj.value);
|
|
//ddump(" savePWLabel: " + savePWLabel);
|
|
//ddump(" label: " + label);
|
|
//ddump(" pwrealm: " + pwrealm);
|
|
|
|
var promptServ = GetPromptService();
|
|
if (!promptServ)
|
|
return false;
|
|
|
|
var ret = false;
|
|
try {
|
|
ret = promptServ.promptPassword(window, dlgTitle, label,
|
|
pwObj, savePWLabel, savePWObj);
|
|
|
|
if (!ret)
|
|
this.transfer.cancel();
|
|
else
|
|
this.updateUsernamePasswordFromPrompt(this.transfer.username,
|
|
pwObj.value, savePWObj.value);
|
|
} catch (e) {
|
|
dumpError(e);
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
privatePromptUsernameAndPassword : function(dlgTitle, label, pwrealm,
|
|
/*inout*/ userObj, /*inout*/ pwObj,
|
|
savePWLabel)
|
|
{
|
|
// HTTP prompts us twice even if user Cancels from 1st attempt!
|
|
|
|
//ddump("privatePromptUsernameAndPassword()");
|
|
//ddump(" pwObj.value: " + pwObj.value);
|
|
//ddump(" userObj.value: " + userObj.value);
|
|
//ddump(" savePWLabel: " + savePWLabel);
|
|
//ddump(" label: " + label);
|
|
//ddump(" pwrealm: " + pwrealm);
|
|
//ddump(" this.transfer.savePassword: " + this.transfer.savePassword);
|
|
//ddump(" this.transfer.username: " + this.transfer.username);
|
|
|
|
if (!dlgTitle)
|
|
dlgTitle = GetString("PasswordTitle");
|
|
if (!pwrealm)
|
|
pwrealm = this.transfer.sitename;
|
|
if (!label)
|
|
label = this.makePWLabel(userObj.value, pwrealm);
|
|
if (!savePWLabel)
|
|
savePWLabel = GetString("SavePassword");
|
|
if (!userObj.value)
|
|
// HTTP PUT uses this dialog if either username or password is bad,
|
|
// so prefill username with the previous value
|
|
userObj.value = this.transfer.username;
|
|
var savePWObj = {value: this.transfer.savePassword};
|
|
|
|
//ddump(" savePWObj.value: " + savePWObj.value);
|
|
//ddump(" userObj.value: " + userObj.value);
|
|
//ddump(" savePWLabel: " + savePWLabel);
|
|
//ddump(" label: " + label);
|
|
//ddump(" pwrealm: " + pwrealm);
|
|
|
|
/* I had the idea to put up the password-only dialog, if we have a
|
|
username. But if the user didn't enter a username in the prefs,
|
|
it first all works correctly, the username+password dialog comes up.
|
|
The problem starts when the user entered the wrong username -
|
|
he won't have a chance to change it anymore. Not sure, if that is bad.
|
|
So, the code for that is below.
|
|
Update: I decided that the user has to enter a username in any case,
|
|
and he can't change it during login. At least not in this case. */
|
|
|
|
if (userObj.value && userObj.value != "")
|
|
{
|
|
// |label| here says "enter username and password ...", so we need
|
|
// to build our own.
|
|
label = this.makePWLabel(userObj.value, pwrealm);
|
|
return this.privatePromptPassword(dlgTitle, label, pwrealm,
|
|
pwObj, null);
|
|
}
|
|
|
|
// We should get here only, if we got an empty username pref from the reg.
|
|
//ddump(" asking for username as well");
|
|
|
|
var ret = false;
|
|
try {
|
|
var promptServ = GetPromptService();
|
|
if (!promptServ)
|
|
return false;
|
|
|
|
ret = promptServ.promptUsernameAndPassword(window,
|
|
dlgTitle, label, userObj, pwObj,
|
|
savePWLabel, savePWObj);
|
|
|
|
if (!ret)
|
|
this.transfer.cancel();
|
|
else
|
|
this.updateUsernamePasswordFromPrompt(userObj.value, pwObj.value,
|
|
savePWObj.value);
|
|
} catch (e) {
|
|
dumpError(e);
|
|
}
|
|
//ddump(" done. result:");
|
|
//ddump(" userObj.value: " + userObj.value);
|
|
//ddump(" pwObj.value: " + pwObj.value);
|
|
//ddump(" savePWObj.value: " + savePWObj.value);
|
|
return ret;
|
|
},
|
|
// Update any data that the user supplied in a prompt dialog
|
|
updateUsernamePasswordFromPrompt : function(username, password,savePassword)
|
|
{
|
|
// Set flag to save login data after transferring, if it changed in
|
|
// dialog and the "SavePassword" checkbox was checked
|
|
this.transfer.saveLogin = (username != this.transfer.username ||
|
|
password != this.transfer.password)
|
|
&& savePassword;
|
|
|
|
//ddump(" username: " + username);
|
|
//ddump(" password: " + password);
|
|
//ddump(" savePW: " + savePassword);
|
|
//ddump(" saveLogin: " + this.transfer.saveLogin);
|
|
|
|
this.transfer.username = username;
|
|
this.transfer.password = password;
|
|
this.transfer.savePassword = savePassword;
|
|
|
|
var uri = GetIOService().newURI(this.transfer.remoteDir, null, null);
|
|
uri.username = username;
|
|
this.transfer.remoteDir = uri.spec; // doesn't work, the spec isn't updated
|
|
this.transfer.dump();
|
|
},
|
|
makePWLabel : function (user, realm)
|
|
{
|
|
if (!user)
|
|
user = "";
|
|
var label = GetString("EnterPasswordForUserAt");
|
|
label = label.replace(/%username%/, user);
|
|
label = label.replace(/%realm%/, realm);
|
|
return label;
|
|
},
|
|
|
|
|
|
// XPCOM
|
|
|
|
// nsISupports
|
|
|
|
QueryInterface : function(aIID)
|
|
{
|
|
// ddump("QI: " + aIID.toString() + "\n");
|
|
if (aIID.equals(Components.interfaces.nsIProgressEventSink)
|
|
|| aIID.equals(Components.interfaces.nsIRequestObserver)
|
|
|| aIID.equals(Components.interfaces.nsIStreamListener)
|
|
|| aIID.equals(Components.interfaces.nsISupports)
|
|
|| aIID.equals(Components.interfaces.nsISupportsWeakReference)
|
|
|| aIID.equals(Components.interfaces.nsIPrompt)
|
|
|| aIID.equals(Components.interfaces.nsIAuthPrompt)
|
|
|| aIID.equals(Components.interfaces.nsIFTPEventSink)
|
|
|| aIID.equals(Components.interfaces.nsIChannelEventSink)
|
|
|| aIID.equals(Components.interfaces.nsIDocShellTreeItem)
|
|
|| aIID.equals(Components.interfaces.nsIInterfaceRequestor))
|
|
return this;
|
|
throw Components.results.NS_NOINTERFACE;
|
|
},
|
|
|
|
// nsIInterfaceRequestor
|
|
|
|
getInterface : function(aIID)
|
|
{
|
|
return this.QueryInterface(aIID);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var gIOService;
|
|
function GetIOService()
|
|
{
|
|
if (gIOService)
|
|
return gIOService;
|
|
|
|
// throw errors into caller
|
|
gIOService = Components.classes["@mozilla.org/network/io-service;1"]
|
|
.getService(Components.interfaces.nsIIOService);
|
|
return gIOService;
|
|
}
|