private static void ReadReference(XmlNode xn, PlgxPluginInfo plgx) { XmlNode xnInc = xn.Attributes.GetNamedItem(XnnInclude); if ((xnInc == null) || string.IsNullOrEmpty(xnInc.Value)) { Debug.Assert(false); return; } string str = xnInc.Value; if (UrlUtil.AssemblyEquals(str, PwDefs.ShortProductName)) { return; // Ignore KeePass references } foreach (XmlNode xnSub in xn.ChildNodes) { if (xnSub.Name == XnnHintPath) { plgx.IncludedReferencedAssemblies.Add( UrlUtil.ConvertSeparators(xnSub.InnerText, '/')); return; } } if (!str.EndsWith(".dll", StrUtil.CaseIgnoreCmp)) { str += ".dll"; } plgx.CompilerParameters.ReferencedAssemblies.Add(str); }
private static void ChangePathRelAbs(IOConnectionInfo ioc, bool bMakeAbsolute) { if (ioc == null) { Debug.Assert(false); return; } if (!ioc.IsLocalFile()) { return; } // Update path separators for current system if (!UrlUtil.IsUncPath(ioc.Path)) { ioc.Path = UrlUtil.ConvertSeparators(ioc.Path); } string strBase = WinUtil.GetExecutable(); bool bIsAbs = UrlUtil.IsAbsolutePath(ioc.Path); if (bMakeAbsolute && !bIsAbs) { ioc.Path = UrlUtil.MakeAbsolutePath(strBase, ioc.Path); } else if (!bMakeAbsolute && bIsAbs) { ioc.Path = UrlUtil.MakeRelativePath(strBase, ioc.Path); } }
/// <summary> /// Strips trailing separator, converts path separators and converts /// to full path /// </summary> private static string GetFullPath(string path) { if (string.IsNullOrWhiteSpace(path)) { return(string.Empty); } path = UrlUtil.ConvertSeparators(path); return(Path.GetFullPath(path)); }
private static void ReadImport(XmlNode xn, PlgxPluginInfo plgx) { if (plgx.ProjectType != PlgxProjectType.VisualBasic) { Debug.Assert(false); return; } XmlNode xnInc = xn.Attributes.GetNamedItem(XnnInclude); if ((xnInc == null) || string.IsNullOrEmpty(xnInc.Value)) { Debug.Assert(false); return; } plgx.VbImports.Add(UrlUtil.ConvertSeparators(xnInc.Value)); }
/// <summary> /// Extracts the file contents to destDir. /// </summary> /// <param name="contents">Contents to extract (from Files property).</param> /// <param name="destDir">Destination directory to store file.</param> public static void ExtractFile(byte[] contents, string destDir) { string path = null; string tempFile = UrlUtil.EnsureTerminatingSeparator(destDir, false) + UrlUtil.ConvertSeparators(path); string tempDir = UrlUtil.GetFileDirectory(tempFile, false, true); if (!Directory.Exists(tempDir)) { Directory.CreateDirectory(tempDir); } byte[] decompressedData = MemUtil.Decompress(contents); File.WriteAllBytes(tempFile, decompressedData); }
private static void CompileEmbeddedRes(PlgxPluginInfo plgx) { foreach (string strResSrc in plgx.EmbeddedResourceSources) { string strResFileName = plgx.BaseFileName + "." + UrlUtil.ConvertSeparators( UrlUtil.MakeRelativePath(plgx.CsprojFilePath, strResSrc), '.'); string strResFile = UrlUtil.GetFileDirectory(plgx.CsprojFilePath, true, true) + strResFileName; if (strResSrc.EndsWith(".resx", StrUtil.CaseIgnoreCmp)) { PrepareResXFile(strResSrc); string strRsrc = UrlUtil.StripExtension(strResFile) + ".resources"; ResXResourceReader r = new ResXResourceReader(strResSrc); ResourceWriter w = new ResourceWriter(strRsrc); r.BasePath = UrlUtil.GetFileDirectory(strResSrc, false, true); foreach (DictionaryEntry de in r) { w.AddResource((string)de.Key, de.Value); } w.Generate(); w.Close(); r.Close(); if (File.Exists(strRsrc)) { plgx.CompilerParameters.EmbeddedResources.Add(strRsrc); Program.TempFilesPool.Add(strRsrc); } } else { File.Copy(strResSrc, strResFile, true); plgx.CompilerParameters.EmbeddedResources.Add(strResFile); } } }
private static bool IsDirTraversal(string str) { if (string.IsNullOrEmpty(str)) { Debug.Assert(false); return(false); } Debug.Assert(UrlUtil.EnsureTerminatingSeparator(str, false) == str); string strCnc = UrlUtil.ConvertSeparators(str, '/'); if (strCnc.EndsWith("/../")) { return(true); } if (strCnc.EndsWith("/./")) { return(true); } return(false); }
private static void PrepareResXFile(string strFilePath) { if (!NativeLib.IsUnix()) { return; } string[] v = File.ReadAllLines(strFilePath, Encoding.UTF8); // Fix directory separators in ResX file; // Mono's ResXResourceReader doesn't convert them for (int i = 0; i < (v.Length - 1); ++i) { if ((v[i].IndexOf(@"<data") >= 0) && (v[i].IndexOf( @"System.Resources.ResXFileRef") >= 0) && (v[i + 1].IndexOf( @"<value>") >= 0)) { v[i + 1] = UrlUtil.ConvertSeparators(v[i + 1]); } } File.WriteAllLines(strFilePath, v, new UTF8Encoding(false)); }
private static void AddFile(BinaryWriter bw, string strRootDir, string strSourceFile) { if (strSourceFile.EndsWith(".suo", StrUtil.CaseIgnoreCmp)) { return; } MemoryStream msFile = new MemoryStream(); BinaryWriter bwFile = new BinaryWriter(msFile); strRootDir = UrlUtil.EnsureTerminatingSeparator(strRootDir, false); string strRel = UrlUtil.ConvertSeparators(UrlUtil.MakeRelativePath( strRootDir + "Sentinel.txt", strSourceFile), '/'); WriteObject(bwFile, PlgxfPath, StrUtil.Utf8.GetBytes(strRel)); byte[] pbData = (File.ReadAllBytes(strSourceFile) ?? MemUtil.EmptyByteArray); if (pbData.LongLength >= (long)(int.MaxValue / 2)) // Max 1 GB { throw new OutOfMemoryException(); } byte[] pbCompressed = MemUtil.Compress(pbData); WriteObject(bwFile, PlgxfData, pbCompressed); WriteObject(bwFile, PlgxfEOF, null); WriteObject(bw, PlgxFile, msFile.ToArray()); bwFile.Close(); msFile.Close(); if (!MemUtil.ArraysEqual(MemUtil.Decompress(pbCompressed), pbData)) { throw new InvalidOperationException(); } }
private static string Compile(string strTmpRoot, PlgxPluginInfo plgx, string strBuildPre, string strBuildPost) { if (strTmpRoot == null) { Debug.Assert(false); return(null); } RunBuildCommand(strBuildPre, UrlUtil.EnsureTerminatingSeparator( strTmpRoot, false), null); PlgxCsprojLoader.LoadDefault(strTmpRoot, plgx); List <string> vCustomRefs = new List <string>(); foreach (string strIncRefAsm in plgx.IncludedReferencedAssemblies) { string strSrcAsm = plgx.GetAbsPath(UrlUtil.ConvertSeparators( strIncRefAsm)); string strCached = PlgxCache.AddCacheFile(strSrcAsm, plgx); if (string.IsNullOrEmpty(strCached)) { throw new InvalidOperationException(); } vCustomRefs.Add(strCached); } CompilerParameters cp = plgx.CompilerParameters; cp.OutputAssembly = UrlUtil.EnsureTerminatingSeparator(strTmpRoot, false) + UrlUtil.GetFileName(PlgxCache.GetCacheFile(plgx, false, false)); cp.GenerateExecutable = false; cp.GenerateInMemory = false; cp.IncludeDebugInformation = false; cp.TreatWarningsAsErrors = false; cp.ReferencedAssemblies.Add(WinUtil.GetExecutable()); foreach (string strCustomRef in vCustomRefs) { cp.ReferencedAssemblies.Add(strCustomRef); } cp.CompilerOptions = "-define:" + GetDefines(); CompileEmbeddedRes(plgx); PrepareSourceFiles(plgx); string[] vCompilers; Version vClr = Environment.Version; int iClrMajor = vClr.Major, iClrMinor = vClr.Minor; if ((iClrMajor >= 5) || ((iClrMajor == 4) && (iClrMinor >= 5))) { vCompilers = new string[] { null, "v4.5", "v4", // Suggested in CodeDomProvider.CreateProvider doc "v4.0", // Suggested in community content of the above "v4.0.30319", // Deduced from file system "v3.5" }; } else if (iClrMajor == 4) // 4.0 { vCompilers = new string[] { null, "v4", // Suggested in CodeDomProvider.CreateProvider doc "v4.0", // Suggested in community content of the above "v4.0.30319", // Deduced from file system "v4.5", "v3.5" }; } else // <= 3.5 { vCompilers = new string[] { null, "v3.5", "v4", // Suggested in CodeDomProvider.CreateProvider doc "v4.0", // Suggested in community content of the above "v4.0.30319", // Deduced from file system "v4.5" }; } CompilerResults cr = null; StringBuilder sbCompilerLog = new StringBuilder(); bool bCompiled = false; for (int iCmp = 0; iCmp < vCompilers.Length; ++iCmp) { if (CompileAssembly(plgx, out cr, vCompilers[iCmp])) { bCompiled = true; break; } if (cr != null) { AppendCompilerResults(sbCompilerLog, vCompilers[iCmp], cr); } } if (!bCompiled) { if (Program.CommandLineArgs[AppDefs.CommandLineOptions.Debug] != null) { SaveCompilerResults(plgx, sbCompilerLog); } throw new InvalidOperationException(); } Program.TempFilesPool.Add(cr.PathToAssembly); Debug.Assert(cr.PathToAssembly == cp.OutputAssembly); string strCacheAsm = PlgxCache.AddCacheAssembly(cr.PathToAssembly, plgx); RunBuildCommand(strBuildPost, UrlUtil.EnsureTerminatingSeparator( strTmpRoot, false), UrlUtil.GetFileDirectory(strCacheAsm, true, false)); return(strCacheAsm); }
private static void ExtractFile(byte[] pbData, string strTmpRoot, PlgxPluginInfo plgx) { MemoryStream ms = new MemoryStream(pbData, false); BinaryReader br = new BinaryReader(ms); string strPath = null; byte[] pbContent = null; while (true) { KeyValuePair <ushort, byte[]> kvp = ReadObject(br); if (kvp.Key == PlgxfEOF) { break; } else if (kvp.Key == PlgxfPath) { strPath = StrUtil.Utf8.GetString(kvp.Value); } else if (kvp.Key == PlgxfData) { pbContent = kvp.Value; } else { Debug.Assert(false); } } br.Close(); ms.Close(); if (!string.IsNullOrEmpty(strPath) && (pbContent != null)) { string strTmpFile = UrlUtil.EnsureTerminatingSeparator(strTmpRoot, false) + UrlUtil.ConvertSeparators(strPath); string strTmpDir = UrlUtil.GetFileDirectory(strTmpFile, false, true); if (!Directory.Exists(strTmpDir)) { Directory.CreateDirectory(strTmpDir); } byte[] pbDecompressed = MemUtil.Decompress(pbContent); File.WriteAllBytes(strTmpFile, pbDecompressed); // Although the temporary directory will be deleted recursively // anyway, add the extracted file here manually, in order to // minimize left-over files in case the recursive deletion fails // due to locked / in-use files Program.TempFilesPool.Add(strTmpFile); if (plgx.LogStream != null) { using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider()) { byte[] pbMD5 = md5.ComputeHash(pbDecompressed); plgx.LogStream.Write(MemUtil.ByteArrayToHexString(pbMD5)); plgx.LogStream.WriteLine(" " + strPath); } } } else { Debug.Assert(false); } }
public string GetAbsPath(string strRelPath) { Debug.Assert(!string.IsNullOrEmpty(this.CsprojFilePath)); return(UrlUtil.MakeAbsolutePath(this.CsprojFilePath, UrlUtil.ConvertSeparators(strRelPath))); }
public static int Main(string[] args) { #if DEBUG // set args here for debugging args = new[] { "--build", @"..\..\..\SamplePlugin\", "../../../SamplePlugin/bin/Debug/", "-c=test.xml" }; #endif var selectedCommand = new Command(); string input = null; string output = null; string config = null; bool verbose = false; var options = new OptionSet() { { "b|build", "create plgx file", v => { if (v != null) { selectedCommand |= Command.Build; } } }, { "l|list", "list contents of plgx file", v => { if (v != null) { selectedCommand |= Command.List; } } }, { "e|extract", "extract file(s) from plgx", v => { if (v != null) { selectedCommand |= Command.Extract; } } }, { "p|package", "package plgx for distribution", v => { if (v != null) { selectedCommand |= Command.Package; } } }, { "h|help|?", "show usage", v => { if (v != null) { selectedCommand |= Command.Help; } } }, { "i|in|input=", "input file or directory", v => { input = v; } }, { "o|out|output=", "output file or directory", v => { output = v; } }, { "c|conf|config|configuration=", "configuration file", v => { config = v; } }, { "v|verbose", "configuration file", v => { verbose = v != null; } } }; try { var extras = options.Parse(args); if (input == null && extras.Count >= 1) { input = extras [0]; extras.RemoveAt(0); } if (output == null && extras.Count >= 1) { output = extras [0]; extras.RemoveAt(0); } if (config == null && extras.Count >= 1) { config = extras [0]; extras.RemoveAt(0); } input = GetFullPath(input); output = GetFullPath(output); config = GetFullPath(config); if (verbose) { Console.WriteLine("input: " + input); Console.WriteLine("output: " + output); Console.WriteLine("config: " + config); Console.WriteLine("extras: " + string.Join(", ", extras)); Console.WriteLine(); } } catch (Exception ex) { Console.WriteLine(ex.Message); selectedCommand = Command.Help; } if (selectedCommand == Command.Help || // build requires source dir (selectedCommand == Command.Build && (input == null || output == null)) || // build requires source dir (selectedCommand == Command.List && input == null) || // selected commands are mutually exclusive Math.Log((double)selectedCommand, 2) % 1 != 0) { Console.WriteLine(GetUsage()); return(1); } switch (selectedCommand) { #region Build Command case Command.Build: try { // populate common information for all plgx var plgx = new PlgxInfo(); plgx.Version = PlgxInfo.PlgxVersion1; plgx.FileUuid = new PwUuid(true); plgx.CreationTime = TimeUtil.SerializeUtc(DateTime.Now); var assm = Assembly.GetAssembly(typeof(Program)).GetName(); plgx.GeneratorName = assm.Name; plgx.GeneratorVersion = StrUtil.ParseVersion(assm.Version.ToString()); // read the optional config file to get the rest of the plgx header // information if (config != null) { var configDoc = new XmlDocument(); configDoc.Load(config); // mono xbuild adds namespaces for some reason, so we have to // strip them or else the serializer fails configDoc = XmlNamespaceStripper.StripNamespace(configDoc); var serializer = new XmlSerializer(typeof(PlgxConfiguration)); if (verbose) { #if DEBUG var writer = new XmlTextWriter(Console.OpenStandardOutput(), Encoding.UTF8); writer.Formatting = Formatting.Indented; configDoc.Save(writer); Console.WriteLine(); #endif serializer.UnknownAttribute += (sender, e) => Console.WriteLine("Unknown attribute: {0} at {1}:{2}", e.Attr.Name, e.LineNumber, e.LinePosition); serializer.UnknownElement += (sender, e) => Console.WriteLine("Unknown element: {0} at {1}:{2}", e.Element.Name, e.LineNumber, e.LinePosition); serializer.UnknownNode += (sender, e) => Console.WriteLine("Unknown node: {0} at {1}:{2}", e.Name, e.LineNumber, e.LinePosition); serializer.UnreferencedObject += (sender, e) => Console.WriteLine("Unreferenced object: {0}", e.UnreferencedId); } configDoc.Save(config); var plgxConfig = serializer.Deserialize(File.OpenRead(config)) as PlgxConfiguration; if (plgxConfig.Prerequisites != null) { if (!string.IsNullOrWhiteSpace(plgxConfig.Prerequisites.KeePassVersion)) { plgx.PrereqKP = StrUtil.ParseVersion(plgxConfig.Prerequisites.KeePassVersion); } if (!string.IsNullOrWhiteSpace(plgxConfig.Prerequisites.DotNetVersion)) { plgx.PrereqNet = StrUtil.ParseVersion(plgxConfig.Prerequisites.DotNetVersion); } if (plgxConfig.Prerequisites.OSSpecified) { plgx.PrereqOS = plgxConfig.Prerequisites.OS.GetValue(); } if (plgxConfig.Prerequisites.PointerSizeSpecified) { plgx.PrereqPtr = uint.Parse(plgxConfig.Prerequisites.PointerSize.GetValue()); } } if (plgxConfig.BuildCommands != null) { if (!string.IsNullOrWhiteSpace(plgxConfig.BuildCommands.PreBuild)) { plgx.BuildPre = plgxConfig.BuildCommands.PreBuild; } if (!string.IsNullOrWhiteSpace(plgxConfig.BuildCommands.PostBuild)) { plgx.BuildPost = plgxConfig.BuildCommands.PostBuild; } } } // read the .csproj file to find which files we need to include in // the plgx var projectFiles = Directory.GetFiles(input, "*.csproj"); if (projectFiles.Length != 1) { Console.WriteLine("Source directory must contain one and only on .csproj file"); return(1); } var project = new XmlDocument(); project.Load(projectFiles [0]); var assemblyNames = project.GetElementsByTagName("AssemblyName"); foreach (XmlNode assemblyName in assemblyNames) { plgx.BaseFileName = assemblyName.InnerText; } // include any files from <PlgxExtras> elements var plgxExtras = project.GetElementsByTagName("PlgxExtras"); foreach (XmlNode extra in plgxExtras) { foreach (XmlNode child in extra.ChildNodes) { if (child.LocalName == "Item") { var source = child.Attributes ["Source"]; var destination = child.Attributes ["Destination"]; if (source != null && !string.IsNullOrWhiteSpace(source.Value) && destination != null && !string.IsNullOrWhiteSpace(destination.Value)) { var sourcePath = UrlUtil.ConvertSeparators(source.Value); sourcePath = Path.Combine(input, sourcePath); sourcePath = Path.GetFullPath(sourcePath); plgx.AddFileFromDisk(sourcePath, destination.Value); } } } } // include all of the project files unless specifically excluded var itemGroups = project.GetElementsByTagName("ItemGroup"); foreach (XmlNode itemGroup in itemGroups) { // make copy of nodes except comments so that we can delete them inside of the for // loop if we need to var children = itemGroup.ChildNodes.Cast <XmlNode>().Where(child => child.NodeType != XmlNodeType.Comment).ToList(); foreach (XmlNode child in children) { if (child.LocalName == "Reference") { foreach (XmlNode childMetadata in child.ChildNodes) { var assemblyPath = Path.GetFullPath( Path.Combine(input, UrlUtil.ConvertSeparators(childMetadata.InnerText))); if (childMetadata.Name == "HintPath" && (child.Attributes ["Include"] == null || (!child.Attributes ["Include"].Value.StartsWith("KeePass,") && !child.Attributes ["Include"].Value.StartsWith("KeePass.exe")))) { if (!assemblyPath.StartsWith(input)) { // TODO - do we really want a fixed folder name here? childMetadata.InnerText = @"References\" + Path.GetFileName(assemblyPath); } plgx.AddFileFromDisk(assemblyPath, childMetadata.InnerText); } } continue; } // technically, the Include attribute is a semicolon separated // list and can include wildcards, but KeePass is only looking // for single files here, so we should not have to take this // into account var includeFile = child.Attributes ["Include"]; if (includeFile != null && !string.IsNullOrWhiteSpace(includeFile.Value)) { // skip "Include" files that are marked for exclusion from // the .plgx var exclude = false; foreach (XmlNode grandchild in child.ChildNodes) { if (grandchild.LocalName == "ExcludeFromPlgx") { exclude = true; break; } } if (exclude) { continue; } // copy all project items that have an Include attribute var includeFilePath = Path.GetFullPath( Path.Combine(input, UrlUtil.ConvertSeparators(includeFile.Value))); // <ProjectReference> elements get deleted. // If the <ProjectReference> element has <PlgxReference> child // elements, new <Reference> elements will be created using the // data from the <PlgxReference> element if (child.LocalName == "ProjectReference") { foreach (XmlNode projectItem in child.ChildNodes) { if (projectItem.LocalName == "PlgxReference") { var source = Path.GetFullPath(Path.Combine(input, UrlUtil.ConvertSeparators(projectItem.InnerText))); var destination = @"Reference\" + Path.GetFileName(source); var projectReference = project.CreateElement("Reference", child.NamespaceURI); projectReference.SetAttribute("Include", Path.GetFileName(destination)); var hintPath = project.CreateElement("HintPath", child.NamespaceURI); hintPath.InnerText = destination; projectReference.AppendChild(hintPath); child.ParentNode.AppendChild(projectReference); plgx.AddFileFromDisk(source, destination); } } child.ParentNode.RemoveChild(child); continue; } plgx.AddFileFromDisk(includeFilePath, includeFile.Value); } } } // write the in-memory project xml document (.csproj) to the plgx // instead of the file on disk since we may have changed it using (var stream = new MemoryStream()) { var writer = new XmlTextWriter(stream, Encoding.UTF8); writer.Formatting = Formatting.Indented; project.Save(writer); plgx.Files.Add(Path.GetFileName(projectFiles [0]), stream.ToArray()); #if DEBUG if (verbose) { Console.WriteLine(Encoding.UTF8.GetString( plgx.Files[Path.GetFileName(projectFiles[0])])); Console.WriteLine(); } #endif } if (verbose) { Console.WriteLine(plgx.ToString(true)); } plgx.WriteFile(output); } catch (Exception ex) { Console.WriteLine(ex.Message); return(1); } break; #endregion #region Extract Command case Command.Extract: Console.WriteLine("Not implemented."); return(1); #endregion #region List Command case Command.List: try { var plgx = PlgxInfo.ReadFile(File.OpenRead(input)); Console.WriteLine(plgx.ToString(true)); } catch (Exception ex) { Console.WriteLine(ex.Message); return(1); } break; #endregion #region Package Command case Command.Package: Console.WriteLine("Not implemented."); return(1); #endregion } return(0); }
private static string Compile(string strTmpRoot, PlgxPluginInfo plgx, string strBuildPre, string strBuildPost) { if (strTmpRoot == null) { Debug.Assert(false); return(null); } RunBuildCommand(strBuildPre, UrlUtil.EnsureTerminatingSeparator( strTmpRoot, false), null); PlgxCsprojLoader.LoadDefault(strTmpRoot, plgx); List <string> vCustomRefs = new List <string>(); foreach (string strIncRefAsm in plgx.IncludedReferencedAssemblies) { string strSrcAsm = plgx.GetAbsPath(UrlUtil.ConvertSeparators( strIncRefAsm)); string strCached = PlgxCache.AddCacheFile(strSrcAsm, plgx); if (string.IsNullOrEmpty(strCached)) { throw new InvalidOperationException(); } vCustomRefs.Add(strCached); } CompilerParameters cp = plgx.CompilerParameters; cp.OutputAssembly = UrlUtil.EnsureTerminatingSeparator(strTmpRoot, false) + UrlUtil.GetFileName(PlgxCache.GetCacheFile(plgx.FileUuid, false, false)); cp.GenerateExecutable = false; cp.GenerateInMemory = false; cp.IncludeDebugInformation = false; cp.TreatWarningsAsErrors = false; cp.ReferencedAssemblies.Add(WinUtil.GetExecutable()); foreach (string strCustomRef in vCustomRefs) { cp.ReferencedAssemblies.Add(strCustomRef); } CompileEmbeddedRes(plgx); PrepareSourceFiles(plgx); CompilerResults cr; if (!CompileAssembly(plgx, out cr, null)) { if (!CompileAssembly(plgx, out cr, "v3.5")) { throw new InvalidOperationException(); } } Program.TempFilesPool.Add(cr.PathToAssembly); Debug.Assert(cr.PathToAssembly == cp.OutputAssembly); string strCacheAsm = PlgxCache.AddCacheAssembly(cr.PathToAssembly, plgx); RunBuildCommand(strBuildPost, UrlUtil.EnsureTerminatingSeparator( strTmpRoot, false), UrlUtil.GetFileDirectory(strCacheAsm, true, false)); return(strCacheAsm); }
private void FinishOpenWithList() { bool bFoundEdge = false; List <OpenWithItem> lOrg = new List <OpenWithItem>(m_lOpenWith); foreach (OpenWithItem it in lOrg) { if (it.FilePathType != OwFilePathType.Executable) { continue; } string strPath = it.FilePath; string strPathL = strPath.ToLowerInvariant(); string strPathN = UrlUtil.ConvertSeparators(strPathL, '/'); string strFileL = UrlUtil.GetFileName(strPathL); string s = UrlUtil.StripExtension(strFileL); if (s == "iexplore") { // https://msdn.microsoft.com/en-us/library/hh826025.aspx AddAppVariant(it, KPRes.Private, "-private"); } else if (s == "msedge") { // The legacy Edge (EdgeHTML) doesn't register itself in the // 'StartMenuInternet' registry key, whereas the new one // (Chromium) does; so, the one that we found must be the // new Edge, which supports a command line option for the // private mode AddAppVariant(it, KPRes.Private, "--inprivate"); bFoundEdge = true; } else if ((s == "firefox") || (s == "palemoon")) { // The command line options -private and -private-window work // correctly with Firefox 49.0.1 (before, they did not); // https://wiki.mozilla.org/Firefox/CommandLineOptions // https://bugzilla.mozilla.org/show_bug.cgi?id=856839 // https://bugzilla.mozilla.org/show_bug.cgi?id=829180 AddAppVariant(it, KPRes.Private, "-private-window"); } else if ((s == "brave") || (s == "brave-browser") || (s == "chrome") || (s == "chromium") || (s == "chromium-browser") || (s == "google-chrome") || (s == "vivaldi")) { // https://www.chromium.org/developers/how-tos/run-chromium-with-flags // https://peter.sh/experiments/chromium-command-line-switches/ AddAppVariant(it, KPRes.Private, "--incognito"); } else if ((s == "opera") || ((s == "launcher") && // Folder "Opera", "Opera Beta", "Opera Developer", ... (strPathN.Contains("/opera/") || strPathN.Contains("/opera ")))) { // Doesn't work with Opera 34.0.2036.25: // AddAppVariant(it, KPRes.Private, "-newprivatetab"); // Doesn't work with Opera 36.0.2130.65: // AddAppVariant(it, KPRes.Private, "--incognito"); // Works with Opera >= 40.0.2308.81: AddAppVariant(it, KPRes.Private, "--private"); } else if (s == "epiphany") { AddAppVariant(it, KPRes.Private, "--incognito-mode"); } else if (s == "midori") { AddAppVariant(it, KPRes.Private, "--private"); } } if (!bFoundEdge) // Add the legacy Edge (EdgeHTML), if available { if (AppLocator.EdgeProtocolSupported) { AddAppByShellExpand("microsoft-edge:" + PlhTargetUri, "Microsoft Edge", AppLocator.EdgePath); } } m_lOpenWith.Sort(OpenWithItem.CompareByName); }