Commit Bandit code to XPI.

This commit is contained in:
Jim Norman 2006-07-28 19:34:44 +00:00
parent 14fd6d8f9f
commit 61d8dab64c
8 changed files with 990 additions and 10 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -5,6 +5,181 @@ var bGlobalPrompt = 'true';
var consoleService = null;
var loginCounter = 0;
var sAttemptedHost = '';
var IAWindow;
var props = null;
var sCurrentPersona = "home";
var bShowValues = false;
function dumpRealms()
{
logMessage("dumpRealms called");
var realms = Packages.Jim.getRealms("jim_realms.xml");
}
function writeTestKey()
{
writeCASASetting("Bandit", "ldapdn", "cn=bandit1,o=novell");
writeCASASetting("Bandit", "password", "11111111");
writeCASASetting("Bandit", "ftoBalance", "100");
writeCASASetting("Bandit", "costCenter", "Marketing");
writeCASASetting("Bandit", "sn", "CASA Last Name");
writeCASASetting("Bandit", "facsimileTelephoneNumber", "111-222-3333");
writeCASASetting("Bandit", "postalAddress", "Los Angeles");
writeCASASetting("Bandit", "manager", "CASA Manager");
writeCASASetting("Bandit", "mail", "casa@novell.com");
writeCASASetting("Bandit", "telephoneNumber", "999-999-8888");
writeCASASetting("Bandit", "givenName", "CASA First name");
writeCASASetting("Bandit", "kBalance", "100000");
writeCASASetting("Bandit", "cn", "Casa");
writeCASASetting("Bandit", "title", "My House");
writeCASASetting("Bandit", "creditCard", "9999-8888-7777-6666");
writeCASASetting("Bandit", "salary", "The same as Steve Jobs");
}
function writeCASASetting(sID, sKey, sValue)
{
var obj = getMiCASAStore();
try
{
res = obj.miCASAWriteKey(sID, sKey, sValue);
}
catch (err)
{
alert(err);
}
}
function readTestKey()
{
readCASASetting("Bandit", "ldapdn");
readCASASetting("Bandit", "password");
}
function readCASASetting(sID, sKey)
{
var obj = getMiCASAStore();
var theValue = new String();
logMessage("Reading from CASA: " + sKey);
try
{
res = obj.miCASAReadKey(sID, sKey, theValue);
logMessage("rcode:"+res + " sValue:"+theValue.value);
}
catch (err)
{
logMessage(err);
}
return theValue.value;
}
function getCASAData(arraySelected)
{
var htData = new java.util.Hashtable();
for (var i=0; i<arraySelected.length; i++)
{
var sValue = readCASASetting("Bandit", arraySelected[i]);
logMessage("From CASA:" + sValue);
if ((sValue != null) && (sValue.length > 0))
{
htData.put(arraySelected[i], sValue);
}
}
return htData;
}
function getPeristentPersona()
{
try
{
var props = loadMapFileAndReturnProps();
var sPersona = props.getProperty("CurrentPersona");
return sPersona;
}
catch (error)
{
return "home";
}
}
function setLocalPersona()
{
sCurrentPersona = "home";
changePersona(sCurrentPersona);
if (bShowValues)
{
showAttribsWithValues();
}
}
function setEnterprisePersona()
{
sCurrentPersona = "work";
changePersona(sCurrentPersona);
if (bShowValues)
{
showAttribsWithValues();
}
}
function setCombinedPersona()
{
sCurrentPersona = "joined";
changePersona(sCurrentPersona);
if (bShowValues)
{
showAttribsWithValues();
}
}
function setCasaPersona()
{
sCurrentPersona = "CASA";
changePersona(sCurrentPersona);
if (bShowValues)
{
showAttribsWithValues();
}
}
function displayCurrentPersona()
{
var curPersonaIDElement = document.getElementById("curPersonaID");
if (curPersonaIDElement != null)
{
curPersonaIDElement.value = sCurrentPersona;
}
}
function changePersona(persona)
{
logMessage("Changing persona to: "+persona);
try
{
var props = loadMapFileAndReturnProps();
props.setProperty("CurrentPersona", persona);
storeProperties(props);
}
catch (err)
{
alert(err);
}
sCurrentPersona = persona;
displayCurrentPersona();
}
function logMessage(message)
{
@ -24,16 +199,207 @@ function logMessage(message)
}
}
function showConsole()
{
var jvm = Components.classes["@mozilla.org/oji/jvm-mgr;1"].getService(Components.interfaces.nsIJVMManager);
if (jvm != null)
{
jvm.showJavaConsole();
}
else
{
logMessage("No JVM");
alert("No JVM");
}
}
function displayUserDir()
{
alert(java.lang.System.getProperty("user.dir"));
}
function displayUserHome()
{
alert(java.lang.System.getProperty("user.home"));
}
function displayJavaInfo()
{
alert("Version: " + java.lang.System.getProperty("java.version") + "\r\nClassPath: " + java.lang.System.getProperty("java.class.path"));
}
function storeProperties(props)
{
var userdir = java.lang.System.getProperty("user.dir");
logMessage("looking for namemap.properties in " + userdir);
try
{
var os = java.io.FileOutputStream(userdir + "/namemap.properties");
props.store(os,null);
os.close();
}
catch (error)
{
alert(error);
}
}
function loadMapFileAndReturnProps()
{
try
{
var userdir = java.lang.System.getProperty("user.dir");
logMessage("looking for namemap.properties in " + userdir);
var is = java.io.FileInputStream(userdir + "/namemap.properties");
//alert(is);
props = new java.util.Properties();
props.load(is);
is.close();
logMessage(props);
return props;
}
catch (ioerror)
{
logMessage(ioerror);
}
}
function mapAttrib(sName)
{
if (sName == null) return;
if (sName.indexOf('opt.')==0)
{
//sName = sName.substring(4);
}
if (props == null)
{
props = loadMapFileAndReturnProps();
if (props == null)
{
logMessage("did not load props");
return sName;
}
}
var newName = props.getProperty(sName);
//alert(newName);
if (newName != null)
{
logMessage("found mapped name: " + newName);
return newName;
}
else
{
logMessage("did not find mapped name: " + sName);
return sName;
}
}
function mapName(sName)
{
if (sName.indexOf('opt.')==0)
{
//sName = sName.subString(4);
}
switch (sName)
{
case "cn":
return 'User_name';
break;
case "mail":
return 'Email_Address';
break;
case "mailStop":
return 'Post-office';
break;
case "jobCode":
return 'Job_Code';
break;
case "costCenter":
return 'Cost.Center';
break;
case "givenName":
return 'First_name';
break;
case "workForceId":
return 'EmployeID';
break;
}
return sName;
}
function handleIA(attribs)
{
var attribsRequested = attribs.split(',');
var params = '';
for (var i=0; i<attribsRequested.length; i++)
{
// TODO: map to readable name.
params += attribsRequested[i] + '=' + mapAttrib(attribsRequested[i]) + '&';
}
// test code
var sPassword = null;
var sPromptIAURL = "chrome://casa/content/ia.xul?" + params;
//if (confirm("show window?"))
{
{
var properties = "modal=yes,status=no,titlebar=no,toolbar=no,menubar=no,location=no,height=600,width=600";
IAWindow = window.openDialog(sPromptIAURL, null, properties);
//alert(IAWindow.document.getElementById('attribList'));
}
}
/*
alert("Screen Dimension\n" +
" width:" +
java.awt.Toolkit.getDefaultToolkit().getScreenSize().width +
" height:" +
java.awt.Toolkit.getDefaultToolkit().getScreenSize().height);
*/
try
{
java.lang.System.out.println(" java.version: " + java.lang.System.getProperty("java.version"));
java.lang.System.out.println("java.class.path: " + java.lang.System.getProperty("java.class.path"));
java.lang.System.out.println(" user.dir: " + java.lang.System.getProperty("user.dir"));
java.lang.System.out.println(" user.home: " + java.lang.System.getProperty("user.home"));
}
catch (jndierror)
{
alert(jndierror);
}
}
function websso()
{
//logMessage("PAGE LOADED");
//logMessage("PAGE LOADED");
var url = window._content.location.host;
var enabledsite = "no";
var ssosite = "no";
var fileindex = 0;
var obj = null;
if (url == null || url.length < 1)
{
logMessage("Host URL not found");
@ -51,11 +417,30 @@ function websso()
ssosite = "yes";
logMessage("Password field");
logMessage(para.item(j).name);
}
}
/*
var banditForm = window._content.document.getElementsByName("Bandit.Form");
if (banditForm != null)
{
alert("Found bandit form");
alert(banditForm.name);
var inputs = banditForm.hasChildNodes();
alert(inputs);
}
*/
if((para.item(j).type == "hidden") && (para.item(j).name == "bandit"))
{
handleIA(para.item(j).value);
return;
}
}
if (ssosite == "yes")
{
//alert('password field found');
var secretId = url;
var sKey = "";
@ -105,8 +490,7 @@ function websso()
}
var formToSubmit;
var formToSubmit;
// looping for inputs and setting the correct values
for (var j = 0; !(j >= para.length) ; j++)
{
@ -133,7 +517,7 @@ function websso()
if (para.item(j).type == "password")
{
formToSubmit = para.item(j).parentNode.parentNode;
// did we get it?
if (formToSubmit.type != "form")
{
@ -309,8 +693,462 @@ function promptuser()
var contentwidth = window.innerWidth;
window.moveTo((sw-contentwidth)/2 , (sh-contentheight)/2);
}
var bAllSelected = false;
function toogleAllAttribsOptional()
{
var lstBoxOpt = document.getElementById('attribListOptional');
var bttnSelectAll = document.getElementById('selectall');
if (bAllSelected)
{
lstBoxOpt.clearSelection();
bAllSelected = false;
bttnSelectAll.label = "Select All ";
}
else
{
lstBoxOpt.selectAll();
bAllSelected = true;
bttnSelectAll.label = " Clear All";
}
}
function selectAllAttribsOptional()
{
var lstBoxOpt = document.getElementById('attribListOptional');
var bttnSelectAll = document.getElementById('selectall');
lstBoxOpt.selectAll();
bAllSelected = true;
bttnSelectAll.label = " Clear All";
}
function selectAllAttribs()
{
var lstBox = document.getElementById('attribList');
lstBox.selectAll();
}
function getAllValues(arraySelected)
{
var sUser = readCASASetting("Bandit", "ldapdn");
var sPass = readCASASetting("Bandit", "password");
if (sUser != null)
{
logMessage("CASA returned:"+sUser);
}
else
{
logMessage("SecretID Bandit not found");
sUser = "cn=admin,o=novell";
}
var htData;
if (sCurrentPersona == "CASA")
{
htData = getCASAData(arraySelected);
}
else
{
try
{
logMessage(" ********* Calling IA");
htData = Packages.Jim.main2("cn=bandit1,o=Novell", sPass, null, sCurrentPersona, arraySelected);
logMessage(" ******** Returned");
}
catch (err)
{
logMessage(" ******** Error calling IA: " + err);
}
}
return htData;
}
function toogleShowHide()
{
var bttn = document.getElementById('bttnShowValues');
clearBoxes();
if (bShowValues)
{
bShowValues = false;
setAttribs();
if (bttn != null)
{
bttn.label = "Show Values";
}
}
else
{
bShowValues = true;
showAttribsWithValues();
if (bttn != null)
{
bttn.label = "Hide Values";
}
}
}
function showAttribsWithValues()
{
logMessage("showAttribsWithValues called");
bShowValues = true;
sCurrentPersona = getPeristentPersona();
displayCurrentPersona();
var parentHost = opener._content.location.host;
var label = document.getElementById('hostsite');
label.value = parentHost;
var url = document.location.toString();
var sAttrib = "";
var sDisplay = "";
var lstBox = document.getElementById('attribList');
var lstBoxOpt = document.getElementById('attribListOptional');
var params = url.substring(url.indexOf('?')+1).split('&');
var csvRequested = "";
//collect requested attribs and query IA for values
for (var i = 0; !(i > params.length - 1); i++)
{
sAttrib = params[i].substring(0, params[i].indexOf('='));
sDisplay = params[i].substring(params[i].indexOf('=')+1);
if (sAttrib.length > 0)
{
if (sAttrib.indexOf('opt') == 0)
{
csvRequested += sAttrib.substring(4);
}
else
{
csvRequested += sAttrib;
}
}
// append comma
if (i < params.length -2)
{
csvRequested += ',';
}
}
//alert(csvRequested);
// query all values requested
var arrayRequested = csvRequested.split(',');
var htValues = getAllValues(arrayRequested);
//alert(htValues);
// clear the current listboxes
clearBoxes();
var sCurrentValue = '';
for (var i = 0; !(i > params.length - 1); i++)
{
sAttrib = params[i].substring(0, params[i].indexOf('='));
sDisplay = params[i].substring(params[i].indexOf('=')+1);
if (sAttrib.length > 0)
{
// reset sCurrentValue
sCurrentValue = '';
if (sAttrib.indexOf('opt') == 0)
{
sCurrentValue = htValues.get(sAttrib.substring(4));
if (sCurrentValue != null)
{
sCurrentValue = " (" + sCurrentValue + ")";
}
else
{
sCurrentValue = '';
}
lstBoxOpt.appendItem(mapAttrib(sDisplay.substring(4)) + sCurrentValue, sAttrib.substring(4));
}
else
{
//alert("getting: " + sAttrib);
//alert(htValues);
sCurrentValue = htValues.get(sAttrib);
if (sCurrentValue != null)
{
sCurrentValue = " (" + sCurrentValue + ")";
}
else
{
sCurrentValue = '';
}
lstBox.appendItem(sDisplay + sCurrentValue, sAttrib);
}
}
}
selectAllAttribs();
centerPrompt();
}
function clearBoxes()
{
// clear required
var lstBox = document.getElementById('attribList');
for (var i=lstBox.getRowCount(); i>0; i--)
{
lstBox.removeItemAt(i-1);
}
// clear optional
lstBox = document.getElementById('attribListOptional');
for (var i=lstBox.getRowCount(); i>0; i--)
{
lstBox.removeItemAt(i-1);
}
}
function setAttribs()
{
logMessage("setAttribs called");
sCurrentPersona = getPeristentPersona();
changePersona(sCurrentPersona);
var parentHost = opener._content.location.host;
var label = document.getElementById('hostsite');
label.value = parentHost;
var url = document.location.toString();
var sAttrib = "";
var sDisplay = "";
var lstBox = document.getElementById('attribList');
var lstBoxOpt = document.getElementById('attribListOptional');
var params = url.substring(url.indexOf('?')+1).split('&');
for (var i = 0; !(i > params.length - 1); i++)
{
sAttrib = params[i].substring(0, params[i].indexOf('='));
sDisplay = params[i].substring(params[i].indexOf('=')+1);
if (sAttrib.length > 0)
{
if (sAttrib.indexOf('opt') == 0)
{
lstBoxOpt.appendItem(mapAttrib(sDisplay.substring(4)), sAttrib.substring(4));
}
else
{
lstBox.appendItem(sDisplay, sAttrib);
}
}
}
selectAllAttribs();
centerPrompt();
}
function getSelectedPersona()
{
var personaLst = document.getElementById('theList');
var personaSelected = personaLst.selectedItems;
return personaSelected[0].value
}
function getSelectedAttribsOptional()
{
var lstBox = document.getElementById('attribListOptional');
var selected = lstBox.selectedItems;
return selected;
}
function displaySelectedAttribsOptional()
{
var selected = getSelectedAttribsOptional();
for (var i=0; i<selected.length; i++)
{
alert(selected[i].value);
}
}
function getSelectedAttribs()
{
var lstBox = document.getElementById('attribList');
var selected = lstBox.selectedItems;
return selected;
}
function displaySelectedAttribs()
{
var selected = getSelectedAttribs();
for (var i=0; i<selected.length; i++)
{
alert(selected[i].value);
}
}
function displayAllSelectedAttribs()
{
displaySelectedAttribs();
displaySelectedAttribsOptional();
}
function getMiCASAStore()
{
const cid = "@novell.com/CASA/MiCASAKeys;1";
//if (obj == null)
{
obj = Components.classes[cid].createInstance();
//alert('test3');
obj = obj.QueryInterface(Components.interfaces.IMiCASAKeys);
//alert(obj);
}
return obj;
}
function getBanditUsername()
{
logMessage("getBanditUsername");
var theValue = new String();
var obj = getMiCASAStore();
logMessage(obj);
if (obj != null)
{
var res = obj.miCASAReadKey("Bandit", "ldapdn", theValue);
logMessage("MiCASAReadKey returnCode: " + res);
if (res == 0)
{
alert(theValue);
}
}
return theValue;
}
function getBanditPassword()
{
logMessage("getBanditPassword");
var theValue = new String();
var obj = getMiCASAStore();
logMessage(obj);
if (obj != null)
{
var res = obj.miCASAReadKey("Bandit", "password", theValue);
logMessage("MiCASAReadKey returnCode: " + res);
if (res == 0)
{
alert(theValue);
}
}
}
function processIA()
{
// get selected persona
//var selectedPersona = getSelectedPersona();
var selectedPersona = sCurrentPersona;
logMessage("Selected Persona: " + selectedPersona);
// get selected attribs
var selected = getSelectedAttribs();
//alert(selected);
// build array of selected attribs
var csvSelected = '';
for (var i=0; i<selected.length; i++)
{
csvSelected += selected[i].value + ',';
}
// get selected attribs
selected = getSelectedAttribsOptional();
for (var i=0; i<selected.length; i++)
{
csvSelected += selected[i].value + ',';
}
//alert(csvSelected);
var arraySelected = csvSelected.split(',');
logMessage("Selected Array: " + arraySelected);
var sUser = readCASASetting("Bandit", "ldapdn");
var sPass = readCASASetting("Bandit", "password");
if (sUser != null)
{
logMessage("CASA returned:"+sUser);
}
else
{
logMessage("SecretID Bandit not found");
sUser = "cn=admin,o=novell";
}
var htData;
if (selectedPersona == "CASA")
{
htData = getCASAData(arraySelected);
}
else
{
try
{
logMessage(" ********* Calling IA");
htData = Packages.Jim.main2("cn=bandit1,o=Novell", sPass, null, selectedPersona, arraySelected);
logMessage(" ******** Returned");
}
catch (err)
{
logMessage(" ******** Error calling IA: " + err);
}
}
//alert(htData);
// if we got the stuff, request url with this stuff
if ((htData != null) && (htData.size() > 0))
{
var repostURL = opener._content.location + "?";
var keys = htData.keys();
while (keys.hasMoreElements())
{
var sKey = keys.nextElement();
var sValue = htData.get(sKey);
repostURL += (sKey + "=" + sValue);
if (keys.hasMoreElements())
repostURL += "&";
}
opener._content.location = repostURL;
}
// close this window
window.close();
}
function closeWin()
{
@ -393,7 +1231,7 @@ function promptuser()
{
if (sValue.length > 0)
{
logMessage('Writing ' + secretID + ' : ' + sKey + ' : xxxxxxx');
logMessage('Writing ' + secretID + '[' + secretID.length + '] : ' + sKey + ' : xxxxxxx');
try {
res = obj.miCASAWriteKey(secretID, sKey, sValue);
if (res != 0)

View File

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window class="dialog"
title="Debug"
id="debugwindow"
orient="vertical"
autostretch="always"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script language="JavaScript" type="text/javascript" src="encode.js"></script>
<script language="JavaScript" type="text/javascript" src="casaOverlay.js"></script>
<script>
var button = document.getElementById("ok");
button.addEventListener('command', closeWin, true);
button = document.getElementById("notnow");
button.addEventListener('command', cancelWin, true);
</script>
<!-- <radiogroup onclick="alert('test');">-->
<vbox color="red">
<image src="chrome://casa/skin/casa-logo2.png"/>
<separator class="thin"/>
<separator class="thin"/>
<hbox>
</hbox>
<separator class="thin"/>
</vbox>
<hbox>
<spacer flex="1"/>
<button id="selectall" label="dumpRealms" onclick="dumpRealms()"/>
<button id="selectall" label="Read test Secret" onclick="readTestKey()"/>
<button id="selectall" label="Write test Secret" onclick="writeTestKey()"/>
<button id="selectall" label="Show Java Info" onclick="displayJavaInfo()"/>
<button id="selectall" label="Open Java Console" onclick="showConsole()"/>
<button id="selectall" label="Show user.dir" onclick="displayUserDir()"/>
<button id="notnow" label="Show user.home" onclick="displayUserHome()"/>
<button id="notnow" label="Show Bandit User" onclick="getBanditUsername()"/>
<button id="notnow" label="Show Bandit Password" onclick="getBanditPassword()"/>
</hbox>
<spacer flex="1"/>
</window>

View File

@ -0,0 +1,92 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window class="dialog"
title="CASA"
id="casawindow"
orient="vertical"
autostretch="always"
onload="setAttribs()"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script language="JavaScript" type="text/javascript" src="encode.js"></script>
<script language="JavaScript" type="text/javascript" src="casaOverlay.js"></script>
<script>
var button = document.getElementById("ok");
button.addEventListener('command', closeWin, true);
button = document.getElementById("notnow");
button.addEventListener('command', cancelWin, true);
</script>
<!-- <radiogroup onclick="alert('test');">-->
<vbox color="red" width="500">
<image src="chrome://casa/skin/casa-logo2.png" oncommand="displayAllSelectedAttribs()"/>
<separator class="thin"/>
<separator class="thin"/>
<hbox>
<strong><label id="hostsite" value="This site"/> </strong>
<text id="hostsitetext" value="is requesting the following information." style="font-weight: bold;"/>
</hbox>
<separator class="thin"/>
<groupbox align="center" orient="horizontal">
<hbox width="20">
<!-- This section commented out
<vbox>
<label control="your-name" value="Select persona to use:"/>
<listbox id="theList" rows="4">
<listitem label="Local Persona (local_xml)" value="local_xml" selected="true"/>
<listitem label="Enterprise Persona (novell_inc)" value="novell_inc" />
<listitem label="Combined Persona" value="novell_inc+local_xml"/>
<listitem label="CASA Persona (not implemented)" value="casa"/>
</listbox>
</vbox>
-->
<vbox>
<label control="attribs" value="Required:" />
<listbox id="attribList" rows="5" seltype="multiple" disabled="true">
</listbox>
</vbox>
<vbox>
<label control="optionalAttribs" value="Optional:"/>
<listbox id="attribListOptional" rows="5" seltype="multiple">
</listbox>
<button id="selectall" label="Select All" onclick="toogleAllAttribsOptional()"/>
</vbox>
</hbox>
</groupbox>
</vbox>
<hbox>
<label control="your-name" value="Context:"/>
<popupset>
<popup id="clipmenu">
<menuitem label="Home Context" oncommand="setLocalPersona();"/>
<menuitem label="Work Context" oncommand="setEnterprisePersona();"/>
<menuitem label="Joined Context" oncommand="setCombinedPersona();"/>
<menuitem label="CASA Context" oncommand="setCasaPersona();"/>
</popup>
</popupset>
<label id="curPersonaID" value="Current Context"/>
<box context="clipmenu">
<description value="(Right click here to change)"/>
</box>
<spacer flex="1"/>
<!--
<button id="selectall" label="Show Selected" onclick="toogleAllSelectedAttribs()"/>
-->
<button id="bttnShowValues" label="Show Values" onclick="toogleShowHide()"/>
<button id="ok" label="Yes" onclick="processIA()"/>
<button id="notnow" label="No" onclick="cancelWin()"/>
</hbox>
<spacer flex="1"/>
</window>

View File

@ -31,7 +31,7 @@
<groupbox align="center" orient="horizontal">
<hbox>
<vbox>
<radiogroup>
<radiogroup tabindex="3">
<radio id="CASA.RememberMe" selected="true" label="Remember my login"/>
<radio id="CASA.RemAndLogin" label="Remember and log me in automatically"/>
<radio id="CASA.Never" label="Never for this site"/>
@ -43,8 +43,8 @@
<hbox>
<spacer flex="1"/>
<button id="notnow" label="Not now" onclick="cancelWin()"/>
<button id="ok" label="Ok" onclick="closeWin()"/>
<button id="notnow" tabindex="4" accesskey="N" label="Not now" onclick="cancelWin()"/>
<button id="ok" tabindex="5" accesskey="O" label="Ok" onclick="closeWin()"/>
</hbox>
<spacer flex="1"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB