using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BoneLib.BoneMenu;
using BoneLib.Notifications;
using HarmonyLib;
using LabFusion.Network;
using LabFusion.Network.Serialization;
using LabFusion.Player;
using LabFusion.SDK.Metadata;
using LabFusion.SDK.Modules;
using LabFusion.Safety;
using MediaDrop;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(MediaDropMod), "MediaDrop", "1.3.1", "ChappieStudios", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("MediaDrop")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MediaDrop")]
[assembly: AssemblyTitle("MediaDrop")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace MediaDrop
{
public sealed class MediaDropMod : MelonMod
{
private static readonly HashSet<string> VideoExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".mp4", ".m4v", ".mov", ".webm", ".avi", ".wmv", ".mkv" };
private static readonly HashSet<string> AudioExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".mp3", ".wav", ".ogg", ".m4a", ".aac", ".flac" };
private static readonly HttpClient HttpClient = new HttpClient
{
Timeout = TimeSpan.FromMinutes(10.0)
};
private const int PreferredPort = 28472;
private const int MaxMenuNameLength = 52;
private const float FusionPresenceUpdateSeconds = 5f;
private const string FusionPresenceKey = "MediaDropMod";
private const string FusionPresenceValue = "1.3.1";
private const string CloudflaredDownloadUrl = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64.exe";
private readonly object _lock = new object();
private readonly Queue<Action> _mainThreadActions = new Queue<Action>();
private readonly Dictionary<string, string> _fusionLinks = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> _allowedTunnelHosts = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private MelonPreferences_Category _preferences;
private MelonPreferences_Entry<bool> _autoCopyPreference;
private MelonPreferences_Entry<bool> _shareToFusionPreference;
private Page _menuPage;
private Page _peopleWithoutModPage;
private string _mediaFolder;
private string _cloudflaredPath;
private bool _autoCopyNewFiles = true;
private bool _shareToFusion = true;
private TcpListener _listener;
private Thread _serverThread;
private volatile bool _serverRunning;
private int _port;
private Process _tunnelProcess;
private string _publicBaseUrl;
private bool _tunnelStarting;
private FileSystemWatcher _watcher;
private string _pendingAutoCopyPath;
private float _nextFusionPresenceUpdateTime;
private float _nextMissingModMenuRefreshTime;
internal static MediaDropMod Instance { get; private set; }
public override void OnInitializeMelon()
{
Instance = this;
SetupPreferences();
RegisterFusionNetworking();
((MelonBase)this).HarmonyInstance.PatchAll(typeof(MediaDropMod).Assembly);
_mediaFolder = Path.Combine(MelonEnvironment.UserDataDirectory, "MediaDrop");
_cloudflaredPath = Path.Combine(_mediaFolder, "cloudflared.exe");
Directory.CreateDirectory(_mediaFolder);
StartServer();
StartWatcher();
BuildMenu();
RestartPublicTunnel();
((MelonBase)this).LoggerInstance.Msg("MediaDrop loaded. Folder: " + _mediaFolder);
}
public override void OnDeinitializeMelon()
{
StopWatcher();
StopTunnel();
StopServer();
Instance = null;
}
public override void OnUpdate()
{
float time = Time.time;
UpdateFusionPresence(time);
RefreshPeopleWithoutModPage(time);
while (true)
{
Action action = null;
lock (_lock)
{
if (_mainThreadActions.Count > 0)
{
action = _mainThreadActions.Dequeue();
}
}
if (action != null)
{
try
{
action();
}
catch (Exception ex)
{
((MelonBase)this).LoggerInstance.Warning("MediaDrop main-thread action failed: " + ex.Message);
}
continue;
}
break;
}
}
private void SetupPreferences()
{
_preferences = MelonPreferences.CreateCategory("MediaDrop");
_autoCopyPreference = _preferences.CreateEntry<bool>("AutoCopyNewFiles", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
_shareToFusionPreference = _preferences.CreateEntry<bool>("ShareToFusion", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
_autoCopyNewFiles = _autoCopyPreference.Value;
_shareToFusion = _shareToFusionPreference.Value;
}
private void RegisterFusionNetworking()
{
try
{
ModuleMessageManager.RegisterHandler<MediaDropLinkMessage>();
((MelonBase)this).LoggerInstance.Msg("MediaDrop Fusion networking registered.");
}
catch (Exception ex)
{
((MelonBase)this).LoggerInstance.Warning("MediaDrop Fusion networking registration skipped: " + ex.Message);
}
}
private void BuildMenu()
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
//IL_0141: Unknown result type (might be due to invalid IL or missing references)
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_0185: Unknown result type (might be due to invalid IL or missing references)
//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
//IL_020e: Unknown result type (might be due to invalid IL or missing references)
//IL_022b: Unknown result type (might be due to invalid IL or missing references)
if (_menuPage == null)
{
_menuPage = Page.Root.CreatePage("MediaDrop", Color.cyan, 0, true);
}
else
{
_menuPage.RemoveAll();
}
List<string> mediaFiles = GetMediaFiles();
string text = ((!string.IsNullOrWhiteSpace(_publicBaseUrl)) ? "Tunnel Ready" : (_tunnelStarting ? "Tunnel Starting" : "No Public Tunnel"));
_menuPage.CreateBool("Auto Copy New Files", Color.yellow, _autoCopyNewFiles, (Action<bool>)SetAutoCopy);
_menuPage.CreateBool("Share To Fusion", Color.cyan, _shareToFusion, (Action<bool>)SetShareToFusion);
_menuPage.CreateFunction(text, string.IsNullOrWhiteSpace(_publicBaseUrl) ? Color.red : Color.green, (Action)CopyTunnelBaseUrl);
_menuPage.CreateFunction("Restart Public Tunnel", Color.yellow, (Action)RestartPublicTunnel);
_menuPage.CreateFunction($"Refresh Files ({mediaFiles.Count})", Color.yellow, (Action)BuildMenu);
_menuPage.CreateFunction("Open Media Folder", Color.green, (Action)OpenMediaFolder);
_menuPage.CreateFunction("Copy Latest Public Link", Color.white, (Action)CopyLatestPublicLink);
AddFilePage("Videos", Color.red, mediaFiles.Where(IsVideoFile).ToList());
AddFilePage("Audio", Color.magenta, mediaFiles.Where(IsAudioFile).ToList());
AddFilePage("All Files", Color.white, mediaFiles);
AddFusionLinksPage();
_peopleWithoutModPage = _menuPage.CreatePage("People Without Mod", Color.cyan, 0, true);
BuildPeopleWithoutModPage();
}
private void AddFilePage(string name, Color color, List<string> files)
{
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: Unknown result type (might be due to invalid IL or missing references)
Page val = _menuPage.CreatePage($"{name} ({files.Count})", color, 0, true);
if (files.Count == 0)
{
val.CreateFunction("No Files Found", Color.gray, (Action)delegate
{
});
return;
}
foreach (string item in files.OrderBy(Path.GetFileName))
{
string capturedFile = item;
val.CreateFunction(TrimMenuName(Path.GetFileName(capturedFile)), Color.white, (Action)delegate
{
CopyOrQueuePublicUrl(capturedFile);
});
}
}
private void SetAutoCopy(bool value)
{
_autoCopyNewFiles = value;
_autoCopyPreference.Value = value;
_preferences.SaveToFile(false);
}
private void SetShareToFusion(bool value)
{
_shareToFusion = value;
_shareToFusionPreference.Value = value;
_preferences.SaveToFile(false);
}
private void AddFusionLinksPage()
{
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
Page val = _menuPage.CreatePage($"Fusion Links ({_fusionLinks.Count})", Color.cyan, 0, true);
if (_fusionLinks.Count == 0)
{
val.CreateFunction("No Links Received", Color.gray, (Action)delegate
{
});
return;
}
foreach (KeyValuePair<string, string> item in _fusionLinks.OrderBy((KeyValuePair<string, string> pair) => pair.Key))
{
string name = item.Key;
string url = item.Value;
val.CreateFunction(TrimMenuName(name), Color.white, (Action)delegate
{
CopyReceivedFusionUrl(name, url);
});
}
}
private void UpdateFusionPresence(float now)
{
if (now < _nextFusionPresenceUpdateTime)
{
return;
}
_nextFusionPresenceUpdateTime = now + 5f;
try
{
if (NetworkInfo.HasServer && PlayerIDManager.LocalID != null && PlayerIDManager.LocalID.IsValid && PlayerIDManager.LocalID.Metadata.Metadata.GetMetadata("MediaDropMod") != "1.3.1")
{
PlayerIDManager.LocalID.Metadata.Metadata.TrySetMetadata("MediaDropMod", "1.3.1");
}
}
catch (Exception ex)
{
((MelonBase)this).LoggerInstance.Warning("MediaDrop Fusion mod check failed: " + ex.Message);
}
}
private void RefreshPeopleWithoutModPage(float now)
{
if (!(now < _nextMissingModMenuRefreshTime))
{
_nextMissingModMenuRefreshTime = now + 5f;
BuildPeopleWithoutModPage();
}
}
private void BuildPeopleWithoutModPage()
{
//IL_018b: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_0152: Unknown result type (might be due to invalid IL or missing references)
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
if (_peopleWithoutModPage == null)
{
return;
}
_peopleWithoutModPage.RemoveAll();
_peopleWithoutModPage.CreateFunction("Refresh", Color.yellow, (Action)BuildPeopleWithoutModPage);
try
{
if (!NetworkInfo.HasServer)
{
_peopleWithoutModPage.CreateFunction("Not In Fusion Lobby", Color.gray, (Action)delegate
{
});
return;
}
List<PlayerID> list = new List<PlayerID>(PlayerIDManager.PlayerIDs);
list.Sort((PlayerID left, PlayerID right) => string.Compare(GetFusionPlayerName(left), GetFusionPlayerName(right), StringComparison.OrdinalIgnoreCase));
int num = 0;
foreach (PlayerID item in list)
{
if (item != null && item.IsValid && !item.IsMe && string.IsNullOrWhiteSpace(item.Metadata.Metadata.GetMetadata("MediaDropMod")))
{
num++;
string fusionPlayerName = GetFusionPlayerName(item);
_peopleWithoutModPage.CreateFunction(TrimMenuName(fusionPlayerName), Color.red, (Action)delegate
{
});
}
}
if (num == 0)
{
_peopleWithoutModPage.CreateFunction("Everyone Has Mod", Color.green, (Action)delegate
{
});
}
}
catch (Exception ex)
{
_peopleWithoutModPage.CreateFunction("Could Not Read Lobby", Color.red, (Action)delegate
{
});
((MelonBase)this).LoggerInstance.Warning("MediaDrop people-without-mod menu failed: " + ex.Message);
}
}
private static string GetFusionPlayerName(PlayerID player)
{
if (player == null)
{
return "Unknown Player";
}
PlayerMetadata metadata = player.Metadata;
object obj;
if (metadata == null)
{
obj = null;
}
else
{
MetadataVariable nickname = metadata.Nickname;
obj = ((nickname != null) ? nickname.GetValueOrEmpty() : null);
}
string text = (string)obj;
if (!string.IsNullOrWhiteSpace(text))
{
return text;
}
PlayerMetadata metadata2 = player.Metadata;
object obj2;
if (metadata2 == null)
{
obj2 = null;
}
else
{
MetadataVariable username = metadata2.Username;
obj2 = ((username != null) ? username.GetValueOrEmpty() : null);
}
string text2 = (string)obj2;
if (!string.IsNullOrWhiteSpace(text2))
{
return text2;
}
return $"Player {player.SmallID}";
}
private void OpenMediaFolder()
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = _mediaFolder,
UseShellExecute = true
});
}
catch (Exception ex)
{
((MelonBase)this).LoggerInstance.Warning("Could not open MediaDrop folder: " + ex.Message);
}
}
private void CopyTunnelBaseUrl()
{
if (string.IsNullOrWhiteSpace(_publicBaseUrl))
{
Notify("MediaDrop", "Public tunnel is not ready yet.");
return;
}
GUIUtility.systemCopyBuffer = _publicBaseUrl;
Notify("MediaDrop", "Copied public tunnel link.");
}
private void CopyLatestPublicLink()
{
string text = GetMediaFiles().OrderByDescending(File.GetLastWriteTimeUtc).FirstOrDefault();
if (string.IsNullOrWhiteSpace(text))
{
Notify("MediaDrop", "No media files found.");
}
else
{
CopyOrQueuePublicUrl(text);
}
}
private void CopyOrQueuePublicUrl(string path)
{
if (!File.Exists(path))
{
return;
}
if (string.IsNullOrWhiteSpace(_publicBaseUrl))
{
_pendingAutoCopyPath = path;
Notify("MediaDrop", "Waiting for public tunnel, then link will copy.");
if (!_tunnelStarting)
{
RestartPublicTunnel();
}
}
else
{
string publicUrlForFile = GetPublicUrlForFile(path);
AllowTunnelUrl(publicUrlForFile);
GUIUtility.systemCopyBuffer = publicUrlForFile;
Notify("MediaDrop", "Copied public link: " + Path.GetFileName(path));
((MelonBase)this).LoggerInstance.Msg("Copied public MediaDrop link: " + publicUrlForFile);
SharePublicUrlWithFusion(Path.GetFileName(path), publicUrlForFile);
}
}
private string GetPublicUrlForFile(string path)
{
string text = Uri.EscapeDataString(Path.GetFileName(path));
return _publicBaseUrl.TrimEnd('/') + "/media/" + text;
}
private void StartWatcher()
{
_watcher = new FileSystemWatcher(_mediaFolder);
_watcher.IncludeSubdirectories = false;
_watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
_watcher.Created += OnMediaFolderChanged;
_watcher.Renamed += OnMediaFolderRenamed;
_watcher.Deleted += OnMediaFolderChanged;
_watcher.EnableRaisingEvents = true;
}
private void StopWatcher()
{
if (_watcher != null)
{
_watcher.EnableRaisingEvents = false;
_watcher.Dispose();
_watcher = null;
}
}
private void OnMediaFolderChanged(object sender, FileSystemEventArgs args)
{
QueueMainThread(BuildMenu);
if (_autoCopyNewFiles && File.Exists(args.FullPath) && IsMediaFile(args.FullPath))
{
QueueMainThread(delegate
{
CopyOrQueuePublicUrl(args.FullPath);
});
}
}
private void OnMediaFolderRenamed(object sender, RenamedEventArgs args)
{
QueueMainThread(BuildMenu);
if (_autoCopyNewFiles && File.Exists(args.FullPath) && IsMediaFile(args.FullPath))
{
QueueMainThread(delegate
{
CopyOrQueuePublicUrl(args.FullPath);
});
}
}
private void StartServer()
{
try
{
_listener = new TcpListener(IPAddress.Loopback, 28472);
_listener.Start();
}
catch
{
_listener = new TcpListener(IPAddress.Loopback, 0);
_listener.Start();
}
_port = ((IPEndPoint)_listener.LocalEndpoint).Port;
_serverRunning = true;
_serverThread = new Thread(ServerLoop)
{
IsBackground = true,
Name = "MediaDrop HTTP Server"
};
_serverThread.Start();
}
private void StopServer()
{
_serverRunning = false;
try
{
_listener?.Stop();
}
catch
{
}
_listener = null;
}
private void RestartPublicTunnel()
{
if (_tunnelStarting)
{
Notify("MediaDrop", "Public tunnel is already starting.");
return;
}
StopTunnel();
_publicBaseUrl = null;
_tunnelStarting = true;
BuildMenu();
Notify("MediaDrop", "Starting public tunnel.");
Task.Run((Func<Task?>)StartTunnelWorker);
}
private async Task StartTunnelWorker()
{
try
{
await EnsureCloudflared();
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = _cloudflaredPath,
Arguments = $"tunnel --no-autoupdate --url http://127.0.0.1:{_port}",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
Process process = new Process
{
StartInfo = startInfo,
EnableRaisingEvents = true
};
process.OutputDataReceived += OnTunnelOutput;
process.ErrorDataReceived += OnTunnelOutput;
process.Exited += OnTunnelExited;
lock (_lock)
{
_tunnelProcess = process;
}
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
}
catch (Exception ex2)
{
Exception ex = ex2;
((MelonBase)this).LoggerInstance.Warning("Could not start MediaDrop public tunnel: " + ex.Message);
QueueMainThread(delegate
{
_tunnelStarting = false;
Notify("MediaDrop Tunnel Failed", ex.Message);
BuildMenu();
});
}
}
private async Task EnsureCloudflared()
{
if (!File.Exists(_cloudflaredPath))
{
QueueMainThread(delegate
{
Notify("MediaDrop", "Downloading public tunnel helper.");
});
byte[] bytes = await HttpClient.GetByteArrayAsync("https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64.exe");
File.WriteAllBytes(_cloudflaredPath, bytes);
}
}
private void OnTunnelOutput(object sender, DataReceivedEventArgs args)
{
if (string.IsNullOrWhiteSpace(args.Data))
{
return;
}
Match match = Regex.Match(args.Data, "https://[-a-zA-Z0-9.]+\\.trycloudflare\\.com");
if (!match.Success)
{
return;
}
string url = match.Value;
QueueMainThread(delegate
{
_publicBaseUrl = url;
AllowTunnelUrl(url);
_tunnelStarting = false;
Notify("MediaDrop", "Public tunnel ready.");
BuildMenu();
if (!string.IsNullOrWhiteSpace(_pendingAutoCopyPath) && File.Exists(_pendingAutoCopyPath))
{
string pendingAutoCopyPath = _pendingAutoCopyPath;
_pendingAutoCopyPath = null;
CopyOrQueuePublicUrl(pendingAutoCopyPath);
}
});
}
private void OnTunnelExited(object sender, EventArgs args)
{
QueueMainThread(delegate
{
((MelonBase)this).LoggerInstance.Warning("MediaDrop public tunnel stopped.");
_publicBaseUrl = null;
_tunnelStarting = false;
BuildMenu();
});
}
private void SharePublicUrlWithFusion(string fileName, string url)
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
if (!_shareToFusion || !NetworkInfo.HasServer)
{
return;
}
try
{
MessageRelay.RelayModule<MediaDropLinkMessage, MediaDropLinkData>(new MediaDropLinkData
{
FileName = fileName,
Url = url
}, CommonMessageRoutes.ReliableToOtherClients);
Notify("MediaDrop Fusion", "Sent link to Fusion players.");
}
catch (Exception ex)
{
((MelonBase)this).LoggerInstance.Warning("Could not share MediaDrop link through Fusion: " + ex.Message);
}
}
internal void ReceiveFusionLink(string fileName, string url)
{
if (!string.IsNullOrWhiteSpace(url))
{
if (string.IsNullOrWhiteSpace(fileName))
{
fileName = "MediaDrop Link";
}
AllowTunnelUrl(url);
_fusionLinks[fileName] = url;
GUIUtility.systemCopyBuffer = url;
Notify("MediaDrop Fusion", "Received link: " + fileName);
BuildMenu();
}
}
private void CopyReceivedFusionUrl(string name, string url)
{
if (!string.IsNullOrWhiteSpace(url))
{
AllowTunnelUrl(url);
GUIUtility.systemCopyBuffer = url;
Notify("MediaDrop Fusion", "Copied link: " + name);
}
}
private void AllowTunnelUrl(string url)
{
if (Uri.TryCreate(url, UriKind.Absolute, out Uri result))
{
_allowedTunnelHosts.Add(result.Host);
}
}
internal static bool IsAllowedMediaDropUrl(string url)
{
if (!Uri.TryCreate(url, UriKind.Absolute, out Uri result))
{
return false;
}
if (result.Scheme != Uri.UriSchemeHttp && result.Scheme != Uri.UriSchemeHttps)
{
return false;
}
if (!result.AbsolutePath.StartsWith("/media/", StringComparison.OrdinalIgnoreCase))
{
return false;
}
MediaDropMod instance = Instance;
if (instance != null && instance._allowedTunnelHosts.Contains(result.Host))
{
return true;
}
return result.Host.EndsWith(".trycloudflare.com", StringComparison.OrdinalIgnoreCase);
}
private void StopTunnel()
{
Process process = null;
lock (_lock)
{
process = _tunnelProcess;
_tunnelProcess = null;
}
if (process == null)
{
return;
}
try
{
if (!process.HasExited)
{
process.Kill();
}
}
catch
{
}
}
private void ServerLoop()
{
while (_serverRunning)
{
try
{
TcpClient client = _listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(delegate
{
HandleClient(client);
});
}
catch
{
if (_serverRunning)
{
Thread.Sleep(50);
}
}
}
}
private void HandleClient(TcpClient client)
{
using (client)
{
using NetworkStream stream = client.GetStream();
try
{
string text = ReadRequestHeader(stream);
if (string.IsNullOrWhiteSpace(text))
{
return;
}
string[] array = text.Split(new string[1] { "\r\n" }, StringSplitOptions.None);
string[] array2 = array[0].Split(' ');
if (array2.Length < 2)
{
WriteStatus(stream, "400 Bad Request", "Bad request.");
return;
}
string text2 = array2[0];
string text3 = array2[1];
if (text2 != "GET" && text2 != "HEAD")
{
WriteStatus(stream, "405 Method Not Allowed", "Only GET and HEAD are supported.");
return;
}
if (!text3.StartsWith("/media/", StringComparison.OrdinalIgnoreCase))
{
WriteStatus(stream, "404 Not Found", "Media file not found.");
return;
}
string path = Uri.UnescapeDataString(text3.Substring("/media/".Length).Split('?')[0]);
path = Path.GetFileName(path);
string text4 = Path.Combine(_mediaFolder, path);
if (!File.Exists(text4) || !IsMediaFile(text4))
{
WriteStatus(stream, "404 Not Found", "Media file not found.");
}
else
{
ServeFile(stream, text4, array, text2 == "HEAD");
}
}
catch
{
}
}
}
private static string ReadRequestHeader(NetworkStream stream)
{
List<byte> list = new List<byte>();
byte[] array = new byte[1];
while (list.Count < 16384 && stream.Read(array, 0, 1) > 0)
{
list.Add(array[0]);
int count = list.Count;
if (count >= 4 && list[count - 4] == 13 && list[count - 3] == 10 && list[count - 2] == 13 && list[count - 1] == 10)
{
break;
}
}
return Encoding.ASCII.GetString(list.ToArray());
}
private static void ServeFile(NetworkStream stream, string filePath, string[] requestLines, bool headOnly)
{
long length = new FileInfo(filePath).Length;
long num = 0L;
long num2 = length - 1;
bool flag = false;
string text = requestLines.FirstOrDefault((string line) => line.StartsWith("Range:", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrWhiteSpace(text))
{
string text2 = text.Substring("Range:".Length).Trim();
if (text2.StartsWith("bytes=", StringComparison.OrdinalIgnoreCase))
{
string[] array = text2.Substring("bytes=".Length).Split('-');
if (array.Length == 2)
{
if (long.TryParse(array[0], out var result))
{
num = Math.Max(0L, result);
}
if (long.TryParse(array[1], out var result2))
{
num2 = Math.Min(length - 1, result2);
}
if (num <= num2)
{
flag = true;
}
}
}
}
long num3 = num2 - num + 1;
string value = (flag ? "206 Partial Content" : "200 OK");
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("HTTP/1.1 ").Append(value).Append("\r\n");
stringBuilder.Append("Content-Type: ").Append(GetContentType(filePath)).Append("\r\n");
stringBuilder.Append("Accept-Ranges: bytes\r\n");
stringBuilder.Append("Content-Length: ").Append(num3).Append("\r\n");
if (flag)
{
stringBuilder.Append("Content-Range: bytes ").Append(num).Append("-")
.Append(num2)
.Append("/")
.Append(length)
.Append("\r\n");
}
stringBuilder.Append("Connection: close\r\n\r\n");
byte[] bytes = Encoding.ASCII.GetBytes(stringBuilder.ToString());
stream.Write(bytes, 0, bytes.Length);
if (headOnly)
{
return;
}
byte[] array2 = new byte[81920];
using FileStream fileStream = File.OpenRead(filePath);
fileStream.Position = num;
long num4 = num3;
while (num4 > 0)
{
int num5 = fileStream.Read(array2, 0, (int)Math.Min(array2.Length, num4));
if (num5 <= 0)
{
break;
}
stream.Write(array2, 0, num5);
num4 -= num5;
}
}
private static void WriteStatus(NetworkStream stream, string status, string message)
{
byte[] bytes = Encoding.UTF8.GetBytes(message);
string s = $"HTTP/1.1 {status}\r\nContent-Type: text/plain\r\nContent-Length: {bytes.Length}\r\nConnection: close\r\n\r\n";
byte[] bytes2 = Encoding.ASCII.GetBytes(s);
stream.Write(bytes2, 0, bytes2.Length);
stream.Write(bytes, 0, bytes.Length);
}
private List<string> GetMediaFiles()
{
if (!Directory.Exists(_mediaFolder))
{
return new List<string>();
}
return Directory.EnumerateFiles(_mediaFolder, "*", SearchOption.TopDirectoryOnly).Where(IsMediaFile).OrderBy(Path.GetFileName)
.ToList();
}
private static bool IsMediaFile(string path)
{
if (!IsVideoFile(path))
{
return IsAudioFile(path);
}
return true;
}
private static bool IsVideoFile(string path)
{
return VideoExtensions.Contains(Path.GetExtension(path));
}
private static bool IsAudioFile(string path)
{
return AudioExtensions.Contains(Path.GetExtension(path));
}
private static string GetContentType(string path)
{
switch (Path.GetExtension(path).ToLowerInvariant())
{
case ".m4v":
case ".mp4":
return "video/mp4";
case ".mov":
return "video/quicktime";
case ".webm":
return "video/webm";
case ".avi":
return "video/x-msvideo";
case ".wmv":
return "video/x-ms-wmv";
case ".mkv":
return "video/x-matroska";
case ".mp3":
return "audio/mpeg";
case ".wav":
return "audio/wav";
case ".ogg":
return "audio/ogg";
case ".m4a":
return "audio/mp4";
case ".aac":
return "audio/aac";
case ".flac":
return "audio/flac";
default:
return "application/octet-stream";
}
}
private static string TrimMenuName(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return "Unnamed File";
}
if (value.Length > 52)
{
return value.Substring(0, 49) + "...";
}
return value;
}
private void QueueMainThread(Action action)
{
lock (_lock)
{
_mainThreadActions.Enqueue(action);
}
}
private void Notify(string title, string message)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Expected O, but got Unknown
try
{
Notifier.Send(new Notification
{
Title = new NotificationText(title),
Message = new NotificationText(message),
ShowTitleOnPopup = true,
PopupLength = 5f,
Type = (NotificationType)0
});
}
catch
{
}
}
}
public sealed class MediaDropLinkData : INetSerializable
{
public string FileName;
public string Url;
public int? GetSize()
{
return SizeExtensions.GetSize(FileName) + SizeExtensions.GetSize(Url);
}
public void Serialize(INetSerializer serializer)
{
serializer.SerializeValue(ref FileName);
serializer.SerializeValue(ref Url);
}
}
public sealed class MediaDropLinkMessage : ModuleMessageHandler
{
protected override void OnHandleMessage(ReceivedMessage received)
{
MediaDropLinkData mediaDropLinkData = ((ReceivedMessage)(ref received)).ReadData<MediaDropLinkData>();
MediaDropMod.Instance?.ReceiveFusionLink(mediaDropLinkData.FileName, mediaDropLinkData.Url);
}
}
[HarmonyPatch(typeof(URLWhitelistManager), "IsURLWhitelisted")]
internal static class FusionUrlWhitelistPatch
{
private static void Postfix(string url, ref bool __result)
{
if (!__result && MediaDropMod.IsAllowedMediaDropUrl(url))
{
__result = true;
}
}
}
}