Jump to content
wright

ICS V9.0 - mobile platforms

Recommended Posts

HI there i just made a try, i started there!

To work on the Android platform using compile directives, i needed to make some modifications to the code in several files. Lets focus on 'svn.overbyte.be/svn/icsv9/Source/OverbyteIcsUtils.pas'.The classTIcsIntegerList is essentially a wrapper around the TList class, since this code was designed for use in a Windows platform target, on a different platform, i needed to make some adjustments.
 

type
    TIcsIntegerList = class(TObject)
    private
        FList     : TList<Integer>; // Use TList<Integer> instead of TList
        function  GetCount: Integer;
        function  GetFirst: Integer;
        function  GetLast: Integer;
        function  GetItem(Index: Integer): Integer;
        procedure SetItem(Index: Integer; const Value: Integer);
    public
        constructor Create; virtual;
        destructor  Destroy; override;
        function    IndexOf(Item: Integer): Integer;
        function    Add(Item: Integer): Integer; virtual;
        procedure   Assign(Source: TIcsIntegerList); virtual;
        procedure   Clear; virtual;
        procedure   Delete(Index: Integer); virtual;
        property    Count: Integer read GetCount;
        property    First: Integer read GetFirst;
        property    Last : Integer read GetLast;
        property    Items[Index: Integer] : Integer read  GetItem
                                                    write SetItem; default;
    end;

// ... other codes

{ TIcsIntegerList }

function TIcsIntegerList.Add(Item: Integer): Integer;
begin
    Result := FList.Add(Item); // No need to typecast Item to Pointer
end;

procedure TIcsIntegerList.Clear;
begin
    FList.Clear;
end;

constructor TIcsIntegerList.Create;
begin
    FList := TList<Integer>.Create; // Use TList<Integer> instead of TList
end;

procedure TIcsIntegerList.Delete(Index: Integer);
begin
    FList.Delete(Index);
end;

destructor TIcsIntegerList.Destroy;
begin
    FList.Free;
    inherited;
end;

function TIcsIntegerList.GetCount: Integer;
begin
    Result := FList.Count;
end;

function TIcsIntegerList.GetFirst: Integer;
begin
    Result := FList.First; // No need to typecast FList.First to Integer
end;

function TIcsIntegerList.GetLast: Integer;
begin
    Result := FList.Last; // No need to typecast FList.Last to Integer
end;

// ... other codes

but i faced a lot of errors when i target android platform and compiled, it gives me errors like:
 

[DCC Error] OverbyteIcsUtils.pas(5931): E2023 Function needs result type
[DCC Error] OverbyteIcsUtils.pas(5933): E2003 Undeclared identifier: 'FList'
[DCC Error] OverbyteIcsUtils.pas(5933): E2003 Undeclared identifier: 'Item'
[DCC Error] OverbyteIcsUtils.pas(5938): E2004 Identifier redeclared: 'TIcsIntegerList'
[DCC Error] OverbyteIcsUtils.pas(5940): E2003 Undeclared identifier: 'FList'
[DCC Error] OverbyteIcsUtils.pas(5945): E2037 Declaration of 'TIcsIntegerList' differs from previous declaration

...:
So how to fix it? Or can anyone point me to the right direction?

Share this post


Link to post
10 hours ago, wright said:

The classTIcsIntegerList is essentially a wrapper around the TList class, since this code was designed for use in a Windows platform target, on a different platform, i needed to make some adjustments.

IMO, you don't need to change TIcsIntegerList to work on another platform. TList exists on all platforms, isn't it?

 

10 hours ago, wright said:

[DCC Error] OverbyteIcsUtils.pas(5933😞 E2003 Undeclared identifier: 'FList'

You should publish what you have on the lines around the error because we don't necessarily have the exact same source code as you have. Here on line 5933, I have the closing end of constructor TIcsFileStreamW.Create.

 

The real difficulty in porting ICS to mobile platform is to replace Windows stuff that is used in ICS code. Mainly everything related to messages. ICS make heavily use of Windows messaging system (https://learn.microsoft.com/en-us/windows/win32/winmsg/windowing) which doesn't exists at all on the mobile platform and must be replaced by something else. Probably the code I wrote for Linux will help you as mobile platforms (Android, iOS share most of Linux API). Look at http://svn.overbyte.be/svn/icsv10/Source/

Share this post


Link to post
5 minutes ago, FPiette said:

IMO, you don't need to change TIcsIntegerList to work on another platform. TList exists on all platforms, isn't it?

TList exist on all platforms, yes! I followed instructions from Migrating_Delphi_Code_to_Mobile_from_Desktop and embarcadero Delphi recommends the use of TList<...>, in our case: TList<Integer> to make it Cros-platform. as in ref: Generics_Collections_TList, stackoverflow: explication of - Remy Lebeau

2 hours ago, FPiette said:

You should publish what you have on the lines around the error because we don't necessarily have the exact same source code as you have

from the original file, these errors are on these lines (5912 to 5940) :

[DCC Error] OverbyteIcsUtils.pas(5912): E2023 Function needs result type
[DCC Error] OverbyteIcsUtils.pas(5914): E2003 Undeclared identifier: 'FList'
[DCC Error] OverbyteIcsUtils.pas(5914): E2003 Undeclared identifier: 'Item'
[DCC Error] OverbyteIcsUtils.pas(5919): E2004 Identifier redeclared: 'TIcsIntegerList'
[DCC Error] OverbyteIcsUtils.pas(5921): E2003 Undeclared identifier: 'FList'
[DCC Error] OverbyteIcsUtils.pas(5926): E2037 Declaration of 'TIcsIntegerList' differs from previous declaration
[DCC Error] OverbyteIcsUtils.pas(5928): E2003 Undeclared identifier: 'FList'
[DCC Error] OverbyteIcsUtils.pas(5933): E2004 Identifier redeclared: 'TIcsIntegerList'
[DCC Error] OverbyteIcsUtils.pas(5935): E2003 Undeclared identifier: 'FList'
[DCC Error] OverbyteIcsUtils.pas(5935): E2003 Undeclared identifier: 'Index'
[DCC Error] OverbyteIcsUtils.pas(5940): E2037 Declaration of 'TIcsIntegerList' differs from previous declaration

 

2 hours ago, FPiette said:

Probably the code I wrote for Linux will help you as mobile platforms (Android, iOS share most of Linux API)

Yes, you've done the bulk of the work, thank you for that! but there are many Types / classes which don't compile on Android/mobile platform such as: "TIcsFileStreamW" in "OverbyteIcsUtils.pas", "iconv"..."iconv_open"..., "_statvfs" (the way we get free space on disk for linux doesn't work for android, i planned to add it), "IcsGetBufferCodepage" (the way to get buffer of codepage for mobile platform need to be implemented, i 'll post mine later). As i sais i'll focus on "TIcsIntegerList" first.

ics v9 tlist err.png

Share this post


Link to post

ICS V9.0 will be very difficult to convert to Android, it should work on MacOS but has not been tested for a few years due to lack of hardware and volunteers. 

 

ICS V10 is currently a very small number of units that does work on Linux for simple sockets, no SSL.  Over the next year or so I will be migrating the V9 code to V10, it's a massive job.  

 

I strongly recommend that initially you concentrate on adding Android support to V10 and making the two FMX samples work on Android.  You will see results far faster than working with V9. 

 

Angus

 

  • Like 1

Share this post


Link to post

Really interesting! My main purpose is to have the WebSocket/Ssl modules working on my projects which target Android platform. I know that porting ICS V9.0 to Android is likely to be a more complex and time-consuming task. And by initially focusing on ICS V10, i can make quicker progress and gain valuable experience with the newer codebase but I would like to use / port "TSslWebSocketCli" with Ssl.

Edited by wright

Share this post


Link to post

The TsslWebSocketCli is a very new component, not heard of any using it yet (I use the websocket server for dynamic web pages).  What are you using it for?

 

It will need the HTTPRest component, but that will be one of the first to be converted.  Adding SSL to V10 is likely to be the hardest part due to the low level changes.  

 

Angus

 

 

Share this post


Link to post
2 hours ago, wright said:

from the original file, these errors are on these lines (5912 to 5940) : 

Those line numbers are useless for me. As I already said, publish the code around the error you get.

 

2 hours ago, wright said:

but there are many Types / classes which don't compile on Android/mobile platform

That's where you have to start. I gave the URL for TWSocket, but there are already a bunch of files. Did you had a look at everything?

The last thing to do is to take the whole ICS and try to rebuilt it. You'll need YEARS to to that. Start with the basis: TWSocket and then higher level components.

2 hours ago, wright said:

Embarcadero recommends the use of TList<...>

Embarcadero wants you to use latest feature. Generics are wonderful, but don't spend time on converting perfectly working code. You'll do that at the last stage.

 

28 minutes ago, wright said:

My main purpose is to have the WebSocket/Ssl modules working on my projects which target Android platform

That is perfect. The path to the port is the inheritance chain for the components you need. At the lowest level, there is TWSocket. Start by TWSocket I made for Linux (Work in progress anyway) and write some basic application to test your code. This is a bottom to top way of programming.

 

Share this post


Link to post
38 minutes ago, FPiette said:

publish the code around the error you get.

- E2023 Function needs result type and E2003 Undeclared identifier: 'FList'

function TIcsIntegerList.Add(Item: Integer): Integer;
begin
    Result := FList.Add(Pointer(Item));
end;

- E2004 Identifier redeclared: 'TIcsIntegerList', E2003 Undeclared identifier: 'FList'

procedure TIcsIntegerList.Clear;
begin
    FList.Clear;
end;

- E2037 Declaration of 'TIcsIntegerList' differs from previous declaration.

constructor TIcsIntegerList.Create;
begin
    FList := TList.Create;
end;

- E2004 Identifier redeclared: 'TIcsIntegerList', E2003 Undeclared identifier: 'FList', E2003 Undeclared identifier: 'Index'

procedure TIcsIntegerList.Delete(Index: Integer);
begin
    FList.Delete(Index);
end;

- E2003 Undeclared identifier: 'iconv_t',  E2003 Undeclared identifier: 'iconv_open'E2003 Undeclared identifier: 'iconv_close' and E2029 Declaration expected but 'IF' found

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
function IcsIsValidAnsiCodePage(const CP: LongWord): Boolean;
{$IFDEF MSWINDOWS}
begin
    Result := IsValidCodePage(CP);
end;
{$ENDIF}
{$IFDEF POSIX}
{$IFDEF MACOS}
begin
    Result := CFLocaleGetIdentifier(CP);
end;
{$ENDIF}
var
    Ctx: iconv_t;
begin
    Result := (CP <> 1200) and (CP <> 1201) and (CP <> 12000) and (CP <> 12001);
    if Result then
    begin
        Ctx := iconv_open(PAnsiChar(IcsIconvNameFromCodePage(CP)), ICONV_UNICODE);
        if Ctx = iconv_t(-1) then
            Result := False
        else begin
            iconv_close(Ctx);
            Result := True;
        end;
    end;
end;
{$ENDIF}

E2003 Undeclared identifier: '_statvfs', E2003 Undeclared identifier: 'statvfs', W1023 Comparing signed and unsigned types - widened both operands, E2029 ')' expected but identifier 'f_bfree' found

function IcsGetFreeDiskSpace(const APath: String): Int64;
{$IFDEF MSWINDOWS}
var
    TotalSpace, FreeSpace : Int64;
begin
    if GetDiskFreeSpaceEx (PChar(APath), FreeSpace, TotalSpace, nil) then
        Result := FreeSpace
    else
        Result := -1;
{$ENDIF}
{$IFDEF POSIX}
var
    FN  : RawByteString; // Path or file name
    Buf : _statvfs;
begin
    FN := UnicodeToAnsi(APath, CP_UTF8);
    if statvfs(PAnsiChar(FN), Buf) = 0 then
        Result := Int64(Buf.f_bfree) * Int64(Buf.f_frsize)  { V8.65 }
    else
        Result := -1;
{$ENDIF}
end;

E2034 Too many actual parameters, with variable "LBOMSize" when compiling on Android platform.

function  IcsGetBufferCodepage(Buf: PAnsiChar; ByteCount: Integer): LongWord; { V8.07 }
var
  LBOMSize: Integer;
begin
  Result := IcsGetBufferCodepage(Buf, ByteCount, LBOMSize);
end;

- - The TIcsFileStreamW class is a Unicode version of the TFileStream class throws errors. It is a custom file stream class defined. On Android, file handling is done differently, i am still looking for a work around. 

  • E2003 Undeclared identifier: 'TIcsFileStreamW'
  • E2029 '=' expected but ';' found
  • E2029 '=' expected but ')' found
  • E2075 This form of method call only allowed in methods of derived types
  • E2003 Undeclared identifier: 'FFileName'
  • E2037 Declaration of 'TIcsFileStreamW' differs from previous declaration
{ TIcsFileStreamW }

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
constructor TIcsFileStreamW.Create(const FileName: UnicodeString; Mode: Word);
begin
{$IFDEF COMPILER12_UP}
    inherited Create(FileName, Mode);
    FFileName := FileName;
{$ELSE}
    Create(Filename, Mode, 0);
{$ENDIF}
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
constructor TIcsFileStreamW.Create(const FileName: UnicodeString; Mode: Word;
  Rights: Cardinal);
begin
{$IFDEF COMPILER12_UP}
    inherited Create(FileName, Mode, Rights);
    FFileName := FileName;
{$ELSE}
    if Mode = fmCreate then
    begin
        inherited Create(IcsFileCreateW(FileName));
        if Cardinal(FHandle) = INVALID_HANDLE_VALUE then
        {$IFDEF COMPILER12_UP}
            raise Exception.CreateResFmt(@SFCreateErrorEx,
                                [ExpandFileName(FileName),
                                SysErrorMessage(GetLastError)]);
        {$ELSE}
            raise Exception.CreateResFmt(@SFCreateErrorEx,
                                [IcsExpandFileNameW(FileName),
                                SysErrorMessage(GetLastError)]);
        {$ENDIF}

    end
    else begin
        inherited Create(IcsFileOpenW(FileName, Mode));
        if Cardinal(FHandle) = INVALID_HANDLE_VALUE then
        {$IFDEF COMPILER12_UP}
            raise Exception.CreateResFmt(@SFCreateErrorEx,
                                [ExpandFileName(FileName),
                                SysErrorMessage(GetLastError)]);
        {$ELSE}
            raise Exception.CreateResFmt(@SFCreateErrorEx,
                                [IcsExpandFileNameW(FileName),
                                SysErrorMessage(GetLastError)]);
        {$ENDIF}
    end;
    FFileName := FileName;
{$ENDIF}
end;

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
constructor TIcsFileStreamW.Create(const Utf8FileName: UTF8String;
  Mode: Word);
begin
{$IFDEF COMPILER12_UP}
    FFileName := Utf8FileName;
    inherited Create(FFileName, Mode);
{$ELSE}
    Create(AnsiToUnicode(Utf8FileName, CP_UTF8), Mode, 0);
{$ENDIF}
end;

 

Share this post


Link to post

Please don't post large chunks of code in this topic, few people need to look at it. 

 

Either attach to a short message as a text file or send it by private message to whoever has requested it.

 

Angus

 

Share this post


Link to post
3 hours ago, wright said:

- E2023 Function needs result type and E2003 Undeclared identifier: 'FList'


function TIcsIntegerList.Add(Item: Integer): Integer;
begin
    Result := FList.Add(Pointer(Item));
end;

This is very clear: FList is not defined in TIcsIntegerList. So look FList at your class declaration, it is missing. Why ? I don't know because I don't have your modified code.

Don't take offense, but if you can solve such a simple error, I doubt you can port ICS to mobile platform.

 

3 hours ago, wright said:

E2003 Undeclared identifier: '_statvfs', E2003 Undeclared identifier: 'statvfs',

Those symbols are defined in Posix.SysStatvfs.pas unit. Check that.

 

By the way, which platform do you use? Android or Apple?

Share this post


Link to post
1 hour ago, FPiette said:

By the way, which platform do you use? Android or Apple?

For the moment: Android (32-64 bit)

1 hour ago, FPiette said:

This is very clear: FList is not defined in TIcsIntegerList. So look FList at your class declaration, it is missing. Why ? I don't know because I don't have your modified code.

FList is declared, as i posted it in the first post.⬇️

23 hours ago, wright said:

type

     TIcsIntegerList = class(TObject)
     private
           FList : TList<Integer>;    // Use TList<Integer> instead of TList
           function GetCount: Integer;

1 hour ago, FPiette said:

Don't take offense, but if you can solve such a simple error, I doubt you can port ICS to mobile platform.

The fact is that, i thought tha was simple, why the IDE kept showing error and compiler failed where everything was declared.
i also tried to reorder the constructor and functions to see if it causes compile errors but nothing solved. I also checked Doc wiki for Constructor overriding, if there is something to do with mobile platform.

Edited by wright

Share this post


Link to post
12 hours ago, wright said:

FList is declared, as i posted it in the first post.

Maybe there is a conditional compilation ($IF...) which hide the declaration?

When you Ctrl-Right-Click on the offending FList, the IDE should move the caret to FList declaration, if he can find it.

 

The error about FList is not the first error. Maybe the compiler is confused by PREVIOUS error. Always resolve the errors in the order the compiler encounter them.

 

Another check you can do: create a manifest syntax error in TIcsIntegerList = class(TObject) and recompile. If the error is not seen, that means the compiler don't see the declaration, probably because of some conditional compilation.

Another check you can do: move all TIcsIntegerList class code to a new unit, use that unit in a trivial test program and compile.

 

Share this post


Link to post

Don't worry about the compile errors with Android, I've just installed Android support for Delphi 12 and get a load of errors when building ICS 9.1 for Android (and a couple of Linux) which I will fix or suppress in the near future. 

 

For ICS V10, just comment out the offending functions like IcsIsValidAnsiCodePage which uses ICON and IcsGetFreeDiskSpace, which are fine for Posix not seemingly not Android, doubt any of this stuff is needed to get basic sockets working on Android.

 

And avoid changes like using generics that are unnecessary to make ICS work on Android.

 

Angus

 

Share this post


Link to post
2 hours ago, Angus Robertson said:

For ICS V10, just comment out the offending functions like IcsIsValidAnsiCodePage which uses ICON

As per the recent updates, the iconv related functions and types have been removed and replaced with cross-platform RTL functions. That's how i managed to migrate the code... but needed to be tested.

function IcsIsValidAnsiCodePage(const CP: LongWord): Boolean;
{$IFDEF MSWINDOWS}
begin
    Result := IsValidCodePage(CP);
end;
{$ENDIF}
{$IFDEF POSIX}
var
  Encoding: TEncoding;
  CodePageStr: AnsiString;
  I: Integer;
begin
  Result := (CP <> 1200) and (CP <> 1201) and (CP <> 12000) and (CP <> 12001);
  if Result then
  begin
    // Find the corresponding character encoding for the code page
    for I := Low(IconvCodepageMapping) to High(IconvCodepageMapping) do
    begin
      if IconvCodepageMapping[I].C = CP then
      begin
        CodePageStr := IconvCodepageMapping[I].A;
        Break;
      end;
    end;

    // Try to get the encoding for the code page
    try
      Encoding := TEncoding.GetEncoding(AnsiString(CodePageStr));
      Result := Assigned(Encoding);
    except
      on E: EEncodingError do
        Result := False;
    end;
  end;
end;
{$ENDIF}

However, codes for posix should work with android platform, but we can use the TEncoding.GetEncoding method to attempt to get an encoding for the given code page. 
 

{$IFDEF ANDROID}
var
  Encoding: TEncoding;
begin
  Result := (CP <> 1200) and (CP <> 1201) and (CP <> 12000) and (CP <> 12001);
  if Result then
  begin
    try
      // Attempt to get the encoding for the code page
      Encoding := TEncoding.GetEncoding(CP);
      Result := Assigned(Encoding);
    except
      // Handle encoding error
      on E: EEncodingError do
        Result := False;
    end;
  end;
end;
{$ENDIF}

 

  • Like 1

Share this post


Link to post
On 11/13/2023 at 5:14 PM, Angus Robertson said:

Adding SSL to V10 is likely to be the hardest part due to the low level changes.  

If you plan to rewrite it anyway, probably you could make it in some pluggable manner to easily integrate another TLS engines? From the client app's view, not much TLS details are ever needed at all - just be able to connect to TLS server. Some could need to accept custom certs, provide user certs or select cyphers. That's all!

Share this post


Link to post
On 11/12/2023 at 10:16 PM, wright said:

but i faced a lot of errors when i target android platform and compiled, it gives me errors like:

I fixed it. I have gone through all the implementations up to above the indexed function. All errors related to what i provided, were due to the compile directives that have encompassed "Compound statements" (begin...end) in function IcsGetLocalTimeZoneBias.
I just added some android compile directives
:
 

//...
{$IFDEF ANDROID}
var
  TimeZone: TTimeZone;
begin
  TimeZone := TTimeZone.Local;
  Result := -Round(TimeZone.GetUtcOffset(Now).TotalMinutes);
end;
{$ENDIF}

 

On 11/14/2023 at 5:00 PM, Angus Robertson said:

just comment out the offending functions like IcsIsValidAnsiCodePage which uses ICON and IcsGetFreeDiskSpace, which are fine for Posix not seemingly Android

Yes! i noticed that.
I'll list all changes after succeeded tests.

Edited by wright

Share this post


Link to post
Quote

probably you could make it in some pluggable manner to easily integrate another TLS engines?

That would be a massive amount of work, OpenSSL functions are buried in a lot of ICS functions at low level. 

 

I looked at your SChannel implementation for ICS when you initially wrote it.

 

At the time the USP was no DLLs, but there are now two separate solutions that avoid distributing separate OpenSSL DLLs, so what do you now see as the benefit of SChannel? 

 

Angus

 

Share this post


Link to post

How to fix => F2047 Circular unit reference to 'Ics.Fmx.OverbyteIcsWSocketS' ? when compiling the "D110InstallVclFmx" project it raised that error in "IcsLinuxD110".
I thought that was due to the changes i did for porting it (ICS v9...) on Android platform, but that wasn't the case. it occured even for win32/64 bit platform too.
I'm talking about the ICS V9.1

Share this post


Link to post
On 11/17/2023 at 7:09 PM, Angus Robertson said:

At the time the USP was no DLLs, but there are now two separate solutions that avoid distributing separate OpenSSL DLLs, so what do you now see as the benefit of SChannel?

What's the 2nd solution? I guess it's not free?

Well, the benefit is native implementation, free from insane upgrade policy of OpenSSL; moreover, RTL socket/http uses it as well, and so does default cURL build.

To add more, there are some OpenSSL forks/analogs now (WolfSSL, BoringSSL) which could be more suitable for some needs. Abstraction of TLS layer would be awesome. Extending the subject wider, I periodically dream of a universal handler stack in TWSocket to enable things like

Socket.handlers := [

  ThrottleHandler,

  TLSHandler,

  HTTPTunnelHandler,

  TLSHandler 

]

(enable throttling, connect via TLS to HTTP tunnel proxy and do TLS connection to remote server) that would provide a simple interface for any combination of protocols. That way TTLSSocket would be nothing more than TWSocket with auto-created TLSHandler.

Want cascade of 5 TLS HTTP tunnel proxies? No problem! Want HTTP inside MsgPack inside Protobuf inside TLS-encryption? Easy. Want custom encrypted protocol? Just write handler.

Please don't take this as criticism, I admire your efforts to keep ICS up-to-date.

Share this post


Link to post

The second option to avoid distributing OpenSSL DLLs was added last week, embedding the DLLs in the application and extracting them when the application is first run to a common directory, this was discussed in the last OpenSSL update message. Other related changes will happen this week. 

 

I always saw the major downside of SChannel that Microsoft is slow to add new features, and they are only added to new Windows versions, even TLS/1.2 took several years to be added to Windows 8/Server 2012 and did not support EC certificates properly.  So exactly the same update policy as OpenSSL, except you have to upgrade the entire OS instead of a couple of DLLs.

 

Angus

 

Share this post


Link to post
23 hours ago, Angus Robertson said:

embedding the DLLs

Ah, that one. Workaround in fact.

23 hours ago, Angus Robertson said:

Microsoft is slow to add new features

Well, you're right here - each approach has its pros and cons. So it's nice when you can choose

Share this post


Link to post

I've asked him to send me the changed units, I'll incorporate any safe Android changes I've not already made in the next couple of weeks, busy with low level SSL improvements.

 

Angus

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×