/// <summary> Get the initial response from the execution properties. </summary> /// <returns>initial response string</returns> internal String GetLastOfflineInitialResponse() { //Extract and decode the initial response from the execution properties IEncryptor encryptor = PersistentOnlyCacheManager.GetInstance(); String initialResponseString = null; int lastOfflineFileVersion = LastOfflineFileVersion; byte[] initialResponseBytes = Encoding.Default.GetBytes(LastOfflineExecutionProperties[ConstInterface.INITIAL_RESPONSE]); if (encryptor.EncryptionDisabled) { initialResponseString = Encoding.UTF8.GetString(initialResponseBytes, 0, initialResponseBytes.Length); } else { if (lastOfflineFileVersion == 1) { initialResponseBytes = Base64.decodeToByte(Encoding.ASCII.GetString(initialResponseBytes, 0, initialResponseBytes.Length)); } initialResponseBytes = encryptor.Decrypt(initialResponseBytes); initialResponseString = Encoding.UTF8.GetString(initialResponseBytes, 0, initialResponseBytes.Length); } return(initialResponseString); }
/// <summary> /// writes content to temporary file. /// </summary> /// <param name="url"></param> /// <param name="content"></param> private void WriteToTemporarySourceFile(String url, byte[] content) { // extract file url & remote time String tempSourceFileUrl = BuildTemporarySourceFileName(GetFileNameFromURL(url)); String remoteTime = GetRemoteTime(url); PersistentOnlyCacheManager.GetInstance().PutFile(tempSourceFileUrl, content, remoteTime); }
/// <summary> Get the contents from the previously cached contents from the requested URL</summary> /// <param name="completeCachedFileRetrievalURL">complete cached file retrieval request (including the server-side time stamp), /// e.g. http://[server]/[requester]?CTX=&CACHE=My Application_DbhDataIds.xml|31/07/2013%2020:15:15</param> /// <param name="decryptResponse">if true, the contents will be be decrypted using the 'encryptionKey' passed to 'PersistentOnlyCacheManager.SetProperties'.</param> /// <returns></returns> internal override byte[] GetContent(string completeCachedFileRetrievalURL, bool decryptResponse) { //TODO: Kaushal. Most of this code is similar to that in HttpManager.GetContent(). //Check if this duplication can be avoided. String remoteTime = null; byte[] response = null; int indexOfCacheToken = completeCachedFileRetrievalURL.IndexOf(ConstInterface.RC_TOKEN_CACHED_FILE); //In disconnected state, we should reach here only for the cached files. Debug.Assert(indexOfCacheToken != -1); // split the url --> url and remote time string[] urlAndRemoteTimePair = HttpUtility.UrlDecode(completeCachedFileRetrievalURL, Encoding.UTF8).Split('|'); String cachedFileRetrievalURL = urlAndRemoteTimePair[0]; // the cached file retrieval request without the timestamp, e.g. http://[server]/[requester]?CTX=&CACHE=MG1VAI3T_MP_0$$$_$_$_N02400$$$$G8FM01_.xml string cachedFileLocalURL = CacheUtils.URLToFileName(cachedFileRetrievalURL); if (urlAndRemoteTimePair.Length > 1 && urlAndRemoteTimePair[1] != null) { remoteTime = urlAndRemoteTimePair[1]; //is the file encrypted? int indexOfNonEncryptedToken = remoteTime.IndexOf(ConstInterface.RC_TOKEN_NON_ENCRYPTED); if (indexOfNonEncryptedToken != -1) { decryptResponse = false; remoteTime = remoteTime.Substring(0, indexOfNonEncryptedToken - 1); } } ICacheManager cacheManager = PersistentOnlyCacheManager.GetInstance(); if (remoteTime == null) { response = cacheManager.GetFile(cachedFileLocalURL); } else { response = cacheManager.GetFile(cachedFileLocalURL, remoteTime); } if (response != null) { if (decryptResponse) { IEncryptor encryptor = PersistentOnlyCacheManager.GetInstance(); response = encryptor.Decrypt(response); } } else { throw new LocalSourceNotFoundException("Some of the required files are missing. Please restart the application when connected to the network."); } return(response); }
/// <summary> /// record an offline-required metadata + its local time /// </summary> /// <param name="requestedURL">complete cache file retrieval request to an offline-required metadata, /// e.g. http://[server]/[requester]?CTX=&CACHE=MG1VAI3T_MP_0$$$_$_$_N02400$$$$G8FM01_.xml|31/07/2013%2020:15:15</param> internal void Collect(string requestedURL) { if (Enabled) { string cachedFileServerFileName; string cachedFileLocalFileName; PersistentOnlyCacheManager.GetInstance().CompleteCacheRequestURLToFileNames(requestedURL, out cachedFileServerFileName, out cachedFileLocalFileName); Debug.Assert(HandleFiles.isExists(cachedFileLocalFileName)); CollectedMetadata[cachedFileServerFileName] = HandleFiles.getFileTime(cachedFileLocalFileName); } }
/// <summary> /// Reads the sources from server and writes its content to temporary sources. /// For Local session reads sources from cache. /// </summary> /// <param name="completeCachedFileRetrievalURL">complete cached file retrieval request (including the server-side time stamp), /// e.g. http://[server]/[requester]?CTX=&CACHE=MG1VAI3T_MP_0$$$_$_$_N02400$$$$G8FM01_.xml|31/07/2013%2020:15:15</param> /// <param name="cachedContentShouldBeReturned">if false, the method will return null as the retrieved content (avoiding retrieval of the requested URL from the local/client cache folder, to save resources / improve performance).</param> /// <param name="allowOutdatedContent">if true, check only file existence at client cache</param> /// <returns>content</returns> internal byte[] ReadSource(String completeCachedFileRetrievalURL, bool cachedContentShouldBeReturned, bool allowOutdatedContent) { Logger.Instance.WriteSupportToLog("ReadSource():>>>>> ", true); byte[] content = null; if (_enableSourcesIntegrity && CommandsProcessorManager.SessionStatus == CommandsProcessorManager.SessionStatusEnum.Remote) { var cachingStrategy = new HttpManager.CachingStrategy() { CanWriteToCache = false, CachedContentShouldBeReturned = cachedContentShouldBeReturned, AllowOutdatedContent = allowOutdatedContent }; content = RemoteCommandsProcessor.GetInstance().GetContent(completeCachedFileRetrievalURL, null, null, false, cachingStrategy); // if sources were modified (i.e. not retrieved from the cache), save the content to a temporary file: if (!cachingStrategy.FoundInCache) { WriteToTemporarySourceFile(completeCachedFileRetrievalURL, content); _commitList.Add(completeCachedFileRetrievalURL); } else { // record/register a cached file path + its local time: OfflineRequiredMetadataCollection.Collect(completeCachedFileRetrievalURL); } if (cachedContentShouldBeReturned) { content = PersistentOnlyCacheManager.GetInstance().Decrypt(content); } } else { if (cachedContentShouldBeReturned) { content = CommandsProcessorManager.GetContent(completeCachedFileRetrievalURL, true); } else { if (!PersistentOnlyCacheManager.GetInstance().IsCompleteCacheRequestURLExistsLocally(completeCachedFileRetrievalURL)) { throw new LocalSourceNotFoundException("Some of the required files are missing. Please restart the application when connected to the network."); //TODO: use a message from mgconst - here as well as in LocalCommandsProcessor.GetContent } } // record/register a cached file path + its local time: OfflineRequiredMetadataCollection.Collect(completeCachedFileRetrievalURL); } Logger.Instance.WriteSupportToLog("ReadSource():<<<< ", true); return(content); }
/// <summary> /// Read old data source repository /// </summary> /// <param name="dataDefinitionIdsUrl"> repository url to read old repository </param> /// <returns>returns old data source repository only if it is modified</returns> private DataSourceDefinitionManager GetOldDataSourceDefinitions(String dataDefinitionIdsUrl) { DataSourceDefinitionManager oldDataSourceDefinitionManager = null; try { // Get old data source repository url from last offline response String oldDataDefinitionIdsUrl = GetLastDataSourcesIdUrl(); // Last offline response can not be available if startup program is modified or in case of executing non-offline program. // In this case , read the repository file available in cache without remote time constraint if (oldDataDefinitionIdsUrl == null) { // split the url --> url and remote time string[] urlAndRemoteTimePair = HttpUtility.UrlDecode(dataDefinitionIdsUrl, Encoding.UTF8).Split('|'); oldDataDefinitionIdsUrl = urlAndRemoteTimePair[0]; } // read content form data source repository byte[] oldRepositoryContents = null; // the old (i.e. without the '_tmp' suffix) DbhDataIds may not be found in case this is the first access to the application. if (PersistentOnlyCacheManager.GetInstance().IsCompleteCacheRequestURLExistsLocally(oldDataDefinitionIdsUrl)) { oldRepositoryContents = LocalCommandsProcessor.GetInstance().GetContent(oldDataDefinitionIdsUrl, true); } // if contents are modified then return old data source repository if (oldRepositoryContents != null && (oldRepositoryContents.Length != NewDataSourceRepositoryContents.Length || !Misc.CompareByteArray(oldRepositoryContents, NewDataSourceRepositoryContents, NewDataSourceRepositoryContents.Length))) { // parse repository content and read data sources and build the data source definition collection oldDataSourceDefinitionManager = new DataSourceDefinitionManager(); oldDataSourceDefinitionManager.DataSourceBuilder.DataSourceReader = GetOldDataSourceBuffer; new DataSourceDefinitionManagerSaxHandler(oldRepositoryContents, oldDataSourceDefinitionManager); } // new repository contents are not needed more for datasource converter NewDataSourceRepositoryContents = null; } catch (Exception ex) { Logger.Instance.WriteExceptionToLog(ex); } CommandsProcessorManager.SessionStatus = CommandsProcessorManager.SessionStatusEnum.Remote; return(oldDataSourceDefinitionManager); }
/// <summary> /// commit modified sources to cache. /// </summary> internal void Commit() { Logger.Instance.WriteSupportToLog("Commit():>>>> ", true); bool success = true; // It is about to start committing the sources. So save the status. If process get terminated during commit, // in next execution, client will get to know the status of sources synchronization and take action accordingly. SourcesSyncStatus.InvalidSources = true; SourcesSyncStatus.SaveToFile(); // Renames the temporary sources to original names. foreach (String url in _commitList) { String tempFileName = BuildTemporarySourceFileName(GetFileNameFromURL(url)); String tempSourceFileFullName = PersistentOnlyCacheManager.GetInstance().URLToLocalFileName(tempFileName); String originalSourceFileFullName = PersistentOnlyCacheManager.GetInstance().URLToLocalFileName(GetFileNameFromURL(url)); String remoteTime = GetRemoteTime(url); // check if source with remotetime exist if (IsFileExistWithRequestedTime(tempSourceFileFullName, remoteTime)) { Logger.Instance.WriteSupportToLog(String.Format("commit(): renaming {0}", tempFileName), true); success = HandleFiles.renameFile(tempSourceFileFullName, originalSourceFileFullName); if (!success) { break; } if (remoteTime != null) { HandleFiles.setFileTime(originalSourceFileFullName, remoteTime); } // record/register a cached file path + its local time: OfflineRequiredMetadataCollection.Collect(url); } } if (!success) { String errorMessage; // sources commit failed // If tables were converted and committed, there structure won't match the sources if (SourcesSyncStatus.TablesIncompatibleWithDataSources == true) { errorMessage = ClientManager.Instance.getMessageString(MsgInterface.RC_ERROR_INCOMPATIBLE_DATASOURCES); } else { errorMessage = ClientManager.Instance.getMessageString(MsgInterface.RC_ERROR_INVALID_SOURCES); } throw new InvalidSourcesException(errorMessage, null); } // Commit is done successfully, clear the status. SourcesSyncStatus.Clear(); Logger.Instance.WriteSupportToLog("Commit():<<<< ", true); }
/// <summary> interact with the cache manager; execute HTTP request if needed; if 'decryptResponse' is true, fresh /// responses from the server will be decrypted (if needed) using the 'encryptionKey' passed to 'setProperties'; </summary> /// <param name="requestedURL">URL to be accessed. If the URL contains a time stamp on the server-side (|timestamp), the time stamp is seperated from the URL and checked against the local/client-side cache.</param> /// <param name="requestContent">content to be sent to server (relevant only for POST method - is null for other methods). may be byte[] or string.</param> /// <param name="requestContentType">type of the content to be sent to server, e.g application/octet-stream (for POST method only).</param> /// <param name="decryptResponse">if true, fresh responses from the server will be decrypted using the 'encryptionKey' passed to 'HttpManager.SetProperties'.</param> /// <param name="allowRetry">whether or not retry after connection failure.</param> /// <param name="cachingStrategy">Enabels or disables cache read/write operations.</param> /// <param name="isError">set to true if server returns error processing the request.</param> /// <returns>response (from the server).</returns> internal byte[] GetContent(String requestedURL, object requestContent, String requestContentType, bool decryptResponse, bool allowRetry, CachingStrategy cachingStrategy, out bool isError) { String urlCachedByRequest = null; // a cached file retrieval request without the timestamp, e.g. http://[server]/[requester]?CTX=&CACHE=MG1VAI3T_MP_0$$$_$_$_N02400$$$$G8FM01_.xml byte[] response = null; long startTime = Misc.getSystemMilliseconds(); TimeSpan requestStartTime = DateTime.Now.TimeOfDay; // for RecentNetworkActivities only bool isCacheRequest = false; try { Logger.Instance.WriteServerToLog( "*************************************************************************************************"); cachingStrategy.FoundInCache = false; isError = false; String remoteTime = null; // GET request if (requestContent == null) { //-------------------------------------------------------------------------------------------- // topic #16 (MAGIC version 1.8 for WIN) RC - Cache security enhancements: // the server reports the time stamp as part as the URL, for example: // http://../mgrqispi.dll?CTX=155842418385&CACHE=KeyboardMapping.xml%7C19/03/2009%2009:45:33 // cache files are accessed via a request, in this example: // http://myserver/MagicScripts/mgrqispi.dll?CTX=155842418385&CACHE=KeyboardMapping.xml // ^ indexOfCacheToken //-------------------------------------------------------------------------------------------- // extract the remote time from the request int indexOfCacheToken = requestedURL.IndexOf(ConstInterface.RC_TOKEN_CACHED_FILE); if (indexOfCacheToken != -1) { isCacheRequest = true; string decodedUrl = HttpUtility.UrlDecode(requestedURL, Encoding.UTF8); requestedURL = CacheUtils.URLToFileName(decodedUrl); string cacheFilePath = String.Empty; CacheUtils.GetCacheFileDetailsFromUrl(decodedUrl, ref cacheFilePath, ref remoteTime, ref decryptResponse); byte[] bufferToSend = Encoding.UTF8.GetBytes(cacheFilePath); IEncryptor encryptor = PersistentOnlyCacheManager.GetInstance(); if (!encryptor.EncryptionDisabled) { byte[] encryptedURL = encryptor.Encrypt(Encoding.UTF8.GetBytes(cacheFilePath)); byte[] base64EncodedEncryptedURL = Base64.encode(encryptedURL); bufferToSend = base64EncodedEncryptedURL; } // prepare URL again by encoding only CACHE value urlCachedByRequest = decodedUrl.Substring(0, indexOfCacheToken) + ConstInterface.RC_INDICATION + ConstInterface.RC_TOKEN_CACHED_FILE + HttpUtility.UrlEncode(bufferToSend); } else if (requestedURL.IndexOf('?') == -1) { // i.e. the url refers to a file on the web server, rather than a requester + query string remoteTime = _httpClient.GetRemoteTime(requestedURL); } } Logger.Instance.WriteServerToLog(requestedURL); byte[] errorResponse = null; // Story#138618: remoteTime will not exist in the cache url. // (for backward compatibility remoteTime exist in url if SpecialTimeStampRIACache is ON) if (remoteTime == null && !isCacheRequest) { // resources for which no remote time could be retrieved (either by the web server or by the runtime-engine, in a cache request url) can't be and aren't cached, // since without a time stamp the client can't decide if the client-side copy is out-dated. //----------------------------- LogAccessToServer("", requestContent); response = GetContentByHTTPRequest(requestedURL, requestContent, requestContentType, allowRetry); Debug.Assert(response != null); errorResponse = CheckAndGetErrorResponse(response); } else { // cachable resources .. //---------------------- ICacheManager cacheManager = PersistentOnlyCacheManager.GetInstance(); if (cachingStrategy.CachedContentShouldBeReturned) { response = (remoteTime == null ? cacheManager.GetFile(requestedURL) : cacheManager.GetFile(requestedURL, remoteTime)); cachingStrategy.FoundInCache = (response != null); } else { // here 'requestedURL' is excluding the server-side time stamp, e.g. /MG1VAI3T_MP_0$$$_$_$_N02400$$$$G8FM01_.xml. Debug.Assert(requestedURL.IndexOf('|') == -1); cachingStrategy.FoundInCache = (cachingStrategy.AllowOutdatedContent || remoteTime == null ? cacheManager.IsExists(requestedURL) : cacheManager.CheckFile(requestedURL, remoteTime)); } if (cachingStrategy.FoundInCache) { Logger.Instance.WriteServerToLog("Found in cache."); } else { LogAccessToServer(String.Format("Not in cache{0}!", cachingStrategy.AllowOutdatedContent ? "" : " or outdated"), null); // get fresh content & put in the cache response = GetContentByHTTPRequest((urlCachedByRequest ?? requestedURL), null, requestContentType, allowRetry); if (response != null) { errorResponse = CheckAndGetErrorResponse(response); // do not save the file, if there is an error. if (errorResponse == null && cachingStrategy.CanWriteToCache) { cacheManager.PutFile(requestedURL, response, remoteTime); } } } } //Error messages are never encrypted. So, do not decrypt them. if (errorResponse != null) { response = errorResponse; isError = true; } else if (decryptResponse && response != null) { IEncryptor encryptor = PersistentOnlyCacheManager.GetInstance(); response = encryptor.Decrypt(response); } #if PocketPC GUIMain.getInstance().MainForm.updateProgressBar(); #endif return(response); } catch (Exception ex) { throw new Exception(ex.Message + OSEnvironment.EolSeq + (new Uri((urlCachedByRequest ?? requestedURL))).GetLeftPart(UriPartial.Path), ex); } finally { //collect statistics var elapsedTime = (uint)(Misc.getSystemMilliseconds() - startTime); // !foundInCache ==> the request was executed by the HTTP client ==> should be recorded if (!cachingStrategy.FoundInCache) { Statistics.RecordRequest(elapsedTime, (ulong)(requestContent != null ? HttpClient.GetRequestContentLength(requestContent) : 0), (ulong)(response != null ? response.Length : 0)); } Logger.Instance.WriteServerToLog( string.Format( "Completed {0}: {1:N0} ms, accumulated: {2:N0} ms (server: {3:N0}), {4}{5}{6}**************************************************************************************************", (cachingStrategy.FoundInCache ? "" : string.Format("#{0:N0}", Statistics.GetRequestsCnt()) ), elapsedTime, Statistics.GetAccumulatedExternalTime(), Statistics.GetAccumulatedServerTime(), (cachingStrategy.FoundInCache ? "" : (response != null ? string.Format("{0:N0} bytes downloaded", response.Length) : "Null response!") ), OSEnvironment.EolSeq, OSEnvironment.TabSeq) ); if (!cachingStrategy.FoundInCache && ClientManager.Instance.getDisplayStatisticInfo()) { // Add the networking activity. var downloadSizeKB = (ulong)(response != null ? response.Length : 0); RecentNetworkActivities.Append(Statistics.GetRequestsCnt(), requestStartTime, elapsedTime, downloadSizeKB, requestedURL); } } }