Embedding aXes in an iFrame and authenticating automatically

Use this Forum to post tips and techniques for using aXes. Please explain in detail. This Forum is managed by the aXes user community. LANSA cannot guarantee the accuracy of any information posted to this Forum.

Moderator: jeanmichel

Post Reply
User avatar
jeanmichel
Posts: 109
Joined: 23 May 2014, 11:37
Location: Sydney

Embedding aXes in an iFrame and authenticating automatically

Post by jeanmichel » 04 Sep 2018, 16:13

For aXes version 4.1.1

This example shows how to embed aXes in an iFrame using messages and login automatically without passing username and passwords in the URL.

The setup is following.
  • 1 page (attached test.html) will present a username and password edit boxes to the user. On this page there is an iframe which will hold the aXes terminal sessions. This page can reside on any server.
  • 1 custom aXes login page to process this login. (attached cust_index.html). This file must be added in the <axes_root>/ts/ts2.
The idea behind this tip is to allows customer who would like to implement a Single Sign On page (without any Kerberos or the likes) where a user can provide their username and password and, assuming all servers use this same username password combinations, the user can log into as many IBMi as they want using aXes.

Test.html

In the code below, replace myserver.local with your server.
Replace in the same URL "testcases" with your project name.
The postLogin() function posts the message with the login data to the iframe (Custom aXes login screen)
The login function creates an iframe and loads the aXes custom login page.

Code: Select all

<html>
	<head>
		<style>
			* {box-sizing: border-box}
			body {font-family: "Lato", sans-serif;overflow:hidden;}

			div.header{
			height:7%;
			}
			
			iframe{
				display:none;
				margin-left: 125px;
				width: 93%;
				height: 92%;
			}

			/* Style the tab */
			div.tab {
				border: 1px solid #ccc;
				background-color: #f1f1f1;
				position:absolute;
				width: 120px;
				height: 100%;
				overflow: hidden;
			}

			/* Style the buttons inside the tab */
			div.tab button {
				display: block;
				background-color: #648a93;
				color: white;
				padding: 12px 5px 12px 5px;
				width: 100%;
				border: 1px solid #ccc;
				outline: none;
				text-align: center;
				cursor: pointer;
				transition: 0.3s;
				font-size: 17px;
			}

			/* Change background color of buttons on hover */
			div.tab button:hover {
				background-color: #aed0ea;
			}

			/* Create an active/current "tab button" class */
			div.tab button.active {
				background-color: #ccc;
			}

			/* Style the tab content */
			.tabcontent {
				padding: 0px 0px;
				border: 2px solid white;
				position:absolute;
				left:10%;
				width:90%;
				height:90%;	
			}

			div.footer {
			  position: absolute;
			  right: 0;
			  bottom: 0;
			  left: 0;
			  padding: 5px;
			  background-color: #00305e;
			  text-align: center;
			  color:white;
			  position:fixed;
			  bottom:0;
			}
		</style>
	</head>	
	<body>
	<div id="loginDIV" class="login" align="center" style="position: absolute; top:30%; left:45%;">
			<input id=Username placeholder="Username"></input>
			<br><br/>
			<input id=Password type="password" placeholder="Password" onkeypress="if (event.keyCode == 13){login()}"></input>
			<br><br/>
			<button id=login onclick="login()" style="background-color: #778899;color: white;width:173;border: 1px solid #ccc;text-align: center;cursor: pointer;transition: 0.3s;font-size: 17px;">Login</button> 
	</div>
		<div id="wrapper">
			<div class="tab" id="tabDIV" style="display: none;">
			</div>
		</div>
		<footer> <div id="footer" class="footer">&copy; 2018 Lansa Pty. Ltd. All rights reserved.</div> </footer>
	</body>
	<script>
        var user = "";
        var password = "";
        // Main page:
        window.onmessage = function(event) {
            alert(event.data);
        };
        function postLogin(e) {
            //alert("Frame loaded.");
            window.setTimeout(function()
            {
                var iframe = document.getElementById('homeFrame');
                var data = { cmd: "login", loginUser: user, loginPwd: password}
                iframe.contentWindow.postMessage(data,'*');
            }, 5000);
        }
        
		function login() {
			var axesFrame = document.createElement("IFRAME");
			user = document.getElementById("Username").value;
			password = document.getElementById("Password").value;
			var logDIV = document.getElementById("loginDIV");
			var tDIV = document.getElementById("tabDIV");
			var fDIV = document.getElementById("wrapper");
			if(user.length < 1 && password.length < 1){
				alert('invalid login');
				return;
			}
			logDIV.style.display='none';
			tDIV.style.display = 'block';
            axesFrame.id = "axes_iframe"
			axesFrame.style.display = 'block';
			axesFrame.setAttribute("ID", "homeFrame");
			axesFrame.setAttribute("height", "100%");
			axesFrame.setAttribute("width", "100%");
			axesFrame.setAttribute("float", "right");
			axesFrame.setAttribute("allowtransparency", true);
			axesFrame.style.background = "#e1eaf1";
            axesFrame.onload = postLogin;
			axesFrame.src = "http://myserver.local/ts/ts2/cust_index.html?definitionSet=testcases&lang=en"; 
			fDIV.appendChild(axesFrame);
		}
	</script>
