I'm having trouble finding out how to get the master occurrence for a series of private appointments using the Exchange Web Service Managed API for C#.
When I retrieve the list of appointments using FindAppointments on the ExchangeService, I manage to get the occurrences within the search criteria. However, when I then try to get the recurring master appointment using Appointment.BindToRecurringMaster where the id is the id of the private occurrence, I get the error "The specified object was not found in the store., Item not found."
Is there any other way for me to retrieve the master occurrence for a private occurrence?
In my example below, I'm authenticating using a service account which as the Reviewer permissions on the target mailbox calendar.
var exchangeService = new ExchangeService();
exchangeService.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
exchangeService.Credentials = new WebCredentials("service.user#organization.com", "password", "domain");
const string email = "other.user#organization.com";
// The following successfully retrieve all appointments including the private occurrence.
var appointments = exchangeService.FindAppointments(
new FolderId(WellKnownFolderName.Calendar, new Mailbox(email)),
new CalendarView(DateTime.UtcNow, DateTime.UtcNow.AddDays(1)));
const string id = "AAMkA..."; // Id of the private occurrence.
// The following fails saying "The specified object was not found in the store., Item not found."
var appointment = Appointment.BindToRecurringMaster(exchangeService, id);
Any help is appreciated.
What your describing is the expected behaviour for private Items if you haven't been delegated access to view private Items via the Delegate Operation. So you would need to either give the Service Account Delegate rights on the Mailbox using the Delegate operations https://msdn.microsoft.com/en-us/library/office/dn641959(v=exchg.150).aspx and set ViewPrivateItems to true. Or give the Service Account FullAccess to the Mailbox or use EWS Impersonation.
Related
The documentation shows how to make a transfer from one wallet to another. In one account.
// Initialize the rpc client and a wallet
var rpcClient = ClientFactory.GetClient(Cluster.TestNet);
var wallet = new Wallet();
// Get the source account
var fromAccount = wallet.GetAccount(0);
// Get the destination account
var toAccount = wallet.GetAccount(1);
// Get a recent block hash to include in the transaction
var blockHash = rpcClient.GetRecentBlockHash();
// Initialize a transaction builder and chain as many instructions as you want before building the message
var tx = new TransactionBuilder().
SetRecentBlockHash(blockHash.Result.Value.Blockhash).
SetFeePayer(fromAccount).
AddInstruction(MemoProgram.NewMemo(fromAccount, "Hello from Sol.Net :)")).
AddInstruction(SystemProgram.Transfer(fromAccount, toAccount.GetPublicKey, 100000)).
Build(fromAccount);
var firstSig = rpcClient.SendTransaction(tx);
How to make a transfer to another account?
Do I need to know the private key of the account to which I will transfer?
Your example has all of the pieces you need. To transfer to another account, you need to create a transaction (using TransactionBuilder), and specifically add a SystemProgram.Transfer instruction to your transaction. Also, in your example, you're sending from fromAccount, which has the private key, to toAccount.PublicKey. You're using the PublicKey of toAccount, so no need for the private key of the recipient.
Note that the example appears to be incorrect, so you should base your code from this example instead: https://github.com/bmresearch/Solnet/blob/8369ac166ed90a7e6b07060178ed70745bd97bc3/src/Solnet.Examples/TransactionBuilderExample.cs#L22
We're using EWS to integrate our CRM with Exchange Online 2010SP2. One of the tasks: provide one calendar for Sales persons with the next rule: every Sales can see all appointments in Scheduler, but can open and see details (body) only his/her own appointment. Appointment are being placed by CRM in response of certain business events. We tried to use impersonate and Sensitivity.Private property. Appointment is being placed with impersonated user name, but it can't be opened by that user neither through OWA nor via Outlook. So it's placed as private appointment of 'master' user (user who created shared calendar and his credentials is used for service connection), he can open it in Outlook or OWA. EWSEditor shows appointment's LastModifiedUser - correct impersonated username (not master's). In Fiddler we can see 'success' Response on appointment placement request (under impersonated user). In OutlookSpy, we can see that appointments PR_SENDER_NAME_W, PR_SENT_REPRESENTING_NAME_W properties shows 'master's' username. We stuck.
Impersonated user has 'owner' rights upon that shared calendar.
If Impersonating doesn't resolve this issue, can Delegate technique do that?
Thanks in advance for any help.
static void Main(string[] args)
{
ExchangeService services = new
ExchangeService(ExchangeVersion.Exchange2010_SP2);
services.Credentials = new WebCredentials("master#exchserver.com",
"MasterPwd");
services.Url = new Uri("https://someserver.com/ews/exchange.asmx");
FolderId rfRootFolderid = new FolderId(WellKnownFolderName.Calendar);
FolderView fvFolderView = new FolderView(100);
DateTime startDate = DateTime.Now.AddDays(1);
DateTime endDate;
string SalesCalendarId = "AAMkADVlMGVjZWVkLT....AADo8XAAA=";
CalendarFolder folder = CalendarFolder.Bind(services, new
FolderId(SalesCalendarId));
TimeSpan ts = new TimeSpan(10, 00, 0);
startDate = startDate.Date + ts;
endDate = startDate.AddMinutes(60);
services.HttpHeaders.Add("X-AnchorMailbox","impersonateduser#exchserver.com");
appointment.Subject = "from Test";
appointment.Body = "Test";
appointment.Start = startDate;
appointment.End = appointment.Start.AddMinutes(30);
appointment.ReminderDueBy = appointment.Start.AddHours(1);
appointment.Sensitivity = Sensitivity.Private;
ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "impersonateduser#exchserver.com");
appointment.Save(SalesCalendarId, SendInvitationsMode.SendToNone);
}
You could use impersonated user to obtain ‘owner’ permissions.
Please include the code below before services.Credentials = new WebCredentials("master#exchserver.com", "MasterPwd")
services.PreAuthenticate = true;
you need to make sure that we have required permissions for EWS Impersonation as per the article mentioned below:
Configuring Exchange Impersonation in Exchange 2010
Using Exchange Impersonation in Exchange 2010
You could reference the following link:
Get all calendar events for a specific user
For some accounts of my company I can't get the email signature. I use the following code to retrieve signatures:
OWAConfig = UserConfiguration.Bind(_service, "OWA.UserOptions", WellKnownFolderName.Root, UserConfigurationProperties.Dictionary);
Normally it works fine but for some accounts, it throws :
The specified object was not found in the store., The configuration object was not found. Name = OWA.UserOptions.
That error will occur for accounts where they have never logged onto OWA before.
The UserConfiguration object won't exist for those accounts and you won't be able to bind to it in code.
You can check for the UserConfiguration object first and then create it, if it doesn't exist:
SearchFilter sf = new SearchFilter.IsEqualTo(ItemSchema.ItemClass, "IPM.Configuration.OWA.UserOptions");
ItemView iv = new ItemView(1);
iv.Traversal = ItemTraversal.Associated;
FindItemsResults<Item> fiResults = Root.FindItems(sf, iv);
UserConfiguration OWAConfig =null;
if (fiResults.Items.Count == 0)
{
OWAConfig = new UserConfiguration(service);
OWAConfig.Save("OWA.UserOptions", Root.ParentFolderId);
}
Note: Creating a new UserConfiguration object may cause issues as it doesn't set the first time OWA logon settings, Regional settings etc.
You will need to test the affected accounts for this and rectify them accordingly.
Im trying to write a simple sample program which checks for any new mail on an Exchange 2010 server. As far as I can tell, the code I've got should work fine.
I setup the service as follows:
ExchangeService service = new ExchangeService();
service.Credentials = new WebCredentials("user#domain.co.uk", "password");
service.Url = new Uri("https://address/owa");
Upon executing the following code:
int unreadMail = 0;
// Add a search filter that searches on the body or subject.
List<SearchFilter> searchFilterCollection = new List<SearchFilter>();
searchFilterCollection.Add(new SearchFilter.ContainsSubstring(ItemSchema.Subject, "Defense"));
// Create the search filter.
SearchFilter searchFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.Or, searchFilterCollection.ToArray());
// Create a view with a page size of 50.
ItemView view = new ItemView(50);
// Identify the Subject and DateTimeReceived properties to return.
// Indicate that the base property will be the item identifier
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject, ItemSchema.DateTimeReceived);
// Order the search results by the DateTimeReceived in descending order.
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
// Set the traversal to shallow. (Shallow is the default option; other options are Associated and SoftDeleted.)
view.Traversal = ItemTraversal.Shallow;
// Send the request to search the Inbox and get the results.
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.Inbox, searchFilter, view);
// Process each item.
foreach (Item myItem in findResults.Items)
{
if (myItem is EmailMessage)
{
if (myItem.IsNew) unreadMail++;
}
}
I get this error (on the FindItemResults line):
'>' is an unexpected token. The expected token is '"' or '''. Line 1, position 63.
This appears to be an error in the XML the API is actually generating, I've tried a few different lots of code (all along the same lines) and not found anything that works.
Any ideas? At a bit of a loss when it comes directly from the API!
Cheers, Daniel.
Nevermind, fixed it - needed to point my service to:
https://servername/ews/Exchange.asmx
and provide regular domain login details such as "username", "password" in order to connect!
This has to be obtained from a remote machine. The following query works not for SIDs, but for group and account names.
"SELECT GroupComponent FROM Win32_GroupUser WHERE PartComponent = \"Win32_UserAccount.Domain='" + accountDomain + "',Name='" + accountName + "'\""
The Win32_Group objects it returns come in the forms of strings, and they only have domain and name (even though Win32_Group has a SID property).
I have this sinking feeling I'll have to:
Turn the SID into an account name by querying Win32_SID;
Perform the query above;
Turn each of the resulting group names into SIDs by querying Win32_Group.
Can you use the System.DirectoryServices.AccountManagement namespace classes?
using (var context = new PrincipalContext( ContextType.Domain ))
{
using (var user = UserPrincipal.FindByIdentity( context, accountName ))
{
var groups = user.GetAuthorizationGroups();
...iterate through groups and find SIDs for each one
}
}
It should work with ContextType.Machine, though you'd need to specify the machine name and have appropriate privileges.
using (var context = new PrincipalContext( ContextType.Machine,
"MyComputer",
userid,
password ))
{
...
}
There's a nice MSDN article (longish, though) on using the new .NET 3.5 account management namespace.
Yes there is but some methods depend on having a domain.
See this page for how to convert a SID
to a user id using P/Invoke and the Windows API, or with .NET 2.0+ and no P/Invoke.
using System.Security.Principal;
// convert the user sid to a domain\name
string account = new SecurityIdentifier(stringSid).Translate(typeof(NTAccount)).ToString();
If you have AD and
the user id in there then use the DirectorySearcher
method or Account Management APIs to find the groups.
Otherwise use the method outlined in
this article to get local
groups.
Now use the API suggested by #tvanfosson to iterate the groups and get the SIDs. Or follow the info below.
In an ASP.NET application it is possible to use code like this to access group info provided a user is authenticated by Windows and not Forms authentication. In this example I've left an interesting note about exceptions that are thrown in that environment but it may apply to other users:
public List<string> GetGroupsFromLogonUserIdentity()
{
List<string> groups = new List<string>();
HttpRequest request = HttpContext.Current.Request;
if (request.LogonUserIdentity.Groups != null)
{
foreach (IdentityReference group in request.LogonUserIdentity.Groups)
{
try
{
groups.Add(group.Translate(typeof(NTAccount)).ToString());
}
catch (IdentityNotMappedException)
{
// Swallow these exceptions without throwing an error. They are
// the result of dead objects in AD which are associated with
// user accounts. In this application users may have a group
// name associated with their AD profile which cannot be
// resolved in the Active Directory.
}
}
}
return groups;
}
LogonUserIdentity is based on the WindowsIdentity class. You could modify my code sample to use WindowsIdentity and function in a non-Web application. Once you iterate over a group you should be able to do something like this to get the SecurityIdentifier:
SecurityIdentifier secid = group as SecurityIdentifier;