
const DEBUG = false;         

const PHOTOBUCKET_PHOTO_CID         = Components.ID('{d3b147b0-a321-11da-a746-0800200c9a66}');

const nsISupports                   = Components.interfaces.nsISupports;
const nsIClassInfo                  = Components.interfaces.nsIClassInfo;
const nsIFactory                    = Components.interfaces.nsIFactory;
const nsIProperties                 = Components.interfaces.nsIProperties;
const nsILocalFile                  = Components.interfaces.nsILocalFile;
const nsIFile                       = Components.interfaces.nsIFile;
const nsIIOService                  = Components.interfaces.nsIIOService;
const nsIFileProtocolHandler        = Components.interfaces.nsIFileProtocolHandler;
const nsIRDFRemoteDataSource        = Components.interfaces.nsIRDFRemoteDataSource;
const nsIRDFDataSource              = Components.interfaces.nsIRDFDataSource;
const flockIPhotoAPI                = Components.interfaces.flockIPhotoAPI;

const PHOTOBUCKET_PHOTO_CONTRACTID  = '@flock.com/?photo-api-photobucket;1';
const DIRECTORY_SERVICE_CONTRACTID  = '@mozilla.org/file/directory_service;1';
const LOCAL_FILE_CONTRACTID         = '@mozilla.org/file/local;1';
const PREFERENCES_CONTRACTID        = '@mozilla.org/preferences-service;1';
const IO_SERVICE_CONTRACTID         = '@mozilla.org/network/io-service;1';

const flockIPhoto = Components.interfaces.flockIPhoto;
const flockIPhotoPerson = Components.interfaces.flockIPhotoPerson;
const flockIPhotoAlbum = Components.interfaces.flockIPhotoAlbum;
const FLOCK_PHOTO_ALBUM_CONTRACTID = '@flock.com/photo-album;1';
const FLOCK_PHOTO_CONTRACTID = '@flock.com/photo;1';
const FLOCK_PHOTOPERSON_CONTRACTID = '@flock.com/photo-person;1';
const FLOCK_PHOTO_ACCOUNT_CONTRACTID = '@flock.com/photo-account;1';
const flockIPhotoAccount = Components.interfaces.flockIPhotoAccount;
const flockIError = Components.interfaces.flockIError;
const FLOCK_ERROR_CONTRACTID = '@flock.com/error;1'

const PHOTOBUCKET_LOGIN_URL         = 'http://photobucket.com/svc/servicelogin.php';
const PHOTOBUCKET_API_URL           = 'http://photobucket.com/svc/api.php';
const PHOTOBUCKET_API_VERSION       = '1.0';
const PHOTOBUCKET_TOKEN_PREF        = 'flock.photo.photobucket.token';


const PHOTOBUCKET_PK = 'e57335b0847da8b1105b6c8bc27d217a';
const PHOTOBUCKET_SCID = '149825788';


const Base64 = {
    chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
    encode: function (aInput) {
        var output = '';
        while (aInput.length > 0) {
            output += Base64.chars[aInput.charCodeAt(0) >> 2];
            output += Base64.chars[((aInput.charCodeAt(0)&0x03) << 4) | 
                (aInput.length>1?((aInput.charCodeAt(1)&0xF0) >> 4):0)];
            output += Base64.chars[aInput.length>1?
                ((aInput.charCodeAt(1)&0x0F)<<2) | 
                (aInput.length>2?((aInput.charCodeAt(2)&0xC0) >> 6):0):64];
            output += Base64.chars[aInput.length>2?
                (aInput.charCodeAt(2)&0x3F):64];
            if (aInput.length > 3) {
                aInput = aInput.substr (3);
            } else {
                break;
            }
        }
        return output;
    },
    decode: function (aInput) {
        const regexp = new RegExp ('[^'+Base64.chars+']', 'g');
        var output = '';

        aInput = aInput.replace (regexp, '');

        // FIXME: aInput MUST now be a multiple of four characters long

        function sixbits (i) {
            if (i == '=') return 0;
            return Base64.chars.indexOf (i);
        }

        while (aInput.length >= 4) {
            output += String.fromCharCode ((sixbits (aInput[0]) << 2) | 
                    (sixbits (aInput[1]) >> 4));
            if (aInput[2] == '=') {
                break;
            }
            output += String.fromCharCode ((sixbits (aInput[1]) << 4) |
                    (sixbits (aInput[2]) >>2));
            if (aInput[3] == '=') {
                break;
            }
            output += String.fromCharCode (((sixbits (aInput[2]) << 6) & 0xC0) |
                    sixbits (aInput[3]));
            aInput = aInput.substr (4);
        }
        return output;
    },
}