</html> 
cust_index.html

The interesting code ins this page is the window.onmessage = function(event). The event receives the data posted by the caller and fills the username and password, and then clicks the login button.

Code: Select all

<!DOCTYPE html>

<!--
      Copyright (C) LANSA Group. All rights reserved
-->

<!--[if lt IE 8 ]> <html class="ie7"> <![endif]-->
<!--[if IE 8 ]>    <html class="ie8"> <![endif]-->
<!--[if IE 9 ]>    <html class="ie9"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html class=""> <!--<![endif]-->

<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Script-Type" content="text/javascript; charset=utf-8" />
<meta http-equiv="Access-Control-Allow-Origin" content="http://localhost"/>
<meta name="robots" content="noindex, nofollow"/>
<title>aXes TS</title>

<script type="text/javascript" src="/ts/axbuildinfo.js"></script>

<link id="_axJQueryBaseTheme_LINK" type="text/css" href="css/jQtheme_smoothness/jquery-ui.min.css" rel="stylesheet">
<link id="_axJQueryTheme_LINK" type="text/css" href="css/jQtheme_smoothness/jquery-ui.min.css" rel="stylesheet"/>

<script type="text/javascript">
AXESBUILD.writeCSSTags(document, ["css/layout.css"]);
</script>

<link id="axCustomPageCSS" type="text/css" href="css/blank.css" rel="stylesheet" />
<link id="axThemeCSS" type="text/css" href="css/blank.css" rel="stylesheet" />

<!-- for custom font -->
<link type="text/css" href="css/udc.css" rel="stylesheet" />

<!--[if lt IE 7]>
<link type="text/css" href="css/layout-ie6.css" rel="stylesheet" />
<![endif]-->

</head>
<body class="default">

<div id="contentWrapper" class="ui-helper-hidden" style="min-height: 120px; min-width: 120px">
<div id="terminalOuter" class="ui-layout-center">
<div class="header">

    <span id="ax-terminal-zoomcontrols" class="ui-widget">
    <span id="ax-terminal-zoomout" class="ui-icon ui-icon-zoomout ui-state-default ui-corner-all" style="float:right;"></span>
    <label class="dd-custom-arrow ui-widget-content" style="float:right; padding-right:2px; background:none; border:none; margin-left:2px;">
    <select id="ax-terminal-setzoom" hideFocus="true" style="padding-right:13px;" class="ui-widget-content noarrow-dropdown">
    <option value="0.5">50%</option>
    <option value="0.75">75%</option>
    <option value="1.0">100%</option>
    <option value="1.5">150%</option>
    <option value="fit">- Fit -</option>
    <option value="auto">- Auto -</option>
    </select>
    </label>
    <span id="ax-terminal-zoomin" class="ui-icon ui-icon-zoomin ui-state-default ui-corner-all" style="float:right;"></span>
    </span>
    <span id="btnToggleWest"></span>

    <ul id="ax-mainmenu" class="ax-menu ui-widget">

</ul>  

