Readout mailroot-path for IIS 6 & 7 using c# - c#

I am trying to read out the mailroot-path for the Drop and Pickup folders for my SMTP Virtual Server Domain. I currently need this for domains created with IIS 6.0 but in the long run will also need my Application to be compatable with IIS 7. Can anybody give me some pointers on how to do this?
Thanks!
Further details:
What I have:
SMTP Virtual Server Domain with a local alias: mydomain
What I want:
The mailroot path (default: C:\inetpub\mailroot) where the Badmail, Drop, Pickup and Queue folders are to be found. If that is not possible, then at least the path to the Drop folder
Why do I want this?
E.g. using IIS 6.0, users can change the Drop directory to a user specific location. I therefore do not want to hardcode the path, but read it out using c#.
I have tried to find this information by:
Reading out virtual directories
Checking the iisWebSite Properties
SmtpDeliveryMethod.PickupDirecotryFomIis
System.Net.Mail.IisPickupDirectory.GetPickupDirectory() --> cannot find Method
Reading the smtp.PickupDirecotryLocation into a string --> returns an empty string
Would really appriciate any help/ideas!

I am not sure but this might help you. As both IIS6 and IIS7 have same kind of setup.
http://www.kbcafe.com/articles/HowTo.SMTP.CSharp.pdf

The information you are looking for is in IIS metabase. There are managed classes to access the metabase, but you can also just read file directly. Here's a sample code:
public static string GetSmtpPickupFolder()
{
var doc = new XmlDocument();
var fileName = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.Windows), #"System32\inetsrv\MetaBase.xml");
if (!File.Exists(fileName))
{
return null;
}
doc.Load(fileName);
var nsMgr = new XmlNamespaceManager(doc.NameTable);
nsMgr.AddNamespace("c", doc.DocumentElement.NamespaceURI);
var node = doc.SelectSingleNode(
"/c:configuration/c:MBProperty/c:IIsSmtpServer", nsMgr);
if (node == null)
{
return null;
}
var attr = node.Attributes["PickupDirectory"];
if (attr == null)
{
return null;
}
return attr.Value;
}
Make sure that you run it as 64 bit application, otherwise file system redirection will send you to c:\windows\SysWOW64 instead

Related

Directory exists is producing inconsistant results on desktop vs server

I have a C# program which checks if a specific directory exists.
It is simply doing:
Directory.Exists(path).
I tried other ways as well. Using DirectoryInfo and using AlphaFS
On my local machine, the path exists. When I run the same program on a server with the same credentials it doesn't exist.
I wonder if it is a group policy issue. But I am able to go up one level and see it.
\server\volume\share\sub directory - Doesn't exist remotely but on my desktop it does
\server\volume\share - Does exist both on my desktop and remote server
Update
I forgot to mention, that since I had access to my desktop, I got the ACL information.
None of the groups were able to translate.
I really just want to get this application to behave the same way is on the server and find out why it is behaving differently.
Update 2
These are physical servers.
My desktop is Liquid VDI
Below is the code:
var path = txtPath.Text;
using (var user = new Impersonation(fuserdomain, fc_user, fc_pass))
{
var alphaExists = Alphaleonis.Win32.Filesystem.Directory.Exists(path);
var alphaDIExists = new Alphaleonis.Win32.Filesystem.DirectoryInfo(path).Exists;
var SystemExists = System.IO.Directory.Exists(path);
var SystemDIExists = new System.IO.DirectoryInfo(path).Exists;
var AlphaHasFiles = false;
var AlphaDIHasFiles = false;
var SystemHasFiles = false;
var SystemDIHasFiles = false;
try
{
Directory.GetFiles(path);
AlphaHasFiles = true;
}
catch { }
try
{
new DirectoryInfo(path).GetFiles();
AlphaDIHasFiles = true;
}
catch { }
try
{
System.IO.Directory.GetFiles(path);
SystemHasFiles = true;
}
catch { }
try
{
new System.IO.DirectoryInfo(path).GetFiles();
SystemDIHasFiles = true;
}
catch { }
MessageBox.Show(string.Format("alphaExists: {0}\nalphaDIExists: {1}\nSystemExists: {2}\nSystemDIExists: {3}\nAlphaGetFiles: {4}\nAlphaDIGetFiles: {5}\nSystemGetFiles: {6}\nSystemDIGetFiles: {7}\n", alphaExists, alphaDIExists, SystemExists, SystemDIExists, AlphaHasFiles, AlphaDIHasFiles, SystemHasFiles, SystemDIHasFiles));
}
Update 3
Although I have workaround this issue; I am still not sure why I would have a difference between my desktop and server. Is there any tool that can help me see where the issue may be?
I've seen the same thing with File.Exists. I never found an answer and finally threw in the towel, I simply try to use it and catch the exception.
Robust code has to catch it anyway, all the test does is avoid trying if the file or directory is not there. (And the PITA that Visual Studio no longer as any way to ignore an exception on a certain line. No problem runtime, annoying in development.)
This is a complete shot in the dark, since we don't have any specific details to go on. e.g. Is the server you're talking about physically yours, or is it a cloud-based server service?
I'd guess that your machine is an older operating system than the server, and the folder that you're trying to access is one of those special folders that has become more locked down with more recent operating systems (particularly on server operating systems) like the "Program Files" folder. So even though the folder exists on both, the method works on your machine but not on the server, due to permissions.
Hope this helps.
As far as I can tell, the Impersonation class in your code is not part of the dot net framework. Googling finds a couple of implementations. Where does it come from and How confident are you that it actually works in your scenario?
For example, if you remove the Impersonation code, and actually run it as that user, does that make it work?
One other clarification... When you say
\server\volume\share
Do you mean this is a network location (e.g. a UNC location), so is the same network path you are trying to access from both machines? If so, this would open up new possibilities for problems like firewalls, etc... Is that location on either of the two machines that we know about from the question, or a different location?

