/****************************************************************************
* SKCD  > skDownload                                                        *
*---------------------------------------------------------------------------*
* skDownload's goal is to download a file at any location (internet,        *
* network...) on the user machine                                           *
*---------------------------------------------------------------------------*
* (c) IDM. 2003                                                             *
* Project  : sk                                                             *
* Language : javascript                                                     *
* Created  : MikaelLebrun, 15/04/2003                                       *
****************************************************************************/

function skDownload() {
    this.mDlFolder    = null;
    this.mExtCallBack = null;
    this.mExtData     = null;
}

skDownload.prototype = {
    QueryInterface: function(xiInterfaceId) {
        if (!xiInterfaceId.equals(Components.interfaces.skIDownload)
            &&!xiInterfaceId.equals(Components.interfaces.nsISupports)) {
            throw Components.results.NS_ERROR_NO_INTERFACE;
        }
        return this;
    },

    /*********************************************************************************************
    FUNCTION : Get()
    This function download a file in the temporary directory of the Operating System
    ****INPUT: 1/ a string [psUrl - the location of the file to download]
               2/ a string [psFileName - the file name to create on the user disk]
               3/ a string [OPTIONAL psSavePath - the directory in which the file will be save]
    ****OUPUT: NOTHING
    ********************************************************************************************/
    Get: function(psUrl, psFileName, psSavePath) {
        try {
            if(psUrl)
              psUrl = psUrl.replace(/([^:])\/+/g, '$1/');
            else
              return false;
            // if the download folder has not been defined get the profile dir
            if( this.mDlFolder == null ) {
                var xoFileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
                                              .getService(Components.interfaces.nsIProperties);
                //http://white.sakura.ne.jp/~piro/xul/tips/x0010.html -> ProfD
                this.mDlFolder = xoFileLocator.get("ProfD", Components.interfaces.nsILocalFile);
                
                var aProfileDir = psSavePath.split("/");
                for (var i=0; i<aProfileDir.length; i++) {
                    this.mDlFolder.append(aProfileDir[i]);
                    if (!this.mDlFolder.exists())
                        this.mDlFolder.create(this.mDlFolder.DIRECTORY_TYPE, 0755);
                }
            }

            saveUrl(psUrl, psFileName, "SaveImageTitle", false, this.mDlFolder, 
                    this.mExtCallback, this.mExtData);
            return true;
        } catch (err) {
            return false;
        }
    }
}

var skDownloadModule = {
    registerSelf: function(compMgr, fileSpec, location, type) {
        compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
        compMgr.registerFactoryLocation(this.myCID,
                                        "SK Download Component",
                                        this.myProgID, 
                                        fileSpec,
                                        location,
                                        type);
    },

    getClassObject: function(compMgr, cid, iid) {
        if (!cid.equals(this.myCID))
            throw Components.results.NS_ERROR_NO_INTERFACE;
        if (!iid.equals(Components.interfaces.nsIFactory))
            throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
        return this.myFactory;
    },

    myCID: Components.ID("a4ca092c-e556-426b-85cb-8a98e342eab0"),

    myProgID: "@idm.fr/sk_download;1",

    myFactory: {
        createInstance: function(outer, iid) {
            if (outer != null)
                throw Components.results.NS_ERROR_NO_AGGREGATION;
            return (new skDownload()).QueryInterface(iid);
        }
    },

    canUnload: function(compMgr) {
        return true;
    }
};

function NSGetModule(compMgr, fileSpec) { return skDownloadModule; }

/*
===============================================
Functions used to download any type of document
===============================================
*/

function saveUrl(psUrl, psFileName, psFilePickerTitleKey, pbBypassCache, xoDir, poExtCallback, poExtData) {
    var oData = {
        url: psUrl,
        fileName: psFileName,
        filePickerTitle: psFilePickerTitleKey,
        bypassCache: pbBypassCache,
        localFileDir: xoDir,
        extCallback: poExtCallback,
        extData: poExtData
    };
    var oSniffer = new nsHeaderSniffer(psUrl, foundHeaderInfo, oData);
}

function foundHeaderInfo(poSniffer, poData) {
    var sContentType = poSniffer.contentType;
    var sContentEncodingType = poSniffer.contentEncodingType;

    var oFile = Components.classes["@mozilla.org/file/local;1"].createInstance().QueryInterface(Components.interfaces.nsILocalFile);
    oFile.initWithFile(poData.localFileDir); //init the new nsILocalFile object with the other one corresponding to the directory 'profiles\.\.\pictures\'
    oFile.append(poData.fileName);

    var sSource = poSniffer.uri;
    var oPersistArgs = {
        source : sSource,
        contentType : sContentType,
        target : oFile,
        postData : null,
        bypassCache : poData.bypassCache
    };
    var oPersist = makeWebBrowserPersist();

    // Compute persist flags.
    const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
    const flags = nsIWBP.PERSIST_FLAGS_NO_CONVERSION | nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
    if (poData.bypassCache)
        oPersist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
    else 
        oPersist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;

    oPersist.saveURI(sSource, null, null, oPersistArgs.postData, null, oPersistArgs.target);
    
    var poCallback = poData.extCallback;
    if( poCallback != null )
        poCallback.callback( poData.extData );
}