</div>
<div id="ax-dev-Toolbar" class="ui-widget-header" style="min-height: 10px; min-width: 10px">
    <div id="ax-dev-Toolbar-extension" class="ui-widget-header ui-corner-all">
        <button type="button" id="ax-dev-Toolbarbtn0" ax_txtscope="Dev" ax_txt="ExtensionsTab.AddUserField"></button>
        <button type="button" id="ax-dev-Toolbarbtn1" ax_txtscope="Dev" ax_txt="ExtensionsTab.DeleteUserField"></button>
        <button type="button" id="ax-dev-Toolbarbtn2" ax_txtscope="Dev" ax_txt="ExtensionsTab.RevertToDefault"></button>
    </div>    
    <div id="ax-dev-Toolbar-edit" class="ui-widget-header ui-corner-all">
        <button type="button" id="ax-dev-ToolbarbtnCopy" ax_txtscope="Dev" ax_txt="ExtensionsTab.CopyUserField"></button>
        <button type="button" id="ax-dev-ToolbarbtnPaste" ax_txtscope="Dev" ax_txt="ExtensionsTab.PasteUserField"></button>
    </div>    
    <div id="ax-dev-Toolbar-alignment" class="ui-widget-header ui-corner-all">
        <button type="button" id="ax-dev-ToolbarbtnVA1" ax_txtscope="Dev" ax_txt="Align.TopEdges"></button>
        <button type="button" id="ax-dev-ToolbarbtnVA2" ax_txtscope="Dev" ax_txt="Align.VerticalCenters"></button>
        <button type="button" id="ax-dev-ToolbarbtnVA3" ax_txtscope="Dev" ax_txt="Align.BottonEdges"></button>
        &nbsp;&nbsp;&nbsp;
        <button type="button" id="ax-dev-ToolbarbtnHA1" ax_txtscope="Dev" ax_txt="Align.LeftEdges"></button>
        <button type="button" id="ax-dev-ToolbarbtnHA2" ax_txtscope="Dev" ax_txt="Align.HorizontalCenters"></button>
        <button type="button" id="ax-dev-ToolbarbtnHA3" ax_txtscope="Dev" ax_txt="Align.RightEdges"></button>
        &nbsp;&nbsp;&nbsp;
        <button type="button" id="ax-dev-ToolbarbtnD1" ax_txtscope="Dev" ax_txt="Align.DistributeVertically"></button>
        <button type="button" id="ax-dev-ToolbarbtnD2" ax_txtscope="Dev" ax_txt="Align.DistributeHorizontally"></button>
        &nbsp;&nbsp;&nbsp;
        <button type="button" id="ax-dev-ToolbarbtnE1" ax_txtscope="Dev" ax_txt="Align.EvenWidths"></button>
        <button type="button" id="ax-dev-ToolbarbtnE2" ax_txtscope="Dev" ax_txt="Align.EvenHeights"></button>
    </div>
    <div id="ax-dev-Toolbar-styling" class="ui-widget-header ui-corner-all">
        <select  class="ui-widget" id="ax-dev-ToolbarbtnF" ax_txtscope="Dev" ax_txt="Styling.Font" ax_isfontselect="true" style="width: 120px; font-size: 14px"></select>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <select  class="ui-widget" id="ax-dev-ToolbarbtnFS" ax_txtscope="Dev" ax_txt="Styling.FontSize" style="width: 25px; font-size: 12px"></select>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <button type="button" id="ax-dev-ToolbarbtnTC"  ax_txtscope="Dev" ax_txt="Styling.TextColor"></button><input type="hidden" id="ax-dev-ToolbarbtnTC-cp" style="width:0px"/>
        <button type="button" id="ax-dev-ToolbarbtnBGC" ax_txtscope="Dev" ax_txt="Styling.BackgroundColor"></button><input type="hidden" id="ax-dev-ToolbarbtnBGC-cp" style="width:0px"/>
        &nbsp;
        <input type="checkbox" id="ax-dev-ToolbarbtnT1"  ax_txtscope="Dev" ax_txt="Styling.Bold" /><label for="ax-dev-ToolbarbtnT1">Bold</label>
        <input type="checkbox" id="ax-dev-ToolbarbtnT2"  ax_txtscope="Dev" ax_txt="Styling.Italic" /><label for="ax-dev-ToolbarbtnT2">Italic</label>
        <input type="checkbox" id="ax-dev-ToolbarbtnT3"  ax_txtscope="Dev" ax_txt="Styling.Underline" /><label for="ax-dev-ToolbarbtnT3">Underline</label>
        &nbsp;
        <input type="checkbox" id="ax-dev-ToolbarbtnTA1" ax_txtscope="Dev" ax_txt="Styling.TextAlignLeft" name="ax-dev-Toolbar-txtAlign"><label for="ax-dev-ToolbarbtnTA1">Text Align Left</label>
        <input type="checkbox" id="ax-dev-ToolbarbtnTA2" ax_txtscope="Dev" ax_txt="Styling.TextAlignCenter" name="ax-dev-Toolbar-txtAlign"><label for="ax-dev-ToolbarbtnTA2">Text Align Center</label>
        <input type="checkbox" id="ax-dev-ToolbarbtnTA3" ax_txtscope="Dev" ax_txt="Styling.TextAlignRight" name="ax-dev-Toolbar-txtAlign"><label for="ax-dev-ToolbarbtnTA3">Text Align Right</label>
    </div>
