Microsoft.Exchange.WebServices.Data.ServiceResponseException: 'There are no public folder servers available.' - directory

further to this question, i have the same problem. PubFolder on Prem , users in O365
I have fetched and added the routing headers from Glen's post but still get the error
GetToken works...
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
GetX headers works...
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/public-folder-access-with-ews-in-exchange
--->> ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10))
Microsoft.Exchange.WebServices.Data.ServiceResponseException: 'There are no public folder servers available.'
static async System.Threading.Tasks.Task Test3()
{
string ClientId = ConfigurationManager.AppSettings["appId"];
string TenantId = ConfigurationManager.AppSettings["tenantId"];
string secret = ConfigurationManager.AppSettings["clientSecret"];
string uMbox = ConfigurationManager.AppSettings["userId"];
string uPwd = ConfigurationManager.AppSettings["userPWD"];
// Using Microsoft.Identity.Client 4.22.0
//https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth//
var cca = ConfidentialClientApplicationBuilder
.Create(ClientId)
.WithClientSecret(secret)
.WithTenantId(TenantId)
.Build();
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
try
{
var authResult = await cca.AcquireTokenForClient(ewsScopes)
.ExecuteAsync();
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
ewsClient.ImpersonatedUserId =
new ImpersonatedUserId(ConnectingIdType.SmtpAddress, uMbox);
AutodiscoverService autodiscoverService = GetAutodiscoverService(uMbox, uPwd);
GetUserSettingsResponse userResponse = GetUserSettings(autodiscoverService, uMbox, 3, UserSettingName.PublicFolderInformation, UserSettingName.InternalRpcClientServer);
string pfAnchorHeader= userResponse.Settings[UserSettingName.PublicFolderInformation].ToString();
string pfMailboxHeader = userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString(); ;
// Make an EWS call
var folders = ewsClient.FindFolders(WellKnownFolderName.MsgFolderRoot, new FolderView(10));
foreach (var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
//get Public folder root
//Include x-anchormailbox header
Console.WriteLine("X-AnchorMailbox value for public folder hierarchy requests: {0}", pfAnchorHeader);
Console.WriteLine("X-PublicFolderMailbox value for public folder hierarchy requests: {0}", pfMailboxHeader);
//var test3 = GetMailboxGuidAddress(ewsClient, pfAnchorHeader, pfMailboxHeader, uMbox);
///https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-route-public-folder-content-requests <summary>
ewsClient.HttpHeaders.Add("X-AnchorMailbox", userResponse.Settings[UserSettingName.PublicFolderInformation].ToString());
//ewsClient.HttpHeaders.Add("X-AnchorMailbox", "SharedPublicFolder#contoso.com");
ewsClient.HttpHeaders.Add("X-PublicFolderMailbox", userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString());
try
{
var pubfolders = ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
foreach (var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
}
catch (MsalException ex)
{
Console.WriteLine($"Error acquiring access token: {ex}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex}");
}
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("Hit any key to exit...");
Console.ReadKey();
}
}
public static AutodiscoverService GetAutodiscoverService(string username, string pwd)
{
AutodiscoverService adAutoDiscoverService = new AutodiscoverService();
adAutoDiscoverService.Credentials = new WebCredentials(username, pwd);
adAutoDiscoverService.EnableScpLookup = true;
adAutoDiscoverService.RedirectionUrlValidationCallback = RedirectionUrlValidationCallback;
adAutoDiscoverService.PreAuthenticate = true;
adAutoDiscoverService.TraceEnabled = true;
adAutoDiscoverService.KeepAlive = false;
return adAutoDiscoverService;
}
public static GetUserSettingsResponse GetUserSettings(
AutodiscoverService service,
string emailAddress,
int maxHops,
params UserSettingName[] settings)
{
Uri url = null;
GetUserSettingsResponse response = null;
for (int attempt = 0; attempt < maxHops; attempt++)
{
service.Url = url;
service.EnableScpLookup = (attempt < 2);
response = service.GetUserSettings(emailAddress, settings);
if (response.ErrorCode == AutodiscoverErrorCode.RedirectAddress)
{
url = new Uri(response.RedirectTarget);
}
else if (response.ErrorCode == AutodiscoverErrorCode.RedirectUrl)
{
url = new Uri(response.RedirectTarget);
}
else
{
return response;
}
}
throw new Exception("No suitable Autodiscover endpoint was found.");
}

Your code won't work against an OnPrem Public folder tree as EWS in Office365 won't proxy to an OnPrem Exchange Org (even if hybrid is setup). (Outlook MAPI is a little different and allows this via versa setup but in that case it never proxies either it just makes a different connection to that store and its all the Outlook client doing this).
Because your trying to use the client credentials oauth flow for that to work onPrem you must have setup hybrid modern authentication https://learn.microsoft.com/en-us/microsoft-365/enterprise/hybrid-modern-auth-overview?view=o365-worldwide. Then you need to acquire a token with an audience set to the local OnPrem endpoint. (this is usually just your onPrem ews endpoint's host name but it should be one of the service principal names configured in your hybrid auth setup Get-MsolServicePrincipal). So in your code you would change
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
to
var ewsScopes = new string[] { "https://OnPrem.whatever.com/.default" };
which will then give you a token with an audience set for the onprem server then you need to send the EWS request to that endpoint so change that eg
ewsClient.Url = new Uri("https://OnPrem.whatever.com/EWS/Exchange.asmx");
if Hybird Modern Auth is setup then you need to default back to use Integrated or Basic Authenticaiton.

Related

.NET Core reverse proxy middleware and signalR

I need to do a reverse proxy middleware in ASP.NET Core. I have something like this. But it doesn't work with SignalR.
Reverse proxy has address 192.168.187.72:5000; SignalR 192.168.187.62:5000. Client connects to proxy, proxy changes the url to SignalR hub url, but in client I always get this error:
The server disconnected before the handshake could be started
Code:
public ReverseProxyMiddleware(RequestDelegate nextMiddleware,ILogger<ReverseProxyMiddleware> logger)
{
_nextMiddleware = nextMiddleware;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
var targetUri = BuildTargetUri(context.Request);
if (targetUri != null)
{
var targetRequestMessage = CreateTargetMessage(context, targetUri);
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
CopyFromTargetResponseHeaders(context, responseMessage);
await responseMessage.Content.CopyToAsync(context.Response.Body);
}
return;
}
await _nextMiddleware(context);
}
private HttpRequestMessage CreateTargetMessage(HttpContext context, Uri targetUri)
{
var requestMessage = new HttpRequestMessage();
CopyFromOriginalRequestContentAndHeaders(context, requestMessage);
requestMessage.RequestUri = targetUri;
requestMessage.Headers.Host = targetUri.Host;
requestMessage.Method = GetMethod(context.Request.Method);
return requestMessage;
}
private void CopyFromOriginalRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage)
{
var requestMethod = context.Request.Method;
var streamContent = new StreamContent(context.Request.Body);
requestMessage.Content = streamContent;
foreach (var header in context.Request.Headers)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
private void CopyFromTargetResponseHeaders(HttpContext context, HttpResponseMessage responseMessage)
{
foreach (var header in responseMessage.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
context.Response.Headers.Remove("transfer-encoding");
}
private static HttpMethod GetMethod(string method)
{
if (HttpMethods.IsDelete(method)) return HttpMethod.Delete;
if (HttpMethods.IsGet(method)) return HttpMethod.Get;
if (HttpMethods.IsHead(method)) return HttpMethod.Head;
if (HttpMethods.IsOptions(method)) return HttpMethod.Options;
if (HttpMethods.IsPost(method)) return HttpMethod.Post;
if (HttpMethods.IsPut(method)) return HttpMethod.Put;
if (HttpMethods.IsTrace(method)) return HttpMethod.Trace;
return new HttpMethod(method);
}
private Uri BuildTargetUri(HttpRequest request)
{
Uri targetUri = null;
StringValues clientId = string.Empty;
targetUri = new Uri($"http://192.168.187.62:5000{request.Path}{request.QueryString}");
_logger.LogInformation($"Request URL {request.Host + request.Path + request.QueryString} Target URL {targetUri.ToString()}");
return targetUri;
}
Are you sure you want to create such functionality manually?
There are many great libraries for reverse proxying, Microsoft has a new project called YARP (https://microsoft.github.io/reverse-proxy/) which is an open source reverse proxy told to be high performance and highly customizable.
If you like I can show you a sample how you can use yarp in an ASP.NET Core app.

Google.Apis.Requests.RequestError Not Found when deleting event

I have below method to delete event in calendar:
public async Task<string> DeleteEventInCalendarAsync(TokenResponse token, string googleUserId, string calendarId, string eventId)
{
string result = null;
try
{
if (_calService == null)
{
_calService = GetCalService(token, googleUserId);
}
// Check if event exist
var eventResource = new EventsResource(_calService);
var erListRequest = eventResource.List(calendarId);
var eventsResponse = await erListRequest.ExecuteAsync().ConfigureAwait(false);
var existingEvent = eventsResponse.Items.FirstOrDefault(e => e.Id == eventId);
if (existingEvent != null)
{
var deleteRequest = new EventsResource.DeleteRequest(_calService, calendarId, eventId);
result = await deleteRequest.ExecuteAsync().ConfigureAwait(false);
}
}
catch (Exception exc)
{
result = null;
_logService.LogException(exc);
}
return result;
}
And I am getting error as follow -
Google.GoogleApiException Google.Apis.Requests.RequestError Not Found [404] Errors [ Message[Not Found] Location[ - ] Reason[notFound] Domain[global] ]
Can you help me understand why this error? Or where I can find the details about these error?
The error you are getting is due to the event's id you are passing doesn't exist or you are passing it in the wrong way. Following the .Net Quickstart I made a simple code example on how to pass the event's id to the Delete(string calendarId, string eventId) method from the Class Events
namespace CalendarQuickstart
{
class Program
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/calendar-dotnet-quickstart.json
static string[] Scopes = { CalendarService.Scope.Calendar };
static string ApplicationName = "Google Calendar API .NET Quickstart";
static void Main(string[] args)
{
UserCredential credential;
using (var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Calendar API service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define request.
EventsResource.ListRequest request = service.Events.List("primary");
// List events.
Events events = request.Execute();
Event existingEvent = events.Items.FirstOrDefault(e => e.Id == "your event id you want to get");
Console.WriteLine("Upcoming events:");
if (existingEvent != null)
{
Console.WriteLine("{0} {1}", existingEvent.Summary, existingEvent.Id);
string deleteEvent = service.Events.Delete("primary", existingEvent.Id).Execute();
}
else
{
Console.WriteLine("No upcoming events found.");
}
Console.Read();
}
}
}
Notice
I made this example in a synchronous syntax way for testing purposes in the console. After you test it and see how it works, you could adapt it to your code. Remember, make your you are passing the correct Id.
Docs
For more info check this doc:
Namespace Google.Apis.Calendar.v3

WebAPI call hangs when return a large amount of data

I have a web api call that I recently added to my app. I returns a complete list of all countries, states and cities in the app (currently 486 rows) I perform this call when all of the reference data for my application loads (I have a base loading page and call the function in my startup class to load all the data there). The challenge is that the call to get all my countries.... hangs and eventually I get "The operation was canceled" error. If I modify my stored procedure that selects the data from the database on the server to only return say 20 rows, it runs fine. Any suggestions?
Below is the code from the startup class:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
namespace GBarScene
{
class StartUpClass
{
public event GeneralDataLoad BaseDataLoadComplete;
public async Task<GBSStartUpEventArgs> ProcessStartup()
{
GBSStartUpEventArgs lobj_EventArgs;
lobj_EventArgs = new GBSStartUpEventArgs();
App.InStartUpDataLoad = true;
try
{
if (!App.IsGeolocationEnabled)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = false;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("NoLocationServicesMessage");
}
else if (!App.InternetIsAvailable)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("NoInternetConnectionFound");
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = false;
}
else
{
Debug.WriteLine("Process StartUp");
await Task.Delay(500);
//Reset values
ViewModelObjects.DayOfWeek.DataLoadProcessed = false;
ViewModelObjects.Languages.DataLoadProcessed = false;
if (await ViewModelObjects.DayOfWeek.LoadData() == false)
// //try it once more
await ViewModelObjects.DayOfWeek.LoadData();
Debug.WriteLine("GBar After DayofWeek Load");
await ViewModelObjects.Languages.LoadData();
Debug.WriteLine("GBar After Languages Load");
if ((ge_AppMode)ViewModelObjects.AppSettings.AppMode == ge_AppMode.CitySelected)
{
//We need to reload the NearbyCities and set the selected one
await ViewModelObjects.NearbyCities.LoadData();
}
Debug.WriteLine("Before load of coutries");
await ViewModelObjects.CountryStateCity.LoadData();
Debug.WriteLine("After load of coutries");
Debug.WriteLine("Count: " + ViewModelObjects.CountryStateCity.CountryItems_ForList.Count.ToString());
ViewModelObjects.NumberOfResults.LoadData();
ViewModelObjects.Perspectives.LoadData();
ViewModelObjects.SearchRadiuses.LoadData();
ViewModelObjects.UseMetric.LoadData();
while (!ViewModelObjects.DayOfWeek.DataLoadProcessed && !ViewModelObjects.Languages.DataLoadProcessed && !App.IsGeolocationEnabled)
{
await Task.Delay(100);
}
if (App.BaseDataLoadError)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = true;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("ErrorLoadingReferenceData");
}
}
Debug.WriteLine("StartUp Process Ended");
BaseDataLoadComplete(this, lobj_EventArgs);
}
catch (Exception ex)
{
App.ProcessException(ex);
}
App.InStartUpDataLoad = false;
return lobj_EventArgs;
}
}
}
This is the helper class that makes all the WebAPI calls:
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace GBarScene
{
public class WebAPICaller: IDisposable
{
HttpClient iobj_HTTPClient = null;
public void Dispose()
{
if (iobj_HTTPClient != null)
iobj_HTTPClient.Dispose();
}
public async Task<string> HTTPGetWebServiceAsync(string ps_URI)
{
string ls_Response = "";
string ls_JSONData = "";
string ls_Prefix = "";
try
{
iobj_HTTPClient = await GetClient();
switch (Device.RuntimePlatform)
{
case Device.Android:
ls_Prefix = App.APIStandardPrefix;
break;
//case Device.Android:
// ls_Prefix = App.APISecurePrefix;
// break;
//case Device.Windows:
//case Device.WinPhone:
// ls_Prefix = App.APISecurePrefix;
// break;
default:
ls_Prefix = App.APISecurePrefix;
break;
}
Debug.WriteLine("before api call");
iobj_HTTPClient.BaseAddress = new Uri(ls_Prefix);
ls_JSONData = await iobj_HTTPClient.GetStringAsync(ps_URI);
Debug.WriteLine("after api call");
ls_Response = System.Net.WebUtility.HtmlDecode(ls_JSONData);
}
catch (Exception ex)
{
Debug.WriteLine("api call error");
App.ProcessException(ex);
}
return ls_Response;
}
public async Task<bool> HTTPPostWebService(string ps_URI, object pobj_BodyObject)
{
HttpResponseMessage lobj_HTTPResponse = null;
bool lb_Response = false;
HttpContent lobj_Content = null;
try
{
if (iobj_HTTPClient != null)
iobj_HTTPClient = await GetClient();
iobj_HTTPClient.BaseAddress = new Uri(App.APISecurePrefix);
lobj_Content = new StringContent(JsonConvert.SerializeObject(pobj_BodyObject == null ? "" : pobj_BodyObject));
lobj_Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
lobj_HTTPResponse = await iobj_HTTPClient.PostAsync(ps_URI, lobj_Content);
if (!lobj_HTTPResponse.IsSuccessStatusCode)
{
Exception lobj_Exception = new Exception(lobj_HTTPResponse.ToString());
lobj_Exception.Source = "HTTPGetWebService for: " + ps_URI;
App.ProcessException(lobj_Exception);
}
else
{
lb_Response = true;
}
}
catch (Exception ex)
{
App.ProcessException(ex);
}
finally
{
if (lobj_HTTPResponse != null)
{
lobj_HTTPResponse.Dispose();
}
//Debug.WriteLine("WebAPICaller-CallWebService-1: Done");
}
return lb_Response;
}
private async Task<HttpClient> GetClient()
{
HttpClient lobj_HTTPClient = null;
if (lobj_HTTPClient == null)
{
lobj_HTTPClient = new HttpClient();
lobj_HTTPClient.DefaultRequestHeaders.Add("Accept", "application/json");
lobj_HTTPClient.MaxResponseContentBufferSize = 2147483647;
lobj_HTTPClient.Timeout = new TimeSpan(0,0,0,0,60000);
}
return lobj_HTTPClient;
}
}
}
Sorry I forget to include the method in the CountryStateCity view model that calls the webapi helper class.
public async Task<bool> LoadData()
{
string ls_Response = "";
string ls_WorkURI = "";
WebAPICaller lobj_WebAPICaller = null;
bool lb_DataLoaded = false;
try
{
IsDataLoaded = false;
//Debug.WriteLine("City Data Load");
lobj_WebAPICaller = new WebAPICaller();
ls_WorkURI = ic_CoutryStateCityAPIUrl.Replace("{Language}", "EN");
ls_Response = await lobj_WebAPICaller.HTTPGetWebServiceAsync(ls_WorkURI);
if (ls_Response.Trim().Length == 0)
{
AddErrorEntry();
}
else
{
CountryItems_ForList = new ObservableCollection<GBSCountry_ForList>();
StateItems_ForList = new ObservableCollection<GBSState_ForList>();
CityItems_ForList = new ObservableCollection<GBSCity_ForList>();
iobj_CountryStateCity = JsonConvert.DeserializeObject<ObservableCollection<GBSCountryStateCity>>(ls_Response);
//Now load the display lists
CountryItems_ForList = new ObservableCollection<GBSCountry_ForList>(
(from lobj_Country in iobj_CountryStateCity
select new GBSCountry_ForList()
{
ID = lobj_Country.Country_Code,
Value = lobj_Country.Country_Name_Text
}).Distinct().ToList());
CountryItems_ForList.Insert(0, new GBSCountry_ForList
{
ID = "XX",
Value = "Base Value"
});
lb_DataLoaded = true;
}
}
catch (Exception ex)
{
AddErrorEntry();
App.ProcessException(ex);
}
finally
{
IsDataLoaded = true;
if (lobj_WebAPICaller != null)
lobj_WebAPICaller.Dispose();
}
return lb_DataLoaded;
}
So after much time, I believe I figured out what the problem is. The problem started to manifest itself again with smaller amounts of data and I could not figure out why. The problem appeared. The issue appears to be the IP address I was using. (I was using the IP address of the actual laptop I was hosting both the App and WebAPIs on.) It appears you have to use one of the other network adaptors for the emulator to have this work reliably.
Here are the steps I used to resolved this:
I launched my Windows 10 mobile emulator.
Click on the >> (Tools) icon in the tool bar of the emulator.
Click on the Network tab of the Additional Tools window.
Look in the list for the network adaptor labeled Desktop Adaptor #1 and copy the IP address.
Edit the Applicationhost.config file in the folder of the WebAPI project.
Find the entry in the file for site name="XXXXX" where XXXXX is the name of the Visual Studio project you are hosting your WebAPIs in.
Within the section of the entry for your WebAPI project, add a binding for the IP address you copied from in step 4. It should look something like this:
<binding protocol="http" bindingInformation="*:56952:169.254.69.220" />
Where 56952 is the port my IIS Express is hosting the WebAPIs on and 169.254.69.220 is the IP address I copied from step 4. After adding this, I was able to connect to locally hosted WebAPIs in IIS Express.
Hope this helps.

