/* * (c) Copyright Ascensio System SIA 2010-2015 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, * EU, LV-1021. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Configuration; using System.Threading; using System.IO; using System.Net; using System.Xml; using System.Text; using System.Web; using FileConverterUtils2; using log4net; using log4net.Config; namespace FileConverterService2 { public static class StringParser { public static string[] ParseArguments(string commandLine) { char[] parmChars = commandLine.ToCharArray(); bool inQuote = false; for (int index = 0; index < parmChars.Length; index++) { if (parmChars[index] == '"') inQuote = !inQuote; if (!inQuote && parmChars[index] == ' ') parmChars[index] = '\n'; } char[] oDelimiters = new char[] { '\n' }; return (new string(parmChars)).Split(oDelimiters, StringSplitOptions.RemoveEmptyEntries); } } public class FileConverter { private static readonly ILog _log = LogManager.GetLogger(typeof(FileConverter)); private class IddleProcess { public System.Diagnostics.Process m_oProcess; public string m_sDirToDelete; public string m_sDirSource; public string m_sDirResult; public string m_sFileTo; public string m_sChangesAuthor = null; public TaskQueueData m_oTaskQueueData; } private class TransportClass { public TaskQueueData m_oTaskQueueData; public AsyncWebRequestOperation m_oAsyncWebRequestOperation; public IAsyncResult m_oAsyncWebRequestOperationResult; } #if! OPEN_SOURCE private static ASCGraphics.CASCWinFonts piWinFonts = null; #else private static OfficeCore.CWinFontsClass piWinFonts = null; #endif private int m_nMaxRunThreads; private readonly ManualResetEvent _stopEvt = new ManualResetEvent(false); private List m_aRunThreads = new List(); private Thread m_oIddlePriority = null; private List m_arrIndentFiles = new List(); private Thread m_oGCThread = null; private object m_oKeyToPercentLock = new object(); private object m_oKeyToPercentValidateLock = new object(); private Dictionary m_mapKeyToPercentValidate = new Dictionary(); private Dictionary m_mapKeyToPercent = new Dictionary(); private Timer m_oPercentTimer = null; private static int m_nRunThreadCount = 0; public FileConverter(string serviceName) { m_nMaxRunThreads = (int)(Environment.ProcessorCount * double.Parse(ConfigurationSettings.AppSettings["maxprocesscount"], new System.Globalization.CultureInfo(0x409))); if (0 >= m_nMaxRunThreads) m_nMaxRunThreads = 1; } public void Start() { if (null == piWinFonts) { bool bIsUseWinFonts = bool.Parse(ConfigurationSettings.AppSettings["fileconverterservice.usewinfonts"] ?? "true"); if (bIsUseWinFonts) { _log.Info("Create WinFonts."); #if! OPEN_SOURCE piWinFonts = new ASCGraphics.CASCWinFonts(); #else piWinFonts = new OfficeCore.CWinFontsClass(); string sFontDir = ConfigurationSettings.AppSettings["utils.common.fontdir"] ?? ""; if(null != sFontDir && !string.IsNullOrEmpty(sFontDir)) sFontDir = Path.GetFullPath(Environment.ExpandEnvironmentVariables(sFontDir)); piWinFonts.Init(sFontDir, true, true); #endif } } Stop(); _stopEvt.Reset(); for (int i = 0; i < m_nMaxRunThreads; i++) { var runThread = new Thread(Run); _log.InfoFormat("Start convertation thread {0} of {1}.", i + 1, m_nMaxRunThreads); runThread.Start(); m_aRunThreads.Add(runThread); } m_oGCThread = new Thread(GCThread); _log.Info("Start garbage collector thread."); m_oGCThread.Start(); } public void Stop() { _stopEvt.Set(); foreach (var runThread in m_aRunThreads) { try { if (!runThread.Join(TimeSpan.FromSeconds(30))) { runThread.Abort(); } } catch(Exception e) { _log.Error("Exception catched while thread stoping.", e); } } m_aRunThreads.Clear(); m_arrIndentFiles.Clear(); if (null != m_oGCThread) { m_oGCThread.Abort(); } } private void Run() { FileConverterUtils2.CTaskQueue oTaskQueue = new FileConverterUtils2.CTaskQueue(); while (!_stopEvt.WaitOne(0)) { FileConverterUtils2.TaskQueueData oTaskQueueData = oTaskQueue.GetTask(); if (null != oTaskQueueData) { DateTime oGetTaskTime = DateTime.UtcNow; _log.DebugFormat("Start Task(id={0}), Time={1}", oTaskQueueData.m_sKey, oGetTaskTime); Interlocked.Increment(ref m_nRunThreadCount); ErrorTypes eError = FileConverterUtils2.ErrorTypes.NoError; bool bNeedParam = false; ITaskResultInterface oTaskResult = TaskResult.NewTaskResult(); Storage oFileStore = new Storage(); string sDirToDelete = Utils.GetTempDirectory(); IddleProcess oNewIddleProcess = new IddleProcess(); oNewIddleProcess.m_sDirToDelete = sDirToDelete; oNewIddleProcess.m_oTaskQueueData = oTaskQueueData; try { string sTempDir = Path.Combine(sDirToDelete, oTaskQueueData.m_sKey); Directory.CreateDirectory(sTempDir); string sDirSource = Path.Combine(sTempDir, "source"); Directory.CreateDirectory(sDirSource); string sDirResult = Path.Combine(sTempDir, "result"); Directory.CreateDirectory(sDirResult); oNewIddleProcess.m_sDirSource = sDirSource; oNewIddleProcess.m_sDirResult = sDirResult; int nFormatFrom = FileFormats.AVS_OFFICESTUDIO_FILE_UNKNOWN; string sFileFrom = ""; string sFileTo = Path.Combine(sDirResult, oTaskQueueData.m_sToFile); if (false == string.IsNullOrEmpty(oTaskQueueData.m_sFromUrl)) { sFileFrom = Path.Combine(sDirSource, oTaskQueueData.m_sKey + "." + oTaskQueueData.m_sFromFormat); eError = DownloadFile(oTaskQueueData.m_sFromUrl, sFileFrom); _log.DebugFormat("DownloadFile complete(id={0})", oTaskQueueData.m_sKey); if (FileConverterUtils2.ErrorTypes.NoError == eError) { nFormatFrom = FileConverterUtils2.FormatChecker.GetFileFormat(sFileFrom); switch(nFormatFrom) { case FileFormats.AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML: { if (".html" != Path.GetExtension(sFileFrom)) { string sNewFileFrom = Path.ChangeExtension(sFileFrom, ".html"); File.Move(sFileFrom, sNewFileFrom); sFileFrom = sNewFileFrom; oTaskQueueData.m_sFromFormat = "html"; } } break; case FileFormats.AVS_OFFICESTUDIO_FILE_OTHER_MS_OFFCRYPTO: eError = ErrorTypes.ConvertMS_OFFCRYPTO; break; case FileFormats.AVS_OFFICESTUDIO_FILE_UNKNOWN: eError = ErrorTypes.ConvertUnknownFormat; break; case FileFormats.AVS_OFFICESTUDIO_FILE_DOCUMENT_TXT: { if (false == oTaskQueueData.m_nCsvTxtEncoding.HasValue) { System.Text.Encoding oEncoding = GetEncoding(sFileFrom); if (null == oEncoding) { string sFileFromName = Path.GetFileName(sFileFrom); string sJson = Utils.GetSerializedEncodingProperty(sFileFromName, null, null); int nReadWriteBytes; byte[] aSettingsPreamble = System.Text.Encoding.UTF8.GetPreamble(); byte[] aSettingsContent = System.Text.Encoding.UTF8.GetBytes(sJson); byte[] aSettingsData = new byte[aSettingsPreamble.Length + aSettingsContent.Length]; aSettingsPreamble.CopyTo(aSettingsData, 0); aSettingsContent.CopyTo(aSettingsData, aSettingsPreamble.Length); using (MemoryStream ms = new MemoryStream(aSettingsData)) oFileStore.WriteFile(Path.Combine(oTaskQueueData.m_sKey, "settings.json"), ms, out nReadWriteBytes); using (FileStream fs = new FileStream(sFileFrom, FileMode.Open)) oFileStore.WriteFile(Path.Combine(oTaskQueueData.m_sKey, sFileFromName), fs, out nReadWriteBytes); bNeedParam = true; } else { oTaskQueueData.m_nCsvTxtEncoding = oEncoding.CodePage; } } } break; case FileFormats.AVS_OFFICESTUDIO_FILE_SPREADSHEET_CSV: { if (false == oTaskQueueData.m_nCsvTxtEncoding.HasValue || false == oTaskQueueData.m_nCsvDelimiter.HasValue) { string sFileFromName = Path.GetFileName(sFileFrom); int? encoding; int? delimiter; GetEncodingAndDelimeter(sFileFrom, out encoding, out delimiter); string sJson = Utils.GetSerializedEncodingProperty(sFileFromName, encoding, delimiter); int nReadWriteBytes; byte[] aSettingsPreamble = System.Text.Encoding.UTF8.GetPreamble(); byte[] aSettingsContent = System.Text.Encoding.UTF8.GetBytes(sJson); byte[] aSettingsData = new byte[aSettingsPreamble.Length + aSettingsContent.Length]; aSettingsPreamble.CopyTo(aSettingsData, 0); aSettingsContent.CopyTo(aSettingsData, aSettingsPreamble.Length); using (MemoryStream ms = new MemoryStream(aSettingsData)) oFileStore.WriteFile(Path.Combine(oTaskQueueData.m_sKey, "settings.json"), ms, out nReadWriteBytes); using (FileStream fs = new FileStream(sFileFrom, FileMode.Open)) oFileStore.WriteFile(Path.Combine(oTaskQueueData.m_sKey, sFileFromName), fs, out nReadWriteBytes); bNeedParam = true; } } break; default: break; } } } else if (false == string.IsNullOrEmpty(oTaskQueueData.m_sFromKey)) { StorageTreeNode oStorageTreeNode = oFileStore.GetTreeNode(oTaskQueueData.m_sFromKey); eError = DownloadStorageTreeNodeToFilesystem(sDirSource, oTaskQueueData.m_sFromKey, "", oStorageTreeNode, oFileStore); _log.DebugFormat("DownloadStorageTreeNodeToFilesystem complete(id={0})", oTaskQueueData.m_sKey); if (oTaskQueueData.m_bFromOrigin.HasValue && true == oTaskQueueData.m_bFromOrigin.Value) { sFileFrom = Path.Combine(sDirSource, "origin"); nFormatFrom = FileConverterUtils2.FormatChecker.GetFileFormat(sFileFrom); } else if (oTaskQueueData.m_bFromSettings.HasValue && true == oTaskQueueData.m_bFromSettings.Value) { string sSettings = Path.Combine(sDirSource, "settings.json"); string sFileFromName; int? codepage; int? delimiter; Utils.GetDeserializedEncodingProperty(File.ReadAllText(sSettings, System.Text.Encoding.UTF8), out sFileFromName, out codepage, out delimiter); if (null != sFileFromName) { sFileFrom = Path.Combine(Path.GetDirectoryName(sSettings), sFileFromName); nFormatFrom = FileConverterUtils2.FormatChecker.GetFileFormat(sFileFrom); } else eError = ErrorTypes.Unknown; } else { oStorageTreeNode = oFileStore.GetTreeNode(oTaskQueueData.m_sKey); eError = DownloadStorageTreeNodeToFilesystem(sDirSource, oTaskQueueData.m_sKey, "", oStorageTreeNode, oFileStore); if (ErrorTypes.NoError == eError) { sFileFrom = Path.Combine(sDirSource, "Editor.bin"); if (FileFormats.AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_PDF == oTaskQueueData.m_nToFormat) { string sFileFromWE = Path.GetFileNameWithoutExtension(sFileFrom); string[] aFiles = Directory.GetFiles(Path.GetDirectoryName(sFileFrom)); Array.Sort(aFiles, Utils.CompareStringByLength); bool bEmptyFile = true; string sTempFile = Path.ChangeExtension(sFileFrom, ".tmp"); using (BinaryWriter writer = new BinaryWriter(File.Open(sTempFile, FileMode.Create))) { bool bFirst = true; for (int i = 0, length = aFiles.Length; i < length; ++i) { string sCurFilename = aFiles[i]; string sCurFileFromWE = Path.GetFileNameWithoutExtension(sCurFilename); if (sCurFileFromWE != sFileFromWE && 0 == sCurFileFromWE.IndexOf(sFileFromWE)) { if (bFirst) bFirst = false; else { writer.Write('\n'); } writer.Write(File.ReadAllBytes(sCurFilename)); bEmptyFile = false; } } } if (false == bEmptyFile) { File.Delete(sFileFrom); File.Move(sTempFile, sFileFrom); } File.Delete(sTempFile); } else nFormatFrom = FileConverterUtils2.FormatChecker.GetFileFormat(sFileFrom); } } if (oTaskQueueData.m_bFromChanges.HasValue && oTaskQueueData.m_bFromChanges.Value) { List aChanges; (new DocsChanges()).GetChanges(oTaskQueueData.m_sFromKey, out aChanges); string sChangesDir = Path.Combine(sDirSource, "changes"); Directory.CreateDirectory(sChangesDir); string sChangesAuthor = null; int nIndexFile = 0; StreamWriter oStreamWriter = null; try { for (int i = 0, length = aChanges.Count; i < length; ++i) { if (null == sChangesAuthor || sChangesAuthor != aChanges[i].userid) { if (null != sChangesAuthor) { oStreamWriter.Write("]"); oStreamWriter.Dispose(); oStreamWriter = null; } sChangesAuthor = aChanges[i].userid; oStreamWriter = new StreamWriter(Path.Combine(sChangesDir, "changes" + (nIndexFile++) + ".json")); oStreamWriter.Write("["); oStreamWriter.Write(aChanges[i].data); } else { oStreamWriter.Write(","); oStreamWriter.Write(aChanges[i].data); } } oNewIddleProcess.m_sChangesAuthor = sChangesAuthor; if (null != oStreamWriter) { oStreamWriter.Write("]"); oStreamWriter.Dispose(); oStreamWriter = null; } } catch { } finally { if (null != oStreamWriter) oStreamWriter.Dispose(); } } } else eError = ErrorTypes.Unknown; int nToFormat = oTaskQueueData.m_nToFormat; if (FileFormats.AVS_OFFICESTUDIO_FILE_CANVAS == nToFormat) { if (FileFormats.AVS_OFFICESTUDIO_FILE_TEAMLAB_XLSY == nFormatFrom || 0 != (FileFormats.AVS_OFFICESTUDIO_FILE_SPREADSHEET & nFormatFrom)) nToFormat = FileFormats.AVS_OFFICESTUDIO_FILE_CANVAS_SPREADSHEET; else if (FileFormats.AVS_OFFICESTUDIO_FILE_TEAMLAB_PPTY == nFormatFrom || 0 != (FileFormats.AVS_OFFICESTUDIO_FILE_PRESENTATION & nFormatFrom)) nToFormat = FileFormats.AVS_OFFICESTUDIO_FILE_CANVAS_PRESENTATION; else nToFormat = FileFormats.AVS_OFFICESTUDIO_FILE_CANVAS_WORD; } else if (FileFormats.AVS_OFFICESTUDIO_FILE_OTHER_TEAMLAB_INNER == nToFormat) { if (FileFormats.AVS_OFFICESTUDIO_FILE_CANVAS_SPREADSHEET == nFormatFrom || FileFormats.AVS_OFFICESTUDIO_FILE_TEAMLAB_XLSY == nFormatFrom || 0 != (FileFormats.AVS_OFFICESTUDIO_FILE_SPREADSHEET & nFormatFrom)) nToFormat = FileFormats.AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSX; else if (FileFormats.AVS_OFFICESTUDIO_FILE_CANVAS_PRESENTATION == nFormatFrom || FileFormats.AVS_OFFICESTUDIO_FILE_TEAMLAB_PPTY == nFormatFrom || 0 != (FileFormats.AVS_OFFICESTUDIO_FILE_PRESENTATION & nFormatFrom)) nToFormat = FileFormats.AVS_OFFICESTUDIO_FILE_PRESENTATION_PPTX; else nToFormat = FileFormats.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCX; sFileTo = Path.ChangeExtension(sFileTo, "." + FileFormats.ToString(nToFormat)); } oNewIddleProcess.m_sFileTo = sFileTo; if (ErrorTypes.NoError == eError && false == bNeedParam) { TimeSpan oWaitTimeout = oTaskQueueData.VisibilityTimeout - (DateTime.UtcNow - oGetTaskTime); int nWaitTimeout = Convert.ToInt32(oWaitTimeout.TotalMilliseconds * 0.95); TaskResultDataToUpdate oTaskResultData = new TaskResultDataToUpdate(); oTaskResultData.eStatus = FileStatus.Convert; oTaskResultData.nStatusInfo = 0; oTaskResult.Update(oTaskQueueData.m_sKey, oTaskResultData); TaskQueueDataConvert oTaskQueueDataConvert = new TaskQueueDataConvert(oTaskQueueData.m_sKey, sFileFrom, nFormatFrom, sFileTo, nToFormat); if (oTaskQueueData.m_nCsvTxtEncoding.HasValue) oTaskQueueDataConvert.m_nCsvTxtEncoding = oTaskQueueData.m_nCsvTxtEncoding.Value; if (oTaskQueueData.m_nCsvDelimiter.HasValue) oTaskQueueDataConvert.m_nCsvDelimiter = oTaskQueueData.m_nCsvDelimiter; if (oTaskQueueData.m_bPaid.HasValue) oTaskQueueDataConvert.m_bPaid = oTaskQueueData.m_bPaid.Value; if (oTaskQueueData.m_bEmbeddedFonts.HasValue) oTaskQueueDataConvert.m_bEmbeddedFonts = oTaskQueueData.m_bEmbeddedFonts.Value; if (oTaskQueueData.m_bFromChanges.HasValue) oTaskQueueDataConvert.m_bFromChanges = oTaskQueueData.m_bFromChanges.Value; #if! OPEN_SOURCE string sFontDir = ConfigurationSettings.AppSettings["utils.common.fontdir"]; #else string sFontDir = ""; #endif if (null != sFontDir && !string.IsNullOrEmpty(sFontDir)) oTaskQueueDataConvert.m_sFontDir = Path.GetFullPath(Environment.ExpandEnvironmentVariables(sFontDir)); string sThemeDir = ConfigurationSettings.AppSettings["fileconverterservice.converter.presentationthemesdir"]; if (null != sThemeDir && !string.IsNullOrEmpty(sThemeDir)) oTaskQueueDataConvert.m_sThemeDir = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), sThemeDir)); lock (m_oKeyToPercentValidateLock) { m_mapKeyToPercentValidate[oTaskQueueData.m_sKey] = 1; } string sConvertApp = ConfigurationSettings.AppSettings["fileconverterservice.converter.filepath"] ?? "FileConverter2.exe"; string sConvertArgs = ConfigurationSettings.AppSettings["fileconverterservice.converter.args"] ?? ""; System.Diagnostics.Process convertProc = new System.Diagnostics.Process(); oNewIddleProcess.m_oProcess = convertProc; convertProc.StartInfo.FileName = sConvertApp; string sConfigPath = Path.Combine(sTempDir, "params.xml"); File.WriteAllText(sConfigPath, TaskQueueDataConvert.SerializeToXml(oTaskQueueDataConvert), System.Text.Encoding.UTF8); convertProc.StartInfo.Arguments = sConvertArgs + " " + EscapeCommandLineArguments(sConfigPath); convertProc.StartInfo.CreateNoWindow = true; convertProc.StartInfo.UseShellExecute = false; convertProc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(convertProc_OutputDataReceived); convertProc.StartInfo.RedirectStandardOutput = true; _log.DebugFormat("Start {0} {1}(id={2})", convertProc.StartInfo.FileName, convertProc.StartInfo.Arguments, oTaskQueueData.m_sKey); convertProc.Start(); convertProc.BeginOutputReadLine(); bool isProcessExit = convertProc.WaitForExit(nWaitTimeout); _log.DebugFormat("Stop WaitForExit({0})(id={1})", nWaitTimeout, oTaskQueueData.m_sKey); if (!isProcessExit) { _log.DebugFormat("Kill() process (id={1})", oTaskQueueData.m_sKey); convertProc.Kill(); } } } catch(Exception e) { eError = ErrorTypes.Convert; _log.Error("Exception catched while convertation thread working.", e); } PostProcess(oNewIddleProcess, eError, bNeedParam); Interlocked.Decrement(ref m_nRunThreadCount); } else { Thread.Sleep(TimeSpan.FromSeconds(double.Parse(ConfigurationSettings.AppSettings["sleeptimeout"] ?? "60"))); } } } private ErrorTypes DownloadFile(string sUrl, string sFileFrom) { ErrorTypes eError = ErrorTypes.NoError; try { AsyncWebRequestOperation oAsyncWebRequestOperation = new AsyncWebRequestOperation(Convert.ToInt64(ConfigurationSettings.AppSettings["maxdownloadbytes"] ?? "10485760")); byte[] aBuffer; if (ErrorTypes.NoError == oAsyncWebRequestOperation.Request(sUrl, "GET", null, null, out aBuffer)) { using(FileStream fs = File.Create(sFileFrom)) { fs.Write(aBuffer, 0, aBuffer.Length); } } else { eError = ErrorTypes.ConvertDownload; _log.ErrorFormat("DownloadFile() from {0} to {1}", sUrl, sFileFrom); } } catch(Exception ex) { eError = ErrorTypes.ConvertDownload; _log.ErrorFormat("DownloadFile() from {0} to {1}", sUrl, sFileFrom); _log.Error("Exception:", ex); } return eError; } private System.Text.Encoding GetEncoding(string sFileFrom) { const int nBytesToRead = 3; byte[] aFirstBytes = new byte[nBytesToRead]; int nBytesReaded = 0; using (FileStream fs = File.OpenRead(sFileFrom)) { nBytesReaded = fs.Read(aFirstBytes, 0, nBytesToRead); } return GetEncodingByContent(aFirstBytes, nBytesReaded); } private void GetEncodingAndDelimeter(string sFileFrom, out int? encoding, out int? delimiter) { encoding = null; delimiter = null; const int nBytesToRead = 1000; byte[] aFirstBytes = new byte[nBytesToRead]; int nBytesReaded = 0; using (FileStream fs = File.OpenRead(sFileFrom)) { nBytesReaded = fs.Read(aFirstBytes, 0, nBytesToRead); } System.Text.Encoding oEncoding = GetEncodingByContent(aFirstBytes, nBytesReaded); if (null != oEncoding) encoding = oEncoding.CodePage; else encoding = System.Text.Encoding.UTF8.CodePage; delimiter = GetDelimeterByContent(aFirstBytes, nBytesReaded); } private int? GetDelimeterByContent(byte[] aFirstBytes, int nBytesReaded) { int? delimiter = null; int nLineLength = nBytesReaded; int nDelimiter = (int)CsvDelimiter.None; int nDelimitersCount = 6; int[] aDelimiters = new int[nDelimitersCount]; for (int i = 0; i < nDelimitersCount; i++) aDelimiters[i] = 0; for (int i = 0; i < nBytesReaded; ++i) { byte cCurChar = aFirstBytes[i]; if ('\n' == cCurChar) { nLineLength = i; break; } else if ('\t' == cCurChar) aDelimiters[(int)CsvDelimiter.Tab]++; else if (';' == cCurChar) aDelimiters[(int)CsvDelimiter.Semicolon]++; else if (':' == cCurChar) aDelimiters[(int)CsvDelimiter.Сolon]++; else if (',' == cCurChar) aDelimiters[(int)CsvDelimiter.Comma]++; else if (' ' == cCurChar) aDelimiters[(int)CsvDelimiter.Space]++; } int nMaxVal = 0; int nMaxIndex = 0; for (int i = 1; i < nDelimitersCount; i++) { if (nMaxVal < aDelimiters[i]) { nMaxVal = aDelimiters[i]; nMaxIndex = i; } } if (nMaxVal > 0) nDelimiter = nMaxIndex; if ((int)CsvDelimiter.None != nDelimiter) delimiter = nMaxIndex; return delimiter; } private static System.Text.Encoding GetEncodingByContent(byte[] aFirstBytes, int nBytesReaded) { System.Text.Encoding oEncoding = null; if (nBytesReaded >= 2) { if (aFirstBytes[0] == 0xFF && aFirstBytes[1] == 0xFE) oEncoding = System.Text.Encoding.Unicode; else if (aFirstBytes[0] == 0xFE && aFirstBytes[1] == 0xFF) oEncoding = System.Text.Encoding.BigEndianUnicode; } if (nBytesReaded >= 3) { if (aFirstBytes[0] == 0xEF && aFirstBytes[1] == 0xBB && aFirstBytes[2] == 0xBF) oEncoding = System.Text.Encoding.UTF8; } return oEncoding; } private void GCThread() { const double dSleepTimeOut = 5; int nSleepCount = 0; while (!_stopEvt.WaitOne(0)) { try { if (nSleepCount <=0 && 0 == m_nRunThreadCount) { ITaskResultInterface oTaskResult = TaskResult.NewTaskResult(); List aTasts = null; int nMaxCount = int.Parse(ConfigurationManager.AppSettings["fileconverterservice.gc.removedtaskatonce"] ?? "10"); ErrorTypes oError = oTaskResult.GetExpired(nMaxCount, out aTasts); if (ErrorTypes.NoError == oError && aTasts != null && aTasts.Count > 0) { Storage oStorage = new Storage(); DocsChanges oDocsChanges = new DocsChanges(); foreach (TaskResultData oTast in aTasts) { if (ErrorTypes.NoError == oTaskResult.Remove(oTast.sKey)) { oStorage.RemovePath(oTast.sKey); oDocsChanges.RemoveChanges(oTast.sKey); } } } else { nSleepCount = (int)(double.Parse(ConfigurationManager.AppSettings["fileconverterservice.gc.runperiod"] ?? "3600") / dSleepTimeOut); } } else { if(nSleepCount > 0) nSleepCount--; Thread.Sleep(TimeSpan.FromSeconds(dSleepTimeOut)); } } catch (Exception e) { _log.Error("Exception catched while garbage collector working.", e); } } } private void PercentUpdateCallback(Object stateInfo) { try { List> mapTemp = new List>(); lock (m_oKeyToPercentLock) { foreach (KeyValuePair entry in m_mapKeyToPercent) mapTemp.Add(entry); m_mapKeyToPercent.Clear(); } ITaskResultInterface oTaskResult = TaskResult.NewTaskResult(); for (int i = 0, length = mapTemp.Count; i < length; ++i) { KeyValuePair elem = mapTemp[i]; lock (m_oKeyToPercentValidateLock) { if (m_mapKeyToPercentValidate.ContainsKey(elem.Key)) { TaskResultDataToUpdate oTaskResultData = new TaskResultDataToUpdate(); oTaskResultData.nStatusInfo = elem.Value; oTaskResult.Update(elem.Key, oTaskResultData); } } } lock (m_oKeyToPercentLock) { if (m_mapKeyToPercent.Count > 0) { int nPeriod = int.Parse(ConfigurationManager.AppSettings["fileconverterservice.percent.runperiod"] ?? "500"); m_oPercentTimer.Change(nPeriod, System.Threading.Timeout.Infinite); } else { m_oPercentTimer.Dispose(); m_oPercentTimer = null; } } } catch { m_oPercentTimer = null; } } private static string EscapeCommandLineArguments(string arg) { return "\"" + arg.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; } private ErrorTypes PostProcess(IddleProcess oIddleProcess, ErrorTypes eError, bool bNeedParam) { try { CTaskQueue oTaskQueue = new CTaskQueue(); ITaskResultInterface oTaskResult = TaskResult.NewTaskResult(); Storage oStorage = new Storage(); TaskQueueData oTaskQueueData = oIddleProcess.m_oTaskQueueData; if (null != oTaskQueueData) { if (oIddleProcess.m_oProcess != null) { oIddleProcess.m_oProcess.CancelOutputRead(); } if (ErrorTypes.NoError == eError && null != oIddleProcess.m_oProcess) { int nExitCode = oIddleProcess.m_oProcess.ExitCode; _log.DebugFormat("ExitCode (code={0};id={1})", nExitCode, oTaskQueueData.m_sKey); bool bUploadFile = true; if (0 != nExitCode) { bUploadFile = false; if (-(int)ErrorTypes.ConvertMS_OFFCRYPTO == nExitCode) eError = ErrorTypes.ConvertMS_OFFCRYPTO; else if (-(int)ErrorTypes.ConvertCorrupted == nExitCode) { eError = ErrorTypes.ConvertCorrupted; bUploadFile = true; } else eError = ErrorTypes.Convert; SaveErrorFile(oIddleProcess.m_sDirToDelete, oTaskQueueData.m_sKey); } if (bUploadFile) { if (null != oIddleProcess.m_sDirResult) { ErrorTypes eErrorUpload = UploadDirectoryToStorage(oIddleProcess.m_sDirResult, oTaskQueueData.m_sKey, "", oStorage); if (ErrorTypes.NoError != eErrorUpload) eError = eErrorUpload; } _log.DebugFormat("UploadDirectoryToStorage complete(id={0})", oTaskQueueData.m_sKey); } } lock (m_oKeyToPercentValidateLock) { m_mapKeyToPercentValidate.Remove(oTaskQueueData.m_sKey); } if (false == string.IsNullOrEmpty(oIddleProcess.m_sDirToDelete) && Directory.Exists(oIddleProcess.m_sDirToDelete)) Directory.Delete(oIddleProcess.m_sDirToDelete, true); TaskResultDataToUpdate oTaskResultDataUpdate = new TaskResultDataToUpdate(); if (null != oIddleProcess.m_oTaskQueueData.m_sFromKey && !string.IsNullOrEmpty(oIddleProcess.m_oTaskQueueData.m_sFromKey)) oTaskResultDataUpdate.sTitle = Path.GetFileName(oIddleProcess.m_sFileTo); if (true == bNeedParam) { oTaskResultDataUpdate.eStatus = FileStatus.NeedParams; oTaskResult.Update(oTaskQueueData.m_sKey, oTaskResultDataUpdate); _log.DebugFormat("oTaskResult.Update complete(status={0};id={1})", oTaskResultDataUpdate.eStatus, oTaskQueueData.m_sKey); } else { if (ErrorTypes.NoError == eError) oTaskResultDataUpdate.eStatus = FileStatus.Ok; else { if (ErrorTypes.ConvertDownload == eError) oTaskResultDataUpdate.eStatus = FileStatus.ErrToReload; else oTaskResultDataUpdate.eStatus = FileStatus.Err; } oTaskResultDataUpdate.nStatusInfo = (int)eError; oTaskResult.Update(oTaskQueueData.m_sKey, oTaskResultDataUpdate); _log.DebugFormat("oTaskResult.Update complete(status={0};id={1})", oTaskResultDataUpdate.eStatus, oTaskQueueData.m_sKey); } if (null != oTaskQueueData.m_sResultCallbackUrl && !string.IsNullOrEmpty(oTaskQueueData.m_sResultCallbackUrl)) { InputCommand cmd = InputCommand.DeserializeFromJson(oTaskQueueData.m_sResultCallbackData); cmd.userid = oIddleProcess.m_sChangesAuthor; string dataJson = InputCommand.SerializeToJson(cmd); byte[] data = System.Text.Encoding.UTF8.GetBytes(dataJson); AsyncWebRequestOperation oAsyncWebRequestOperation = new AsyncWebRequestOperation(); TransportClass oTransportClass = new TransportClass(); oTransportClass.m_oTaskQueueData = oTaskQueueData; oTransportClass.m_oAsyncWebRequestOperation = oAsyncWebRequestOperation; oTransportClass.m_oAsyncWebRequestOperationResult = oAsyncWebRequestOperation.RequestBegin(oTaskQueueData.m_sResultCallbackUrl, "POST", "application/json", data, RequestCallback, oTransportClass); _log.DebugFormat("Request to url='{0}' data='{1}' begin(id={2})", oTaskQueueData.m_sResultCallbackUrl, dataJson, oTaskQueueData.m_sKey); } oTaskQueue.RemoveTask(oTaskQueueData.m_oDataKey); _log.DebugFormat("oTaskResult.RemoveTask complete(id={0})", oTaskQueueData.m_sKey); _log.DebugFormat("End Task(id={0})", oTaskQueueData.m_sKey); } } catch(Exception e) { _log.Error("Exception catched in PostProcess:", e); } return eError; } private void RequestCallback(IAsyncResult ar) { TransportClass oTransportClass = ar.AsyncState as TransportClass; try { byte[] aOutput; ErrorTypes eError = oTransportClass.m_oAsyncWebRequestOperation.RequestEnd(oTransportClass.m_oAsyncWebRequestOperationResult, out aOutput); if (_log.IsDebugEnabled) { try { _log.DebugFormat("RequestCallback Response='{0}'(id={1})", Encoding.UTF8.GetString(aOutput), oTransportClass.m_oTaskQueueData.m_sKey); } catch { } } } catch (Exception e) { _log.Error("Exception catched in RequestCallback:", e); } } private void SaveErrorFile(string sPath, string sKey) { string sErrorDir = ConfigurationSettings.AppSettings["fileconverterservice.converter.errorfiles"]; if (false == string.IsNullOrEmpty(sErrorDir) && false == string.IsNullOrEmpty(sPath)) UploadDirectoryToStorage(sPath, sErrorDir, "", new Storage()); } private ErrorTypes UploadDirectoryToStorage(string sSourceDir, string sSourceFileStoreDir, string sLocalPath, Storage oFileStore) { ErrorTypes eResult = ErrorTypes.NoError; string[] aFiles = Directory.GetFiles(Path.Combine(sSourceDir, sLocalPath)); for (int i = 0, length = aFiles.Length; i < length; ++i) { string sFile = aFiles[i]; string sFileStorePath = Path.Combine(sSourceFileStoreDir, sLocalPath); sFileStorePath = Path.Combine(sFileStorePath, Path.GetFileName(sFile)); using (FileStream fs = new FileStream(sFile, FileMode.Open)) { int nReadWriteBytes; oFileStore.WriteFile(sFileStorePath, fs, out nReadWriteBytes); } } string[] aDirectories = Directory.GetDirectories(Path.Combine(sSourceDir, sLocalPath)); for (int i = 0, length = aDirectories.Length; i < length; ++i) { string sDir = aDirectories[i]; string sDirLocal = Path.Combine(sLocalPath, Path.GetFileName(sDir)); string sDirStorePath = Path.Combine(sSourceFileStoreDir, sDirLocal); oFileStore.CreateDirectory(sDirStorePath); ErrorTypes eTypes = UploadDirectoryToStorage(sSourceDir, sSourceFileStoreDir, sDirLocal, oFileStore); if (ErrorTypes.NoError != eTypes) { eResult = eTypes; break; } } return eResult; } ErrorTypes DownloadStorageTreeNodeToFilesystem(string sTargetDir, string sKey, string sLocalDir, StorageTreeNode oNode, Storage oFileStore) { ErrorTypes eError = ErrorTypes.NoError; for (int i = 0, length = oNode.m_aSubNodes.Count; i < length; ++i) { StorageTreeNode oSubNode = oNode.m_aSubNodes[i]; string sLocalPath = Path.Combine(sLocalDir, oSubNode.m_sName); if (oSubNode.m_bIsDirectory) { Directory.CreateDirectory(Path.Combine(sTargetDir, sLocalPath)); eError = DownloadStorageTreeNodeToFilesystem(sTargetDir, sKey, sLocalPath, oSubNode, oFileStore); } else { try { using (FileStream fs = new FileStream(Path.Combine(sTargetDir, sLocalPath), FileMode.Create)) { int nReadWriteBytes; oFileStore.ReadFile(Path.Combine(sKey, sLocalPath), fs, out nReadWriteBytes); } } catch(Exception e) { eError = ErrorTypes.ConvertReadFile; _log.Error("Exception catched in DownloadStorageTreeNodeToFilesystem:", e); } } if (ErrorTypes.NoError != eError) break; } return eError; } void convertProc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) { if (!String.IsNullOrEmpty(e.Data)) { try { string[] aArgs = StringParser.ParseArguments(e.Data); if (2 <= aArgs.Length) { int nPercent = System.Convert.ToInt32(aArgs[1].Replace("\"", "")); string sKey = aArgs[0].Replace("\"", ""); lock (m_oKeyToPercentLock) { m_mapKeyToPercent[sKey] = nPercent; if (null == m_oPercentTimer) m_oPercentTimer = new Timer(PercentUpdateCallback, null, 0, System.Threading.Timeout.Infinite); } } } catch { } } } } }