</div>
<div id="terminalInner" class="ui-layout-content axdefault" style="min-height: 50px; min-width: 50px;">
<!-- In order to avoid unexpected scrollbars and strange scaling effects when
using zoom in IE, it is necessary to wrap the content in a couple of DIVs. -->
<div id="terminalWrapper" class="terminalTheme ui-layout-center"><div id="terminalContent"></div></div>

</div>
<div id="extensionInfo" style="display: none;">
    <table id="extensionDetailedInfo"><tr>
        <td id="extensionDetailedInfoType" class="ax-terminal-statusbar-extensiontype"></td>
        <td id="extensionDetailedInfoValue" class="ax-terminal-statusbar-extensionvalue"></td>
        <td id="extensionDetailedInfoRow" class="ax-terminal-statusbar-extensionrow"></td>
        <td id="extensionDetailedInfoCol" class="ax-terminal-statusbar-extensioncol"></td>
    </tr></table>
</div>
<div class="footer">
    <span id="terminalStatusIndicator" class="ax-terminal-statusbar-button-left"></span>
    <span id="terminalRefreshButton" class="ax-terminal-statusbar-button-left"></span>
    <span id="terminalCancelButton" class="ax-terminal-statusbar-button-left"></span>

    <span id="terminalClearButton" class="ax-terminal-statusbar-button-right"></span>
    <div class="ax-terminal-statusbar-messages">

    <select id="terminalMessages" disabled="disabled"></select>

    </div>

</div>

</div>


<div class="devTheme ui-layout-west">
    <ul>
        <li><a href="#ax-keysTab" ax_txtscope="Core" ax_txt="Keyboards">Keyboards</a></li>
        <li class="ui-helper-hidden" aria-controls="ax-developerTab"><a href="../dev/ts2devtab.html" ax_txtscope="Core" ax_txt="Developer">Developer</a></li>
    </ul>
    <!-- add wrapper that Layout will auto-size to 'fill space' -->
    <div class="ui-layout-content ui-corner-bottom">
        <div id="ax-keysTab">

<fieldset id="ax-keysTab-fkeys">
    <legend ax_txtscope="core" ax_txt="Keyboard Maps">Keyboard Maps</legend>
    <span ax_txtscope="core" ax_txt="Current map" style="padding-right: 5px;" >Current map:</span>
    <label class="dd-custom-arrow" style="width:150px; padding-right: 5px;">
    <select id="ax-keymap-select" style="width: 100%; padding-right: 12px;" class="ui-widget-content noarrow-dropdown"></select>
    </label>
</fieldset>

<fieldset id="ax-keysTab-fkeys"><legend ax_txtscope="core" ax_txt="Function Keys">Function Keys</legend>

    <button class="ax-fkeybutton">F1</button>
    <button class="ax-fkeybutton">F2</button>
    <button class="ax-fkeybutton">F3</button>
    <button class="ax-fkeybutton">F4</button>
    <button class="ax-fkeybutton">F5</button>
    <button class="ax-fkeybutton">F6</button>
    <button class="ax-fkeybutton">F7</button>
    <button class="ax-fkeybutton">F8</button>
    <button class="ax-fkeybutton">F9</button>
    <button class="ax-fkeybutton">F10</button>
    <button class="ax-fkeybutton">F11</button>
    <button class="ax-fkeybutton">F12</button>
    <button class="ax-fkeybutton">F13</button>
    <button class="ax-fkeybutton">F14</button>
    <button class="ax-fkeybutton">F15</button>
    <button class="ax-fkeybutton">F16</button>
    <button class="ax-fkeybutton">F17</button>
    <button class="ax-fkeybutton">F18</button>
    <button class="ax-fkeybutton">F19</button>
    <button class="ax-fkeybutton">F20</button>
    <button class="ax-fkeybutton">F21</button>
    <button class="ax-fkeybutton">F22</button>
    <button class="ax-fkeybutton">F23</button>
    <button class="ax-fkeybutton">F24</button>

</fieldset>

<fieldset id="ax-keysTab-udkeys"><legend ax_txtscope="core" ax_txt="Other Keys">Other Keys</legend>
    <!--button keystab becomes dynamically loaded based on xxxkeymap.xml-->
</fieldset>
    </div>
        <div id="ax-developerTab"></div>
    </div>
</div>

</div>

<div id="loadingIndicator" class="ui-widget">&nbsp;<span ax_txtscope="Core" ax_txt="Loading..."></span></div>

<div id="ax-terminalVerifyUserDialog" style="display:none;">
<p id="ax-terminalVerifyUserMessage"></p>
    <form>
    <fieldset>
        <label for="ax-terminalVerifyName" ax_txtscope="Core" ax_txt="Name:">Name:</label>
        <input type="text" name="ax-terminalVerifyName" id="ax-terminalVerifyName" class="text ui-widget-content ui-corner-all" />
        <label for="ax-terminalVerifyPassword" ax_txtscope="Core" ax_txt="Password:">Password:</label>
        <input type="password" name="ax-terminalVerifyPassword" id="ax-terminalVerifyPassword" value="" class="text ui-widget-content ui-corner-all" />
    </fieldset>
    </form>
</div>

<div id="ax-aboutDialog" style="display:none;">
    	<div class="abtDlgHeader">
        <img class="abtDlgLogo" src="css/images/centerlogo.png"></img>
        <!-- Version is set by JavaScript -->
        <span id="ax-aboutDialog-version"></span>
    	</div>
        <span id="ax-aboutDialog-copyright" style="display: block;">
        &#169; <span ax_txtscope="Core" ax_txt="Copyright"></span>
        <span ax_txtscope="Core" ax_txt="Copyright.Reserved" style="display: block;"></span></span>

</div>

<div id="ax-devGridSettingsDialog" style="display:none;">
<label for="ax-devGridSettings-Size" ax_txtscope="Dev" ax_txt="GridSize">Grid size:</label>
<input type="number" id="ax-devGridSettings-Size" name="ax-devGridSettings-Size" size="3" class="ui-widget-content" />
<br />
<label for="ax-devGridSettings-Front" ax_txtscope="Dev" ax_txt="GridBringToFront">Bring to front</label>
<input type="checkbox" id="ax-devGridSettings-Front" name="ax-devGridSettings-Front" />
<br />
<label for="ax-devGridSettings-Snap" ax_txtscope="Dev" ax_txt="GridSnapToGrid">Snap to grid</label>
<input type="checkbox" id="ax-devGridSettings-Snap" name="ax-devGridSettings-Snap" />

</div>

<div style="display:none;">
<bgsound id="ax-sounds-error-bg" src="#" autostart="true">
</div>

