I don't need to test my programs. I have an error-correcting modem.

Legacy:ProfileImporter

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to: navigation, search
UT2003 :: Object >> Actor >> Info >> InternetInfo >> InternetLink >> TcpLink >> ProfileImporter (Ladder1.46)
  1. //-----------------------------------------------------------
  2. //	Ladder.ProfileImporter
  3. //
  4. // 	Handles all communication with foreign servers containing
  5. //  exported profile information
  6. //  Stores remote profile URL locations
  7. //-----------------------------------------------------------
  8. class ProfileImporter extends TcpLink config(LadderProfiles);
  9.  
  10. const LOGTAG = 'ProfileImport';
  11.  
  12. var enum EImportResult
  13. {
  14. 	IMPORT_BadURL,			// Incorrect URL format
  15. 	IMPORT_ResolveError,	// Couldn't resolve URL
  16. 	IMPORT_BindError,		// Couldn't bind port
  17. 	IMPORT_InvalidProfile,	// File was not a profile
  18. 	IMPORT_TimeOut,			// Timed out waiting
  19. 	IMPORT_ServerError,		// Unspecified Foreign Host Error
  20. 	IMPORT_BadRequest,		// Receieved 400 Error
  21. 	IMPORT_Unauthorized,	// Receieved 401 Error
  22. 	IMPORT_Forbidden,		// Receieved 403 Error
  23. 	IMPORT_NotFound,		// Receieved 404 Error
  24. 	IMPORT_Success			// Profile Successfully Imported
  25. } ImportResult;
  26.  
  27. var bool				bWaiting;		//Waiting for ImportResult
  28.  
  29. struct ZipLocation
  30. {
  31. 	var string ProfileURL;
  32. 	var string ZipName;
  33. 	var string ZipURL;
  34. };
  35.  
  36. var IpAddr				ServerIP;
  37. var LadderGameRules		LadderRules;
  38. var ProfileConfigSet	RemoteProfile;
  39. var LadderQuery			QueryPage;
  40.  
  41. var Session				ImportS;
  42. var WebRequest			QueryRequest;
  43. var WebResponse			QueryResponse;
  44.  
  45. var string				Buffer;
  46. var string				Header;
  47. var string 				LF;		// Line Feed
  48. var string				CR;		// Carriage Return
  49. var string				Token;	// Token Delimiter
  50.  
  51. var string				ProfileServerAddress;
  52. var string				RemoteProfilePath;
  53.  
  54. // Localized strings
  55. var localized string	BeginImportText;
  56.  
  57. // Localized error messages
  58. var localized string	ErrorText;
  59. var localized string	ErrorURLText;
  60. var localized string	ErrorResolvingText;
  61. var localized string	ErrorBindingText;
  62. var localized string	ErrorTimeoutText;
  63. var localized string	ErrorFNFText;
  64. var localized string	ErrorBadRequestText;
  65. var localized string	ErrorUnauthorizedText;
  66. var localized string	ErrorForbiddenText;
  67. var localized string	ErrorNoSession;
  68. var localized string	ErrorNoBuffer;
  69.  
  70. var localized string	BadImportURLText;
  71.  
  72. var config array<ZipLocation>	RemoteZips;	// Remote profile zips locations
  73.  
  74. function PostBeginPlay()
  75. {
  76. 	Super.PostBeginPlay();
  77. 	Disable('Tick');
  78.  
  79. 	LF = Chr(10);
  80. 	CR = Chr(13);
  81. 	Token = Chr(21);
  82. }
  83.  
  84. function Tick(float DeltaTime)
  85. {
  86. 	Super.Tick(DeltaTime);
  87.  
  88. 	if (!bWaiting)
  89. 	{
  90. 		// Finish importing profile data, let LadderQuery finish sending page
  91. 		QueryPage.RemoteImport(Self);
  92. 		Disable('Tick');
  93. 	}
  94. }
  95.  
  96. function Timer()
  97. {
  98. 	log(ErrorTimeoutText @ ProfileServerAddress,LOGTAG);
  99. 	ImportResult = IMPORT_Timeout;
  100. 	bWaiting = False;
  101.  
  102. // Manually close the connection since
  103. // we didn't receive a response
  104. 	Close();
  105. }
  106.  
  107. // Called from the Management Portal page
  108. function bool Connect(string ProfileURL, Session TempS)
  109. {
  110. 	local string L, R;
  111.  
  112. 	bWaiting = True;
  113. 	Enable('Tick');
  114.  
  115. 	log( BeginImportText @ ProfileURL, LOGTAG);
  116. 	if ( Left(ProfileURL, 7) ~= "http://")
  117. 		ProfileServerAddress = ParseDelimited(ProfileURL,"/",3);
  118. 	else
  119. 	{
  120. 		log( ErrorURLText @ "\"http://\"!", LOGTAG);
  121. 		ImportResult = IMPORT_BadURL;
  122. 		bWaiting = False;
  123. 		return false;
  124. 	}
  125.  
  126. 	ServerIP.Port = 80;
  127. 	if (InStr(ProfileServerAddress, ":") != -1)
  128. 	{
  129. 		Divide(ProfileServerAddress, ":", L, R);
  130. 		ProfileServerAddress = L;
  131. 		ServerIP.Port = int(R);
  132. 	}
  133.  
  134. 	RemoteProfilePath = "/" $ ParseDelimited(ProfileURL,"/",4,True);
  135.  
  136. 	Buffer = "";
  137. 	SetTimer(20.0, False);
  138. 	ImportS = TempS;
  139. 	Resolve( ProfileServerAddress );
  140. 	return true;
  141. }
  142.  
  143. event ResolveFailed()
  144. {
  145. 	log( ErrorResolvingText @ ProfileServerAddress, LOGTAG);
  146. 	ImportResult = IMPORT_ResolveError;
  147. 	bWaiting = False;
  148. }
  149.  
  150. event Resolved(IpAddr NumericIP)
  151. {
  152. 	if( NumericIP.Addr == 0 )
  153. 	{
  154. 		log( ErrorResolvingText @ ProfileServerAddress, LOGTAG);
  155. 		ImportResult = IMPORT_ResolveError;
  156. 		bWaiting = False;
  157. 		return;
  158. 	}
  159. 	ServerIP.Addr = NumericIP.Addr;
  160.  
  161. 	// Bind the local port.
  162. 	if( BindPort() == 0 )
  163. 	{
  164. 		log( ErrorBindingText, LOGTAG);
  165. 		ImportResult = IMPORT_BindError;
  166. 		bWaiting = False;
  167. 		return;
  168. 	}
  169. // Everything went OK - proceed to open connection
  170. 	Open( ServerIP );
  171. }
  172.  
  173. event Opened()
  174. {
  175. 	if (LinkMode != MODE_Line)
  176. 		LinkMode = MODE_Line;
  177.  
  178. 	SetTimer(0.0, False);		// Stop timeout counter
  179.  
  180. 	// Check header of file first, to make sure it's a valid file
  181. 	RequestFile(0);
  182. }
  183.  
  184. // Connection was closed by foreign host - Check header for response code
  185. event Closed()
  186. {
  187. 	local string Result;
  188.  
  189. // First find header and respond accordingly
  190. 	if (SeperateHeaders(Buffer))
  191. 		Result = ProcessHeaders();
  192. 	else
  193. 	{
  194. 		ImportResult = IMPORT_ServerError;
  195. 		bWaiting = False;
  196. 		return;
  197. 	}
  198.  
  199. 	if (Result == "")
  200. 	{
  201. 		ImportResult = IMPORT_ServerError;
  202. 		bWaiting = False;
  203. 		return;
  204. 	}
  205.  
  206.  /*    Possible Response Codes:
  207.  		"200"   ; OK
  208.         "201"   ; Created
  209.         "202"   ; Accepted
  210.         "204"   ; No Content
  211.         "301"   ; Moved Permanently
  212.         "302"   ; Moved Temporarily
  213.         "304"   ; Not Modified
  214.         "400"   ; Bad Request
  215.         "401"   ; Unauthorized
  216.         "403"   ; Forbidden
  217.         "404"   ; Not Found
  218.         "500"   ; Internal Server Error
  219.         "501"   ; Not Implemented
  220.         "502"   ; Bad Gateway
  221.         "503"   ; Service Unavailable
  222. */
  223. 	switch (Result)
  224. 	{
  225. 	case "200":
  226. 	// Buffer should now only contain the actual document
  227. 	// Fill ImportS from document info
  228. 		if (FillSessionInfo())
  229. 			ImportResult = IMPORT_Success;
  230. 		break;
  231. 	case "400":
  232. 		log( ErrorBadRequestText, LOGTAG);
  233. 		ImportResult = IMPORT_BadRequest;
  234. 		break;
  235. 	case "401":
  236. 		log( ErrorUnauthorizedText, LOGTAG);
  237. 		ImportResult = IMPORT_Unauthorized;
  238. 		break;
  239. 	case "403":
  240. 		log( ErrorForbiddenText, LOGTAG);
  241. 		ImportResult = IMPORT_Forbidden;
  242. 		break;
  243. 	default:
  244. 		log( ErrorFNFText, LOGTAG);
  245. 		ImportResult = IMPORT_NotFound;
  246. 	}
  247. 	bWaiting = False;
  248. }
  249.  
  250. function bool FillSessionInfo()
  251. {
  252. 	local string Line;
  253. 	local string Type;
  254. 	local string Info;
  255. 	local bool bValidProfile;
  256.  
  257. 	local string Token1, Token2, Token3, Token4, Token5, Token6, Token7;
  258.  
  259. 	if (ImportS == None)
  260. 	{
  261. 		log( ErrorNoSession, LOGTAG);
  262. 		ImportResult = IMPORT_ServerError;
  263. 		return false;
  264. 	}
  265.  
  266. 	if (Buffer == "")
  267. 	{
  268. 		log(ErrorNoBuffer, LOGTAG);
  269. 		ImportResult = IMPORT_ServerError;
  270. 		return False;
  271. 	}
  272. 	else
  273. 	{
  274. 		while ( (Left(Buffer,1) ~= CR) || (Left(Buffer,1) ~= LF) )
  275. 			Buffer = Mid(Buffer,1);
  276. 	}
  277.  
  278. 	while (ReadBufferedLine(Line))
  279. 	{
  280. 		Divide(Line,Chr(58),Type,Info);
  281.  
  282. 		Token1 = ParseDelimited(Info,Token,1);
  283. 		Token2 = ParseDelimited(Info,Token,2);
  284. 		Token3 = ParseDelimited(Info,Token,3);
  285. 		Token4 = ParseDelimited(Info,Token,4);
  286. 		Token5 = ParseDelimited(Info,Token,5);
  287. 		Token6 = ParseDelimited(Info,Token,6);
  288. 		Token7 = ParseDelimited(Info,Token,7);
  289.  
  290. 		switch (Type)
  291. 		{
  292. 		case "Profile":
  293. 			ImportS.setValue("ProfileName",Token1,True);
  294. 			ImportS.setValue("GameType",Token2,True);
  295. 			ImportS.setValue("MaxMaps",Token3,True);
  296. 			ImportS.setValue("GameName",Token4,True);
  297. 			ImportS.setValue("ACClass",Token5,True);
  298. 			if (Token6 != "") ImportS.setValue("CustomGameLoc",Token6,True);
  299. 			if (Token7 != "") ImportS.setValue("CustomACLoc",Token7,True);
  300. 			bValidProfile = True;
  301. 			break;
  302.  
  303. 		case "Map":
  304. 			ImportS.setMap(Token1,int(Token2),True,bool(Token3));
  305. 			if (Token4 != "") AddRemoteZipLocation(Token1,Token4);
  306. 			break;
  307.  
  308. 		case "Mutator":
  309. 			ImportS.setMutator(Token2,True,bool(Token3));
  310. 			if (Token4 != "") AddRemoteZipLocation(Token2,Token4);
  311.  
  312. 		case "Data":
  313. 			ImportS.setValue(Token1,Token2,True);
  314. 			break;
  315. 		}
  316. 	}
  317.  
  318. 	if (bValidProfile)
  319. 		return true;
  320.  
  321. 	ImportResult = IMPORT_InvalidProfile;
  322. 	return false;
  323. }
  324.  
  325. function AddRemoteZipLocation(string PackageName, string PackageLocation)
  326. {
  327. 	local int i;
  328. 	local bool ZipFound;
  329.  
  330. 	local ZipLocation Zip;
  331.  
  332. 	i = InStr(PackageName, ".");
  333. 	if (i != -1)
  334. 		PackageName = Left(PackageName,i);
  335.  
  336. 	Zip.ProfileURL = ProfileServerAddress $ RemoteProfilePath;
  337. 	Zip.ZipName = PackageName;
  338. 	Zip.ZipURL = PackageLocation;
  339.  
  340. 	for (i = 0; i < RemoteZips.Length; i++)
  341. 	{
  342. 		if (RemoteZips[i].ProfileURL ~= Zip.ProfileURL && RemoteZips[i].ZipName ~= Zip.ZipName)
  343. 		{
  344. 			ZipFound = True;
  345. 			break;
  346. 		}
  347. 	}
  348.  
  349. 	if (ZipFound) RemoteZips[i] = Zip;
  350. 	else RemoteZips[RemoteZips.Length] = Zip;
  351.  
  352. 	SaveConfig();
  353. }
  354.  
  355. function string FindRemoteZipLocation(string PackageName, string ProfileURL, optional bool bAllowOtherLocations)
  356. {
  357. 	local int i;
  358.  
  359. 	i = InStr(PackageName, ".");
  360. 	if (i != -1) PackageName = Left(PackageName,i);
  361.  
  362. 	for (i = 0;i < RemoteZips.Length; i++)
  363. 		if ((RemoteZips[i].ZipName ~= PackageName) && ((RemoteZips[i].ProfileURL ~= ProfileURL) || (bAllowOtherLocations)) )
  364. 			return RemoteZips[i].ZipURL;
  365.  
  366. 	return "";
  367. }
  368.  
  369. function bool SeperateHeaders(string WebPage)
  370. {
  371. 	local int i;
  372.  
  373. 	//CR$LF$CR$LF seperates header and document
  374. 	i = InStr(WebPage, CR$LF$CR$LF);
  375.  
  376. 	if (i == -1)
  377. 		return false;
  378.  
  379. 	Header = Left(WebPage, i);
  380. 	Buffer = Mid(WebPage, i + 3);
  381. 	return true;
  382. }
  383.  
  384. function string ProcessHeaders()
  385. {
  386. 	if (Header == "")
  387. 		return "";
  388.  
  389. 	//Remove all CR, leaving only LF
  390. 	Header = RemoveStr(Header,CR);
  391.  
  392. 	//Parse Response Code from server, ex. HTTP/1.1 200 OK
  393. 	return Mid(ParseDelimited(Header,LF,1),9,3);
  394. }
  395.  
  396. event ReceivedText(string Text)
  397. {
  398. 	Buffer = Buffer $ Text;
  399. }
  400.  
  401. event ReceivedLine(string Line)
  402. {
  403. 	local string Result;
  404.  
  405. 	if (SeperateHeaders(Line))
  406. 		Result = ProcessHeaders();
  407.  
  408. 	if (Result == "200")
  409. 	{
  410. 		Result = FindContentType(Line);
  411. 		if (Result ~= "text/plain")
  412. 		{
  413. 			RequestFile(1);
  414. 			LinkMode = Mode_Text;
  415. 		}
  416. 		else
  417. 		{
  418. 			ImportResult = IMPORT_InvalidProfile;
  419. 			bWaiting = False;
  420. 		}
  421. 	}
  422.  
  423. 	else
  424. 	{
  425. 		ImportResult = IMPORT_NotFound;
  426. 		bWaiting = False;
  427. 	}
  428. }
  429.  
  430. function string FindContentType(string Text)
  431. {
  432. 	local array<string> Lines;
  433. 	local string Tmp;
  434. 	local int i, j;
  435.  
  436. 	Buffer = Text;
  437. 	while (ReadBufferedLine(Tmp))
  438. 		Lines[Lines.Length] = Tmp;
  439.  
  440. 	for (i=0;i<Lines.Length;i++)
  441. 	{
  442. 		if (Left(Lines[i],13) ~= "Content-Type:")
  443. 		{
  444. 			Tmp = Mid(Lines[i],14);
  445. 			j = InStr(Tmp,";");
  446. 			if (j >= 0)
  447. 				Tmp = Left(Tmp,j);
  448. 		}
  449. 	}
  450.  
  451. 	return Tmp;
  452. }
  453.  
  454. function RequestFile(int RequestType)
  455. {
  456. 	local string RequestMethod, ControlText;
  457.  
  458. 	if (RequestType == 0)
  459. 	{
  460. 		RequestMethod = "HEAD";
  461. 		ControlText = "Keep-Alive";
  462. 	}
  463. 	else
  464. 	{
  465. 		RequestMethod = "GET";
  466. 		ControlText = "close";
  467. 	}
  468.  
  469. 	SendText(RequestMethod@RemoteProfilePath@"HTTP/1.1");
  470. 	SendText("Host:"@ProfileServerAddress);
  471. 	SendText("Accept: text/plain");					// Accept only plain txt files
  472. 	SendText("User-agent: ProfileManager; UT2003"@Level.EngineVersion);
  473. 	SendText("Pragma: no-cache");					// Do not allow proxies to send cached information
  474. 	SendText("Cache-control: no-cache");
  475. 	SendText("Connection:"@ControlText);					// No further information to be sent
  476. 	SendText("");
  477. }
  478.  
  479. function bool ReadBufferedLine(out string Text)
  480. {
  481. 	local int i;
  482.  
  483. 	i = InStr(Buffer, LF);
  484.  
  485. 	if(i == -1)
  486. 		return False;
  487.  
  488. 	if (Mid(Buffer, i-1, 1) == CR)
  489. 		Text = Left(Buffer, i-1);
  490. 	else Text = Left(Buffer, i);
  491.  
  492. 	Buffer = Mid(Buffer, i+1);
  493. 	return True;
  494. }
  495.  
  496. // Copied from BufferedTcpLink
  497. function string ParseDelimited(string Text, string Delimiter, int Count, optional bool bToEndOfLine)
  498. {
  499. 	local string Result;
  500. 	local int Found, i;
  501. 	local string s;
  502.  
  503. 	Result = "";
  504. 	Found = 1;
  505.  
  506. 	for(i=0;i<Len(Text);i++)
  507. 	{
  508. 		s = Mid(Text, i, 1);
  509. 		if(InStr(Delimiter, s) != -1)
  510. 		{
  511. 			if(Found == Count)
  512. 			{
  513. 				if(bToEndOfLine)
  514. 					return Result$Mid(Text, i);
  515. 				else
  516. 					return Result;
  517. 			}
  518.  
  519. 			Found++;
  520. 		}
  521. 		else
  522. 		{
  523. 			if(Found >= Count)
  524. 				Result = Result $ s;
  525. 		}
  526. 	}
  527.  
  528. 	return Result;
  529. }
  530.  
  531. function string RemoveStr(string Source,string mask)
  532. {
  533. 	local int i;
  534. 	local string left,right;
  535.  
  536. 	i = InStr(Source,mask);
  537. 	while (i != -1)
  538. 	{
  539. 		Divide(Source,mask,left,right);
  540. 		Source = Left$Right;
  541. 		i = InStr(Source,Mask);
  542. 	}
  543.  
  544. 	return Source;
  545. }