Connecting to Public Folders using RDO & MAPI - c#

I am trying to connect to my companies Exchange 2003 server using RDO & MAPI which I've never done before. I have found a pretty good site that uses Outlook's Redemption (http://www.dimastr.com/redemption/home.htm) but with all of the examples on the site using VB.NET and me not being great at programming it's a bit difficult to get this working.
So far I have this code
static void ConnectToExchange()
{
object oItems;
//string outLookUser = "My Profile Name";
string outLookUser = "username#xxx.xxxx";
string ToEmailAddress = "username#xxxx.com";
string FromEmailAddress = "username#xxx.com";
string outLookServer = "xxservernamexx";
string sMessageBody =
"\n outLookUser: " + outLookUser +
"\n outLookServer: " + outLookServer +
"\n\n";
RDOSession Session = new RDOSession();
try
{
Session.LogonExchangeMailbox(outLookUser,outLookServer);
int mailboxCount = Session.Stores.Count;
string defaultStore = Session.Stores.DefaultStore.Name;
RDOFolder TestTaxCert = Session.GetFolderFromPath(#"\\Public Folders\All Public Folders\TestTaxCert");
}
catch (Exception ex)
{
Session = null;
//System.Web.Mail.SmtpMail.Send(ToEmailAddress, FromEmailAddress, "Error", sMessageBody + " " + ex.Message);
}
finally
{
if ((Session != null))
{
if (Session.LoggedOn)
{
Session.Logoff();
}
}
}
}
}
My problem is that once the program hits the Session.LogonExchangeMailbox(outLookUser,outLookServer); line, a prompt appears asking for my credentials (username, domain, password) and no matter what information I fed the prompt it denied permission.
SO if someone can help me with that and then also with connecting to the public folders...that'd be greaaat

Make sure your code is running as the domain user specified in the call to LogonExchangeMailbox.
Did you really mean 2003, or is it Exchange 2013?

Related

Dynamics365/O365 login for WinForms Application active MFA

I am working on an application that connects to Dynamics 365 and creates contacts there, for example.
My question now is how to configure the login.
For testing, multi-factor authentication is disabled and it works so far. However, in the production system, MFA will be active.
I want to avoid users using an app password.
Is there a way to pop up the standard O365 login popup window from within a WinForms application?
Unfortunately I couldn't find anything about it.
What would make the most sense here?
Thats my code so far:
public CrmServiceClient connect_crm()
{
string ConnectionString = "AuthType = OAuth; " +
"Username= " + textBox1.Text + ";" +
"Password=" + textBox2.Text + ";" +
"Url=https://company.dynamics.com;" +
"RedirectUri=app://;" +
"AppId=XXXXXXXX-XXXXX-XXXX-XXXX-XXXXXXXXXXXXX;" +
"LoginPrompt=Auto";
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
svc = new CrmServiceClient(ConnectionString);
try
{
if (svc != null && svc.IsReady)
{
label4.BackColor = Color.LightGreen;
label4.Text = "Connected";
CreateContact2(svc);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return svc;
}
I've got the solution!
You need to use the client secret from the app, you registered in azure.
And you have to create an application user in dynamics. (via power platform admin ceter)
See here: https://softchief.com/2021/08/03/connect-dynamics-365-from-console-c-application-in-mfa-enabled-access-using-client-secret-and-azure-client-id/
public CrmServiceClient connect_crm()
{
string ConnectionString = "AuthType = ClientSecret; " +
"Url=https://company.dynamics.com;" +
"ClientId=XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX;" + //AppID
"ClientSecret=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; //ClientKey
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
svc = new CrmServiceClient(ConnectionString);
try
{
if (svc != null && svc.IsReady)
{
label4.BackColor = Color.LightGreen;
label4.Text = "Connected!";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return svc;
}

Get-MsolUser command error from C#

I am trying to execute a piece of Azure script to check if the user object is synced from on-prem AD to Azure as below.
username follows the pattern of a UPN. example: john.Smith#ed.com
//Check Azure to see if user is synced to office 365
private static bool IsAccountSyncedToOffice365(string username)
{
StringBuilder cmd = CreateAzureConnectScript();
//cmd.AppendLine("Get-MsolUser -UserPrincipalName " + username + " -ErrorAction SilentlyContinue");
cmd.AppendLine("$global:res = \"false\"");
cmd.AppendLine("$global:user = \"false\"");
cmd.AppendLine("try{ if(($global:user=Get-MsolUser -UserPrincipalName " + username + " -ErrorAction Stop).ImmutableId -ne $null) { $global:res = \"true\"} } Catch { $global:errorMessage = $_.Exception.Message}");
try
{
Collection<PSObject> results;
string output, error, errorMessageAzureCnn = "";
do
{
results = null;
output = "";
error = "";
var rs = CreateAzureRunspace();
var pipe = rs.CreatePipeline();
pipe.Commands.AddScript(cmd.ToString());
results = pipe.Invoke();
output = (rs.SessionStateProxy.PSVariable.GetValue("res")) != null ? rs.SessionStateProxy.PSVariable.GetValue("res").ToString() : "false";
error = (rs.SessionStateProxy.PSVariable.GetValue("errorMessage")) != null ? rs.SessionStateProxy.PSVariable.GetValue("errorMessage").ToString() : "null";
errorMessageAzureCnn = (rs.SessionStateProxy.PSVariable.GetValue("errorMessageAzureCnn")) != null ? rs.SessionStateProxy.PSVariable.GetValue("errorMessageAzureCnn").ToString() : "null";
ExceptionManager.Publish(new Exception("LOG: Queried Azure at:" + DateTime.Now + " for user:" + username + " Result: " + output + " Error: " + error + " errorMessageAzureCnn: " + errorMessageAzureCnn));
Thread.Sleep(60000); //sleep for 60 seconds
pipe.Dispose();
rs.Close();
rs.Dispose();
} while (output.Trim().ToLower() != "true");
ExceptionManager.Publish(new Exception("LOG: " + username + " is found synced to Azure at: " + DateTime.Now));
cmd.Clear();
return true;
}
catch (Exception ex)
{
ExceptionManager.Publish(new Exception("Error checking Azure to see if the user is synced to office 365 or not.. " + ex.Message));
throw ex;
}
}
private static StringBuilder CreateAzureConnectScript()
{
StringBuilder ss = new StringBuilder();
MSCredential cred = new MSCredential();
var username = cred.username;
var pwd = cred.password;
try
{
ss.AppendLine("try {");
ss.AppendLine("$password = ConvertTo-SecureString \"" + pwd + "\" -AsPlainText –Force");
ss.AppendLine("$credential = New-Object System.Management.Automation.PsCredential(\"" + username + "\",$password)");
ss.AppendLine("$cred = Get-Credential -cred $credential");
ss.AppendLine("Import-Module MSOnline");
ss.AppendLine("Start-Sleep -s 10");
ss.AppendLine("Connect-Msolservice -cred $cred");
ss.AppendLine("} Catch { $global:errorMessageAzureCnn = $_.Exception.Message }");
//ExceptionManager.Publish(new Exception("LOG:pwd: " + pwd + " uname:" + username));
return ss;
}
catch (Exception ex)
{
ExceptionManager.Publish(new Exception("Error enabling the remote mailbox.. " + ex.Message));
throw ex;
}
}
While the script executes successfully through Powershell Window on the same server having got all the latest versions of the modules installed. When trying to execute the same command from C# code it throws the below exception collected from the powershell exception handling $global:errorMessage = $_.Exception.Message.
Show Details Exception (0): [] LOG: Queried Azure at:7/30/2015 12:00:55 PM for user:testuser0385#xxx.com Result: false Error: You must call the Connect-MsolService cmdlet before calling any other cmdlets. errorMessageAzureCnn: null
Worth mentioning that I have got the same code as below working in one server but it is throwing the below error on a production server (Windows Server 2008 R2 Datacenter) and only via the code it is happening. via the powershell window it works perfectly fine.
Good to know your thoughts about what looks wrong or needed to be looked into.
Thanks!
It suggests that the sign in is failing, but you're pushing on with the Get-MsolUser anyway. The Connect-MsolService cmdlet fails if it cannot communicate to the Microsoft Online Services Sign-In Assistant. Ref: https://community.office365.com/en-us/f/156/t/252201
Has the production server got all the pre-requisites installed: Microsoft Online Services Sign-In Assistant and .NET 3.5? We had a problem in production (Azure PAAS) where the guest OS image was automatically updated and was missing .NET 3.5, which broke our Azure AD PowerShell processes.

Unauthoriezed Access Exception

i have a cloud database server like application on my computer that i'm hosting my game on. However, every time an user tries to save data i get an UnauthorizedAccessException.
Im running it by admin and i dont have any specias right in my folder so i have no idea what's the problem.
Here's my code:
public const string root = "D:/DATABASE/";
public static void WriteData(string playername, string type, string data)
{
if (!Directory.Exists("D:/DATABASE/" + playername))
{
Directory.CreateDirectory("D:/DATABASE/" + playername);
Directory.CreateDirectory("D:/DATABASE/" + playername + "/weapons");
}
if (type != "Weapon")
{
using (StreamWriter sw = new StreamWriter("D:/DATABASE/" + playername + "/" + type + ".sav"))
{
sw.WriteLine(data);
}
}
else
{
string[] dat = data.Split('%');
using (StreamWriter sw = new StreamWriter("D:/DATABASE/" + playername + "/weapons/" + dat[0] + ".gfa"))
{
string[] lines = dat[1].Split('#');
foreach (string cline in lines)
{
sw.WriteLine(cline);
}
}
}
}
public static string ReadLoadout(string playername)
{
string output = "";
string[] items = new string[2];
using (StreamReader sr = new StreamReader(root + playername + "/loadout.gfl"))
{
items[0] = sr.ReadLine();
items[1] = sr.ReadLine();
}
int c = 0;
foreach (string citem in items)
{
if (c > 0) output += "$";
output += citem + "%" + GetCompressedWeaponFile(playername, citem);
c++;
}
return output;
}
public static string GetCompressedWeaponFile(string playerName, string weaponName)
{
string output = "";
using (StreamReader sr = new StreamReader(root + playerName + "/weapons/" + weaponName))
{
string line = " ";
int c = 0;
while (line != null)
{
line = sr.ReadLine();
if (line != null)
{
if (c > 0) output += "#";
output += line;
}
c++;
}
}
return output;
}
public static void RegisterNewUser(string username, string password, string email)
{
string udir = root + username;
Directory.CreateDirectory(udir);
Directory.CreateDirectory(udir + "/weapons");
Directory.CreateDirectory(udir + "/loadouts");
File.WriteAllText(udir + "/password.sav", password);
File.WriteAllText(udir + "/level.sav", "1");
File.WriteAllText(udir + "/money.sav", "1000");
File.WriteAllText(udir + "/email.sav", email);
File.WriteAllText(udir + "/loadout.gfl", "");
using (StreamWriter sw = new StreamWriter(root + "emails.txt", true))
{
sw.WriteLine(email);
}
Email.Send(email, "New Account Registration", string.Format(mailTemplate, username, password));
}
public static void EditLoadout(string username, string items)
{
File.WriteAllLines(root + username + "/loadout.gfl",items.Split('#'));
}
It is difficult to provide specific help without more information. Here are a few of troubleshooting suggestions:
1) Try running your code on a different machine. Specifically your development computer. Do you still have the same error? If not, then there is indeed a permission problem.
2) Have you tried checking the stack trace of the exception?
When you run the application on your own computer, try using the IDE to display the exception. Yes, the problem may ultimately be in a low-level class, but you should be able to break on the error and go back in the call stack to see which method in your code is actually throwing the error.
3) Check the actual exception, even for a system-level exception.
Chances are, if you are able to debug this in the IDE, that you will see property information that will give you a hint. Is it in a directory method or a file write method? Check additional properties. Somewhere it might give you the text of the path (assuming it's a file issue) that it failed on that that could help narrow things down too.
4) Add Exception handling to your code
This is a good rule of thumb, and you should really do this anyway to make a stronger program. Regardless of who's method you are calling (yours, someone else's, or a system method) you need to determine where it should be handled.
For example, in your code, in the RegisterNewUser() method, consider something like:
public static void RegisterNewUser(string username, string password, string email)
{
try
{
string udir = root + username;
Directory.CreateDirectory(udir);
Directory.CreateDirectory(udir + "/weapons");
Directory.CreateDirectory(udir + "/loadouts");
File.WriteAllText(udir + "/password.sav", password);
File.WriteAllText(udir + "/level.sav", "1");
File.WriteAllText(udir + "/money.sav", "1000");
File.WriteAllText(udir + "/email.sav", email);
File.WriteAllText(udir + "/loadout.gfl", "");
using (StreamWriter sw = new StreamWriter(root + "emails.txt", true))
{
sw.WriteLine(email);
}
Email.Send(email, "New Account Registration", string.Format(mailTemplate, username, password));
}
catch (Exception ex)
{
// Create a method to display or log the exception, with it's own error handler
LogAndDisplayExceptions(ex);
// Send the user a message that we failed to add them. Put this in it's own try-catch block
// ideally, for readability, in it's own method.
try
{
Email.Send(email, "Failed to register", "An error occurred while trying to add your account.");
}
catch (Exception exNested)
{
LogAndDisplayExceptions(exNested);
}
}
}
5) Add a "crash-and-burn" exception handler to "main"
In the method that is your "top method" (it's hard to tell in the snippet you provided since there are few methods that would attempt to write to the disk) you could wrap your code in a try - catch block and print the exception or write it to disk.
If you have having trouble writing the exception to disk, I would suggest creating an error file first, make sure that the user account that is running the program can write to it, and then in the catch block open the file for APPEND. This should make it easier to get to the error text.
6) When all else fails, use the Debug class or Console class to write the traditional "I made it to line x."
While this will not solve your problem, it should help you get more information that will provide more insight into where your code is causing an error.

Registry values not getting stored in database

I am reading Registry key values and storing them in string variables, and passing these values as parameters in NewReg method to store them into database, but i am getting this error message you must use an updateable query and therefore this message user not added in database,
where I am going wrong? How I can achieve this ?
public partial class LoginForm : Form
{
DBHandling db = new DBHandling();
public LoginForm()
{
try
{
RegistryKey key = Registry.ClassesRoot;
RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(#"Software\StudentExm");
string usrname = registryKey.GetValue("UserName").ToString();
string password = registryKey.GetValue("Password").ToString();
string emailId = registryKey.GetValue("EmailID").ToString();
string contctno = registryKey.GetValue("ContactNo").ToString();
string regDate = registryKey.GetValue("RegDate").ToString();
registryKey.Close();
if (NewReg(usrname, password, emailId, contctno, regDate))
{
MessageBox.Show("User Name: " + usrname + "\n" +
"Password: " + password + "\n" +
"Email ID: " + emailId + "\n" +
"Contact No: " + contctno + "\n" +
"Reg Date: " + regDate);
}
else
{
MessageBox.Show("User not added in the database.");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
}
NewReg method in DBHandling Class
public bool NewReg(string usrName, string passwrd, string emailID, string contactNo,string regdat)
{
bool flag = false;
try
{
//string sql = "INSERT INTO test(nametest,pass,email,contact,regdate) Values('" + usrName + "','" + passwrd + "','" + emailID + "','" + contactNo + "','" + System.DateTime.Now + "')";
string sql = "INSERT INTO UserInfo([UserName],[Password],[email],[contact],[regdate]) Values(#UserInfo,#Password,#email,#contact,#regdate)";
cmd = new OleDbCommand(sql, acccon);
cmd.Parameters.AddRange(new[] {
new OleDbParameter("#UserName", usrName),
new OleDbParameter("#Password", passwrd),
new OleDbParameter("#EmailID", emailID),
new OleDbParameter("#ContactNo", contactNo),
new OleDbParameter("#RegDate", regdat)
});
cmd.ExecuteNonQuery();
flag = true;
}
catch (Exception err)
{
MessageBox.Show(err.Message.ToString());
}
return flag;
}
Thanks in advance for any help
I Guess the Problem Because of this Line:
string usrname = registryKey.GetValue("UserName") + Environment.NewLine;
The Code U can change like this and Try.
string usrname = registryKey.GetValue("UserName").ToString();
Were as you are assigning the Value with + Environment.NewLine;. It should not be.,
That is why you must use an updateable query error message.
And this is Your Custom Message user not added in database. Because of Condition Fails its getting displayed.
Hope it helps., :)
As I said in my comment, it's a file permissions issue.
This error USUALLY occurs when you attempts to perform an UPDATE or
some other action that alters the information in the database.
This error occurs because dataobjects what ever ado or ADO.NET is
unable to write to the Microsoft Access database due to insufficient
write/modify permissions, which can be easily fixed by granting
read/write to IUSR_ and IWAM_.
IUSR_<machineName> and IWAM_<machineName> come into focus when an anonymous web user needs to modify the Microsoft Access
database & while doing so Jet creates an .ldb file to handle database
locking. If the above mentioned 2 users don't have the necessary
permissions, the lock file isn't created & ultimately the Microsoft
Access database can't be modified..
FIX:
To fix this problem, right click on the folder which holds the
Microsoft Access database & choose properties. Use the Security tab in
Explorer to adjust the properties for this folder so that the Internet
Guest account has the correct writable permissions.
Also make sure that the MDB file itself isn't marked as read-only, and
that you don't have the MDB file open; particularly in exclusive mode;
while trying to reach the Microsoft Access database from ASP
source: http://forums.asp.net/t/154273.aspx/9/10
Try like this
string keyName = #"HKEY_CURRENT_USER\...\Node[Name]";
string valueName = "Value"; // Value in registry Ex. RegDate
object Obj = Registry.GetValue(keyName, valueName, "Exist");
if (Obj == null)
{
}
else
{
Do your Code ....
}

MAPI_E_FAILONEPROVIDER from Redemption

I'm trying to user Redemption to update a user's Outlook contacts. The user I'm affecting is passed in the exchangeUser, call him "Target User".
This code works when I run it logged in as myself:
public OutlookFolders(string outlookRootFolder, string exchangeUser, string mailServer)
{
var session = new RDOSessionClass();
session.LogonExchangeMailbox(exchangeUser, mailServer);
session.Stores.FindExchangePublicFoldersStore();
var store = session.GetSharedMailbox(exchangeUser);
//...
}
I tried to log in as a 3rd user "Test User" who is not me and is not "Target User". My program brings up a password prompt at runtime when it gets to FindExchangePublicFoldersStore, and if I don't fill in my credentials it fails with the error:
System.Runtime.InteropServices.COMException (0x8004011D): Error in
IMAPISession.OpenMsgStore(pbExchangeProviderPrimaryUserGuid):
MAPI_E_FAILONEPROVIDER
ulVersion: 0
Error: Microsoft Exchange is not available. Either there are network
problems or the Exchange computer is down for maintenance.
Component: Microsoft Exchange Information Store
ulLowLevelError: 2147746069
ulContext: 1318
I tried giving "Test User" owner permission on "Target User's" Mailbox and Contacts folder. Doesn't seem to make a difference. What other permissions need to be set for this to work?
The rule of thumb is to run your code as a user who can access the mailboxes in question, call LogonExchangeMailbox for the current user, then open other users' mailboxes using GetSharedMailbox.
Here's the code for Dmitry's answer.
It also uses a function from Milan's blog.
public OutlookFolders(string exchangeUser, string mailServer)
{
var session = new RDOSessionClass();
var userFullName = GetFullName("DOMAIN-NT\\" + Environment.UserName);
session.LogonExchangeMailbox(userFullName, mailServer);
session.Stores.FindExchangePublicFoldersStore();
var store = session.GetSharedMailbox(exchangeUser);
rootFolder = store.GetDefaultFolder((rdoDefaultFolders)OlDefaultFolders.olFolderContacts);
}
public static string GetFullName(string strLogin)
{
string str = "";
string strDomain;
string strName;
// Parse the string to check if domain name is present.
int idx = strLogin.IndexOf('\\');
if (idx == -1)
{
idx = strLogin.IndexOf('#');
}
if (idx != -1)
{
strDomain = strLogin.Substring(0, idx);
strName = strLogin.Substring(idx + 1);
}
else
{
strDomain = Environment.MachineName;
strName = strLogin;
}
DirectoryEntry obDirEntry = null;
try
{
obDirEntry = new DirectoryEntry("WinNT://" + strDomain + "/" + strName);
PropertyCollection coll = obDirEntry.Properties;
object obVal = coll["FullName"].Value;
str = obVal.ToString();
}
catch (System.Exception ex)
{
str = ex.Message;
}
return str;
}

Categories

Resources