function photoBucketPhotoService() { /*{{{*/
    debug ('CONSTRUCTOR: photoBucketPhotoService\n');
    this.state = flockIPhotoAPI.LOGGED_OUT;
    this.init();

}/*}}}*/

photoBucketPhotoService.prototype.shortName = "photobucket";
photoBucketPhotoService.prototype.serviceName = "Photobucket";


photoBucketPhotoService.prototype.handlePhotosResult = function(aXML) {
try {
        var rval = new Array();
        var photoList = aXML.getElementsByTagName("photo");
        for (i = 0; i < photoList.length; i++) {
            var photo = photoList[i];
            var id = photo.getAttribute("uploaddate");
            var title = photo.getAttribute("name");
            var username = photo.getAttribute("username");
            
            var page_url = "";
            var thumb_url = photo.getElementsByTagName('thumb')[0].firstChild.nodeValue;
            var small_url = photo.getElementsByTagName('url')[0].firstChild.nodeValue;
            // this is a photobucket api issue - need a fix - 
            try {
                page_url = photo.getElementsByTagName('browseurl')[0].firstChild.nodeValue;
                page_url = page_url.replace('&amp;', '&');
            }
            catch(e) {
                page_url = small_url;
                
            }
     
            var newPhoto = Components.classes[FLOCK_PHOTO_CONTRACTID].createInstance(flockIPhoto);
            newPhoto.webPageUrl = page_url;
            newPhoto.thumbnail = thumb_url;
            newPhoto.midSizePhoto = thumb_url;
            newPhoto.largeSizePhoto = small_url;
            newPhoto.title = title;
            newPhoto.username = username;
            newPhoto.id = id;
            rval.push(newPhoto);
        }   
        }
        catch(e) {
            debug(e);
        }
        return rval;
}

photoBucketPhotoService.prototype.getContacts = function(aListener) {
        return -1;// throw unsupported exception
}

photoBucketPhotoService.prototype.getValidPerson = function(aListener, aURL) {
    var user = aURL.match(/albums\/[^\/]*\/([^\/]*)\//)[1];
    this.findByUsername( {
        onFindByUsernameResult: aListener.onGetValidPerson, 
        onError: aListener.onError
        }, user );
}

photoBucketPhotoService.prototype.findByUsername = function(aListener, aUsername) {
    var inst = this;
    var myListener = {
        onResult: function (aXML) {
            var user = aXML.getElementsByTagName("photo")[0];
            var newUserObj = Components.classes[FLOCK_PHOTOPERSON_CONTRACTID].createInstance(flockIPhotoPerson);
            newUserObj.id = user.getAttribute("username");
            newUserObj.username = aUsername;
            newUserObj.fullname = aUsername;
            newUserObj.service = inst;
            aListener.onFindByUsernameResult(newUserObj);
        },
        onError: function (aXML) {
            aListener.onError(aXML);
        },
    }
    var params = {};
    params.username = aUsername;
    var dict = flock.photo.util.params2Dictionary(params);
    this.call(myListener, "getrecentuserphotos", dict);
}

function MultiGetter() {
}

MultiGetter.prototype = {
    mTimer: Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer),
    init: function(aSvc, aListener, aEnumerator) {
        this.mHasNew = false;
        this.mNeedSave = false;
        this.mSvc = aSvc;
        this.mListener = aListener;
        this.mArray = new Array();
        while(aEnumerator.hasMoreElements()) {
            var p = aEnumerator.getNext();
            p.QueryInterface(Components.interfaces.flockIPhotoPerson);

            this.mArray.push(p);
        }
        this.next();
    },
    finish: function() {
        //dump("FINITI\n");
        if(this.mHasNew) {
            this.mListener.onGetMostRecentPhotoForList(Components.interfaces.flockIPhotoAPIListener.LIST_HAS_NEW);
        }
        else if(this.mNeedSave) {
            this.mListener.onGetMostRecentPhotoForList(Components.interfaces.flockIPhotoAPIListener.LIST_NEED_SAVE);
        }
        else {
            this.mListener.onGetMostRecentPhotoForList(Components.interfaces.flockIPhotoAPIListener.LIST_NO_CHANGE);
        }
        return;
    },
    updatePerson: function(aPerson, aPhoto) {
        //dump(aPerson.username + "updating person\n");
        var seq = aPerson.seq;
        var newSeq = parseInt(aPhoto.id);
        if(newSeq>seq) {
            var oldHasNew = aPerson.hasNew;
            aPerson.seq = aPhoto.id;
            this.mNeedSave = true;
            if(seq!=0) {
                this.mHasNew = true;
                aPerson.hasNew = true
                if(oldHasNew==false) {
                    aPerson.lastNewSeq = aPhoto.id;
                }
            }
        }
    },
    notify: function() {
        this.doNext();
    },
    next: function() {
        this.mTimer.initWithCallback(this, 1000, 0);
    },
    doNext: function() {
        //dump("NEXT PLEASE\n");
        var inst = this;

        if(!this.mArray.length) {
            this.finish();
            return;
        }
        var person = this.mArray.pop();

        var listener = {
            onSearchResult: function(aEnumerator) {
            //dump("b search result\n");
                while(aEnumerator.hasMoreElements()) {
                    var photo = aEnumerator.getNext();
                    inst.updatePerson(person, photo);
                    break;
                }
                inst.next();
            },
            onError: function(aError) {
            //dump("b search eror\n");
                inst.finish();
            },
        }
        this.mSvc.search(listener, person.id, "", "", 1, 1);
    },
}

photoBucketPhotoService.prototype.getMostRecentPhotoForList = function(aListener, aEnumerator) {
    var mg = new MultiGetter();
    mg.init(this, aListener, aEnumerator);
}

photoBucketPhotoService.prototype.search = function(aListener, aUserid, aTags, aFreeText, aCount, aPage) {
    var inst = this;
    
    var myListener = {
        onResult: function (aXML) {
            var rval = inst.handlePhotosResult(aXML);
            var enum = {
                hasMoreElements: function() {
                    return (rval.length>0);
                },
                getNext: function() {
                    return rval.shift();
                },

            }
            aListener.onSearchResult(enum);
        },
        onError: function (aError) {
            aListener.onError(aError);
        },
    }
    var params = new Object();
    
    if (aFreeText && aFreeText.length)
        params.query = aFreeText;
    if (aTags && aTags.length)
        params.query = aTags;
    if (aUserid)
        params.username = aUserid;
    if (aPage)
        params.page = aPage;
    params.num = '50';
         
    var dict = flock.photo.util.params2Dictionary(params);
    if (params.query != null)
        this.call(myListener, "search", dict);
    else if (params.username != null)
        this.authenticatedCall(myListener, "getrecentuserphotos", dict);
    else
        this.call(myListener, "getrecentphotos", dict);
}

photoBucketPhotoService.prototype.createAlbum = function(aListener, aTitle) {
    var newAlbum = Components.classes[FLOCK_PHOTO_ALBUM_CONTRACTID].createInstance(flockIPhotoAlbum);
    newAlbum.title = aTitle;
    newAlbum.id = aTitle;
    aListener.onCreateAlbum(newAlbum);
}

photoBucketPhotoService.prototype.handleAlbumsResult = function(aXML) {
    var rval = new Array();
    var albumList = aXML.getElementsByTagName("photoalbum");
    for (i = 0; i < albumList.length; i++) {
        var album = albumList[i];
        var title = album.getAttribute("name");
        var id = title;

        var newAlbum = Components.classes[FLOCK_PHOTO_ALBUM_CONTRACTID].createInstance(flockIPhotoAlbum);
        newAlbum.id = id;
        newAlbum.title = title;

        rval.push(newAlbum);
    }    
    return rval;
}

photoBucketPhotoService.prototype.getAccountStatus = function(aListener) {
    var inst = this;
    var myListener = {
        onResult:function (aXML) {
            try {
                var photoAccount = Components.classes[FLOCK_PHOTO_ACCOUNT_CONTRACTID].createInstance(flockIPhotoAccount);
                photoAccount.username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;;
                photoAccount.maxSpace = aXML.getElementsByTagName('megabytes_allowed')[0].firstChild.nodeValue;
                photoAccount.usedSpace = aXML.getElementsByTagName('megabytes_used')[0].firstChild.nodeValue;
                var isPremium = aXML.getElementsByTagName('premium')[0].firstChild.nodeValue;
                photoAccount.maxFileSize = '';
                photoAccount.usageUnits = "megabytes";
                photoAccount.isPremium = (isPremium == "true" ? true:false);
                aListener.onGetAccountStatus(photoAccount);
                
            } catch (ex) {
                debug('getaccountstatus error >>>>>>>>>>>>>>>>>>>>' + ex + '\n');   
            }  
        },
        onError: function (aError) {
            aListener.onError(aError);
        },  
    }   
    var params = new Object();
    var dict = params2Dictionary(params);
    this.authenticatedCall(myListener, "getuserinfo", dict);
    
}

photoBucketPhotoService.prototype.getAlbums = function(aListener) {
        var inst = this;
        var myListener = {
            onResult: function (aXML) {
                try {
                var rval = inst.handleAlbumsResult(aXML);
                var enum = {
                    hasMoreElements: function() {
                        return (rval.length>0);
                    },
                    getNext: function() {
                        return rval.shift();
                    },
                }
                aListener.onGetAlbumsResult(enum);
                } catch(e) {
                debug(e);
                    aListener.onError(null);
                }
            },
            onError: function (aXML) {
                aListener.onError(aXML);
            },
        }
        var params = new Object();
        params.recurse = "true";
        params.view = "flat";
        params.media = "none";
        var dict = params2Dictionary(params);
        this.authenticatedCall(myListener, "getuseralbum", dict);
}

photoBucketPhotoService.prototype.state = flockIPhotoAPI.LOGGED_OUT;
photoBucketPhotoService.prototype.mAuthUser = null;
photoBucketPhotoService.prototype.mUploader = null;
photoBucketPhotoService.prototype.mApiUrl = PHOTOBUCKET_API_URL;
photoBucketPhotoService.prototype.iconUrl = "chrome://browser/skin/flock/photo/photobucketIcon.png";

/* features for service */
photoBucketPhotoService.prototype.supportsFeature = function(aFeature) {
    var supports = {};
    supports.tags = false;
    supports.title = false;
    supports.fileName = true;
    supports.contacts = false;
    supports.privacy = false;
    return (supports[aFeature] == true);
}

photoBucketPhotoService.prototype.mSupportsTitle = false;
photoBucketPhotoService.prototype.mSupportsTags = false;
photoBucketPhotoService.prototype.mSupportsContacts = false;
photoBucketPhotoService.prototype.mSupportsPrivacey = false;
photoBucketPhotoService.prototype.mFirstRunUsername = "flockteam";

photoBucketPhotoService.prototype.init = function () { /*{{{*/
   this.mAuthUser = {};
   this.checkPersistedToken();
   this.setFirstRunUser();
   
}/*}}}*/

photoBucketPhotoService.prototype.checkPersistedToken = function () { /*{{{*/
    var token = flock_getCharPref(PHOTOBUCKET_TOKEN_PREF);
    if (token) {
        this.debug('the token were checking is ' + token);
        // try to get the session from the persisted token
        var inst = this;
        var myListener = {
            onResult: function(aXML) {
                try {
                    var resp = aXML.getElementsByTagName('response');
                    if (resp[0].getAttribute('stat') == 'ok') {
                        var username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;
                        var sessionkey = aXML.getElementsByTagName('session_key')[0].firstChild.nodeValue;
                        inst.mApiUrl = aXML.getElementsByTagName('url')[0].firstChild.nodeValue;
                        inst.getAuthUser().id = username;
                        inst.getAuthUser().username = username;
                        inst.getAuthUser().fullname = username;
                        inst.getAuthUser().sessionkey = sessionkey;
                                             
                        inst.state = flockIPhotoAPI.LOGGED_IN;
                    } else {
                        inst.state = flockIPhotoAPI.LOGGED_OUT;
                        inst.mAuthUser = null;
                        this.mUploader = null;
                    }
                }
                catch(e) {
                    debug(e);
                }
            },   
            onError: function(aErr) {
                return false;
            },   
        } 
        var params = {};      
        params.service_token = token;
        inst.call(myListener, "getsession", flock.photo.util.params2Dictionary(params)); 
    }
    //this.mAuthUser.sessionkey = null;
}/*}}}*/

photoBucketPhotoService.prototype.getAuthState = function () { /*{{{*/
    return this.state;
}/*}}}*/

photoBucketPhotoService.prototype.__defineGetter__('supportsTitle', function () { return this.mSupportsTitle; })
photoBucketPhotoService.prototype.__defineGetter__('supportsTags', function () { return this.mSupportsTags; })
photoBucketPhotoService.prototype.__defineGetter__('supportsContacts', function () { return this.mSupportsContacts; })
photoBucketPhotoService.prototype.__defineGetter__('supportsPrivacey', function () { return this.mSupportsPrivacey; })
photoBucketPhotoService.prototype.__defineGetter__('firstRunUser', srv_getFirstRunUser)
photoBucketPhotoService.prototype.__defineGetter__('authState', function () { return this.state; })

function srv_getFirstRunUser() {
    var overrideUser = flock_getCharPref('flock.photo.photobucket.firstrun.user');
    return (overrideUser != null)?overrideUser : "flockteam";
}

photoBucketPhotoService.prototype.logout = function () { /*{{{*/
    this.state = flockIPhotoAPI.LOGGED_OUT;
    this.mAuthUser = null;
    this.mUploader = null;
    // let's not try this token again on browser restart
    flock_setCharPref(PHOTOBUCKET_TOKEN_PREF, '')
}/*}}}*/

