// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Net; using System.Web; using System.Data; namespace Spludlow { public static class Call { public static object Now( string address, string assembly, string type, string method) { return Now(address, assembly, type, method, null, null, CallFlags.None); } public static object Now( string address, string assembly, string type, string method, CallFlags flags) { return Now(address, assembly, type, method, null, null, flags); } public static object Now( string address, string assembly, string type, string method, object[] parameters) { return Now(address, assembly, type, method, parameters, null, CallFlags.None); } public static object Now( string address, string assembly, string type, string method, object[] parameters, CallFlags flags) { return Now(address, assembly, type, method, parameters, null, flags); } public static object Now( string address, string assembly, string type, string method, object[] parameters, object[] constructorArguments, CallFlags flags) { if (address.Contains(":") == true) throw new ApplicationException("Call; Address should be only the host."); Spludlow.CallMethod call = new Spludlow.CallMethod(address, assembly, type, method, parameters, constructorArguments, flags, -1); Spludlow.CallSet callSet = new Spludlow.CallSet(call); return Run(callSet); } public static void Queue( string address, string assembly, string type, string method) { Queue(address, assembly, type, method, null, null, CallFlags.None); } public static void Queue( string address, string assembly, string type, string method, CallFlags flags) { Queue(address, assembly, type, method, null, null, flags); } public static void Queue( string address, string assembly, string type, string method, object[] parameters) { Queue(address, assembly, type, method, parameters, null, CallFlags.None); } public static void Queue( string address, string assembly, string type, string method, object[] parameters, CallFlags flags) { Queue(address, assembly, type, method, parameters, null, flags); } public static void Queue( string address, string assembly, string type, string method, object[] parameters, object[] constructorArguments, CallFlags flags) { if (address.Contains(":") == false) address += ":Run"; Spludlow.CallMethod call = new Spludlow.CallMethod(address, assembly, type, method, parameters, constructorArguments, flags, -1); Spludlow.CallSet callSet = new Spludlow.CallSet(call); Run(callSet); } class ProgramState { public bool ErrorOnWrite = false; } public static void Program(string[] args) { ProgramState state = new ProgramState(); DateTime start = DateTime.Now; string ruler = new string('=', 32); WriteLine(ruler, false, state); WriteLine("START: " + DateTime.Now, false, state); WriteLine(ruler, false, state); try { object result = ProgramWork(args); if (result != null) { Type type = result.GetType(); WriteLine(type.FullName, false, state); WriteLine(ruler, false, state); if (SimpleEncoding.IsSimple(type) == true) { WriteLine(SimpleEncoding.Encode(result, type.Name), false, state); WriteLine(ruler, false, state); } } WriteLine("FINISH: " + DateTime.Now + ", " + Spludlow.Text.TimeTook(start), false, state); WriteLine(ruler, false, state); Environment.Exit(0); } catch (Exception ee) { Spludlow.Log.Error("Spludlow Process", ee); WriteLine("ERROR: " + DateTime.Now + ", " + Spludlow.Text.TimeTook(start), true, state); WriteLine(ee.ToString(), true, state); WriteLine(ruler, true, state); Environment.Exit(-1); } } private static void WriteLine(string mesasge, bool stdErr, ProgramState state) { if (state.ErrorOnWrite == true) return; try { if (stdErr == false) Console.WriteLine(mesasge); else Console.Error.WriteLine(mesasge); } catch (Exception ee) { Spludlow.Log.Warning("Spludlow Process Console Write, Stopping Output", ee); state.ErrorOnWrite = true; } } private static object ProgramWork(string[] args) { Dictionary> arguments = Spludlow.Arguments.Decode(args); if (arguments.Keys.Count == 0) return new string[] { "error", "Usage: Spludlow-Process.exe ..." }; Spludlow.CallMethod method; if (arguments.Values.Count == 1 && arguments.ContainsKey("") == true && arguments[""].Count == 1) { string callTextFilename = arguments[""][0]; if (File.Exists(callTextFilename) == false) return new string[] { "error", "Single Argument CallText filename does not exist: " + callTextFilename }; method = Spludlow.CallText.Read(File.ReadAllText(callTextFilename)); } else { method = new CallMethod(arguments); } return Run(method); } public static void Parallel(Spludlow.CallSet[] callSets, Spludlow.CallSet resultCall) { Spludlow.ParallelResults.New(DateTime.Now.ToString(), callSets, resultCall); } public static void Parallel(Spludlow.CallMethod[] callMethods, Spludlow.CallSet resultCall) { Spludlow.CallSet[] callSets = new CallSet[callMethods.Length]; for (int index = 0; index < callSets.Length; ++index) callSets[index] = new CallSet(callMethods[index]); Spludlow.ParallelResults.New(DateTime.Now.ToString(), callSets, resultCall); } public static void Parallel(Spludlow.CallMethod[] callMethods, Spludlow.CallMethod resultMethod) { Spludlow.CallSet[] callSets = new CallSet[callMethods.Length]; for (int index = 0; index < callSets.Length; ++index) callSets[index] = new CallSet(callMethods[index]); Spludlow.CallSet resultCall = new CallSet(resultMethod); Spludlow.ParallelResults.New(DateTime.Now.ToString(), callSets, resultCall); } public static object Serial(Spludlow.CallSet callSet) { return Run(callSet); } public static object Serial(Spludlow.CallMethod[] callMethods) { Spludlow.CallSet callSet = new Spludlow.CallSet(callMethods); return Run(callSet); } public static object Run(Spludlow.CallMethod callMethod) { Spludlow.CallSet callSet = new Spludlow.CallSet(callMethod); return Run(callSet); } public static object Run(Spludlow.CallSet callSet) { callSet = Route(callSet); if (callSet.CurrentMethod.Result == null) return null; return Spludlow.Parameters.DecodeParameter(callSet.CurrentMethod.Result); } public static Spludlow.CallSet Route(Spludlow.CallSet callSet) { if (callSet.CurrentMethod.Flags.HasFlag(CallFlags.TraceRoute) == true) { callSet.Trace += DateTime.Now + Environment.NewLine; } ++callSet.Hop; Spludlow.CallMethod call = callSet.CurrentMethod; if (call.Host == null || call.Flags.HasFlag(CallFlags.InProcess) == true) { return Execute(callSet); } string address = Spludlow.Config.HostAddress(call.Host); bool remoteNetwork = false; if (address.StartsWith("@") == true) { address = address.Substring(1); remoteNetwork = true; } bool webProcess = true; if (System.Web.HttpRuntime.AppDomainId == null) webProcess = false; bool correctQueue = true; if ((webProcess == false) && (call.Queue != null) && (call.Queue != call.PickUpQueue)) correctQueue = false; bool corectProcess = true; if ((call.Queue == null && webProcess == false) || (call.Queue != null && webProcess == true)) corectProcess = false; if (call.Host != Environment.MachineName) corectProcess = false; if (correctQueue == false) corectProcess = false; if (callSet.Hop > 8) { //Spludlow.Log.Error("Call; Route too many hops", callSet.Trace); // sort logging out !!!!!!!!!!!!!!!!!!!!!!!!!!!!! throw new ApplicationException("Call; Route too many hops:" + "call.Host:" + call.Host + " " + "Environment.MachineName:" + Environment.MachineName + " " + "address:" + address + " " + "remoteNetwork:" + remoteNetwork + " " + "webProcess:" + webProcess + " " + "correctQueue:" + correctQueue + " " + "corectProcess:" + corectProcess + " " + "call.Queue:" + call.Queue + " " + "call.PickUpQueue:" + call.PickUpQueue + " " + "address:" + address + " " + "address:" + address + " " + Environment.NewLine + callSet.Trace); } // If first hop (always routes) or not in right place then route !!! HTTP get rid == 1 if (callSet.Hop == 1 || corectProcess == false) { // Web Service OR on remote network with QueueDirect OR second queue hop from routered queue - not happy? if (call.Queue == null || (remoteNetwork == true && call.Flags.HasFlag(Spludlow.CallFlags.QueueDirect) == true) || (callSet.Hop > 1 && correctQueue == false)) return Spludlow.WebServices.PostWCF(address, callSet); // Drop on queue if local or drop on router string host = call.Host; string queue = call.Queue; if (remoteNetwork == true) { host = Spludlow.Config.Host("Router"); queue = "Route." + Spludlow.Config.NetworkId(call.Host); } Spludlow.MessageQueues.Send(callSet, host, queue); return callSet; } if (callSet.CurrentMethod.Flags.HasFlag(CallFlags.TraceRoute) == true) { Spludlow.Log.Report("Route Trace", callSet.Trace); } return Execute(callSet); } public static Spludlow.CallSet Execute(Spludlow.CallSet callSet) { Spludlow.CallMethod call = callSet.CurrentMethod; // dont decode pass through ??? // and pass object back, don't encode result ??? object result = Spludlow.Reflections.Invoke( call.Assembly, call.Type, call.Method, Spludlow.Parameters.DecodeParameters(call.Parameters), Spludlow.Parameters.DecodeParameters(call.ConstructorArguments)); if (result != null && call.Flags.HasFlag(CallFlags.IgnoreResult) == true) result = null; bool large = call.Flags.HasFlag(CallFlags.LargeResult); bool store = call.Flags.HasFlag(CallFlags.StoreResult); string[] encodedResult; try { encodedResult = Spludlow.Parameters.EncodeParameter(result, large, store); } catch (Exception ee) { throw new ApplicationException("Execute; Encoding Result:\t" + ee.Message, ee); } call.Result = encodedResult; // Re encoding here ! if (call.Flags.HasFlag(CallFlags.ReportResult) == true) { string subject = "Report Result: " + call.Method + "(" + Spludlow.Parameters.ShortText(call.Parameters) + ") " + encodedResult[0]; Spludlow.Log.Report(subject, new object[] { result }); } if (call.Flags.HasFlag(CallFlags.EmailResult) == true) { string subject = "Email Result: " + call.Method + "(" + Spludlow.Parameters.ShortText(call.Parameters) + ") " + encodedResult[0]; Spludlow.Email.SendMail.Send(subject, new object[] { result }); } if (call.Flags.HasFlag(CallFlags.DumpResult) == true && result != null) { string dumpFilename = Directory.GetCurrentDirectory() + @"\" + Spludlow.Text.TimeStamp(); Type type = result.GetType(); if (typeof(DataSet).IsAssignableFrom(type) == true) { Spludlow.Data.TextTable.WriteDirectory(dumpFilename, (DataSet)result); } else { File.WriteAllText(dumpFilename + ".txt", Convert.ToString(result)); } } // params[] is re encoded ??? if (call.ManagedResultsKey != null) // make sure result host is right for large paramters (if part of series may be differnt) { Spludlow.Call.Run(new CallMethod(call.ManagedResultsAddress, "Spludlow", "Spludlow.ParallelResults", "Result", new object[] { call.Result, call.ManagedResultsKey, call.ManagedResultsIndex })); //Spludlow.ManagedResults.Result(call.Result, call.ManagedResultsKey, call.ManagedResultsIndex); } if (callSet.CallIndex == (callSet.Calls.Length - 1)) return callSet; ++callSet.CallIndex; callSet.Hop = 0; call = callSet.CurrentMethod; if (call.PrevResultParamIndex != -1) { if (call.Flags.HasFlag(CallFlags.ConstructorIndex) == false) call.Parameters[call.PrevResultParamIndex] = encodedResult; else call.ConstructorArguments[call.PrevResultParamIndex] = encodedResult; } return Route(callSet); } private static void Trace(Spludlow.CallSet info) { StringBuilder text = new StringBuilder(info.Trace); string application; if (System.Web.HttpContext.Current != null) application = System.Web.Hosting.HostingEnvironment.ApplicationHost.GetSiteName(); else application = System.AppDomain.CurrentDomain.FriendlyName; if (text.Length == 0) text.AppendLine("Time\tMachineName\tHost\tApplication\tQueue\tIndex\tHop"); text.Append(DateTime.Now); text.Append("\t"); text.Append(Environment.MachineName); text.Append("\t"); text.Append(info.CurrentMethod.Host); text.Append("\t"); text.Append(application); text.Append("\t"); text.Append(info.CurrentMethod.PickUpQueue); text.Append("\t"); text.Append(info.CallIndex); text.Append("\t"); text.Append(info.Hop); text.AppendLine(); info.Trace = text.ToString(); } public static string Upload(string host, string sourceFilename) { return Upload(host, sourceFilename, ""); } public static string Upload(string host, string sourceFilename, string targetFilename) { using (FileStream stream = new FileStream(sourceFilename, FileMode.Open)) return Upload(host, stream, targetFilename); } public static string Upload(string host, byte[] data) { return Upload(host, data, ""); } public static string Upload(string host, byte[] data, string targetFilename) { using (MemoryStream stream = new MemoryStream(data)) return Upload(host, stream, targetFilename); } public static string Upload(string host, Stream sourceStream, string targetFilename) { string hostAddress = Spludlow.Config.HostAddress(host); if (hostAddress.StartsWith("@") == true) hostAddress = hostAddress.Substring(1); StringBuilder address = new StringBuilder(hostAddress); address.Append("HTTP.aspx?"); address.Append("HOST="); address.Append(host); address.Append("&FILE="); address.Append(targetFilename); hostAddress = address.ToString(); Spludlow.Net.Http.WebResponseInfo responseInfo = Spludlow.Net.Http.PostStream(hostAddress, sourceStream, "application/octet-stream", Spludlow.Security.HttpHeader); return responseInfo.Text; } public static byte[] DownloadData(string host, string remoteFilename) { using (MemoryStream memoryStream = new MemoryStream()) { Download(host, remoteFilename, memoryStream); return memoryStream.ToArray(); } } public static string DownloadText(string host, string remoteFilename) { return DownloadText(host, remoteFilename, Encoding.Default); } public static string DownloadText(string host, string remoteFilename, Encoding encoding) { using (MemoryStream memoryStream = new MemoryStream()) { Download(host, remoteFilename, memoryStream); memoryStream.Position = 0; using (StreamReader reader = new StreamReader(memoryStream, encoding)) { return reader.ReadToEnd(); } } } public static string Download(string host, string remoteFilename) { return Download(host, remoteFilename, ""); } public static string Download(string host, string remoteFilename, string localFilename) { if (localFilename.Length == 0 || localFilename.Contains(@"\") == false) { if (localFilename.Length == 0) localFilename = null; localFilename = Spludlow.WebServices.CreateProgramDataTempFilename(".dat"); } else { if (File.Exists(localFilename) == true) { File.Delete(localFilename); Spludlow.Log.Warning("Download; Delete existing file:\t" + localFilename); } } try { using (FileStream stream = new FileStream(localFilename, FileMode.Create)) Download(host, remoteFilename, stream); } catch { if (File.Exists(localFilename) == true) File.Delete(localFilename); throw; } return localFilename; } public static string Download(string host, string remoteFilename, Stream targetStream) // return ??? { string hostAddress = Spludlow.Config.HostAddress(host); if (hostAddress.StartsWith("@") == true) hostAddress = hostAddress.Substring(1); StringBuilder address = new StringBuilder(hostAddress); address.Append("HTTP.aspx?"); address.Append("HOST="); address.Append(host); address.Append("&FILE="); address.Append(remoteFilename); hostAddress = address.ToString(); Spludlow.Net.Http.WebResponseInfo responseInfo = Spludlow.Net.Http.GetStream(hostAddress, targetStream, Spludlow.Security.HttpHeader); return responseInfo.ContentType; } /// /// For testing only - totaly pointless. Performs and upload and download in a single HTTP post. Will check hashs. /// public static void UpDown(string host, string localUpFilename, string localDownFilename, string remoteFilename) { if (File.Exists(localUpFilename) == false) throw new ApplicationException("UpDown; local up file not there: " + localUpFilename); if (File.Exists(localDownFilename) == true) { Spludlow.Log.Warning("Up Down; Deleted existing local down file: "+ localDownFilename); File.Delete(localDownFilename); } StringBuilder address = new StringBuilder(); address.Append(Spludlow.Config.HostAddress(host, true)); address.Append("HTTP.aspx?"); address.Append("HOST="); address.Append(host); address.Append("&UPDOWN="); address.Append(HttpUtility.UrlEncode(remoteFilename)); using (FileStream uploadStream = new FileStream(localUpFilename, FileMode.Open)) { using (FileStream downloadStream = new FileStream(localDownFilename, FileMode.Create)) { Spludlow.Net.Http.WebRequestInfo requestInfo = new Net.Http.WebRequestInfo(); requestInfo.Address = address.ToString(); requestInfo.Method = WebRequestMethods.Http.Post; requestInfo.GetStream = downloadStream; requestInfo.PostStream = uploadStream; requestInfo.ContentType = "application/octet-stream"; requestInfo.Timeout = Spludlow.Net.Http.PostTimeout; Spludlow.Net.Http.WebResponseInfo responseInfo = Spludlow.Net.Http.WebOperation(requestInfo); Spludlow.Log.Finish("Up Down; Finished", new object[] { responseInfo }); } } string localUp = Spludlow.Hashing.MD5HexFile(localUpFilename); string localDown = Spludlow.Hashing.MD5HexFile(localDownFilename); string remote = (string)Spludlow.Call.Now(host, "Spludlow", "Spludlow.Hashing", "MD5HexFile", new object[] { remoteFilename }); if (localDown != localUp || remote != localUp) Spludlow.Log.Error("Up Down; Checksums (localUp, localDown, remote): " + localUp + ", " + localDown + ", " + remote); else Spludlow.Log.Info("Up Down; Checksums OK"); } public static string[] SplitAddress(string address) { if (address == null || address.Length == 0 || address == "\"\"") return new string[] { null, null }; string host; string queue = null; int index = address.IndexOf(":"); if (index != -1) { host = address.Substring(0, index).Trim(); queue = address.Substring(index + 1).Trim(); } else { host = address.Trim(); } return new string[] { host, queue }; } } }