function nsHeaderSniffer(psUrl, poCallback, poData) {
    this.mCallback = poCallback;
    this.mData = poData;
    this.uri = makeURL(psUrl);
    this.linkChecker = Components.classes["@mozilla.org/network/urichecker;1"]
                                 .createInstance().QueryInterface(Components.interfaces.nsIURIChecker);

    var iFlags;
    if (poData.bypassCache) {
        iFlags = Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
    } else {
        iFlags = Components.interfaces.nsIRequest.LOAD_FROM_CACHE;
    }
    this.linkChecker.init(this.uri);
    this.linkChecker.loadFlags = iFlags;
    this.linkChecker.asyncCheck(this, null);
}

nsHeaderSniffer.prototype = {

    // ---------- nsISupports methods ----------
    QueryInterface: function (iid) {
        if (!iid.equals(Components.interfaces.nsIRequestObserver)
            &&!iid.equals(Components.interfaces.nsISupports) 
            &&!iid.equals(Components.interfaces.nsIInterfaceRequestor) 
            &&!iid.equals(Components.interfaces.nsIAuthPrompt)) {
            throw Components.results.NS_ERROR_NO_INTERFACE;
        }
        return this;
    },

    // ---------- nsIInterfaceRequestor methods ----------
    getInterface : function(iid) {
        return this.QueryInterface(iid);
    },

    // ---------- nsIRequestObserver methods ----------
    onStartRequest: function (pxoRequest, pxoContext) { },
  
    onStopRequest: function (pxoRequest, pxoContext, piStatus) {
        try {
            netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
            if (piStatus == 0) { // NS_BINDING_SUCCEEDED, so there's something there
                var xiLinkChecker = pxoRequest.QueryInterface(Components.interfaces.nsIURIChecker);
                var xiChannel = xiLinkChecker.baseRequest.QueryInterface(Components.interfaces.nsIChannel);
                this.contentType = xiChannel.contentType;
                try {
                    var xiHttpChannel = xiChannel.QueryInterface(Components.interfaces.nsIHttpChannel);
                    var xiEncodedChannel = xiChannel.QueryInterface(Components.interfaces.nsIEncodedChannel);
                    this.contentEncodingType = null;
                    // There may be content-encodings on the channel.  Multiple content
                    // encodings are allowed, eg "Content-Encoding: gzip, uuencode".  This
                    // header would mean that the content was first gzipped and then
                    // uuencoded.  The encoding enumerator returns MIME types
                    // corresponding to each encoding starting from the end, so the first
                    // thing it returns corresponds to the outermost encoding.
                    var xoEncodingEnumerator = xiEncodedChannel.contentEncodings;
                    if (xoEncodingEnumerator && xoEncodingEnumerator.hasMoreElements()) {
                        try {
                            this.contentEncodingType = xoEncodingEnumerator.getNext()
                                .QueryInterface(Components.interfaces.nsISupportsCString).data;
                        } catch (e) { }
                    }
                    this.mContentDisposition = xiHttpChannel.getResponseHeader("content-disposition");
                } catch (e) { }
                if (!this.contentType || this.contentType == "application/x-unknown-content-type") {
                    // We didn't get a type from the server.  Fall back on other type detection mechanisms
                    throw "Unknown Type";
                }
            } else {
                dump("Error saving link piStatus = 0x" + piStatus.toString(16) + "\n");
                return;
            }
        } catch (e) {
            try {
                var sUrl = this.uri.QueryInterface(Components.interfaces.nsIURL);
                var sExtension = sUrl.fileExtension;
                if (sExtension) {
                    var oMimeInfo = getMIMEInfoForExtension(sExtension);
                    if (oMimeInfo)
                        this.contentType = oMimeInfo.MIMEType;
                }
            } catch (e) {
                // Not much we can do here.  Give up.
            }
        }
        this.mCallback(this, this.mData);
    }
};

function makeWebBrowserPersist() {
    const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
    const persistIID = Components.interfaces.nsIWebBrowserPersist;
    return Components.classes[persistContractID].createInstance(persistIID);
}

function makeURL(psUrl) {
    var xoService = Components.classes["@mozilla.org/network/io-service;1"]
                              .getService(Components.interfaces.nsIIOService);
    return xoService.newURI(psUrl, null, null);  
}

function getMIMEService() {
    const mimeSvcContractID = "@mozilla.org/mime;1";
    const mimeSvcIID = Components.interfaces.nsIMIMEService;
    const mimeSvc = Components.classes[mimeSvcContractID].getService(mimeSvcIID);
    return mimeSvc;
}

function getMIMEInfoForExtension(psExtension) {
    try {  
        return getMIMEService().GetFromExtension(psExtension);
    } catch (e) { }
    return null;
}
