Legacy:ProfileImporter
From Unreal Wiki, The Unreal Engine Documentation Site
UT2003 :: Object >> Actor >> Info >> InternetInfo >> InternetLink >> TcpLink >> ProfileImporter (Ladder1.46)
//-----------------------------------------------------------
// Ladder.ProfileImporter
//
// Handles all communication with foreign servers containing
// exported profile information
// Stores remote profile URL locations
//-----------------------------------------------------------
class ProfileImporter extends TcpLink config(LadderProfiles);
const LOGTAG = 'ProfileImport';
var enum EImportResult
{
IMPORT_BadURL, // Incorrect URL format
IMPORT_ResolveError, // Couldn't resolve URL
IMPORT_BindError, // Couldn't bind port
IMPORT_InvalidProfile, // File was not a profile
IMPORT_TimeOut, // Timed out waiting
IMPORT_ServerError, // Unspecified Foreign Host Error
IMPORT_BadRequest, // Receieved 400 Error
IMPORT_Unauthorized, // Receieved 401 Error
IMPORT_Forbidden, // Receieved 403 Error
IMPORT_NotFound, // Receieved 404 Error
IMPORT_Success // Profile Successfully Imported
} ImportResult;
var bool bWaiting; //Waiting for ImportResult
struct ZipLocation
{
var string ProfileURL;
var string ZipName;
var string ZipURL;
};
var IpAddr ServerIP;
var LadderGameRules LadderRules;
var ProfileConfigSet RemoteProfile;
var LadderQuery QueryPage;
var Session ImportS;
var WebRequest QueryRequest;
var WebResponse QueryResponse;
var string Buffer;
var string Header;
var string LF; // Line Feed
var string CR; // Carriage Return
var string Token; // Token Delimiter
var string ProfileServerAddress;
var string RemoteProfilePath;
// Localized strings
var localized string BeginImportText;
// Localized error messages
var localized string ErrorText;
var localized string ErrorURLText;
var localized string ErrorResolvingText;
var localized string ErrorBindingText;
var localized string ErrorTimeoutText;
var localized string ErrorFNFText;
var localized string ErrorBadRequestText;
var localized string ErrorUnauthorizedText;
var localized string ErrorForbiddenText;
var localized string ErrorNoSession;
var localized string ErrorNoBuffer;
var localized string BadImportURLText;
var config array<ZipLocation> RemoteZips; // Remote profile zips locations
function PostBeginPlay()
{
Super.PostBeginPlay();
Disable('Tick');
LF = Chr(10);
CR = Chr(13);
Token = Chr(21);
}
function Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
if (!bWaiting)
{
// Finish importing profile data, let LadderQuery finish sending page
QueryPage.RemoteImport(Self);
Disable('Tick');
}
}
function Timer()
{
log(ErrorTimeoutText @ ProfileServerAddress,LOGTAG);
ImportResult = IMPORT_Timeout;
bWaiting = False;
// Manually close the connection since
// we didn't receive a response
Close();
}
// Called from the Management Portal page
function bool Connect(string ProfileURL, Session TempS)
{
local string L, R;
bWaiting = True;
Enable('Tick');
log( BeginImportText @ ProfileURL, LOGTAG);
if ( Left(ProfileURL, 7) ~= "http://")
ProfileServerAddress = ParseDelimited(ProfileURL,"/",3);
else
{
log( ErrorURLText @ "\"http://\"!", LOGTAG);
ImportResult = IMPORT_BadURL;
bWaiting = False;
return false;
}
ServerIP.Port = 80;
if (InStr(ProfileServerAddress, ":") != -1)
{
Divide(ProfileServerAddress, ":", L, R);
ProfileServerAddress = L;
ServerIP.Port = int(R);
}
RemoteProfilePath = "/" $ ParseDelimited(ProfileURL,"/",4,True);
Buffer = "";
SetTimer(20.0, False);
ImportS = TempS;
Resolve( ProfileServerAddress );
return true;
}
event ResolveFailed()
{
log( ErrorResolvingText @ ProfileServerAddress, LOGTAG);
ImportResult = IMPORT_ResolveError;
bWaiting = False;
}
event Resolved(IpAddr NumericIP)
{
if( NumericIP.Addr == 0 )
{
log( ErrorResolvingText @ ProfileServerAddress, LOGTAG);
ImportResult = IMPORT_ResolveError;
bWaiting = False;
return;
}
ServerIP.Addr = NumericIP.Addr;
// Bind the local port.
if( BindPort() == 0 )
{
log( ErrorBindingText, LOGTAG);
ImportResult = IMPORT_BindError;
bWaiting = False;
return;
}
// Everything went OK - proceed to open connection
Open( ServerIP );
}
event Opened()
{
if (LinkMode != MODE_Line)
LinkMode = MODE_Line;
SetTimer(0.0, False); // Stop timeout counter
// Check header of file first, to make sure it's a valid file
RequestFile(0);
}
// Connection was closed by foreign host - Check header for response code
event Closed()
{
local string Result;
// First find header and respond accordingly
if (SeperateHeaders(Buffer))
Result = ProcessHeaders();
else
{
ImportResult = IMPORT_ServerError;
bWaiting = False;
return;
}
if (Result == "")
{
ImportResult = IMPORT_ServerError;
bWaiting = False;
return;
}
/* Possible Response Codes:
"200" ; OK
"201" ; Created
"202" ; Accepted
"204" ; No Content
"301" ; Moved Permanently
"302" ; Moved Temporarily
"304" ; Not Modified
"400" ; Bad Request
"401" ; Unauthorized
"403" ; Forbidden
"404" ; Not Found
"500" ; Internal Server Error
"501" ; Not Implemented
"502" ; Bad Gateway
"503" ; Service Unavailable
*/
switch (Result)
{
case "200":
// Buffer should now only contain the actual document
// Fill ImportS from document info
if (FillSessionInfo())
ImportResult = IMPORT_Success;
break;
case "400":
log( ErrorBadRequestText, LOGTAG);
ImportResult = IMPORT_BadRequest;
break;
case "401":
log( ErrorUnauthorizedText, LOGTAG);
ImportResult = IMPORT_Unauthorized;
break;
case "403":
log( ErrorForbiddenText, LOGTAG);
ImportResult = IMPORT_Forbidden;
break;
default:
log( ErrorFNFText, LOGTAG);
ImportResult = IMPORT_NotFound;
}
bWaiting = False;
}
function bool FillSessionInfo()
{
local string Line;
local string Type;
local string Info;
local bool bValidProfile;
local string Token1, Token2, Token3, Token4, Token5, Token6, Token7;
if (ImportS == None)
{
log( ErrorNoSession, LOGTAG);
ImportResult = IMPORT_ServerError;
return false;
}
if (Buffer == "")
{
log(ErrorNoBuffer, LOGTAG);
ImportResult = IMPORT_ServerError;
return False;
}
else
{
while ( (Left(Buffer,1) ~= CR) || (Left(Buffer,1) ~= LF) )
Buffer = Mid(Buffer,1);
}
while (ReadBufferedLine(Line))
{
Divide(Line,Chr(58),Type,Info);
Token1 = ParseDelimited(Info,Token,1);
Token2 = ParseDelimited(Info,Token,2);
Token3 = ParseDelimited(Info,Token,3);
Token4 = ParseDelimited(Info,Token,4);
Token5 = ParseDelimited(Info,Token,5);
Token6 = ParseDelimited(Info,Token,6);
Token7 = ParseDelimited(Info,Token,7);
switch (Type)
{
case "Profile":
ImportS.setValue("ProfileName",Token1,True);
ImportS.setValue("GameType",Token2,True);
ImportS.setValue("MaxMaps",Token3,True);
ImportS.setValue("GameName",Token4,True);
ImportS.setValue("ACClass",Token5,True);
if (Token6 != "") ImportS.setValue("CustomGameLoc",Token6,True);
if (Token7 != "") ImportS.setValue("CustomACLoc",Token7,True);
bValidProfile = True;
break;
case "Map":
ImportS.setMap(Token1,int(Token2),True,bool(Token3));
if (Token4 != "") AddRemoteZipLocation(Token1,Token4);
break;
case "Mutator":
ImportS.setMutator(Token2,True,bool(Token3));
if (Token4 != "") AddRemoteZipLocation(Token2,Token4);
case "Data":
ImportS.setValue(Token1,Token2,True);
break;
}
}
if (bValidProfile)
return true;
ImportResult = IMPORT_InvalidProfile;
return false;
}
function AddRemoteZipLocation(string PackageName, string PackageLocation)
{
local int i;
local bool ZipFound;
local ZipLocation Zip;
i = InStr(PackageName, ".");
if (i != -1)
PackageName = Left(PackageName,i);
Zip.ProfileURL = ProfileServerAddress $ RemoteProfilePath;
Zip.ZipName = PackageName;
Zip.ZipURL = PackageLocation;
for (i = 0; i < RemoteZips.Length; i++)
{
if (RemoteZips[i].ProfileURL ~= Zip.ProfileURL && RemoteZips[i].ZipName ~= Zip.ZipName)
{
ZipFound = True;
break;
}
}
if (ZipFound) RemoteZips[i] = Zip;
else RemoteZips[RemoteZips.Length] = Zip;
SaveConfig();
}
function string FindRemoteZipLocation(string PackageName, string ProfileURL, optional bool bAllowOtherLocations)
{
local int i;
i = InStr(PackageName, ".");
if (i != -1) PackageName = Left(PackageName,i);
for (i = 0;i < RemoteZips.Length; i++)
if ((RemoteZips[i].ZipName ~= PackageName) && ((RemoteZips[i].ProfileURL ~= ProfileURL) || (bAllowOtherLocations)) )
return RemoteZips[i].ZipURL;
return "";
}
function bool SeperateHeaders(string WebPage)
{
local int i;
//CR$LF$CR$LF seperates header and document
i = InStr(WebPage, CR$LF$CR$LF);
if (i == -1)
return false;
Header = Left(WebPage, i);
Buffer = Mid(WebPage, i + 3);
return true;
}
function string ProcessHeaders()
{
if (Header == "")
return "";
//Remove all CR, leaving only LF
Header = RemoveStr(Header,CR);
//Parse Response Code from server, ex. HTTP/1.1 200 OK
return Mid(ParseDelimited(Header,LF,1),9,3);
}
event ReceivedText(string Text)
{
Buffer = Buffer $ Text;
}
event ReceivedLine(string Line)
{
local string Result;
if (SeperateHeaders(Line))
Result = ProcessHeaders();
if (Result == "200")
{
Result = FindContentType(Line);
if (Result ~= "text/plain")
{
RequestFile(1);
LinkMode = Mode_Text;
}
else
{
ImportResult = IMPORT_InvalidProfile;
bWaiting = False;
}
}
else
{
ImportResult = IMPORT_NotFound;
bWaiting = False;
}
}
function string FindContentType(string Text)
{
local array<string> Lines;
local string Tmp;
local int i, j;
Buffer = Text;
while (ReadBufferedLine(Tmp))
Lines[Lines.Length] = Tmp;
for (i=0;i<Lines.Length;i++)
{
if (Left(Lines[i],13) ~= "Content-Type:")
{
Tmp = Mid(Lines[i],14);
j = InStr(Tmp,";");
if (j >= 0)
Tmp = Left(Tmp,j);
}
}
return Tmp;
}
function RequestFile(int RequestType)
{
local string RequestMethod, ControlText;
if (RequestType == 0)
{
RequestMethod = "HEAD";
ControlText = "Keep-Alive";
}
else
{
RequestMethod = "GET";
ControlText = "close";
}
SendText(RequestMethod@RemoteProfilePath@"HTTP/1.1");
SendText("Host:"@ProfileServerAddress);
SendText("Accept: text/plain"); // Accept only plain txt files
SendText("User-agent: ProfileManager; UT2003"@Level.EngineVersion);
SendText("Pragma: no-cache"); // Do not allow proxies to send cached information
SendText("Cache-control: no-cache");
SendText("Connection:"@ControlText); // No further information to be sent
SendText("");
}
function bool ReadBufferedLine(out string Text)
{
local int i;
i = InStr(Buffer, LF);
if(i == -1)
return False;
if (Mid(Buffer, i-1, 1) == CR)
Text = Left(Buffer, i-1);
else Text = Left(Buffer, i);
Buffer = Mid(Buffer, i+1);
return True;
}
// Copied from BufferedTcpLink
function string ParseDelimited(string Text, string Delimiter, int Count, optional bool bToEndOfLine)
{
local string Result;
local int Found, i;
local string s;
Result = "";
Found = 1;
for(i=0;i<Len(Text);i++)
{
s = Mid(Text, i, 1);
if(InStr(Delimiter, s) != -1)
{
if(Found == Count)
{
if(bToEndOfLine)
return Result$Mid(Text, i);
else
return Result;
}
Found++;
}
else
{
if(Found >= Count)
Result = Result $ s;
}
}
return Result;
}
function string RemoveStr(string Source,string mask)
{
local int i;
local string left,right;
i = InStr(Source,mask);
while (i != -1)
{
Divide(Source,mask,left,right);
Source = Left$Right;
i = InStr(Source,Mask);
}
return Source;
}