How can i save files in folder within the IIS of outside of the Application folder in asp.net

In my web application, i have some files those are saving within application it's creating a folder for saving files but i need to save those file outside of the application and inside of IIS.how can i do this?
With in application Folder we are using below code
Server.MapPath(Path)
For Saving in IIS How can i Write?
Thank you
you need to create a virtual directory that points ti the folder outside.
Go to IIS right click on your website. click on Add Virtual directry from the menu.Give an alias for the directory select your desired folder and you are done. it will consider this outside folder as an internal folder and work the same way. check this link How to: Create and Configure Virtual Directories in IIS 7.0
Disclaimer: but you will have to do this after hosting to iis i.e publishing. while using visual studio in dev environment i.e debugging it will store in internal directories only
Edit: for creating virtual directories this is the code. I have not tested its validity.
static void CreateVDir(string metabasePath, string vDirName, string physicalPath)
{
// metabasePath is of the form "IIS://<servername>/<service>/<siteID>/Root[/<vdir>]"
// for example "IIS://localhost/W3SVC/1/Root"
// vDirName is of the form "<name>", for example, "MyNewVDir"
// physicalPath is of the form "<drive>:\<path>", for example,"C:\Inetpub\Wwwroot"
try
{
DirectoryEntry site = new DirectoryEntry(metabasePath);
string className = site.SchemaClassName.ToString();
if ((className.EndsWith("Server")) || (className.EndsWith("VirtualDir")))
{
DirectoryEntries vdirs = site.Children;
DirectoryEntry newVDir = vdirs.Add(vDirName, (className.Replace("Service", "VirtualDir")));
newVDir.Properties["Path"][0] = physicalPath;
newVDir.Properties["AccessScript"][0] = true;
// These properties are necessary for an application to be created.
newVDir.Properties["AppFriendlyName"][0] = vDirName;
newVDir.Properties["AppIsolated"][0] = "1";
newVDir.Properties["AppRoot"][0] = "/LM" + metabasePath.Substring(metabasePath.IndexOf("/", ("IIS://".Length)));
newVDir.CommitChanges();
}
else
}
catch (Exception ex)
{
}
}
Normally you can not create a folder outside the root path i.e. if you have your application in say C:\inetpub\testapp you can only create a folder inside testapp. This restriction is for security reason where a web server is not supposed to allow access to anything above root folder.
Moreover it's not recommended to write any folders/files in the root folder as writing to root folder cause appdomain to recycle after certain number of writes (default is 15) causing session loss. See my answer here.
However there is a workaround
Add a path of your server to web.config and then fetch it in your code.Use something like below in the appsettings section of web.config
<add key="logfilesPath" value="C:\inetpub\MyAppLogs" />
Create a folder of above path and add Users group to your folder and give that group full permission (read/write). (Adding permission is very important)
In your code you can fetch as below
string loggerPath = (ConfigurationManager.AppSettings["logfilesPath"]);
Hope this helps

How can I check if some IIS modules are present

