function PasswordDatasource(){if(!PasswordDatasource.encryptor)PasswordDatasource.encryptor=CreateAESManager();if(!PasswordDatasource.passwordCache)PasswordDatasource.passwordCache={};if(!PasswordDatasource.exceptionCache){var a=localStorage.getItem("pwstoreExceptions");PasswordDatasource.exceptionCache=a?JSON.parse(a):{}}PasswordDatasource.passwordDb||this.LoadPasswordDb();this.orderIsImportant=false}
PasswordDatasource.prototype={LoadPasswordDb:function(){if(Xmarks.Settings.Get("current-username")){PasswordDatasource.passwordCache={};var a=this;if(!PasswordDatasource.passwordDb)PasswordDatasource.passwordDb=openDatabase("pwstore","","Password files",38400);if(PasswordDatasource.passwordDb){var b=Xmarks.Settings.GetUserHash();PasswordDatasource.passwordDb.transaction(function(c){c.executeSql("CREATE TABLE '"+b+"' (rowId INTEGER PRIMARY KEY AUTOINCREMENT, domainKey TEXT, data TEXT);",[])},function(c){Xmarks.LogWrite("Failed to create new password db, it probably exists.  Attempting to load password data.  Error: "+
c.message);PasswordDatasource.passwordDb.transaction(function(d){d.executeSql("SELECT * FROM '"+b+"';",[],function(e,f){if(f&&f.rows)for(var g=0;g<f.rows.length;g++){var i=unescape(f.rows.item(g).domainKey),j=unescape(f.rows.item(g).data);j=eval(j);j.dbkey=f.rows.item(g).rowId;a.AddNodeToCache(i,j)}})},function(d){Xmarks.LogWrite("Unable to load from password store ["+b+"]: "+d.message)},function(){Xmarks.LogWrite("Finished loading from password store ["+b+"]")})},function(){Xmarks.LogWrite("Password db successfully created, no need to read from it")})}else Xmarks.LogWrite("ERROR: Unable to open password db!")}else Xmarks.LogWrite("Not signed in, no password data to load")},
CheckForMatch:function(a){var b=Xmarks.ExtractProtocolAndDomain(a);if(!b)return[];var c=PasswordDatasource.passwordCache[b];if(!c)return[];for(var d=[],e=0;e<c.length;e++){var f=c[e];if(!f["private"])try{this.decode(f)}catch(g){Xmarks.LogWrite("Exception while decrypting domain match ["+b+"] for nid ["+f.nid+"]");continue}a.indexOf(f["private"].hostname)==0&&d.push(f["private"])}return d},ClearLocalStore:function(a){PasswordDatasource.passwordCache={};if(!PasswordDatasource.passwordDb){Xmarks.LogWrite("ERROR: Unable to open password db!");
a(-1)}var b=Xmarks.Settings.GetUserHash();PasswordDatasource.passwordDb.transaction(function(c){c.executeSql("DELETE FROM '"+b+"';",[])},function(c){Xmarks.LogWrite("Unable to clear password store ["+b+"]: "+c.message);a(-1)},function(){Xmarks.LogWrite("Password store ["+b+"] cleared.");a(0)})},AcceptNodes:function(a,b){Xmarks.LogWrite("Adding new nodes to password store");var c=a.Node(NODE_ROOT);if(c.children)for(var d=0;d<c.children.length;d++){var e=a.Node(c.children[d]);try{e.ntype=="password"?
this.AddNode(e):Xmarks.LogWrite("Skipping unknown node type: "+JSON.stringify(e))}catch(f){Xmarks.LogWrite("Exception ["+JSON.stringify(f)+"] while adding password node: "+JSON.stringify(e))}}Xmarks.LogWrite("Removing unused nids from password store");c=[];for(var g in PasswordDatasource.passwordCache)if(PasswordDatasource.passwordCache.hasOwnProperty(g))for(d=0;d<PasswordDatasource.passwordCache[g].length;d++){e=PasswordDatasource.passwordCache[g][d];a.Node(e.nid,false,true)||c.push(e.nid)}for(d=
0;d<c.length;d++)this.RemoveNode(c[d]);b(0)},AddNode:function(a){a["private"]||this.decode(a);var b=Xmarks.ExtractProtocolAndDomain(a["private"].hostname),c=this.AddNodeToCache(b,a);if(c)if(PasswordDatasource.passwordDb){var d=Xmarks.Settings.GetUserHash(),e=a.toSource(),f="INSERT INTO '"+d+"' ('domainKey', 'data') VALUES ('"+escape(b)+"', '"+escape(e)+"');";if(c=="update"&&a.dbkey)f="UPDATE '"+d+"' SET 'data'='"+escape(e)+"' WHERE 'rowId'='"+a.dbkey+"';";PasswordDatasource.passwordDb.transaction(g,
i,j);var g=function(h){h.executeSql(f,[])},i=function(h){Xmarks.LogWrite("Error updating password store ["+d+"]: "+h.message)},j=function(){}}else Xmarks.LogWrite("ERROR: Unable to open password db!")},AddNodeToCache:function(a,b){if(PasswordDatasource.passwordCache[a]){for(var c=0;c<PasswordDatasource.passwordCache[a].length;c++)if(PasswordDatasource.passwordCache[a][c].nid==b.nid)if(PasswordDatasource.passwordCache[a][c].data==b.data)return null;else{Xmarks.LogWrite("Updating item ["+b.nid+"] in: "+
a);PasswordDatasource.passwordCache[a][c]=b;return"update"}Xmarks.LogWrite("Adding new password item to: "+a);PasswordDatasource.passwordCache[a].push(b)}else{Xmarks.LogWrite("Creating new domainKey for password item: "+a);PasswordDatasource.passwordCache[a]=[b]}return"add"},RemoveNode:function(a){Xmarks.LogWrite("Removing password node: "+a);for(var b in PasswordDatasource.passwordCache)if(PasswordDatasource.passwordCache.hasOwnProperty(b))for(var c=0;c<PasswordDatasource.passwordCache[b].length;c++){var d=
PasswordDatasource.passwordCache[b][c];if(d.nid==a){Xmarks.LogWrite("Found nid ["+a+"], removing from password store");PasswordDatasource.passwordCache[b].splice(c,1);if(d.dbkey){var e=Xmarks.Settings.GetUserHash();PasswordDatasource.passwordDb.transaction(f,g,i);var f=function(j){var h="DELETE FROM '"+e+"' WHERE rowId="+d.dbkey+";";Xmarks.LogWrite("Removing nid from db: "+h);j.executeSql(h,[])},g=function(j){Xmarks.LogWrite("Error remove nid from password store ["+e+"]: "+j.message)},i=function(){Xmarks.LogWrite("nid removed from password store ["+
e+"]")}}return true}}Xmarks.LogWrite("nid not found, skipping");return false},Merge:function(a,b,c){function d(k){if(!k.encodedPin){Xmarks.LogWrite("User canceled pin, canceling merge");c(-1)}Xmarks.Settings.Set(Xmarks.Settings.GetUserHash()+"password-pin",k.encodedPin,k.persist);e()}function e(){for(var k=f.children||[],o=k.length;o--;){var m=k[o],n=b.Node(m),l=a.Node(m,false,true);if(l){var p=h.decode(n);l=h.decode(l);if(p.password!=l.password){l=a.Node(m,true);delete l["private"];l.data=n.data}}else a.Do_insert(m,
h.GetSafeInsertAttrs(n))}delete h._oldpin;c&&c(0)}var f=b.Node(NODE_ROOT,false,true);if(f==null)c&&c(0);else{var g=a.Node(NODE_ROOT,false,true);if(g==null){var i=new Date;i={password:hex_md5(i.getTime().toString())};a.Do_insert(NODE_ROOT,{ntype:"folder",children:[],data:this.encrypt(JSON.stringify(i))});g=a.Node(NODE_ROOT,false,true)}if(!g.data){i=new Date;i={password:hex_md5(i.getTime().toString())};var j=a.Node(NODE_ROOT,true);delete j["private"];j.data=this.encrypt(JSON.stringify(i))}var h=this;
h._oldpin=Xmarks.Settings.Get(Xmarks.Settings.GetUserHash()+"password-pin","");if(h.VerifyPinData(h._oldpin,g.data))if(h.VerifyPinData(h._oldpin,f.data))e();else{Xmarks.LogWrite("Can't decode snode with current PIN, asking for a second");Xmarks.VerifyUserPin(d,function(k){return h.VerifyPinData(k,f.data)})}else{Xmarks.LogWrite("Can't decode dnode with current PIN, asking for a second");Xmarks.VerifyUserPin(d,function(k){return h.VerifyPinData(k,g.data)})}}},GetSafeInsertAttrs:function(a){this.encode(a);
return a.GetSafeAttrs()},decode:function(a){if(!a["private"]){var b=this.decrypt(a.data);if(b.substr(b.length-2)==",}")b=b.substring(0,b.length-2)+"}";a["private"]=JSON.parse(b)}return a["private"]},decrypt:function(a){if(!a){Xmarks.LogWrite("Cannot decrypt empty string");throw 3;}var b=PasswordDatasource.encryptor.decrypt(Base64.decode(Xmarks.Settings.Get(Xmarks.Settings.GetUserHash()+"password-pin","")),a),c=b.indexOf("password")>=0;if(!c&&this._oldpin!==undefined){b=PasswordDatasource.encryptor.decrypt(Base64.decode(this._oldpin),
a);c=b.indexOf("password")>=0}if(!c){Xmarks.LogWrite("Unable to decrypt data, invalid PIN?");throw 3;}return b},encrypt:function(a){return PasswordDatasource.encryptor.encrypt(Base64.decode(Xmarks.Settings.Get(Xmarks.Settings.GetUserHash()+"password-pin","")),a)},encode:function(a){if(!a.data)a.data=this.encrypt(JSON.stringify(a["private"]));return a.data},GenerateNid:function(a){return hex_md5("password|".concat(a.hostname,"|",a.formSubmitURL,"|",a.httpRealm,"|",a.usernameField,"|",a.passwordField,
"|",a.username))},VerifyPinData:function(a,b){Xmarks.LogWrite("Verifying PIN (Testing Node)");Xmarks.LogWrite("Node Stats: "+(b?b.length:"null"));var c=PasswordDatasource.encryptor.decrypt(a,b);Xmarks.LogWrite("Node Verification: "+(c.indexOf("password")>=0?"true":"false"));return c.indexOf("password")>=0},DoesPinUnlockLocalData:function(a){for(var b in PasswordDatasource.passwordCache)if(PasswordDatasource.passwordCache.hasOwnProperty(b))if(PasswordDatasource.passwordCache[b].length>0)return this.VerifyPinData(a,
PasswordDatasource.passwordCache[b][0].data);return false},ProvideNodes:function(a,b,c){var d=[],e;for(e in PasswordDatasource.passwordCache)if(PasswordDatasource.passwordCache.hasOwnProperty(e))for(var f=0;f<PasswordDatasource.passwordCache[e].length;f++){d.push(PasswordDatasource.passwordCache[e][f].nid);b.apply(a,[PasswordDatasource.passwordCache[e][f]])}e={password:hex_md5((new Date).getTime().toString())};d=new Node(NODE_ROOT,{ntype:"folder",children:d,"private":e,data:this.encrypt(JSON.stringify(e))});
b.apply(a,[d]);c.call(a,0)},compareNodes:function(a,b,c){if(a.nid==NODE_ROOT)return false;a=this.decode(a);var d=this.decode(b);if(a.password==d.password)return false;c.data=this.encode(b);return true},EncodeAll:function(){Xmarks.LogWrite("Encoding all passwords nodes");for(var a in PasswordDatasource.passwordCache)if(PasswordDatasource.passwordCache.hasOwnProperty(a))for(var b=0;b<PasswordDatasource.passwordCache[a].length;b++){var c=PasswordDatasource.passwordCache[a][b];try{this.encode(c);if(PasswordDatasource.passwordDb)if(node.dbkey){var d=
Xmarks.Settings.GetUserHash(),e=node.toSource(),f="UPDATE '"+d+"' SET 'data'='"+escape(e)+"' WHERE 'rowId'='"+node.dbkey+"';";PasswordDatasource.passwordDb.transaction(g,i,j);var g=function(k){k.executeSql(f,[])},i=function(k){Xmarks.LogWrite("Error updating password store ["+d+"]: "+k.message)},j=function(){}}else Xmarks.LogWrite("ERROR: No dbkey for nid! "+c.nid);else Xmarks.LogWrite("ERROR: Unable to open password db!")}catch(h){Xmarks.LogWrite("Unable to encode nid ["+c.nid+"]: "+JSON.stringify(h))}}},
DecodeAll:function(){Xmarks.LogWrite("Decoding all passwords nodes");for(var a in PasswordDatasource.passwordCache)if(PasswordDatasource.passwordCache.hasOwnProperty(a))for(var b=0;b<PasswordDatasource.passwordCache[a].length;b++){var c=PasswordDatasource.passwordCache[a][b];try{this.decode(c)}catch(d){Xmarks.LogWrite("Unable to decode nid ["+c.nid+"]: "+JSON.stringify(d))}}},GetPasswordList:function(){var a=[],b;for(b in PasswordDatasource.passwordCache)if(PasswordDatasource.passwordCache.hasOwnProperty(b))for(var c=
0;c<PasswordDatasource.passwordCache[b].length;c++){var d=PasswordDatasource.passwordCache[b][c];if(!d["private"])try{this.decode(d)}catch(e){Xmarks.LogWrite("Exception while decrypting domain match ["+b+"] for nid ["+d.nid+"]");continue}a.push({nid:d.nid,hostname:d["private"].hostname,username:d["private"].username,password:d["private"].password})}a.sort(function(f,g){if(f.hostname.toLowerCase()<g.hostname.toLowerCase())return-1;if(f.hostname.toLowerCase()>g.hostname.toLowerCase())return 1;return 0});
return a},AddException:function(a){a=Xmarks.ExtractProtocolAndDomain(a);if(PasswordDatasource.exceptionCache[a])Xmarks.LogWrite("Exception already exists for domain ["+a+"], ignoring.");else{Xmarks.LogWrite("Adding password domain exception ["+a+"]");PasswordDatasource.exceptionCache[a]=true;localStorage.setItem("pwstoreExceptions",JSON.stringify(PasswordDatasource.exceptionCache))}},RemoveException:function(a){a=Xmarks.ExtractProtocolAndDomain(a);if(!PasswordDatasource.exceptionCache[a]){Xmarks.LogWrite("Exception to remove doesn't exist ["+
a+"], ignoring.");return false}Xmarks.LogWrite("Removing password domain exception ["+a+"]");delete PasswordDatasource.exceptionCache[a];localStorage.setItem("pwstoreExceptions",JSON.stringify(PasswordDatasource.exceptionCache));return true},DeleteLocalExceptions:function(){Xmarks.LogWrite("Removing all password domain exceptions");PasswordDatasource.exceptionCache={};localStorage.setItem("pwstoreExceptions",JSON.stringify(PasswordDatasource.exceptionCache))},IsInExceptionList:function(a){a=Xmarks.ExtractProtocolAndDomain(a);
return PasswordDatasource.exceptionCache[a]},GetExceptionList:function(){return PasswordDatasource.exceptionCache},handleNidConflict:function(){return"local"}};
