Old 8th May 2007, 14:49   #41
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
thanks everyone for testing, I will need to find a new way to detect if i need my runas dialog

Please report the output of this console app: http://stashbox.org/19958/TokenElevation.exe (Based on code from http://blogs.msdn.com/cjacks/archive...ows-Vista.aspx )

IntOp $PostCount $PostCount + 1

Last edited by Anders; 8th May 2007 at 15:14.
Anders is offline   Reply With Quote
Old 8th May 2007, 15:12   #42
MarcOfChaos
Junior Member
 
Join Date: May 2007
Location: Germany
Posts: 6
Hi,

Did some further research:
TokenElevationTypeDefault only means that the user does not have a split token. This should be true for 'nearly' every user that is not an administrator.
I also found a clue on how to determine whether UAC is enabled.
One could try to check in the registry whether the policy is enabled or not.
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA
Possible values: Enabled (1), Disabled(0)

Hope this helps...
MarcOfChaos is offline   Reply With Quote
Old 8th May 2007, 15:25   #43
MarcOfChaos
Junior Member
 
Join Date: May 2007
Location: Germany
Posts: 6
Hi,

find attached the screenshots of the TokenElevation.exe.
Attached Files
File Type: zip screenshots.zip (26.3 KB, 528 views)
MarcOfChaos is offline   Reply With Quote
Old 8th May 2007, 17:21   #44
helix400
Member
 
Join Date: Oct 2003
Posts: 51
I tested TokenElevation as well. I got different result when I ran the app from a regular folder with a standard user account. Screenshots are attached below.
Attached Files
File Type: zip tokenelevationscreenshots.zip (43.1 KB, 491 views)
helix400 is offline   Reply With Quote
Old 8th May 2007, 19:35   #45
helix400
Member
 
Join Date: Oct 2003
Posts: 51
Just an update for those following the thread. I worked with Anders more today. He got a version working where the UAC dialog pops up when it should (when UAC is turned on), and the Run As dialog pops up when it should (when UAC is turned off). In all situations, the test apps worked. There's still the issue of the installer window popping up behind active windows...he'll work on that later.
helix400 is offline   Reply With Quote
Old 8th May 2007, 22:24   #46
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
Ok, I created a wiki page @ http://nsis.sourceforge.net/UAC_plug-in , only done limited testing, but I hope its fixed, please report back

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 9th May 2007, 05:10   #47
fishsauce
Junior Member
 
Join Date: Jul 2005
Posts: 40
Sorry, but I got kind of lost as to how this plug-in helps to create shortcuts in the user's own start menu instead of the all users one. Can anyone give me a bit of guidance? Thanks.
fishsauce is offline   Reply With Quote
Old 9th May 2007, 06:42   #48
helix400
Member
 
Join Date: Oct 2003
Posts: 51
It's more of an overall UAC thing, which has a nice side effect of solving the Vista user shortcuts problem.

The way this plugin works is the following:

1) First, the NSIS script needs to say it wants user privileges, not admin privileges.
2) The NSIS installer launches, and quickly calls its .onInit code (no form has shown by this point yet). That code makes a new process and attempts to elevate it to admin mode. Anders calls the first process the outer process, and this second process the inner process.
3) If needed, a UAC or Run As dialog is shown. After that, the inner process should be elevated to admin access. Then this admin/inner process can display. This is the main installer app that users will see. At this time, two processes are running. One as a user, another as an admin.
4) Now that you have an admin process, you can continue to let your script do its thing. If you ever need to do something at a user level, you do it through the UAC plugin. For example, UAC::Exec can execute something with user privileges and not admin privileges.

With all this in place, you can do some system calls to create a shortcut as a user. Personally, I think the UAC plugin should have some extra features added on, such as UAC::CreateUserShortcut or something like that. But then, Anders has been working really hard on just getting the bugs worked out of the current plugin...I'm just happy he's providing a fairly clean solution compared to the really ugly alternatives out there in the Vista world.
helix400 is offline   Reply With Quote
Old 10th May 2007, 05:18   #49
fishsauce
Junior Member
 
Join Date: Jul 2005
Posts: 40
I see... thanks for the nice, detailed overview. Now to figure out how to actually make those shortcuts using UAC::Exec...
fishsauce is offline   Reply With Quote
Old 10th May 2007, 08:32   #50
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
You should not use UAC::Exec for that, use ExecCodeSegment

UAC_RealWorldExample.nsi does:

Function CreateShortcuts
CreateShortcut "$Desktop\${APPNAME}.lnk" "$Windir\Notepad.exe"
FunctionEnd

Section "Desktop Shortcut"
GetFunctionAddress $0 CreateShortcuts
UAC::ExecCodeSegment $0
SectionEnd

the CreateShortcuts function will execute as the original user (You get no feedback in the install log, hopefully I will implement some kind of DetailPrint that "works the other way")

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 11th May 2007, 07:06   #51
fishsauce
Junior Member
 
Join Date: Jul 2005
Posts: 40
So... just to make sure I'm understanding this correctly... basically we encapsulate the stuff we want to do as the original user in a function, then use UAC::ExecCodeSegment on the function address to execute the stuff?

Sounds pretty simple... thanks a lot!
fishsauce is offline   Reply With Quote
Old 11th May 2007, 15:28   #52
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
yes, the only problem is that you get no output in the install log since its actually executing in a hidden installer

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 11th May 2007, 21:10   #53
helix400
Member
 
Join Date: Oct 2003
Posts: 51
With Anders permission, I've added more documentation to the plug-in page at: http://nsis.sourceforge.net/UAC_plug-in

Some of it was based off comments mentioned in this thread. So if you find yourself saying "Hey, I said that!", it's because you did.
helix400 is offline   Reply With Quote
Old 12th May 2007, 12:37   #54
stb
Senior Member
 
Join Date: Mar 2004
Location: Germany, Paderborn
Posts: 177
@Anders: I hope you consider my RunAs dialog ideas. Here's another feature request (which is quite obvious):

Allow communication from outer to inner installer. It would be nice if the ExecCodeSegment could return values. Either by allowing ExecCodeSegment the other way, too (outer calls inner => return values by a callback). Or by exchanging values internally using some UAC stack (UAC::Push, UAC::Pop, callable by both instances). Or something else.

The outer installer runs with user privileges, so it is a bit difficult to exchange information using some workaround (registry location). They both use different TMP (pluginsdir). Perhaps there is a possibility via windows API. I think about something like PeekMessage / PostThreadMessage (but those might interfere the GUI event handling...)
stb is offline   Reply With Quote
Old 12th May 2007, 15:31   #55
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
Yeah, I have already thought about adding stack support. The problem is, you can't send a message the other way without reducing the security of the admin instance

As for changes to the run-as dialog, those are way down on the list, you are only supposed to see that dialog if you are non admin with uac off. And I'm not sure if using my dialog is smart on 2k/xp, I don't know how much it changes if the machine has a custom GINA and stuff like that. Maybe I could implement some sort of show function callback and you could do whatever you want to my dialog

This thing was really just to support starting a process on the finish page, not sure how smart it is to add to much support for other stuff. Duplicating every NSIS api call to provide proper feedback is just too much work. The outer instance never gets past .onInit so there are no DetailPrints i can just "steal" from the listview either and there is no way to hook into the nsis calls as far as i know

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 12th May 2007, 16:28   #56
stb
Senior Member
 
Join Date: Mar 2004
Location: Germany, Paderborn
Posts: 177
Please make your RunAs dialog at least customizable (language strings). Is this an privileges problem? I guess the RunAs dialog is an outer process thing.

Regarding security issue (passing values from outer to inner): I think about two ways:

a) the inner process has admin privileges. We could use this to let the inner process read those values out of the process context / environment /... of the outer one.

Very simple solution: create a temp file and pass the file to the inner process as CMD. So both processes could use this file. Problem: The privileges of the inner process could be insufficient. The file could be encrypted by EFS and the admin user would not have the key. So file is not good...

b) is the inner process technically a child process of the outer one? Perhaps there is something in windows which allows limiting the communication to father-child.
stb is offline   Reply With Quote
Old 12th May 2007, 16:40   #57
stb
Senior Member
 
Join Date: Mar 2004
Location: Germany, Paderborn
Posts: 177
http://support.microsoft.com/kb/95900/en

Perhaps Memory Mapped Files could help:
http://msdn2.microsoft.com/en-us/library/ms810613.aspx

I know you started UAC only for the Finish-Exec problem. And the current solution is quite nice. The "Passing parameters back" is not as important (for me). Anyway the plugin is great, thanks for your effort.
stb is offline   Reply With Quote
Old 12th May 2007, 16:59   #58
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
The biggest problem is not how to pass data back, its what to pass. Even if I added UAC:etailPrint that works the other way, what strings would you pass back?

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 12th May 2007, 17:05   #59
helix400
Member
 
Join Date: Oct 2003
Posts: 51
Quote:
Originally posted by Anders
Yeah, I have already thought about adding stack support. The problem is, you can't send a message the other way without reducing the security of the admin instance
I stumbled on a new Vista API function that allows an admin process to register a type of message so that user-level process could send it messages of that type. Unfortunately, I can't remember the name of it now (my work computer probably has it in the browser history).

Also, in XP and 2000, can a user-level process send messages to an admin-level process? I was under the impression they could.

I don't know if this is any help for you, just chiming in with a thought.

Last edited by helix400; 12th May 2007 at 17:51.
helix400 is offline   Reply With Quote
Old 12th May 2007, 17:16   #60
stb
Senior Member
 
Join Date: Mar 2004
Location: Germany, Paderborn
Posts: 177
I don't need UAC::DetailPrint. What I want is doing some processing for current user (not just Exec on finish):
Reading HKCU and writing HKCU, reading files from the user's profile/writing files (SetShellVarContext).

Doing actions (write) is possible using your UAC::ExecuteCodeSegment. But I cannot read values. I want to read a value (outer process), process it in the main installer (inner process), show it in the GUI, allow user options etc. and then do the action using UAC::ExecuteCodeSegment.

Reading those values using the inner process is no option because the files / keys could have privilege problems (or EFS encryption). So the original (outer) user account has to be used here.

At first I just want to pass some bool (yes/no), but in general it would be nice to at least allow int numbers and when possible strings.
stb is offline   Reply With Quote
Old 12th May 2007, 19:18   #61
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
* Check wiki for new version with basic language support. (See readme.txt and UAC_RealWorldExample.nsi)

Yes, on < Vista you can send messages to any window. I know I can use ChangeWindowMessageFilter to allow messages from the outer instance, but that would add possible security issues.

You can read the HKCU part of the registry from the inner instance unless the permissions on that key has done something to block admins (You would probably never see this in practice) You can probably do something with the access control plugin to make sure the inner instance can read from the outer instance's $pluginsdir.

I'm not sure if I want to add support for any kind of message passing from outer to inner, but you are free to fork the code ofcourse. The UAC plugin is all about support for all users type installers so why would you read user specific values?

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 23rd May 2007, 11:03   #62
RichZ
Junior Member
 
Join Date: May 2007
Posts: 7
Anders,

I've been playing around with your plug-in and my installer now works perfectly under a limited Vista account.

I did however notice a problem with the uninstaller; When I start the uninstaller the Vista elevation dialog pops up as expected, when I enter the correct info and click OK my uninstaller UI however doesn't pop up.
When I check the task manager I do see 2 uninstaller processes running, 1 as a normal user and 1 as an administrator.

After some investigation I noticed that this problem only occurs when my uninstaller uses the Modern UI. When I do not use the modern UI the uninstaller works as expected. Do you have any idea what's going on?
If you need any more info from me please let me know.
RichZ is offline   Reply With Quote
Old 23rd May 2007, 12:29   #63
stb
Senior Member
 
Join Date: Mar 2004
Location: Germany, Paderborn
Posts: 177
I noticed a similar issue with Vista (which seems to be a Vista bug) and solved the problem by calling

ShowWindow $HWNDPARENT "${SW_SHOW}"

in the show callback function of the first visible GUI page (welcome page).
stb is offline   Reply With Quote
Old 23rd May 2007, 14:11   #64
RichZ
Junior Member
 
Join Date: May 2007
Posts: 7
Quote:
Originally posted by stb
I noticed a similar issue with Vista (which seems to be a Vista bug) and solved the problem by calling

ShowWindow $HWNDPARENT "${SW_SHOW}"