Directory levels Quotas on remote shared folder

I have 2 servers in AD (2008R2)
On one of them I have shared folder (c:\Shared\dirForUserAAA ==> \DC1\dir1)
On other one I have c# program that must manage folder quota on \DC1\dir1
Is it possible and how it can be done?
I try to use this piece of code, but it works only on local paths :(
public static void SetQuotaToFolder(string UNCPathForQuota, int quotaLimitBytes)
{
if (!Directory.Exists(UNCPathForQuota))
{
Directory.CreateDirectory(UNCPathForQuota);
}
// Create our interface
IFsrmQuotaManager FSRMQuotaManager = new FsrmQuotaManagerClass();
IFsrmQuota Quota = null;
try
{
// First we need to see if there is already a quota on the directory.
Quota = FSRMQuotaManager.GetQuota(UNCPathForQuota);
// If there is quota then we just set it to our new size
Quota.QuotaLimit = quotaLimitBytes;
}
catch (COMException e)
{
unchecked
{
if (e.ErrorCode == (int)0x80045301)
{
// There is no quota on this directory so we need to create it.
Quota = FSRMQuotaManager.CreateQuota(UNCPathForQuota);
// And then set our desired quota
Quota.QuotaLimit = quotaLimitBytes;
}
else
{
// some other COM exception occured so we return the error
Console.WriteLine(e);
return;
}
}
}
catch (Exception e)
{
// Generic error handling would go here
Console.WriteLine(e);
return;
}
// and finally we commit our changes.
Quota.Commit();
}
}
Old Question, but if someone needs a hint:
Open a RemotePowershell on the server where your folders are saved. Then use the Cmdlets from here
Some code snippets:
Open Runspace:
public static Runspace CreateAndOpen(string domain, string username, string password, string computername)
{
string userName = username + "#" + domain;
var securePassword = password.ToSecureString();
PSCredential credential = new PSCredential(username, securePassword);
var connectionInfo = new WSManConnectionInfo(false, computername, 5985, "/wsman", shellUri, credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
connectionInfo.OpenTimeout = 2 * 60 * 1000; // 2 minutes
Runspace powershellRunspace = RunspaceFactory.CreateRunspace(connectionInfo);
powershellRunspace.Open();
return powershellRunspace;
}
Set a quota on a path
public void SetQuotaTemplateOnPath(Runspace runspace, string path, string template)
{
using (var pipe = runspace.CreatePipeline())
{
var newFsrmQuotaCommand = new Command("New-FsrmQuota");
newFsrmQuotaCommand.Parameters.Add("Path", path);
newFsrmQuotaCommand.Parameters.Add("Template", template);
newFsrmQuotaCommand.Parameters.Add("Confirm", false);
pipe.Commands.Add(newFsrmQuotaCommand);
var results = pipe.Invoke();
if (pipe.Error.Count > 0)
{
//Handle error
}
}
}

Send Email From Amazon SES in ASP.NET MVC App

I host my web app which is written in .net mvc2 on amazon ec2. currrently use gmail smtp to send email. beacuse of google for startup email quota cant send more than 500 email a day. So decide to move amazon ses. How can use amazon ses with asp.net mvc2? How about configuration etc? Is email will send via gmail? because our email provider is gmail. etc.
Send Email via Amazon is a right decision. Because when you move to amazon you will immediately get 2000 email free per day which is greater than googla apps 500 emails quota a day.
Step by Step:
Go to http://aws.amazon.com/ses
and click Sign Up for Amazon SES.
To get your AWS access identifiers
verify your email address - email
which you will send email via. You
need perl packages installled on
your computer to test email
features.
include:amazonses.com to your dns record.
Step by step documentation.
http://docs.aws.amazon.com/ses/latest/DeveloperGuide/getting-started.html
There is a Amazon SES (Simple Email Service) C# Wrapper on codeplex you can use this wrapper to send emails.
Amazon SES C# Wrapper
Easiest way is to download the SDK via Nuget (package is called AWSSDK) or download the SDK from Amazon's site. The sdk download from their site has an example project that shows you how to call their API to send email. The only configuration is plugging in your api keys. The trickiest part is verifying your send address (and any test receipients) but their is an API call there too to send the test message. You will then need to log in and verify those email addresses. The email will be sent through Amazon (that is the whole point) but the from email address can be your gmail address.
#gandil I created this very simple code to send emails
using Amazon;
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
using System.IO;
namespace SendEmail
{
class Program
{
static void Main(string[] args)
{
//Remember to enter your (AWSAccessKeyID, AWSSecretAccessKey) if not using and IAM User with credentials assigned to your instance and your RegionEndpoint
using (var client = new AmazonSimpleEmailServiceClient("YourAWSAccessKeyID", "YourAWSSecretAccessKey", RegionEndpoint.USEast1))
{
var emailRequest = new SendEmailRequest()
{
Source = "FROMADDRESS#TEST.COM",
Destination = new Destination(),
Message = new Message()
};
emailRequest.Destination.ToAddresses.Add("TOADDRESS#TEST.COM");
emailRequest.Message.Subject = new Content("Hello World");
emailRequest.Message.Body = new Body(new Content("Hello World"));
client.SendEmail(emailRequest);
}
}
}
}
You can find the code in here https://github.com/gianluis90/amazon-send-email
Download AWSSDK.dll file from internet
use following name-spaces
using Amazon;
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
using System.Net.Mail;
2 . Add to web config...
<appSettings>
<add key="AWSAccessKey" value="Your AWS Access Key" />
<add key="AWSSecretKey" value="Your AWS secret Key" />
</appSettings>
3 . Add a AWSEmailSevice class to your project that will allow to send mail via AWS ses...
public class AWSEmailSevice
{
//create smtp client instance...
SmtpClient smtpClient = new SmtpClient();
//for sent mail notification...
bool _isMailSent = false;
//Attached file path...
public string AttachedFile = string.Empty;
//HTML Template used in mail ...
public string Template = string.Empty;
//hold the final template data list of users...
public string _finalTemplate = string.Empty;
//Template replacements varibales dictionary....
public Dictionary<string, string> Replacements = new Dictionary<string, string>();
public bool SendMail(MailMessage mailMessage)
{
try
{
if (mailMessage != null)
{
//code for fixed things
//from address...
mailMessage.From = new MailAddress("from#gmail.com");
//set priority high
mailMessage.Priority = System.Net.Mail.MailPriority.High;
//Allow html true..
mailMessage.IsBodyHtml = true;
//Set attachment data..
if (!string.IsNullOrEmpty(AttachedFile))
{
//clear old attachment..
mailMessage.Attachments.Clear();
Attachment atchFile = new Attachment(AttachedFile);
mailMessage.Attachments.Add(atchFile);
}
//Read email template data ...
if (!string.IsNullOrEmpty(Template))
_finalTemplate = File.ReadAllText(Template);
//check replacements ...
if (Replacements.Count > 0)
{
//exception attached template..
if (string.IsNullOrEmpty(_finalTemplate))
{
throw new Exception("Set Template field (i.e. file path) while using replacement field");
}
foreach (var item in Replacements)
{
//Replace Required Variables...
_finalTemplate = _finalTemplate.Replace("<%" + item.Key.ToString() + "%>", item.Value.ToString());
}
}
//Set template...
mailMessage.Body = _finalTemplate;
//Send Email Using AWS SES...
var message = mailMessage;
var stream = FromMailMessageToMemoryStream(message);
using (AmazonSimpleEmailServiceClient client = new AmazonSimpleEmailServiceClient(
ConfigurationManager.AppSettings["AWSAccessKey"].ToString(),
ConfigurationManager.AppSettings["AWSSecretKey"].ToString(),
RegionEndpoint.USWest2))
{
var sendRequest = new SendRawEmailRequest { RawMessage = new RawMessage { Data = stream } };
var response = client.SendRawEmail(sendRequest);
//return true ...
_isMailSent = true;
}
}
else
{
_isMailSent = false;
}
}
catch (Exception ex)
{
throw ex;
}
return _isMailSent;
}
private MemoryStream FromMailMessageToMemoryStream(MailMessage message)
{
Assembly assembly = typeof(SmtpClient).Assembly;
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
MemoryStream stream = new MemoryStream();
ConstructorInfo mailWriterContructor =
mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);
object mailWriter = mailWriterContructor.Invoke(new object[] { stream });
MethodInfo sendMethod =
typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
if (sendMethod.GetParameters().Length == 3)
{
sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true, true }, null); // .NET 4.x
}
else
{
sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true }, null); // .NET < 4.0
}
MethodInfo closeMethod =
mailWriter.GetType().GetMethod("Close", BindingFlags.Instance | BindingFlags.NonPublic);
closeMethod.Invoke(mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { }, null);
return stream;
}
}
Use above class to send mail anyone with attachment and template varibales replacement (it's optional)
// Call this method to send your email
public string SendEmailViaAWS()
{
string emailStatus = "";
//Create instance for send email...
AWSEmailSevice emailContaint = new AWSEmailSevice();
MailMessage emailStuff = new MailMessage();
//email subject..
emailStuff.Subject = "Your Email subject";
//region Optional email stuff
//Templates to be used in email / Add your Html template path ..
emailContaint.Template = #"\Templates\MyUserNotification.html";
//add file attachment / add your file ...
emailContaint.AttachedFile = "\ExcelReport\report.pdf";
//Note :In case of template
//if youe want to replace variables in run time
//just add replacements like <%FirstName%> , <%OrderNo%> , in HTML Template
//if you are using some varibales in template then add
// Hold first name..
var FirstName = "User First Name";
// Hold email..
var OrderNo = 1236;
//firstname replacement..
emailContaint.Replacements.Add("FirstName", FirstName.ToString());
emailContaint.Replacements.Add("OrderNo", OrderNo.ToString());
// endregion option email stuff
//user OrderNo replacement...
emailContaint.To.Add(new MailAddress("TOEmail#gmail.com"));
//mail sent status
bool isSent = emailContaint.SendMail(emailStuff);
if(isSent)
{
emailStatus = "Success";
}
else
{
emailStatus = "Fail";
}
return emailStatus ; }
Following is how I sent email with attachment
public static void SendMailSynch(string file1, string sentFrom, List<string> recipientsList, string subject, string body)
{
string smtpClient = "email-smtp.us-east-1.amazonaws.com"; //Correct it
string conSMTPUsername = "<USERNAME>";
string conSMTPPassword = "<PWD>";
string username = conSMTPUsername;
string password = conSMTPPassword;
// Configure the client:
System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient(smtpClient);
client.Port = 25;
client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(username, password);
client.EnableSsl = true;
client.Credentials = credentials;
// Create the message:
var mail = new System.Net.Mail.MailMessage();
mail.From = new MailAddress(sentFrom);
foreach (string recipient in recipientsList)
{
mail.To.Add(recipient);
}
mail.Bcc.Add("test#test.com");
mail.Subject = subject;
mail.Body = body;
mail.IsBodyHtml = true;
Attachment attachment1 = new Attachment(file1, MediaTypeNames.Application.Octet);
ContentDisposition disposition = attachment1.ContentDisposition;
disposition.CreationDate = System.IO.File.GetCreationTime(file1);
disposition.ModificationDate = System.IO.File.GetLastWriteTime(file1);
disposition.ReadDate = System.IO.File.GetLastAccessTime(file1);
mail.Attachments.Add(attachment1);
client.Send(mail);
}

Resources