I am working on an installer and am looking to programatically check in IIS (7 and above) to see if certain modules are present.
This will have to work on Windows server 2008 R2 and above.
I am looking for AppWarmupModule and DynamicCompressionModule.
Any info or suggestions would be much appreciated.
Load the contents of the file:
C:\Windows\System32\inetsrv\config\applicationHost.config
You can put it into a XML document and use xPath to find
<configuration><system.webServer><globalModules><add name="DynamicCompressionModule"
or string-parse or Regex the content otherwise to find out whether the strings you are looking at are present.
Solving similar problem for IIS10, but also for installation to remote servers. Direct reading xml file is not solution for me.
In my case, I need to find if AspNetCoreModuleV2 is installed or not, but this should works for every module.
Based on Microsoft docs I used approach below. It returns list of globalModules in applicationHost.config for selected server. (with using Microsoft.Web.Administration)
internal static ConfigurationElementCollection GetModules(string remoteServerName = null)
{
ServerManager mgr;
mgr = string.IsNullOrEmpty(remoteServerName) ? new ServerManager() : ServerManager.OpenRemote(remoteServerName);
Configuration config = mgr.GetApplicationHostConfiguration();
ConfigurationSection globalModulesSection = config.GetSection("system.webServer/globalModules");
ConfigurationElementCollection globalModulesCollection = globalModulesSection.GetCollection();
return globalModulesCollection;
}
And then simple enumerate:
ConfigurationElementCollection modules = GetModules();
foreach (var item in modules)
{
System.Diagnostics.Debug.WriteLine(item.Attributes["name"].Value);
// do whatever you want
}

Declaring per-user (roaming user) configuration settings for a plugin DLL?