in the show callback function of the first visible GUI page (welcome page).
Thanks, I have added a custom callback function to the uninstaller as you described and this fixed it. For me the problem only occurs for the uninstaller and not for the installer which to me seems kind of strange (what's the difference between installing and uninstalling for the OS?).
If anybody is interested I have added the following code to my script:


!define MUI_CUSTOMFUNCTION_UNGUIINIT un.GUIInit

Function un.GUIInit
ShowWindow $HWNDPARENT "${SW_SHOW}"
FunctionEnd
RichZ is offline   Reply With Quote
Old 23rd May 2007, 14:19   #65
stb
Senior Member
 
Join Date: Mar 2004
Location: Germany, Paderborn
Posts: 177
I noticed this in the installer with UAC disabled in Vista when self-executing the installer (copy in TEMP). This inner process (outer is finished because it doesn't wait) did not show any GUI Window (style was invisible). With XP SP2 this window was always visible.

Thanks for the hint to GUIINIT, I think that location is better.
stb is offline   Reply With Quote
Old 23rd May 2007, 15:33   #66
RichZ
Junior Member
 
Join Date: May 2007
Posts: 7
Quote:
Originally posted by stb
I noticed this in the installer with UAC disabled in Vista when self-executing the installer (copy in TEMP). This inner process (outer is finished because it doesn't wait) did not show any GUI Window (style was invisible). With XP SP2 this window was always visible.

Thanks for the hint to GUIINIT, I think that location is better.
I cannot reproduce what you describe above, what do you mean by "self-executing the installer (copy in TEMP)"?

Also for Anders I noticed another glitch.
On one of my test machines (actually a VMware machine) I have an administrator account without a password (I know this is unsafe ). With UAC disabled I get a nice run as dialog, I am however not able to use my administrator account because of the blank password.
RichZ is offline   Reply With Quote
Old 23rd May 2007, 16:06   #67
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
First off, I am able to reproduce the invisible uninstaller window on XP Pro SP2. Will try to look into this at some point. For now, use the showwindow hack

Edit: I think i fixed it, please try with the latest version on the wiki


RichZ: Could you take a screenshot of the dialog? Do you get a error message?

IntOp $PostCount $PostCount + 1

Last edited by Anders; 23rd May 2007 at 17:58.
Anders is offline   Reply With Quote
Old 23rd May 2007, 18:02   #68
RichZ
Junior Member
 
Join Date: May 2007
Posts: 7
Quote:
Originally posted by Anders
First off, I am able to reproduce the invisible uninstaller window on XP Pro SP2. Will try to look into this at some point. For now, use the showwindow hack

Edit: I think i fixed it, please try with the latest version on the wiki


RichZ: Could you take a screenshot of the dialog? Do you get a error message?
I have attached a screenshot, it states that a possible reason can be because of a blank password, but I have not enforced such a policy.
Attached Images
File Type: png uac.png (7.5 KB, 445 views)
RichZ is offline   Reply With Quote
Old 23rd May 2007, 18:10   #69
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
I wanted a screenshot of the runas dialog and not the error but it doesnt really matter.

There is nothing i can do about this, CreateProcessWithLogonW fails with blank passwords by default. You can set limitblankpassworduse=0 in HKLM\SYSTEM\CurrentControlSet\Control\Lsa to override this

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 23rd May 2007, 18:14   #70
RichZ
Junior Member
 
Join Date: May 2007
Posts: 7
Quote:
Originally posted by Anders
I wanted a screenshot of the runas dialog and not the error but it doesnt really matter.

There is nothing i can do about this, CreateProcessWithLogonW fails with blank passwords by default. You can set limitblankpassworduse=0 in HKLM\SYSTEM\CurrentControlSet\Control\Lsa to override this
Alright, thanks for the solution, not a big problem because a blank administrator password is not what you want anyway

I will test your solution for the invisible dialog later on.
RichZ is offline   Reply With Quote
Old 23rd May 2007, 18:20   #71
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
If you turn UAC on, will it elevate even if the admin password is blank?

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 23rd May 2007, 18:21   #72
RichZ
Junior Member
 
Join Date: May 2007
Posts: 7
Anders, I have checked the latest build (v0.0.6b) and the problem with the invisible dialogs is indeed fixed, thanks for the quick response.
RichZ is offline   Reply With Quote
Old 23rd May 2007, 18:22   #73
RichZ
Junior Member
 
Join Date: May 2007
Posts: 7
Quote:
Originally posted by Anders
If you turn UAC on, will it elevate even if the admin password is blank?
Yes with UAC on there is no problem, it will elevate with an empty admin password.
RichZ is offline   Reply With Quote
Old 23rd May 2007, 19:29   #74
helix400
Member
 
Join Date: Oct 2003
Posts: 51
Oh good, the UAC plugin is still improving.

Just to let you know Anders, we've deployed an installer containing it to several hundreds people so far. There haven't been any issues found by anyone yet.
helix400 is offline   Reply With Quote
Old 17th July 2007, 18:53   #75
introspect
Junior Member
 
Join Date: Jul 2007
Posts: 1
Question source for UAC plugin not compiling

I am sure I am just being stupid here but I can't get the uac plugin source to compile (found here: http://nsis.sourceforge.net/UAC_plug-in)

TokenOrigin is not defined. Where is TokenOrigin defined?

Anyways TokenOrigin is being used in the UAC.h header file in this enum definition:
enum _TOKEN_INFORMATION_CLASS___VISTA {
TokenElevationType = (TokenOrigin+1),
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
};

Thanks in advance for any help you can provide, and my apologies if this is the wrong place to post this.
introspect is offline   Reply With Quote
Old 17th July 2007, 20:36   #76
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
in WinNT.h (You need a somewhat recent Platform sdk, mine is from 2003)

_TOKEN_INFORMATION_CLASS___VISTA is my fake enum since I don't have the latest Platform SDK installed

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 3rd October 2007, 00:08   #77
RobertStrong
Junior Member
 
Join Date: Jan 2006
Posts: 22
Anders, would you consider adding a method that created a process using CreateProcessWithTokenW using the desktop's token? This would allow an uninstaller to create a process that isn't elevated. I can provide the code to accomplish this and it seems that this would be a good plugin to add this to vs. creating a plugin that just did this.
RobertStrong is offline   Reply With Quote
Old 3rd October 2007, 01:16   #78
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,153
Yeah, I would consider it (Depending on how you get this token, remember, the user could use a shell that is not explorer.exe etc.)

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 3rd October 2007, 04:42   #79
RobertStrong
Junior Member
 
Join Date: Jan 2006
Posts: 22
Something like this?

code:
extern "C"
void __declspec(dllexport) LaunchWithTopMostWinToken(HWND hWndParent, int string_size,
char *variables, stack_t **stacktop,
extra_parameters *extra)
{
EXDLL_INIT();
{
char szCmdLine[MAX_PATH];

if (popstring(szCmdLine) == 0)
{
if (!pCreateProcessWithTokenW)
{
// CreateProcessWithTokenW is not present on WinXP or earlier
*(FARPROC *)&pCreateProcessWithTokenW =
GetProcAddress(GetModuleHandle("advapi32.dll"),
"CreateProcessWithTokenW");
if (!pCreateProcessWithTokenW)
{
pushstring(OUT_ERR);
return;
}
}
// use the token from the topmost window (highest in the z-order) to drop the privilege
HWND hwndTopMostWin = GetWindow(hWndParent, GW_HWNDFIRST);

DWORD dwProcessId;
GetWindowThreadProcessId(hwndTopMostWin, &dwProcessId);

HANDLE hProcessTopMostWin = OpenProcess(MAXIMUM_ALLOWED, FALSE, dwProcessId);
if (!hProcessTopMostWin)
{
pushstring(OUT_ERR);
return;
}

HANDLE hTokenTopMostWin;
BOOL ok = OpenProcessToken(hProcessTopMostWin, MAXIMUM_ALLOWED, &hTokenTopMostWin);
CloseHandle(hProcessTopMostWin);
if (!ok)
{
pushstring(OUT_ERR);
return;
}

HANDLE hNewToken;
ok = DuplicateTokenEx(hTokenTopMostWin,
MAXIMUM_ALLOWED,
NULL,
SecurityDelegation,
TokenPrimary,
&hNewToken);
CloseHandle(hTokenTopMostWin);
if (!ok)
{
pushstring(OUT_ERR);
return;
}

STARTUPINFOW si = {sizeof(si), 0};
PROCESS_INFORMATION pi = {0};

WCHAR wszCmdLine[MAX_PATH * 2];
MultiByteToWideChar(CP_ACP, 0, szCmdLine, lstrlen(szCmdLine) + 1,
wszCmdLine, sizeof(wszCmdLine) / sizeof(wszCmdLine[0]));

if (wszCmdLine)
{
ok = pCreateProcessWithTokenW(hNewToken,
0, // profile is already loaded
NULL,
wszCmdLine,
0, // No special process creation flags
NULL, // inherit my environment
NULL, // use my current directory
&si,
&pi);
}
CloseHandle(hNewToken);
if (ok)
{
pushstring(OUT_SUCCESS);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return;
}

}
pushstring(OUT_ERR);
}
}

RobertStrong is offline   Reply With Quote
Old 3rd October 2007, 05:09   #80
RobertStrong
Junior Member
 
Join Date: Jan 2006
Posts: 22
Forgot pCreateProcessWithTokenW

code:
BOOL (WINAPI *pCreateProcessWithTokenW)(HANDLE,
DWORD,
LPCWSTR,
LPWSTR,
DWORD,
LPVOID,
LPCWSTR,
LPSTARTUPINFOW,
LPPROCESS_INFORMATION);

RobertStrong is offline   Reply With Quote
Reply
Go Back   Winamp & Shoutcast Forums > Developer Center > NSIS Discussion

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump