403 lines
14 KiB
C#
Executable File
403 lines
14 KiB
C#
Executable File
//
|
|
// Program.cs
|
|
//
|
|
// Author:
|
|
// beckermj <>
|
|
//
|
|
// Copyright (c) 2022 ${CopyrightHolder}
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
using System;
|
|
using System.Configuration;
|
|
using System.IO;
|
|
|
|
using Mocha.Core;
|
|
using Mocha.OMS;
|
|
using Mocha.Web.Server.Sys;
|
|
using UniversalEditor.IO;
|
|
|
|
namespace Mocha.Web.Server
|
|
{
|
|
class MainClass : Sys.WebApplication
|
|
{
|
|
public MainClass()
|
|
{
|
|
ShortName = "mocha-web";
|
|
}
|
|
|
|
public static void Main(string[] args)
|
|
{
|
|
(new MainClass()).Start();
|
|
}
|
|
|
|
protected override void OnRequestReceived(RequestEventArgs e)
|
|
{
|
|
base.OnRequestReceived(e);
|
|
|
|
System.IO.StreamReader sr = new System.IO.StreamReader(e.Stream);
|
|
System.IO.StreamWriter sw = new System.IO.StreamWriter(e.Stream);
|
|
|
|
SessionContext ctx = new SessionContext();
|
|
|
|
if (((HttpRequest)e.Request).Method == "POST")
|
|
{
|
|
System.Collections.Generic.Dictionary<string, string> postData = new System.Collections.Generic.Dictionary<string, string>();
|
|
|
|
if (e.Headers.ContainsKey("Content-Length"))
|
|
{
|
|
string strContentLength = e.Headers["Content-Length"];
|
|
int intContentLength = Int32.Parse(strContentLength);
|
|
char[] buffer = new char[intContentLength];
|
|
sr.ReadBlock(buffer, 0, intContentLength);
|
|
|
|
string buf = new string(buffer);
|
|
string[] values = buf.Split(new char[] { '&' });
|
|
|
|
foreach (string val in values)
|
|
{
|
|
string[] kvp1 = val.Split(new char[] { '=' });
|
|
string name = kvp1[0];
|
|
string value = kvp1[1];
|
|
|
|
postData[name] = FormDecode(value);
|
|
}
|
|
}
|
|
|
|
OnPost(new PostEventArgs(e.Request, e.Response, postData));
|
|
}
|
|
else if (((HttpRequest)e.Request).Method == "GET")
|
|
{
|
|
string relativePath = ((HttpRequest)e.Request).Path;
|
|
string[] pathSplit = relativePath.Split(new char[] { '/' }, StringSplitOptions.None);
|
|
if (pathSplit.Length >= 2)
|
|
{
|
|
if (pathSplit[1] == String.Empty)
|
|
{
|
|
Write404(sw, ctx);
|
|
}
|
|
else if (pathSplit.Length >= 2)
|
|
{
|
|
if (pathSplit.Length >= 3)
|
|
{
|
|
if (pathSplit[2] == "attachment")
|
|
{
|
|
string tenantName = pathSplit[1];
|
|
ctx.TenantName = tenantName;
|
|
|
|
string instanceId = pathSplit[3];
|
|
string key = pathSplit[4];
|
|
|
|
InstanceKey ik = InstanceKey.Parse(instanceId);
|
|
|
|
Mocha.OMS.Oms oms = ctx.GetOms();
|
|
Instance instImage = oms.GetInstance(ik);
|
|
if (oms.VerifyAccessKeyForOmsAttachment(instImage, key, ctx.GetOmsAttachmentEntropy()))
|
|
{
|
|
string physicalFilename = String.Format("{0}Images/Uploads/{1}.png", String.Empty, instImage.GlobalIdentifier.ToString("b"));
|
|
if (System.IO.File.Exists(physicalFilename))
|
|
{
|
|
|
|
byte[] data = System.IO.File.ReadAllBytes(physicalFilename);
|
|
string mimetype = oms.GetAttributeValue<string>(instImage, KnownAttributeGuids.Text.ContentType);
|
|
sw.WriteLine("HTTP/1.1 200 OK");
|
|
sw.WriteLine(String.Format("Content-Type: {0}", mimetype));
|
|
sw.WriteLine(String.Format("Content-Length: {0}", data.Length));
|
|
sw.WriteLine();
|
|
sw.Flush();
|
|
|
|
sw.BaseStream.Write(data, 0, data.Length);
|
|
sw.BaseStream.Flush();
|
|
|
|
// FIXME: !!!!!! figure out how to properly deal with large files and "connection reset"
|
|
System.Threading.Thread.Sleep(1000);
|
|
|
|
sw.Close();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Write404(sw, ctx);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Write401(sw, ctx);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (pathSplit[1] == "css")
|
|
{
|
|
if (pathSplit[2] == "uwt.css")
|
|
{
|
|
sw.WriteLine("HTTP/1.1 200 OK");
|
|
sw.WriteLine("Content-Type: text/css");
|
|
sw.WriteLine("Server: Mocha User Interface Service");
|
|
sw.WriteLine();
|
|
sw.Flush();
|
|
|
|
System.IO.Stream st = typeof(MainClass).Assembly.GetManifestResourceStream("Mocha.Web.Server.Pages.css.css");
|
|
st.CopyTo(e.Stream);
|
|
sw.Close();
|
|
return;
|
|
}
|
|
else if (pathSplit[2] == "uwt.css" || pathSplit[2] == "uwt.less")
|
|
{
|
|
sw.WriteLine("HTTP/1.1 200 OK");
|
|
sw.WriteLine("Content-Type: text/css");
|
|
sw.WriteLine("Server: Mocha User Interface Service");
|
|
sw.WriteLine();
|
|
sw.Flush();
|
|
|
|
string DefaultThemeName = ConfigurationManager.AppSettings["Mocha.Default.Theme"] ?? "Pleasanton";
|
|
string[] themeFiles = System.IO.Directory.GetFiles("Themes", "*", SearchOption.AllDirectories);
|
|
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
|
foreach (string themeFile in themeFiles)
|
|
{
|
|
string[] pathParts = themeFile.Split(new char[] { System.IO.Path.DirectorySeparatorChar }, StringSplitOptions.None);
|
|
if (pathParts.Length > 2)
|
|
{
|
|
string themeName = pathParts[1];
|
|
if (themeName == "Mobile" || themeName == DefaultThemeName)
|
|
{
|
|
sb.AppendLine(System.IO.File.ReadAllText(themeFile));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine(System.IO.File.ReadAllText(themeFile));
|
|
}
|
|
}
|
|
|
|
if (pathSplit[2].EndsWith(".less"))
|
|
{
|
|
sw.WriteLine(sb.ToString());
|
|
}
|
|
else
|
|
{
|
|
dotless.Core.configuration.DotlessConfiguration config = new dotless.Core.configuration.DotlessConfiguration();
|
|
config.MinifyOutput = true;
|
|
sw.WriteLine(dotless.Core.Less.Parse(sb.ToString(), config).Replace('\n', ' ').Replace('\t', ' ').Replace(" ", " "));
|
|
}
|
|
|
|
sw.Close();
|
|
return;
|
|
}
|
|
else if (pathSplit[2] == "Fonts")
|
|
{
|
|
string fontName = pathSplit[3];
|
|
string fontFile = pathSplit[4];
|
|
|
|
int indexOfQ = fontFile.IndexOf('?');
|
|
if (indexOfQ != -1)
|
|
{
|
|
fontFile = fontFile.Substring(0, indexOfQ);
|
|
}
|
|
|
|
sw.WriteLine("HTTP/1.1 200 OK");
|
|
sw.WriteLine("Content-Type: application/x-font-opentype");
|
|
sw.WriteLine();
|
|
sw.Flush();
|
|
|
|
e.Response.WriteFile("Fonts/" + fontName + "/" + fontFile);
|
|
|
|
e.Stream.Close();
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// assume tenant name
|
|
ctx.TenantName = pathSplit[1];
|
|
|
|
if (pathSplit.Length >= 2)
|
|
{
|
|
if (pathSplit.Length == 5 && pathSplit[1] == "madi" && pathSplit[2] == "authgwy")
|
|
{
|
|
ctx.TenantName = pathSplit[3];
|
|
if (pathSplit[4] == "tenant-config.xml")
|
|
{
|
|
sw.WriteLine("HTTP/1.1 200 OK");
|
|
sw.WriteLine("Content-Type: application/xml");
|
|
sw.WriteLine("Server: Mocha User Interface Service");
|
|
sw.WriteLine();
|
|
|
|
sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
|
|
sw.WriteLine("<mul:Tenant_Config xmlns:mml=\"urn:net.alcetech.schemas.Mocha.Model!1.0\" xmlns:wul=\"urn:net.alcetech.schemas.Mocha.UserInterface!1.0\" xmlns:mcl=\"urn:net.alcetech.schemas.Mocha.UserInterface!2.0\" xmlns:mc=\"urn:net.alcetech.schemas.Mocha/bsvc\" xmlns:nyw=\"urn:com.netyourwork/aod\" ");
|
|
|
|
System.Collections.Generic.Dictionary<string, string> dict = new System.Collections.Generic.Dictionary<string, string>();
|
|
dict["Allow_Attachments_And_Documents_To_Be_Shared_With_Other_Mobile_Apps"] = "0";
|
|
dict["Apns_Allowed"] = "0";
|
|
dict["Canvas_Is_Enabled"] = "1";
|
|
dict["Canvas_Hex_Code"] = "#005cb9,#0875e1";
|
|
dict["Clear_Mobile_SSO_Webview_Cookies_on_Login"] = "0";
|
|
dict["Deter_Screenshots"] = "0";
|
|
dict["Disable_Add_To_Contact"] = "0";
|
|
dict["Disable_Importing_Attachments_From_Third-Party_Cloud_Services"] = "0";
|
|
dict["Disable_Location_Service"] = "0";
|
|
dict["Disable_Mail_To"] = "0";
|
|
dict["Disable_Voice_in_Assistant_on_Mobile"] = "1";
|
|
dict["Enable_Blue_Primary_Buttons"] = "0";
|
|
dict["Enable_Certificate_Based_SSO"] = "0";
|
|
dict["Enable_DOM_Storage"] = "0";
|
|
dict["Enable_Email_Annotations"] = "1";
|
|
dict["Enable_export_to_Worksheets"] = "1";
|
|
dict["Enable_Fingerprint_Authentication"] = "1";
|
|
dict["Enable_Geospace_Visualization"] = "0";
|
|
dict["Enable_Mobile_Browser_SSO_for_Native_Apps"] = "0";
|
|
dict["Enable_New_Profile"] = "0";
|
|
dict["Error_Message_for_Browser"] = "This Browser is not supported by Workday. Please contact your IT department.";
|
|
dict["Is_FedRAMP_Tenant"] = "0";
|
|
dict["Google_Cloud_Messaging_Project_Number"] = "739632724832";
|
|
dict["Hide_Links_to_Web_from_Deep_Linking_and_Inbox"] = "0";
|
|
dict["Login_URL"] = "https://wd5-impl.workday.com/wday/authgwy/cityoforlando1/login-saml2.htmld";
|
|
dict["Logout_URL"] = "https://cityoforlando.okta.com";
|
|
dict["OMS_Note"] = "IMPL - Powered by Workday";
|
|
dict["Pin_Auth_Enabled"] = "0";
|
|
dict["Pin_Max_Attempts"] = "0";
|
|
dict["Pin_Max_Length"] = "0";
|
|
dict["Pin_Min_Length"] = "0";
|
|
dict["Reset_Password_Online"] = "0";
|
|
dict["Show_Change_Password_Link"] = "1";
|
|
dict["Show_Forgotten_Password_Link"] = "0";
|
|
dict["SignOn_Custom_Message"] = "<p>City of Orlando - Production</p>";
|
|
dict["SignOn_Message_Locale"] = "en_US";
|
|
dict["SignOn_Note"] = "<p>City of Orlando - Production</p>";
|
|
dict["SignOn_Tenant_Logo_Url"] = "/cityoforlando1/images/signon.xml";
|
|
dict["System_Confidence_Level"] = "PROD";
|
|
dict["System_Note"] = "Your Implementation tenant will be unavailable for a maximum of 12 hours during the next Weekly Service Update; starting on Friday, December 30, 2022 at 6:00 PM Pacific Time (Los Angeles) (GMT-8) until Saturday, December 31, 2022 at 6:00 AM Pacific Time (Los Angeles) (GMT-8).";
|
|
dict["Use_One_Time_Use_Link"] = "0";
|
|
foreach (System.Collections.Generic.KeyValuePair<string, string> kvp in dict)
|
|
{
|
|
sw.WriteLine(String.Format("\t{0}=\"{1}\"", kvp.Key, kvp.Value));
|
|
}
|
|
sw.WriteLine(" />");
|
|
sw.Close();
|
|
return;
|
|
}
|
|
if (pathSplit[4] == "login.htmld")
|
|
{
|
|
// we are at the login page
|
|
// here we find the current tenant, and ask it to retrieve the PageBuilder login page
|
|
|
|
//FIXME: make this better
|
|
Mocha.OMS.Oms oms = ctx.GetOms();
|
|
|
|
Instance instTenant = oms.GetTenantInstance();
|
|
if (instTenant == null)
|
|
{
|
|
WriteGenericMessage(sw, ctx, "Invalid Tenant", "The URL you have provided is invalid. Please contact your system administrator if you require assistance.");
|
|
return;
|
|
}
|
|
|
|
// Tenant@get login Page for Tenant and Application parm
|
|
|
|
sw.WriteLine("HTTP/1.1 200 OK");
|
|
sw.WriteLine("Content-Type: text/html");
|
|
sw.WriteLine();
|
|
|
|
PageBuilder2 pb = new PageBuilder2(oms, ctx);
|
|
|
|
Instance instLoginPage = oms.GetInstance(KnownInstanceGuids.Pages.LoginPage);
|
|
|
|
pb.RenderPage(instLoginPage, sw);
|
|
sw.Close();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Write404(sw, ctx);
|
|
return;
|
|
}
|
|
}
|
|
|
|
object instLoginUser = null;
|
|
if (instLoginUser == null)
|
|
{
|
|
WriteLoginRedirect(sw, ctx);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Write404(sw, ctx);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnPost(PostEventArgs e)
|
|
{
|
|
System.IO.StreamWriter sw = new StreamWriter(e.Response.Stream);
|
|
sw.WriteLine("HTTP/1.1 200 OK");
|
|
sw.WriteLine();
|
|
sw.WriteLine(String.Format("<html><head><title></title></head><body>Your stuff is {0}</body></html>", e.PostData.Count));
|
|
sw.Flush();
|
|
|
|
e.Response.End();
|
|
}
|
|
|
|
private static string FormDecode(string value)
|
|
{
|
|
string plusReplace = value.Replace('+', ' ');
|
|
plusReplace = plusReplace.Replace("%2B", "+");
|
|
return plusReplace;
|
|
}
|
|
|
|
private static void WriteGenericMessage(StreamWriter sw, SessionContext ctx, string v1, string v2)
|
|
{
|
|
sw.WriteLine("HTTP/1.1 404 Not Found");
|
|
sw.WriteLine("Content-Type: text/html");
|
|
sw.WriteLine("Server: Mocha User Interface Service");
|
|
|
|
sw.WriteLine();
|
|
|
|
sw.WriteLine(String.Format("<html><head><link rel=\"stylesheet\" type=\"text/css\" media=\"screen, projection\" href=\"/css/uwt.css\" /><title>{0}</title></head><body>", ctx.PageTitle));
|
|
sw.WriteLine(String.Format("<h1>{0}</h1><p>{1}</p></body></html>", v1, v2));
|
|
|
|
sw.Close();
|
|
}
|
|
|
|
private static void WriteLoginRedirect(StreamWriter sw, SessionContext ctx)
|
|
{
|
|
/*
|
|
var redirectUrl = 'https://{originalUrl}/wday/authgwy/{tenant}/login.htmld?returnTo=%2f{tenant}%2fhome.htmld';
|
|
var anchor = encodeURIComponent(window.location.hash);
|
|
if (anchor.length > 0) {
|
|
redirectUrl = redirectUrl.replace(/([?|&]returnTo=[^\&;]+)/, '$1' + anchor);
|
|
}
|
|
window.location = redirectUrl;
|
|
*/
|
|
sw.WriteLine("HTTP/1.1 302 Found");
|
|
sw.WriteLine(String.Format("Location: /madi/authgwy/{0}/login.htmld", ctx.TenantName));
|
|
sw.WriteLine();
|
|
sw.Close();
|
|
}
|
|
|
|
private static void Write404(StreamWriter sw, SessionContext ctx)
|
|
{
|
|
WriteGenericMessage(sw, ctx, "Not Found", "The requested URL was not found on this server.");
|
|
}
|
|
private static void Write401(StreamWriter sw, SessionContext ctx)
|
|
{
|
|
WriteGenericMessage(sw, ctx, "Not Authorized", "The requested URL was not found on this server.");
|
|
}
|
|
}
|
|
}
|