00001
00002
00003
00004
00005
00006
00007
00008 class ProfileImporter extends TcpLink config(LadderProfiles);
00009
00010 const LOGTAG = 'ProfileImport';
00011
00012 var enum EImportResult
00013 {
00014 IMPORT_BadURL,
00015 IMPORT_ResolveError,
00016 IMPORT_BindError,
00017 IMPORT_InvalidProfile,
00018 IMPORT_TimeOut,
00019 IMPORT_ServerError,
00020 IMPORT_BadRequest,
00021 IMPORT_Unauthorized,
00022 IMPORT_Forbidden,
00023 IMPORT_NotFound,
00024 IMPORT_Success
00025 } ImportResult;
00026
00027 var bool bWaiting;
00028
00029 struct ZipLocation
00030 {
00031 var string ProfileURL;
00032 var string ZipName;
00033 var string ZipURL;
00034 };
00035
00036 var IpAddr ServerIP;
00037 var LadderGameRules LadderRules;
00038 var ProfileConfigSet RemoteProfile;
00039 var LadderQuery QueryPage;
00040
00041 var Session ImportS;
00042 var WebRequest QueryRequest;
00043 var WebResponse QueryResponse;
00044
00045 var string Buffer;
00046 var string Header;
00047 var string LF;
00048 var string CR;
00049 var string Token;
00050
00051 var string ProfileServerAddress;
00052 var string RemoteProfilePath;
00053
00054
00055 var localized string BeginImportText;
00056
00057
00058 var localized string ErrorText;
00059 var localized string ErrorURLText;
00060 var localized string ErrorResolvingText;
00061 var localized string ErrorBindingText;
00062 var localized string ErrorTimeoutText;
00063 var localized string ErrorFNFText;
00064 var localized string ErrorBadRequestText;
00065 var localized string ErrorUnauthorizedText;
00066 var localized string ErrorForbiddenText;
00067 var localized string ErrorNoSession;
00068 var localized string ErrorNoBuffer;
00069
00070 var localized string BadImportURLText;
00071
00072 var config array<ZipLocation> RemoteZips;
00073
00074 function PostBeginPlay()
00075 {
00076 Super.PostBeginPlay();
00077 Disable('Tick');
00078
00079 LF = Chr(10);
00080 CR = Chr(13);
00081 Token = Chr(21);
00082 }
00083
00084 function Tick(float DeltaTime)
00085 {
00086 Super.Tick(DeltaTime);
00087
00088 if (!bWaiting)
00089 {
00090
00091 QueryPage.RemoteImport(Self);
00092 Disable('Tick');
00093 }
00094 }
00095
00096 function Timer()
00097 {
00098 log(ErrorTimeoutText @ ProfileServerAddress,LOGTAG);
00099 ImportResult = IMPORT_Timeout;
00100 bWaiting = False;
00101
00102
00103
00104 Close();
00105 }
00106
00107
00108 function bool Connect(string ProfileURL, Session TempS)
00109 {
00110 local string L, R;
00111
00112 bWaiting = True;
00113 Enable('Tick');
00114
00115 log( BeginImportText @ ProfileURL, LOGTAG);
00116 if ( Left(ProfileURL, 7) ~= "http://")
00117 ProfileServerAddress = ParseDelimited(ProfileURL,"/",3);
00118 else
00119 {
00120 log( ErrorURLText @ "\"http://\"!", LOGTAG);
00121 ImportResult = IMPORT_BadURL;
00122 bWaiting = False;
00123 return false;
00124 }
00125
00126 ServerIP.Port = 80;
00127 if (InStr(ProfileServerAddress, ":") != -1)
00128 {
00129 Divide(ProfileServerAddress, ":", L, R);
00130 ProfileServerAddress = L;
00131 ServerIP.Port = int(R);
00132 }
00133
00134 RemoteProfilePath = "/" $ ParseDelimited(ProfileURL,"/",4,True);
00135
00136 Buffer = "";
00137 SetTimer(20.0, False);
00138 ImportS = TempS;
00139 Resolve( ProfileServerAddress );
00140 return true;
00141 }
00142
00143 event ResolveFailed()
00144 {
00145 log( ErrorResolvingText @ ProfileServerAddress, LOGTAG);
00146 ImportResult = IMPORT_ResolveError;
00147 bWaiting = False;
00148 }
00149
00150 event Resolved(IpAddr NumericIP)
00151 {
00152 if( NumericIP.Addr == 0 )
00153 {
00154 log( ErrorResolvingText @ ProfileServerAddress, LOGTAG);
00155 ImportResult = IMPORT_ResolveError;
00156 bWaiting = False;
00157 return;
00158 }
00159 ServerIP.Addr = NumericIP.Addr;
00160
00161
00162 if( BindPort() == 0 )
00163 {
00164 log( ErrorBindingText, LOGTAG);
00165 ImportResult = IMPORT_BindError;
00166 bWaiting = False;
00167 return;
00168 }
00169
00170 Open( ServerIP );
00171 }
00172
00173 event Opened()
00174 {
00175 if (LinkMode != MODE_Line)
00176 LinkMode = MODE_Line;
00177
00178 SetTimer(0.0, False);
00179
00180
00181 RequestFile(0);
00182 }
00183
00184
00185 event Closed()
00186 {
00187 local string Result;
00188
00189
00190 if (SeperateHeaders(Buffer))
00191 Result = ProcessHeaders();
00192 else
00193 {
00194 ImportResult = IMPORT_ServerError;
00195 bWaiting = False;
00196 return;
00197 }
00198
00199 if (Result == "")
00200 {
00201 ImportResult = IMPORT_ServerError;
00202 bWaiting = False;
00203 return;
00204 }
00205
00206
00223 switch (Result)
00224 {
00225 case "200":
00226
00227
00228 if (FillSessionInfo())
00229 ImportResult = IMPORT_Success;
00230 break;
00231 case "400":
00232 log( ErrorBadRequestText, LOGTAG);
00233 ImportResult = IMPORT_BadRequest;
00234 break;
00235 case "401":
00236 log( ErrorUnauthorizedText, LOGTAG);
00237 ImportResult = IMPORT_Unauthorized;
00238 break;
00239 case "403":
00240 log( ErrorForbiddenText, LOGTAG);
00241 ImportResult = IMPORT_Forbidden;
00242 break;
00243 default:
00244 log( ErrorFNFText, LOGTAG);
00245 ImportResult = IMPORT_NotFound;
00246 }
00247 bWaiting = False;
00248 }
00249
00250 function bool FillSessionInfo()
00251 {
00252 local string Line;
00253 local string Type;
00254 local string Info;
00255 local bool bValidProfile;
00256
00257 local string Token1, Token2, Token3, Token4, Token5, Token6, Token7;
00258
00259 if (ImportS == None)
00260 {
00261 log( ErrorNoSession, LOGTAG);
00262 ImportResult = IMPORT_ServerError;
00263 return false;
00264 }
00265
00266 if (Buffer == "")
00267 {
00268 log(ErrorNoBuffer, LOGTAG);
00269 ImportResult = IMPORT_ServerError;
00270 return False;
00271 }
00272 else
00273 {
00274 while ( (Left(Buffer,1) ~= CR) || (Left(Buffer,1) ~= LF) )
00275 Buffer = Mid(Buffer,1);
00276 }
00277
00278 while (ReadBufferedLine(Line))
00279 {
00280 Divide(Line,Chr(58),Type,Info);
00281
00282 Token1 = ParseDelimited(Info,Token,1);
00283 Token2 = ParseDelimited(Info,Token,2);
00284 Token3 = ParseDelimited(Info,Token,3);
00285 Token4 = ParseDelimited(Info,Token,4);
00286 Token5 = ParseDelimited(Info,Token,5);
00287 Token6 = ParseDelimited(Info,Token,6);
00288 Token7 = ParseDelimited(Info,Token,7);
00289
00290 switch (Type)
00291 {
00292 case "Profile":
00293 ImportS.setValue("ProfileName",Token1,True);
00294 ImportS.setValue("GameType",Token2,True);
00295 ImportS.setValue("MaxMaps",Token3,True);
00296 ImportS.setValue("GameName",Token4,True);
00297 ImportS.setValue("ACClass",Token5,True);
00298 if (Token6 != "") ImportS.setValue("CustomGameLoc",Token6,True);
00299 if (Token7 != "") ImportS.setValue("CustomACLoc",Token7,True);
00300 bValidProfile = True;
00301 break;
00302
00303 case "Map":
00304 ImportS.setMap(Token1,int(Token2),True,bool(Token3));
00305 if (Token4 != "") AddRemoteZipLocation(Token1,Token4);
00306 break;
00307
00308 case "Mutator":
00309 ImportS.setMutator(Token2,True,bool(Token3));
00310 if (Token4 != "") AddRemoteZipLocation(Token2,Token4);
00311
00312 case "Data":
00313 ImportS.setValue(Token1,Token2,True);
00314 break;
00315 }
00316 }
00317
00318 if (bValidProfile)
00319 return true;
00320
00321 ImportResult = IMPORT_InvalidProfile;
00322 return false;
00323 }
00324
00325 function AddRemoteZipLocation(string PackageName, string PackageLocation)
00326 {
00327 local int i;
00328 local bool ZipFound;
00329
00330 local ZipLocation Zip;
00331
00332 i = InStr(PackageName, ".");
00333 if (i != -1)
00334 PackageName = Left(PackageName,i);
00335
00336 Zip.ProfileURL = ProfileServerAddress $ RemoteProfilePath;
00337 Zip.ZipName = PackageName;
00338 Zip.ZipURL = PackageLocation;
00339
00340 for (i = 0; i < RemoteZips.Length; i++)
00341 {
00342 if (RemoteZips[i].ProfileURL ~= Zip.ProfileURL && RemoteZips[i].ZipName ~= Zip.ZipName)
00343 {
00344 ZipFound = True;
00345 break;
00346 }
00347 }
00348
00349 if (ZipFound) RemoteZips[i] = Zip;
00350 else RemoteZips[RemoteZips.Length] = Zip;
00351
00352 SaveConfig();
00353 }
00354
00355 function string FindRemoteZipLocation(string PackageName, string ProfileURL, optional bool bAllowOtherLocations)
00356 {
00357 local int i;
00358
00359 i = InStr(PackageName, ".");
00360 if (i != -1) PackageName = Left(PackageName,i);
00361
00362 for (i = 0;i < RemoteZips.Length; i++)
00363 if ((RemoteZips[i].ZipName ~= PackageName) && ((RemoteZips[i].ProfileURL ~= ProfileURL) || (bAllowOtherLocations)) )
00364 return RemoteZips[i].ZipURL;
00365
00366 return "";
00367 }
00368
00369 function bool SeperateHeaders(string WebPage)
00370 {
00371 local int i;
00372
00373
00374 i = InStr(WebPage, CR$LF$CR$LF);
00375
00376 if (i == -1)
00377 return false;
00378
00379 Header = Left(WebPage, i);
00380 Buffer = Mid(WebPage, i + 3);
00381 return true;
00382 }
00383
00384 function string ProcessHeaders()
00385 {
00386 if (Header == "")
00387 return "";
00388
00389
00390 Header = RemoveStr(Header,CR);
00391
00392
00393 return Mid(ParseDelimited(Header,LF,1),9,3);
00394 }
00395
00396 event ReceivedText(string Text)
00397 {
00398 Buffer = Buffer $ Text;
00399 }
00400
00401 event ReceivedLine(string Line)
00402 {
00403 local string Result;
00404
00405 if (SeperateHeaders(Line))
00406 Result = ProcessHeaders();
00407
00408 if (Result == "200")
00409 {
00410 Result = FindContentType(Line);
00411 if (Result ~= "text/plain")
00412 {
00413 RequestFile(1);
00414 LinkMode = Mode_Text;
00415 }
00416 else
00417 {
00418 ImportResult = IMPORT_InvalidProfile;
00419 bWaiting = False;
00420 }
00421 }
00422
00423 else
00424 {
00425 ImportResult = IMPORT_NotFound;
00426 bWaiting = False;
00427 }
00428 }
00429
00430 function string FindContentType(string Text)
00431 {
00432 local array<string> Lines;
00433 local string Tmp;
00434 local int i, j;
00435
00436 Buffer = Text;
00437 while (ReadBufferedLine(Tmp))
00438 Lines[Lines.Length] = Tmp;
00439
00440 for (i=0;i<Lines.Length;i++)
00441 {
00442 if (Left(Lines[i],13) ~= "Content-Type:")
00443 {
00444 Tmp = Mid(Lines[i],14);
00445 j = InStr(Tmp,";");
00446 if (j >= 0)
00447 Tmp = Left(Tmp,j);
00448 }
00449 }
00450
00451 return Tmp;
00452 }
00453
00454 function RequestFile(int RequestType)
00455 {
00456 local string RequestMethod, ControlText;
00457
00458 if (RequestType == 0)
00459 {
00460 RequestMethod = "HEAD";
00461 ControlText = "Keep-Alive";
00462 }
00463 else
00464 {
00465 RequestMethod = "GET";
00466 ControlText = "close";
00467 }
00468
00469 SendText(RequestMethod@RemoteProfilePath@"HTTP/1.1");
00470 SendText("Host:"@ProfileServerAddress);
00471 SendText("Accept: text/plain");
00472 SendText("User-agent: ProfileManager; UT2003"@Level.EngineVersion);
00473 SendText("Pragma: no-cache");
00474 SendText("Cache-control: no-cache");
00475 SendText("Connection:"@ControlText);
00476 SendText("");
00477 }
00478
00479 function bool ReadBufferedLine(out string Text)
00480 {
00481 local int i;
00482
00483 i = InStr(Buffer, LF);
00484
00485 if(i == -1)
00486 return False;
00487
00488 if (Mid(Buffer, i-1, 1) == CR)
00489 Text = Left(Buffer, i-1);
00490 else Text = Left(Buffer, i);
00491
00492 Buffer = Mid(Buffer, i+1);
00493 return True;
00494 }
00495
00496
00497 function string ParseDelimited(string Text, string Delimiter, int Count, optional bool bToEndOfLine)
00498 {
00499 local string Result;
00500 local int Found, i;
00501 local string s;
00502
00503 Result = "";
00504 Found = 1;
00505
00506 for(i=0;i<Len(Text);i++)
00507 {
00508 s = Mid(Text, i, 1);
00509 if(InStr(Delimiter, s) != -1)
00510 {
00511 if(Found == Count)
00512 {
00513 if(bToEndOfLine)
00514 return Result$Mid(Text, i);
00515 else
00516 return Result;
00517 }
00518
00519 Found++;
00520 }
00521 else
00522 {
00523 if(Found >= Count)
00524 Result = Result $ s;
00525 }
00526 }
00527
00528 return Result;
00529 }
00530
00531 function string RemoveStr(string Source,string mask)
00532 {
00533 local int i;
00534 local string left,right;
00535
00536 i = InStr(Source,mask);
00537 while (i != -1)
00538 {
00539 Divide(Source,mask,left,right);
00540 Source = Left$Right;
00541 i = InStr(Source,Mask);
00542 }
00543
00544 return Source;
00545 }