VorDa Posted August 10, 2007 Posted August 10, 2007 Can someone help me to decode this source writen in pascal? I'm want to make some phone-pc program, but I need pdu<->txt decoder from this source. expandcollapse popup{ --------------------------------------------------------- } { Purposes : } { 1. Convert ASCII to 7-bit PDU } { 2. Convert 7-bit, 8-bit and 16-bit PDU to ASCII } { 3. Decode/Parsing the hexadecimal (PDU) of SMS message } { 4. Encode ASCII characters to be sent as SMS ready string } { } { --------------------------------------------------------- } { Coder : Daniel Eka Nugraha } { URL : http://www.averagecoder.com/ } { Email : pinoez@yahoo.com } { --------------------------------------------------------- } { This is not a perfect one, so Use it at your own risk ! } { --------------------------------------------------------- } unit SMSCODEC; interface uses Windows, Messages, SysUtils, Classes, StrUtils, Math, Dialogs; type TSMSCODEC = class(TComponent) private { Private declarations } protected { Protected declarations } public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function Binerkan(Angka: integer): string; function Bin2Long(StrBiner: string): integer; function PDU7BIT2ASCII(StrPDU: string; PanjangUDH, PanjangData: integer): string; function PDU8BIT2ASCII(StrPDU: string; PanjangUDH, PanjangData: integer): string; function PDU16BIT2ASCII(StrPDU: string; PanjangUDH, PanjangData: integer): string; function ASCII2PDU7BIT(StrASCII: string): string; function DecodeSMS(StrSMS : string) : TStringList; function EncodeSMS(StrTujuan, StrPesan: string): string; published { Published declarations } end; procedure Register; implementation var Nono: integer; EQ7BIT2ASCII : array [0..127] of integer; // Table persamaan 7-bit ke ASCII EQASCII27BIT : array [0..255] of integer; // Table persamaan ASCII ke 7-bit procedure Register; begin RegisterComponents('averagecoder', [TSMSCODEC]); end; constructor TSMSCODEC.Create(AOwner: TComponent); var i : integer; begin inherited Create(AOwner); // This is the transition table from 7-bit to ASCII Nono := -1; EQ7BIT2ASCII[0] := 64; EQ7BIT2ASCII[1] := 163; EQ7BIT2ASCII[2] := 36; EQ7BIT2ASCII[3] := 165; EQ7BIT2ASCII[4] := 232; EQ7BIT2ASCII[5] := 223; EQ7BIT2ASCII[6] := 249; EQ7BIT2ASCII[7] := 236; EQ7BIT2ASCII[8] := 242; EQ7BIT2ASCII[9] := 199; EQ7BIT2ASCII[10] := 10; EQ7BIT2ASCII[11] := 216; EQ7BIT2ASCII[12] := 248; EQ7BIT2ASCII[13] := 13; EQ7BIT2ASCII[14] := 197; EQ7BIT2ASCII[15] := 229; EQ7BIT2ASCII[16] := Nono; EQ7BIT2ASCII[17] := 95; EQ7BIT2ASCII[18] := Nono; EQ7BIT2ASCII[19] := Nono; EQ7BIT2ASCII[20] := Nono; EQ7BIT2ASCII[21] := Nono; EQ7BIT2ASCII[22] := Nono; EQ7BIT2ASCII[23] := Nono; EQ7BIT2ASCII[24] := Nono; EQ7BIT2ASCII[25] := Nono; EQ7BIT2ASCII[26] := Nono; EQ7BIT2ASCII[27] := Nono; EQ7BIT2ASCII[28] := 198; EQ7BIT2ASCII[29] := 230; EQ7BIT2ASCII[30] := 223; EQ7BIT2ASCII[31] := 201; EQ7BIT2ASCII[32] := 32; EQ7BIT2ASCII[33] := 33; EQ7BIT2ASCII[34] := 34; EQ7BIT2ASCII[35] := 35; EQ7BIT2ASCII[36] := 164; EQ7BIT2ASCII[37] := 37; EQ7BIT2ASCII[38] := 38; EQ7BIT2ASCII[39] := 39; EQ7BIT2ASCII[40] := 40; EQ7BIT2ASCII[41] := 41; EQ7BIT2ASCII[42] := 42; EQ7BIT2ASCII[43] := 43; EQ7BIT2ASCII[44] := 44; EQ7BIT2ASCII[45] := 45; EQ7BIT2ASCII[46] := 46; EQ7BIT2ASCII[47] := 47; EQ7BIT2ASCII[48] := 48; EQ7BIT2ASCII[49] := 49; EQ7BIT2ASCII[50] := 50; EQ7BIT2ASCII[51] := 51; EQ7BIT2ASCII[52] := 52; EQ7BIT2ASCII[53] := 53; EQ7BIT2ASCII[54] := 54; EQ7BIT2ASCII[55] := 55; EQ7BIT2ASCII[56] := 56; EQ7BIT2ASCII[57] := 57; EQ7BIT2ASCII[58] := 58; EQ7BIT2ASCII[59] := 59; EQ7BIT2ASCII[60] := 60; EQ7BIT2ASCII[61] := 61; EQ7BIT2ASCII[62] := 62; EQ7BIT2ASCII[63] := 63; EQ7BIT2ASCII[64] := 161; EQ7BIT2ASCII[65] := 65; EQ7BIT2ASCII[66] := 66; EQ7BIT2ASCII[67] := 67; EQ7BIT2ASCII[68] := 68; EQ7BIT2ASCII[69] := 69; EQ7BIT2ASCII[70] := 70; EQ7BIT2ASCII[71] := 71; EQ7BIT2ASCII[72] := 72; EQ7BIT2ASCII[73] := 73; EQ7BIT2ASCII[74] := 74; EQ7BIT2ASCII[75] := 75; EQ7BIT2ASCII[76] := 76; EQ7BIT2ASCII[77] := 77; EQ7BIT2ASCII[78] := 78; EQ7BIT2ASCII[79] := 79; EQ7BIT2ASCII[80] := 80; EQ7BIT2ASCII[81] := 81; EQ7BIT2ASCII[82] := 82; EQ7BIT2ASCII[83] := 83; EQ7BIT2ASCII[84] := 84; EQ7BIT2ASCII[85] := 85; EQ7BIT2ASCII[86] := 86; EQ7BIT2ASCII[87] := 87; EQ7BIT2ASCII[88] := 88; EQ7BIT2ASCII[89] := 89; EQ7BIT2ASCII[90] := 90; EQ7BIT2ASCII[91] := 196; EQ7BIT2ASCII[92] := 204; EQ7BIT2ASCII[93] := 209; EQ7BIT2ASCII[94] := 220; EQ7BIT2ASCII[95] := 167; EQ7BIT2ASCII[96] := 191; EQ7BIT2ASCII[97] := 97; EQ7BIT2ASCII[98] := 98; EQ7BIT2ASCII[99] := 99; EQ7BIT2ASCII[100] := 100; EQ7BIT2ASCII[101] := 101; EQ7BIT2ASCII[102] := 102; EQ7BIT2ASCII[103] := 103; EQ7BIT2ASCII[104] := 104; EQ7BIT2ASCII[105] := 105; EQ7BIT2ASCII[106] := 106; EQ7BIT2ASCII[107] := 107; EQ7BIT2ASCII[108] := 108; EQ7BIT2ASCII[109] := 109; EQ7BIT2ASCII[110] := 110; EQ7BIT2ASCII[111] := 111; EQ7BIT2ASCII[112] := 112; EQ7BIT2ASCII[113] := 113; EQ7BIT2ASCII[114] := 114; EQ7BIT2ASCII[115] := 115; EQ7BIT2ASCII[116] := 116; EQ7BIT2ASCII[117] := 117; EQ7BIT2ASCII[118] := 118; EQ7BIT2ASCII[119] := 119; EQ7BIT2ASCII[120] := 120; EQ7BIT2ASCII[121] := 121; EQ7BIT2ASCII[122] := 122; EQ7BIT2ASCII[123] := 228; EQ7BIT2ASCII[124] := 246; EQ7BIT2ASCII[125] := 241; EQ7BIT2ASCII[126] := 252; EQ7BIT2ASCII[127] := 224; // This is the transition table from ASCII to 7-BIT; index reversing :) for i := 0 to High(EQ7BIT2ASCII) do begin if EQ7BIT2ASCII[i] <> Nono then EQASCII27BIT[EQ7BIT2ASCII[i]] := i; end; end; { TSMSCODEC.Create } destructor TSMSCODEC.Destroy; begin inherited Destroy; end; { TSMSCODEC.Destroy } { -------------------------------------------------------------------------------- function Binerkan -> Convert integer to binary -> parameters: Angka: integer; -> the integer value -> Return value: string of 1 and 0 (nothing genius at all :D) -------------------------------------------------------------------------------- } function TSMSCODEC.Binerkan(Angka : integer): string; var i : Integer; hasil : array [0..7] of Integer; SISA : array [0..7] of Integer; StrHasil : string; begin result := ''; if angka > 1 Then begin i := 1; repeat hasil[i] := Trunc(angka / 2); SISA[i] := angka mod 2; angka := hasil[i]; i := i + 1; StrHasil := IntToStr(SISA[i - 1]) + StrHasil; until hasil[i - 1] < 2; StrHasil := IntToStr(hasil[i - 1]) + StrHasil; end else if angka = 1 then StrHasil := '00000001' else if angka = 0 Then StrHasil := '00000000'; for i := 1 to (8 - Length(StrHasil)) do StrHasil := '0' + StrHasil; result := StrHasil; end; { TSMSCODEC.Binerkan } { -------------------------------------------------------------------------------- function Bin2Long -> Convert binaty string to integer -> Parameter: StrBiner: string; -> the binary string; -> Return value: integer; -------------------------------------------------------------------------------- } function TSMSCODEC.Bin2Long(StrBiner : string): integer; var i, x : integer; begin x := 0; for i := 1 to Length(StrBiner) do begin x := x + StrToInt(Copy(StrBiner, i, 1)) * Trunc(Power(2,(Length(StrBiner) - i))); end; result := x; end; { TSMSCODEC.Bin2Long } { -------------------------------------------------------------------------------- function PDU7BIT2ASCII -> Convert PDU (string 7-bit PDU) to ASCII -> Parameter: StrPDU: string -> the PDUString PanjangUDH: integer; -> the length of the UDH PanjangData: integer; -> the length of the data -> return value: ASCII string -------------------------------------------------------------------------------- } function TSMSCODEC.PDU7BIT2ASCII(StrPDU : string; PanjangUDH, PanjangData: integer) : string; var i,j,x,z : integer; y, StrASCII : string; ArrPDU : array of string; Arr7Bit : array of string; ArrASCII : array of string; begin j := 1; z := 0; // 2 PDU chars represent 1 ASCII chars, so the length of the // ASCII chars sequence is 1/2 of the string PDU length SetLength(ArrPDU,Length(StrPDU) div 2); // Load each pair of PDU string to the appropriate index of ArrPDU while j < Length(StrPDU) do begin ArrPDU[z] := '$' + Copy(StrPDU,j,2); // '$' + string = hexadecimal j := j + 2; Inc(z); end; // Convert each ArrPDU item to binary for decoding for i := 0 to High(ArrPDU) do begin y := ArrPDU[i]; ArrPDU[i] := ''; ArrPDU[i] := Binerkan(StrToInt(y)); end; x := 0; SetLength(Arr7BIT,x+1); // The core process - the bit mutation for i := 0 to High(ArrPDU) do begin if i mod 7 = 0 then begin Arr7BIT[x] := RightStr(ArrPDU[i],7); end else begin if i mod 7 = 6 then begin Arr7BIT[x] := RightStr(ArrPDU[i],1) + LeftStr(ArrPDU[i-1],6); x := x + 1; SetLength(Arr7BIT,x+1); Arr7BIT[x] := LeftStr(ArrPDU[i],7); end else Arr7BIT[x] := RightStr(ArrPDU[i],Length(ArrPDU[i])-(i mod 7)-1) + LeftStr(ArrPDU[i-1],i mod 7); end; x := x + 1; SetLength(Arr7BIT,x+1); end; { for } j := 0; while j <= High(Arr7BIT) do begin SetLength(ArrASCII,Length(ArrASCII)+1); if Bin2Long(Arr7BIT[j]) = 27 then begin inc(j); case Bin2Long(Arr7BIT[j]) of 10 : ArrASCII[High(ArrASCII)] := Chr(12); 20 : ArrASCII[High(ArrASCII)] := Chr(94); 40 : ArrASCII[High(ArrASCII)] := Chr(123); 41 : ArrASCII[High(ArrASCII)] := Chr(125); 47 : ArrASCII[High(ArrASCII)] := Chr(92); 60 : ArrASCII[High(ArrASCII)] := Chr(91); 61 : ArrASCII[High(ArrASCII)] := Chr(126); 62 : ArrASCII[High(ArrASCII)] := Chr(93); 64 : ArrASCII[High(ArrASCII)] := Chr(124); else ArrASCII[High(ArrASCII)] := Chr(0); end; end else begin ArrASCII[High(ArrASCII)] := Chr(EQ7BIT2ASCII[Bin2Long(Arr7BIT[j])]); end; Inc(j); end; { while } SetLength(ArrASCII,PanjangData); for i := 0 to High(ArrASCII) do StrASCII := StrASCII + (ArrASCII[i]); ArrPDU := nil; Arr7Bit := nil; ArrASCII := nil; // if no UDH, ignore the octet info of UDH length, // not part of SMS message. if PanjangUDH > 0 then StrASCII := Copy(StrASCII,PanjangUDH + 3,Length(StrASCII)); Result := StrASCII; end; { TSMSCODEC.PDU7BIT2ASCII } { -------------------------------------------------------------------------------- function PDU8BIT2ASCII -> Convert string 8-bit PDU to ASCII -> parameter: strPDU: string; -> the 8 bit PDU string PanjangUDH: integer; -> length of the UDH PanjangData: integer; -> Length of the data -> return value: string (ASCII) -------------------------------------------------------------------------------- } function TSMSCODEC.PDU8BIT2ASCII(StrPDU : string; PanjangUDH, PanjangData: integer) : string; var StrASCII : string; i, x, PjPDUPesan : integer; begin if PanjangUDH <> 0 then begin x := (PanjangUDH * 2) + 2; // PDU info UDH StrPDU := RightStr(StrPDU,(PanjangData * 2) - x); // PDU Pesan end; PjPDUPesan := Length(StrPDU); i := 1; while i <= PjPDUPesan do begin if i mod 2 = 0 then begin StrASCII := StrASCII + Chr(EQ7BIT2ASCII[StrToInt('$' + LeftStr(StrPDU,2))]); StrPDU := RightStr(StrPDU,Length(StrPDU)-2); end; Inc(i); end; result := StrASCII; end; { TSMSCODEC.PDU8BIT2ASCII } { -------------------------------------------------------------------------------- function PDU16BIT2ASCII -> COnvert StrPDU (string 16-bit PDU) to ASCII -> parameter: strPDU: string; -> the 8 bit PDU string PanjangUDH: integer; -> length of the UDH PanjangData: integer; -> Length of the data -> Return value: string (ASCII) -------------------------------------------------------------------------------- } function TSMSCODEC.PDU16BIT2ASCII(StrPDU : string; PanjangUDH, PanjangData: integer) : string; var StrASCII : string; i, x, PjPDUPesan : integer; begin if PanjangUDH <> 0 then begin x := (PanjangUDH * 2) + 2; StrPDU := RightStr(StrPDU,(PanjangData * 2) - x); end; PjPDUPesan := Length(StrPDU); i := 1; while i <= PjPDUPesan do begin if i mod 4 = 0 then begin StrASCII := StrASCII + Chr(EQ7BIT2ASCII[StrToInt('$' + LeftStr(StrPDU,4))]); StrPDU := RightStr(StrPDU,Length(StrPDU)-4); end; Inc(i); end; result := StrASCII; end; { TSMSCODEC.PDU16BIT2ASCII } { -------------------------------------------------------------------------------- function ASCII2PDU7BIT -> Convert par StrASCII (string ASCII) to 7-bit PDU -> parameter: strASCII: string; -> the ASCII string -> Return value: string (7-bit PDU) -------------------------------------------------------------------------------- } function TSMSCODEC.ASCII2PDU7BIT(StrASCII : string) : string; var i,x,z : integer; ArrPDU : array of string; Arr7Bit : array of string; StrPDU : string; begin for i := 1 to Length(StrASCII) do begin SetLength(Arr7BIT,Length(Arr7BIT)+1); case Ord(StrASCII[i]) of 12 : begin SetLength(Arr7BIT,Length(Arr7BIT)+1); Arr7BIT[High(Arr7BIT)-1] := RightStr(Binerkan(27),7); Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(10),7); end; 94 : begin SetLength(Arr7BIT,Length(Arr7BIT)+1); Arr7BIT[High(Arr7BIT)-1] := RightStr(Binerkan(27),7); Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(20),7); end; 123 : begin SetLength(Arr7BIT,Length(Arr7BIT)+1); Arr7BIT[High(Arr7BIT)-1] := RightStr(Binerkan(27),7); Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(40),7); end; 125 : begin SetLength(Arr7BIT,Length(Arr7BIT)+1); Arr7BIT[High(Arr7BIT)-1] := RightStr(Binerkan(27),7); Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(41),7); end; 92 : begin SetLength(Arr7BIT,Length(Arr7BIT)+1); Arr7BIT[High(Arr7BIT)-1] := RightStr(Binerkan(27),7); Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(47),7); end; 91 : begin SetLength(Arr7BIT,Length(Arr7BIT)+1); Arr7BIT[High(Arr7BIT)-1] := RightStr(Binerkan(27),7); Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(60),7); end; 126 : begin SetLength(Arr7BIT,Length(Arr7BIT)+1); Arr7BIT[High(Arr7BIT)-1] := RightStr(Binerkan(27),7); Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(61),7); end; 93 : begin SetLength(Arr7BIT,Length(Arr7BIT)+1); Arr7BIT[High(Arr7BIT)-1] := RightStr(Binerkan(27),7); Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(62),7); end; 124 : begin SetLength(Arr7BIT,Length(Arr7BIT)+1); Arr7BIT[High(Arr7BIT)-1] := RightStr(Binerkan(27),7); Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(64),7); end; else begin Arr7BIT[High(Arr7BIT)] := RightStr(Binerkan(EQASCII27BIT[Ord(StrASCII[i])]),7); end; end; { case } end; { for } i := 1; x := 1; z := 0; SetLength(ArrPDU,z+1); while i <= High(Arr7BIT) do begin if (i > 1) and ((i) mod 8 = 0) then begin x := 1; inc(i); end else begin ArrPDU[z] := RightStr(Arr7BIT[i],x) + Arr7BIT[i-1]; SetLength(Arr7BIT[i],Length(Arr7BIT[i])-x); inc(z); SetLength(ArrPDU,z+1); Inc(x); Inc(i); end; end; ArrPDU[High(ArrPDU)] := Arr7BIT[High(Arr7BIT)]; for i := 0 to High(ArrPDU) do StrPDU := StrPDU + IntToHex(Bin2Long(ArrPDU[i]),2); ArrPDU := nil; Arr7Bit := nil; {if RightStr(StrPDU,2) = '00' then StrPDU := LeftStr(StrPDU,Length(StrPDU)-2);} Result := StrPDU; end; { TSMSCODEC.ASCII2PDU7BIT } { -------------------------------------------------------------------------------- procedure DecodeSMS -> Decode the SMSPDU then parse the element (such as the sender and the message) to be returned. -> Return value: stringlist (length: 2, index 0: sender number, index 1: the message -------------------------------------------------------------------------------- } function TSMSCODEC.DecodeSMS(StrSMS : string) : TStringList; var i, j, x, y, z, PanjangUDH, PanjangData : integer; StrBiner, StrPDU, StrPengirim, s : string; PakeUDH, Alpha : boolean; Elemen : TStringList; zDCS : integer; zDCSBinIdx23 : string; begin Elemen := TStringList.Create; i := 1; PakeUDH := False; Alpha := False; x := StrToInt('$' + Copy(StrSMS,i,2)); i := i + 2; i := i + (x * 2); x := StrToInt('$' + Copy(StrSMS,i,2)); StrBiner := Binerkan(x); if StrBiner[2] = '1' then begin PakeUDH := True; end; i := i + 2; x := StrToInt('$' + Copy(StrSMS,i,2)); y := x; i := i + 2; z := StrToInt('$' + Copy(StrSMS,i,2)); StrBiner := Binerkan(z); if Copy(StrBiner,2,3) = '101' then Alpha := True; i := i + 2; if x mod 2 <> 0 then x := x + 1; s := Copy(StrSMS,i,x); for j := 1 to Length(s) do begin if j mod 2 = 0 then begin StrPengirim := StrPengirim + s[j] + s[j-1]; end; end; if Alpha = True then begin StrPengirim := PDU7BIT2ASCII(s,0,Length(s)); StrPengirim := StringReplace(StrPengirim,'@','',[rfReplaceAll]); end else StrPengirim := Copy(StrPengirim,1,y); Elemen.Add(StrPengirim); i := i + x + 2; // Data Coding Schemes (DCS) z := StrToInt('$' + Copy(StrSMS,i,2)); StrBiner := Binerkan(z); zDCSBinIdx23 := Copy(StrBiner,5,2); zDCS := 0; if zDCSBinIdx23 = '00' then zDCS := 0; // 7-bit if zDCSBinIdx23 = '01' then zDCS := 1; // 8-bit if zDCSBinIdx23 = '10' then zDCS := 2; // UCS2 if zDCSBinIdx23 = '11' then zDCS := 0; // reserved (pake 7-bit) i := i + 2; x := 14; // abaikan SCTS i := i + x; PanjangData := StrToInt('$' + Copy(StrSMS,i,2)); i := i + 2; StrPDU := Copy(StrSMS,i,Length(StrSMS)); if PakeUDH then PanjangUDH := StrToInt('$' + Copy(StrSMS,i,2)) else PanjangUDH := 0; case zDCS of 0 : Elemen.Add(PDU7BIT2ASCII(StrPDU, PanjangUDH, PanjangData)); 1 : Elemen.Add(PDU8BIT2ASCII(StrPDU, PanjangUDH, PanjangData)); 2 : Elemen.Add(PDU16BIT2ASCII(StrPDU, PanjangUDH, PanjangData)); else Elemen.Add(PDU7BIT2ASCII(StrPDU, PanjangUDH, PanjangData)); end; Result := Elemen; end; { TSMSCODEC.DecodeSMS } { -------------------------------------------------------------------------------- procedure EncodeSMS -> Compose an SMS message -> Paramaters: strTujuan = string; -> The destination number strPesan = string; -> Your message to be sent -> Return value: SMS-PDU -------------------------------------------------------------------------------- } function TSMSCODEC.EncodeSMS(StrTujuan, StrPesan: string): string; var zTujuan, zPesan, Gabung: string; i, pjTujuan : integer; begin pjTujuan := Length(StrTujuan); // If length of StrTujuan is odd, add "F" to the end if Length(StrTujuan) mod 2 = 1 then StrTujuan := StrTujuan + 'F'; // The reversing process of destination number (swapped nibble) for i := 1 to Length(StrTujuan) do begin if i mod 2 = 0 then begin zTujuan := zTujuan + StrTujuan[i] + StrTujuan[i-1]; end; end; // We use internasional format here, add 91 in front of the destination number zTujuan := IntToHex(pjTujuan,2) + '91' + zTujuan; if Length(StrPesan) > 0 then zPesan := ASCII2PDU7BIT(StrPesan); // We use 7Bit encoding here // Add the hexadecimal reperesentation of the PDU length (1/2 of the number of chars) in front of the PDU zPesan := IntToHex(Length(StrPesan),2) + zPesan; // We use the SMSC number in the cellphone/gsmmodem, no status report, no UDH Gabung := '000100' + zTujuan + '0000' + zPesan; result := Gabung; end; { TSMSCODEC.EncodeSMS } end. Anyone to try?
Generator Posted August 10, 2007 Posted August 10, 2007 I think nobody is going to do it because the length of this code, i suggest you find some other way around this.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now