photoBucketPhotoService.prototype.getToken = function(aListener) {
     var inst = this;
     var tokenListener = {
        onResult: function (aXML) {
      
           try {
                var resp = aXML.getElementsByTagName('response');
                if (resp[0].getAttribute('stat') == 'ok') {
                    //rval = resp[0].firstChild.nodeValue;
                    aListener.onResult(aXML);
                } else {
                    aListener.onError(null);//TODO - figure out errors
                    //rval = -1;
                }
                
            }
            catch(e) {
                debug(e);
            }
        },
        onError: function (aError) {
        inst.debug(aError.errorString);
            aListener.onError(aError);
        },
    }
    var params = new Object();
    this.call(tokenListener, "getservicetoken", flock.photo.util.params2Dictionary(params));
}

photoBucketPhotoService.prototype.debug = function(aString) {
    debug("\n\nphotoBucketPhotoService: " + aString + "\n");
}

photoBucketPhotoService.prototype.doLogin = function (aListener) { /*{{{*/
    var windowManager = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
    var windowManagerInterface = windowManager.QueryInterface( Components.interfaces.nsIWindowMediator);
    var topWindow = windowManagerInterface.getMostRecentWindow(null);
    topWindow.open(PHOTOBUCKET_LOGIN_URL, "_blank", "chrome,close,titlebar,resizable=yes,toolbar,dialog=no,scrollbars=yes,modal,centerscreen");

}/*}}}*/

photoBucketPhotoService.prototype.login = function (aListener) { /*{{{*/
    var inst = this;
    if(this.state==flockIPhotoAPI.LOGGING_IN) return;
    this.state=flockIPhotoAPI.LOGGING_IN;

    var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
    var inst = this;
    timerFunc = {
        notify: function(aTimer) {
            inst.doLogin(aListener);
        },
    }
    timer.initWithCallback(timerFunc,0,0);//re-check token

}/*}}}*/



photoBucketPhotoService.prototype._isLoggedIn = function (aListener) { /*{{{*/
    if (this.mAuthUser == null)
        return;
    return (this.getAuthUser().sessionkey!=null);
}/*}}}*/

photoBucketPhotoService.prototype.__defineGetter__('isLoggedIn', function () { return this._isLoggedIn(); })

photoBucketPhotoService.prototype.dictionary2Params = function (aDictionary) { /*{{{*/
    var params = {};
    var obj = {};
    var count = {};
    var keys = aDictionary.getKeys(count, obj);
    for(var i=0;i<keys.length;++i) {
        var supports = aDictionary.getValue(keys[i]);
        var supportsString = supports.QueryInterface(Components.interfaces.nsISupportsString);
        var val = supportsString.toString();
        params[keys[i]] = val;
    }
    return params;
}/*}}}*/

photoBucketPhotoService.prototype.getAuthUser = function () { /*{{{*/
    return this.mAuthUser;
}/*}}}*/

photoBucketPhotoService.prototype.getAuthPerson = function () { /*{{{*/
    var user = this.mAuthUser
    var person = {};
    person.id = user.id;
    person.fullname = user.fullname;
    person.username = user.username;
    person.service = this;
    return person;

}/*}}}*/

//ASY - this should be implemented
photoBucketPhotoService.prototype._isUploading = function () { /*{{{*/
    return false;
}/*}}}*/

photoBucketPhotoService.prototype.__defineGetter__('isUploading', function () { return this._isUploading(); })

photoBucketPhotoService.prototype.doCall = function (aListener, aMethod, aDictionary, aAuth) { /*{{{*/
    var inst = this;
    var params = flock.photo.util.dictionary2Params(aDictionary);   
    var url = this.buildUrl(inst.mApiUrl, aMethod, params, aAuth);

    this.debug(url + " < call\n");
    this.req = Components.classes[XMLHTTPREQUEST_CONTRACTID].createInstance(nsIXMLHttpRequest);
    this.req.open('GET', url, true);
    var req = this.req;
    this.req.onreadystatechange = function (aEvt) {
        if(req.readyState == 4) {
            try {
                if(req.status/100 == 2) {
                    //inst.debug(req.responseText);
                    var rsp = req.responseXML.getElementsByTagName("response")[0];
                    var stat = rsp.getAttribute("stat");
                    if(stat!="ok") {
                        aListener.onError(inst.getError('SERVICE_ERROR', req.responseXML));
                    }
                    else {
                        aListener.onResult(req.responseXML);
                    }
                }
                else {
                    aListener.onError(inst.getError('HTTP_ERROR', req.responseXML));
                }
            } catch(e) {
                //inst.debug(e);
                // beware of constant
                aListener.onError(inst.getError('HTTP_ERROR',null, "9001"));
            }
        }
    }
    this.req.send(null);
    
}/*}}}*/

photoBucketPhotoService.prototype.call = function (aListener, aMethod, aDictionary, aAuth) { /*{{{*/
    return this.doCall(aListener, aMethod, aDictionary, aAuth);   
}/*}}}*/

photoBucketPhotoService.prototype.authenticatedCall = function (aListener, aMethod, aDictionary) { /*{{{*/
    //XXX FIX ME 
    this.debug('authenticatedCall ' + aMethod);
    if(!this.isLoggedIn) {
       this.debug('not logged in, so trying unauth call for ' + aMethod);
       return this.call(aListener, aMethod, aDictionary);
    }
    this.debug('+++++' + this.getAuthUser().sessionkey);
    return this.call(aListener, aMethod, aDictionary, true);
}/*}}}*/

photoBucketPhotoService.prototype.buildUrl = function (aUrl, aMethod, aParams, aAuth) { /*{{{*/
    var qs = "";
    var strConcat = "";
    for(var p in aParams) {
        qs += "&" + escape(p) + "=" + escape(aParams[p]); 
        strConcat += escape(aParams[p]);
    }
    var rval = aUrl  
            + "?method=" + aMethod
            + "&version=" + PHOTOBUCKET_API_VERSION;
    if(aAuth) {
        rval += "&session_key=" + this.getAuthUser().sessionkey;
    }
    rval += "&scid=" + PHOTOBUCKET_SCID 
            + qs
            + "&sig=" + this.buildSig(aMethod, aParams, aAuth);
    this.debug("this is your buildUrl: " + rval + "\n");
    return rval;
}/*}}}*/

photoBucketPhotoService.prototype.buildSig = function (aMethod, aParams, aAuth) { /*{{{*/
    var qs = "";
    var strConcat = "";
    for(var p in aParams) {
        qs += "&" + escape(p) + "=" + escape(aParams[p]); 
        if (p == 'service_token')
            strConcat += escape(aParams[p]);
    }
    
    this.debug(strConcat + "<<<<<<<<strconcat\n");
    var preHash = aMethod +  PHOTOBUCKET_PK + PHOTOBUCKET_SCID + strConcat + (aAuth?this.getAuthUser().sessionkey:"");
    this.debug('going to sig::: ' + preHash);
    
    var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
    converter.charset = "UTF-8";
    
    var inputStream = converter.convertToInputStream(preHash);
    return hex_md5_stream(inputStream);
}/*}}}*/

photoBucketPhotoService.prototype.doLogin = function (aListener) { /*{{{*/
    var inst = this;
    var myListener = {
        onResult: function(aXML) {
            var windowManager = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
            var windowManagerInterface = windowManager.QueryInterface( Components.interfaces.nsIWindowMediator);
            var topWindow = windowManagerInterface.getMostRecentWindow(null);

            var params = {};
            params.service_token = aXML.getElementsByTagName('service_token')[0].firstChild.nodeValue;
            // persist the token
            flock_setCharPref(PHOTOBUCKET_TOKEN_PREF, params.service_token);
            var loginUrl = inst.buildUrl(PHOTOBUCKET_LOGIN_URL, "login", params);
            var url = "chrome://browser/content/flock/photo/photoLoginWindow.xul?" + "url=" + escape(loginUrl) + "&finalString=" + escape("You have granted");
            topWindow.open(url,"_blank", "chrome,close,titlebar,resizable=yes,toolbar,dialog=no,scrollbars=yes,modal,centerscreen");
            var mySessionListener = {
                onResult: function(aXML) {
                    var username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;
                    var sessionkey = aXML.getElementsByTagName('session_key')[0].firstChild.nodeValue;
                    inst.mApiUrl = aXML.getElementsByTagName('url')[0].firstChild.nodeValue;
                    inst.getAuthUser().id = username;
                    inst.getAuthUser().username = username;
                    inst.getAuthUser().fullname = username;
                    inst.getAuthUser().sessionkey = sessionkey; 
                    inst.state = flockIPhotoAPI.LOGGED_IN;
                    aListener.onAuth();
                },   
                onError: function(aErr) {
                    inst.state = flockIPhotoAPI.LOGGED_OUT;
                    aListener.onError(aErr);
                },   
            }
            
            inst.call(mySessionListener, "getsession", flock.photo.util.params2Dictionary(params));
            // we are waiting till window closes
        },
        onError: function(aErr) {
            inst.state = flockIPhotoAPI.LOGGED_OUT;
            aListener.onError(aErr);
        },
    }
    
    if (!this.mAuthUser) {
        this.mAuthUser = {};   
    }
    this.getToken(myListener);
}

photoBucketPhotoService.prototype.upload2 = function (aListener, aUpload, aFile) {
    var inst = this;
    if(!this.isLoggedIn) throw "Error - assertion failure, should be loggedin";
    var sigParams = {};
    var myParams = {};


this.debug('uploading to album ' + aUpload.album);
    myParams.version = PHOTOBUCKET_API_VERSION;
    myParams.scid = PHOTOBUCKET_SCID;
    myParams.method = 'upload';
    myParams.description = aUpload.description;
    myParams.filename = aUpload.fileName;
    myParams.album_name = aUpload.album;
    myParams.session_key = this.getAuthUser().sessionkey;
    myParams.sig = this.buildSig('upload', sigParams, true);
    
    sigParams.session_key = this.getAuthUser().sessionkey;
   
    this.mUploader = new PhotoUploader();
    this.mUploader.setEndpoint(inst.mApiUrl);
    this.mUploader.photoParam = "uploadfile";

    var myListener = {
        onResult: function(aXML) {
            var photo = aXML.getElementsByTagName("photo")[0];
            if(photo) {
                inst.debug('uploaded the pic named ' + photo.getAttribute('name'));
                var rval = inst.handlePhotosResult(aXML);
                aListener.onUploadComplete(aUpload);
                aListener.onUploadFinalized(aUpload, rval[0]);
            }
            else {
                aListener.onError(inst.getError('SERVICE_ERROR', aXML));
            }
        },
        onError: function(aErrorCode) {
            if (aErrorCode) {
                aListener.onError(inst.getError('HTTP_ERROR', null, aErrorCode));
            } else {
                aListener.onError(inst.getError('SERVICE_ERROR', null));
            }
        },
    }
    this.mUploader.upload(myListener, aFile, myParams, aUpload);
}

photoBucketPhotoService.prototype.getError = function(aErrorType, aXML, aHTTPErrorCode) {
    var errorNode;
    var code;
    var serverErrorMessage;
    var error = Components.classes[FLOCK_ERROR_CONTRACTID].createInstance(flockIError);
    
    if (aErrorType == "HTTP_ERROR") {
        error.errorCode = aHTTPErrorCode;
        return error;
    } else if (aErrorType == "SERVICE_ERROR") {
        try {
            errorNode = aXML.getElementsByTagName("error")[0];
            code = errorNode.getAttribute('code');
            serverErrorMessage = errorNode.getAttribute('msg');
            this.debug(code + "\n" + serverErrorMessage);
        } catch (ex) {
            code = "-1"
        }
        
        if (!code) code = "-1"
        switch (code) {
             case "-1":
                 error.errorCode = error.PHOTOSERVICE_INVALID_RESPONSE;
             break;
             
             case "007":
                 error.errorCode = error.PHOTOSERVICE_LOGIN_FAILED;
             break;
             
             case "008":
                 error.errorCode = error.PHOTOSERVICE_LOGIN_FAILED;
             break;
             
             case "102":
                 error.errorCode = error.PHOTOSERVICE_INVALID_USER;
             break;
             
             case "103":
                error.errorCode = error.PHOTOSERVICE_INVALID_SEARCH_QUERY;
             break;
             
             case "104":
                error.errorCode = error.PHOTOSERVICE_INVALID_SEARCH_QUERY;
             break;

             case "105":
                error.errorCode = error.PHOTOSERVICE_INVALID_UPLOAD_FILE;
             break;
             
            case "106":
                error.errorCode = error.PHOTOSERVICE_INVALID_SEARCH_QUERY;
             break;
             
            case "107":
                error.errorCode = error.PHOTOSERVICE_INVALID_ALBUM;
            break;
            
            case "108":
                error.errorCode = error.PHOTOSERVICE_PRIVATE_ALBUM;
            break;
            
            case "109":
                error.errorCode = error.PHOTOSERVICE_PRIVATE_ALBUM;
            break;
            
            case "110":
                error.errorCode = error.PHOTOSERVICE_NO_FILE_UPLOADED;
            break;
            
            case "111":
                error.errorCode = error.PHOTOSERVICE_ALBUM_IS_OVER_SIZE_LIMIT;
            break;
            
            case "112":
                error.errorCode = error.PHOTOSERVICE_INVALID_UPLOAD_FILE;
            break;
            
            case "113":
                error.errorCode = error.PHOTOSERVICE_DUPLICATE_FILENAME;
            break;
            
            default:
                error.errorCode = error.PHOTOSERVICE_UNKNOWN_ERROR;
                if (serverErrorMessage && serverErrorMessage.length)
                    error.serviceErrorString = serverErrorMessage;
            break;
             
        }
        this.debug("errorcode2String " + error.errorString);
        return error;
    }
}
photoBucketPhotoService.prototype.upload = function (aListener, aFile, aDictionary, aUpload) { /*{{{*/
    var inst = this;
    if(!this.isLoggedIn) throw "Error - assertion failure, should be loggedin";
    var params = this.dictionary2Params(aDictionary);
    this.mUploader = new PhotoUploader();
    var myListener = {
        onResult: function(aXML) {
            var photo = aXML.getElementsByTagName("photo")[0];
            inst.debug('uploaded the pic named ' + photo.getAttribute('name'));
            if(photo) {
                aListener.onUploadComplete(aUpload);
            } else {
                aListener.onError(inst.getError('SERVICE_ERROR', aXML));
            }
        },
        onError: function(aXML) {
            aListener.onError(inst.getError('SERVICE_ERROR', aXML));
        },
    }
    for(var p in params) {
        debug(p + " " + params[p] + "\n");
    }   
    var sigParams = {};
    var myParams = {};
    
    myParams.version = PHOTOBUCKET_API_VERSION;
    myParams.scid = PHOTOBUCKET_SCID;
    myParams.method = 'upload';
    myParams.description = params.description;
    myParams.title = params.title;
    myParams.session_key = this.getAuthUser().sessionkey;
    myParams.sig = this.buildSig('upload', sigParams, true);
    
    sigParams.session_key = this.getAuthUser().sessionkey;
   
    this.mUploader.setEndpoint(inst.mApiUrl);
    this.mUploader.photoParam = "uploadfile";
    this.mUploader.upload(myListener, aFile, myParams, aUpload);
}/*}}}*/

photoBucketPhotoService.prototype.cancelUpload = function () { /*{{{*/
    try { this.mUploader.req.abort(); }
    catch(e){};
}/*}}}*/

photoBucketPhotoService.prototype.setFirstRunUser = function () { /*{{{*/
  // set the first run user -- this is useful for  partner builds can set
  // set a pref on install to see what photobucket accoutn they want to default to
   var pbFirstRunUser = flock_getCharPref('flock.photo.photobucket.firstrun.user')
   if (pbFirstRunUser)
    flock_setCharPref('flock.photo.firstrun.user', pbFirstRunUser)
   else
    flock_setCharPref('flock.photo.firstrun.user', 'jointheflock')

}/*}}}*/



loadLibraryFromSpec('chrome://browser/content/flock/common/flocksafe.js');
loadLibraryFromSpec("chrome://browser/content/flock/common/md5.js");
loadLibraryFromSpec("chrome://browser/content/flock/photo/photoAPI.js");
loadLibraryFromSpec("chrome://browser/content/flock/photo/photo.js");

function loadLibraryFromSpec(aSpec) {
    var loader = Components.classes['@mozilla.org/moz/jssubscript-loader;1']
        .getService(Components.interfaces.mozIJSSubScriptLoader);

    loader.loadSubScript(aSpec);
}

photoBucketPhotoService.prototype.flags = nsIClassInfo.SINGLETON;
photoBucketPhotoService.prototype.classDescription = "Photo Bucket Photo Service";
photoBucketPhotoService.prototype.getInterfaces = function (count) {
    var interfaceList = [flockIPhotoAPI, nsIClassInfo];
    count.value = interfaceList.length;
    return interfaceList;
}
photoBucketPhotoService.prototype.getHelperForLanguage = function (count) {return null;}/*}}}*/

// the nsISupports implementation/*{{{*/
photoBucketPhotoService.prototype.QueryInterface =
function (iid) {
    if (!iid.equals(flockIPhotoAPI) && 
        !iid.equals(nsIClassInfo) &&
        !iid.equals(nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    if (iid.equals(nsIRDFDataSource) && !this.dataSourceSetup) {
    }
    return this;
}/*}}}*/

// Module implementation/*{{{*/
var PhotoModule = new Object();

PhotoModule.registerSelf =
function (compMgr, fileSpec, location, type)
{
    compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);

    compMgr.registerFactoryLocation(PHOTOBUCKET_PHOTO_CID, 
                                    "Photo Bucket Photo JS Component",
                                    PHOTOBUCKET_PHOTO_CONTRACTID, 
                                    fileSpec, 
                                    location,
                                    type);
    //necessary category registration
    var catmgr = Components.classes["@mozilla.org/categorymanager;1"]
        .getService (Components.interfaces.nsICategoryManager);
    catmgr.addCategoryEntry('flockIPhotoAPI', 'photobucket', PHOTOBUCKET_PHOTO_CONTRACTID, true, true);
}

PhotoModule.getClassObject =
function (compMgr, cid, iid) {
    if (!cid.equals(PHOTOBUCKET_PHOTO_CID))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    
    if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    
    return PhotoServiceFactory;
}

PhotoModule.canUnload =
function(compMgr)
{
    debug("Unloading component.");
    return true;
}/*}}}*/
    
/* factory object *//*{{{*/
var PhotoServiceFactory = new Object();

PhotoServiceFactory.createInstance =
function (outer, iid) {
    debug("CI: " + iid + "\n");
    if (outer != null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;

    return (new photoBucketPhotoService()).QueryInterface(iid);
}/*}}}*/

/* entrypoint *//*{{{*/
function NSGetModule(compMgr, fileSpec) {
    return PhotoModule;
}/*}}}*/