How can I setup support for per (roaming) user configuration settings for a particular plugin .DLL loaded from another application?
I have a .DLL that is loaded as add-in/plugin from another application, and want to persist configuration settings particular for this one independently of the main application that loads it, based on machine, .dll (=executable), roaming user or user profile.
I have found the System.Configuration.ExeConfigurationFileMap class that looks likely to provide what I need, but I can't figure out how to setup the right paths specific for my (plugin) application.
What code I have so far is:
public class MyConfigurationSettings : ConfigurationSection
{
public static MyConfigurationSettings GetSection (ConfigurationUserLevel ConfigLevel)
{
string configFile = Assembly.GetAssembly(typeof(MyConfigurationSettings)).Location + ".config";
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileMap.ExeConfigFilename = configFile;
configFileMap.LocalUserConfigFilename = <localUserConfigFile>; // ??? What filename to place here and how to get it based on the current environment ???
configFileMap.RoamingUserConfigFilename = <roamingUserConfigFile>; // ???;
System.Configuration.Configuration Config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigLevel);
// ...
}
}
Can anyone point me into the right direction? The available documentation and search results are too confusing or insufficient for me to get this right. Sorry , if this seems to be a silly question, but my C# (.NET) skills are going to get rusty after 4+ years not using it for earning daily bread.
I also believe it's not primarily an issue about configuration settings management, but how to get paths for installation specific application instance configurations.
You set RoamingUserConfigFilename = RoamingName.config and put it under Roaming Profile:
%AppData%\[AppName]\[Vendor]\[CodedPath]\[Version]\RoamingName.config
Also you set LocalUserConfigFilename = LocalName.config and put it under Local Profile:
%LocalAppData%\[AppName]\[Vendor]\[CodedPath]\[Version]\LocalName.config
Now calling
ConfigurationManager.OpenMappedExeConfiguration(
exeMap,
ConfigurationUserLevel.PerUserRoamingAndLocal);
config will be read in the following order:
Source on MSDN blogs.
For samples search for User.config under c:\Users\[User]\AppData. Also see CP article.
Code Sample:
public static MyConfigurationSettings GetSection (ConfigurationUserLevel ConfigLevel)
{
try
{
string appDataPath = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string localDataPath = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
System.Configuration.ExeConfigurationFileMap exeMap = new ExeConfigurationFileMap();
exeMap.ExeConfigFilename = System.IO.Path.Combine(appDataPath, #"MyCompany\MyPlugin\Default.config");
exeMap.RoamingUserConfigFilename = System.IO.Path.Combine(appDataPath, #"MyCompany\MyPlugin\Roaming.config");
exeMap.LocalUserConfigFilename = System.IO.Path.Combine(localDataPath, #"MyCompany\MyPlugin\Local.config");
System.Configuration.Configuration Config = ConfigurationManager.OpenMappedExeConfiguration(exeMap,ConfigLevel);
return (MyConfigurationSettings)Config.GetSection("MyConfigurationSettings");
}
catch (Exception ex) {
// ...
}
return null; // or throw an appropriate exception
}
Generally load the configuration from the special folder where you put it in and put it into one of the roaming (i.e. non local) locations. The OS handles the rest as per roaming specifications.
The usage of ConfigSections is totally irrelevant unless there is a very very special need to use the config file for that. In any .NET project I have seen in the last 10 years this file was never used for user specific settings.

Directory.CreateDirectory access to path is denied?

I have server-client application, it's a file manager
my problem is when I go inside a folder which requires access control like system folders, it becomes to read-only, but I need to move/delete or create new folder, how can I get the permission to do that?
here's how I create a new folder at the server side
public void NewFolder(string path)
{
try
{
string name = #"\New Folder";
string current = name;
int i = 0;
while (Directory.Exists(path + current))
{
i++;
current = String.Format("{0} {1}", name, i);
}
Directory.CreateDirectory(path + current);
Explore(path); //this line is to refresh the items in the client side after creating the new folder
}
catch (Exception e)
{
sendInfo(e.Message, "error");
}
}
There are often directories on a drive that even a user with administrator privileges cannot access. A directory with a name like "HDDRecovery" is quite likely to be troublesome like this. Surely it contains sensitive data that helps the user recover from disk failure. Another directory that fits this category is "c:\system volume information", it contains restore point data.
An admin can change the permissions on folders like this. But of course that doesn't solve the real problem nor is it a wise thing to do. Your user can't and shouldn't. Be sure to write code that deals with permission problems like this, simply catch the IOExeption. Keep the user out of trouble by never showing a directory that has the Hidden or System attribute set. They are the "don't mess with me" attributes.
If you want to remove directory read-only attribute use this: http://social.msdn.microsoft.com/Forums/en/vblanguage/thread/cb75ea00-f9c1-41e5-ac8e-296c302827a4
If you want to access system folders you can run your program as local administrator.
I had a similar problem (asp.net MVC vs2017) with this code:
Directory.CreateDirectory("~/temp");
Here is my solution:
// Create path on your web server
System.IO.Directory.CreateDirectory(System.Web.HttpContext.Current.Server.MapPath("~/temp"));
I also ran into an issue similar to this, but I was able to manually navigate through Windows Explorer and create directories.
However, my web app, running in VS on my laptop, hosted through my local IIS and not the built-in IIS deal for VS, was triggering the Access Denied issue.
So when I was hitting the error in code, I drilled down to glean more data from the System.Environment object and found the user, which of course was the App Pool that my app was running under in IIS.
So I opened IIS and opened the Advanced Settings for the app pool in question and changed the Identity to run under Network Service. Click OK. "cmd -> iisreset" for good measure. Try the app again, and SUCCESS!!!!
I had the same issue when creating a directory. I used DirectorySecurity as shown below:
DirectorySecurity securityRules = new DirectorySecurity();
securityRules.AddAccessRule(new FileSystemAccessRule(#"Domain\AdminAccount1", FileSystemRights.Read, AccessControlType.Allow));
securityRules.AddAccessRule(new FileSystemAccessRule(#"Domain\YourAppAllowedGroup", FileSystemRights.FullControl, AccessControlType.Allow));
DirectoryInfo di = Directory.CreateDirectory(path + current, securityRules);
Also keep in mind about the security as explained by Hans Passant's answer.
Full details can be found on MSDN.
So the complete code:
public void NewFolder(string path)
{
try
{
string name = #"\New Folder";
string current = name;
int i = 0;
while (Directory.Exists(path + current))
{
i++;
current = String.Format("{0} {1}", name, i);
}
//Directory.CreateDirectory(path + current);
DirectorySecurity securityRules = new DirectorySecurity();
securityRules.AddAccessRule(new FileSystemAccessRule(#"Domain\AdminAccount1", FileSystemRights.Read, AccessControlType.Allow));
securityRules.AddAccessRule(new FileSystemAccessRule(#"Domain\YourAppAllowedGroup", FileSystemRights.FullControl, AccessControlType.Allow));
DirectoryInfo di = Directory.CreateDirectory(path + current, securityRules);
Explore(path); //this line is to refresh the items in the client side after creating the new folder
}
catch (Exception e)
{
sendInfo(e.Message, "error");
}
}
My suspicion is that when you are running the application in client/server mode, the server portion needs to be running as Administrator, in addition to possibly removing read-only or system flags, to be able to do what you want.
That said, I agree with #HansPassant- it sounds like what you are trying to do is ill-advised.
Solved:
Directory created on remote server using below code & setting.
Share folder and give the full permission rights also in Advance
setting in the folder.
DirectoryInfo di = Directory.CreateDirectory(#"\\191.168.01.01\Test\Test1");
Test is destination folder where to create new Test1 folder(directory)

Categories