<script type="text/javascript">
var isTheAxesWindow = true;
window.onmessage = function(event) {
    var data = event.data;
    if (data.cmd == "login")
    {
        $("#loginUser").val(data.loginUser);
        $("#loginPwd").val(data.loginPwd);
        $("#btnDoLogin").click();
    }
    else if (data.cmd == "sendKey")
    {
        AXES.currentForm.postAIDKey(data.key);
    }
};
(function()
{
    var s = window.location.search;
    var ext = (/(^\?|&)nominjs(&|$)/i.test(s)) ? ".js" : ".min.js";

    var jsFileArray = ["js/modernizr" + ext,
                        "js/jquery" + ext,
                        "js/jquery-ui-all" + ext,
                        "js/jquery-ui-custom.js",
                        "js/jquery.layout" + ext,
                        "js/jquery-plugins.min.js",
                        "js/jquery.validate.min.js",
                        "../screens/jq.jquery.plugin.min.js",
                        "js/digest.js",
                        "js/axcore.js",
                        "js/axlog.js",
                        "js/axtext2.js",
                        "js/axextension.js",
                        "js/axextensionbase.js",
                        "js/axscreens.js",
                        "js/axtx.js",
                        "js/axterm.js",
                        "js/axlayout.js",
                        "js/axkeys.js",
                        "js/axtables.js",
                        "js/axcompat.js",
                        "js/axexdata.js",
                        "js/axibmiservice.js",
                        "/wba/jsmlink.js",
                        "../axusrglobal.js"];
    
    AXESBUILD.writeCSSTags(document, ["css/jquery-ui-custom.css"]);
    AXESBUILD.writeCSSTags(document, ["css/font-awesome.min.css"]);
    if (/(?:^\?|&)dev(?:=|&|$)/i.test(s))
    {
        AXESBUILD.writeCSSTags(document, ["../dev/ts2tabstyles.css", "dev/codemirror.css","dev/lint.css"]);
        AXESBUILD.writeCSSTags(document, ["dev/cssdialog.css"]);
        jsFileArray.push("dev/codemirror.js");
		jsFileArray.push("dev/javascript.js");		
		jsFileArray.push("dev/css.js");
		jsFileArray.push("dev/jshint.js");
		jsFileArray.push("dev/lint.js");
		jsFileArray.push("dev/javascript-lint.js");
            jsFileArray.push("js/axdev.js");
            jsFileArray.push("js/axeditors.js");
        }

    AXESBUILD.writeScriptTags(document, jsFileArray);
})();
</script>


</body>
</html>

This example is only showing 1 iFrame with 1 server. It can be extended with multiple iFrames and multiple servers.
Regards,

Jean-Michel Rapin

LANSA Pty Ltd
email: JeanMichel.Rapin@lansa.com.au
Address: 122 Arthur Street, North Sydney, NSW 2060, Australia
Tel: +61 289 070 262 http://www.lansa.com | http://blog.lansa.com |

jaimosky
Posts: 38
Joined: 30 May 2017, 16:48

Re: Embedding aXes in an iFrame and authenticating automatically

Post by jaimosky » 13 Sep 2018, 23:01

Hi Jean Michel,

I tried your example but when I come to redirect to cust_index.html I get here

Code: Select all

        $("#loginUser").val(data.loginUser);
Runtime error in Javascript, "$" is undefined. It seems as it is using "$" before loading jquery....

Jaime

safalpiya
Posts: 17
Joined: 17 Oct 2018, 04:28

Re: Embedding aXes in an iFrame and authenticating automatically

Post by safalpiya » 18 Oct 2018, 04:26

When we clear cache file from browser, single sign on doesn't works.
Regards,
Safal Piya

User avatar
jeanmichel
Posts: 109
Joined: 23 May 2014, 11:37
Location: Sydney

Re: Embedding aXes in an iFrame and authenticating automatically

Post by jeanmichel » 18 Oct 2018, 08:53

Hi Safal,

I realized that there were errors in the cust_login.html. So I have updated the original post.
I do not have any issues with this version when login in after clearing the cache.
Please also note that this example is for aXes 4.1.1.
Regards,

Jean-Michel Rapin

LANSA Pty Ltd
email: JeanMichel.Rapin@lansa.com.au
Address: 122 Arthur Street, North Sydney, NSW 2060, Australia
Tel: +61 289 070 262 http://www.lansa.com | http://blog.lansa.com |

eduardorama
Posts: 13
Joined: 19 Aug 2020, 11:26

Re: Embedding aXes in an iFrame and authenticating automatically

Post by eduardorama » 22 Aug 2020, 11:55

Hi Jean Michel,
I know this is for version 4.1.1. but is there a way to make it work for version 4.2?
Mine is stuck in loading and I can see that the resources used are no longer there.
error.PNG
error.PNG (128.52 KiB) Viewed 26021 times

Post Reply