Connecting to RETS Servers with UserAgent Requirement - rets

I am hoping there is someone here who is familiar with a Real Estate data standard known as RETS. The National Association of Realtors provides a dll for interfacing with their services called libRETS, but it is not being supported like it once was and recent events have prompted us to create our own as a replacement. For logistics reasons, we can't do this in Core and are using the current C#.Net 4.7.2.
There are 2 or 3 different "security levels" for connecting to a RETS Server, with the method being a per case basis from one MLS to the next. We can successfully connect to those who only require a login and password, but are hitting a wall on those who also require what is called a UserAgent and UserAgentPassword, which must passed somehow using Md5 encryption. The server is returning:
The remote server returned an error: (401) Unauthorized.
private WebResponse GetLoginBasicResponse()//*** THIS ONE WORKS ***
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var request = (HttpWebRequest)WebRequest.Create(new Uri(_cred.loginUri));
request.Method = "GET";
request.Headers.Add("RETS-Version", _retsVersion);
request.Credentials = new NetworkCredential(_login, _password);
return request.GetResponse();
}
catch (Exception ex)
{
string ignore = ex.Message;
return null;
}
}
private WebResponse GetLoginWithUserAgentResponse()//*** THIS ONE DOES NOT WORK ***
{
try
{
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var request = (HttpWebRequest)WebRequest.Create(new Uri(_cred.loginUri));
request.Method = "GET";
request.Headers.Add("RETS-Version", _retsVersion);
if (!string.IsNullOrEmpty(_cred.userAgent))
{
request.UserAgent = Md5(_cred.userAgent + ":" + _cred.userAgentPassword);
//request.Headers.Add("RETS-UA-Authorization", "Digest " + Md5(_cred.userAgent + ":" + _cred.userAgentPassword));
}
request.Credentials = new NetworkCredential(_login, _password);
return request.GetResponse();
}
catch (Exception ex)
{
string ignore = ex.Message;
return null;
}
}
public string Md5(string input) //*** Borrowed this from from .NET Core Project and presume it works
{
// Use input string to calculate MD5 hash
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
{
byte[] inputBytes = Encoding.ASCII.GetBytes(input);
byte[] hashBytes = md5.ComputeHash(inputBytes);
// Convert the byte array to hexadecimal string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
sb.Append(hashBytes[i].ToString("X2"));
}
return sb.ToString();
}
}

Page 20 of this document describes how to build the UA header: https://www.ranww.org/documents/resources/rets_1_8.pdf
There’s a few other fields you need to include.

We were not able to solve the issue in .NET but found a .NET Core project in GitHub that we are using instead. https://github.com/CrestApps/RetsConnector
This case can be closed

Not seeing an option to "Mark as Answer". Have tried both MS Edge and Google Chrome

Related

MinIO Uploading a file with name containing especial characters issues an HTTP 403 Forbidden

Uploading an object from a .net webclient to MinIO where the object contains special characters in the name issues an HTTP 403.
This object fails: old_me_bold+19(1).jpg
This object is ok: old_me_bold19.jpg
The message is:
The request signature we calculated does not match the signature you provided. Check your key and signing method.
What is breaking in MinIO?
Encoding the Url did not work either. I also tried PresignedPutObjectAsync to create a tmp Url already signed then I did a PUT using an HttpClient with the same result. To test that this way would work, I removed the special characters from the name of the file and the client was able to send the file to MinIO.
This code fails:
public async System.Threading.Tasks.Task PutAttachementAsync(
Attachment attachment,
string bukectName,
string objectName,
string attachmentType = "application/octet-stream")
{
using (HttpClient client = new HttpClient())
{
var minioClient = GetMinioClient();
client.BaseAddress = new Uri(Config.Instance.GetConfig("MinIOURL"));
var tempFileName = CreateAttachmentTempFile(attachment, out bool virusScanResult);
if (!virusScanResult)
return;
using (FileStream str = new FileStream(tempFileName.FileAttachmentPath, FileMode.Open))
{
try
{
String signedTMPUrl = await minioClient.PresignedPutObjectAsync(bukectName, objectName, 60 * 60 * 24);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(attachmentType));
HttpResponseMessage response = await client.PutAsync(signedTMPUrl, new StreamContent(str));
if (response.IsSuccessStatusCode)
{
Log.InfoFormat("Request Message Information:- \n\n" + response.RequestMessage + "\n");
Log.InfoFormat("Response Message Header \n\n" + response.Content.Headers + "\n");
}
else
{
Log.InfoFormat("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}
}
catch (MinioException e)
{
Log.Error("Error occurred: " + e.Message);
}
}
}
}
That code is running from an ASP.Net application hosted in IIS. I wonder if IIS is injecting something into the header before the message goes to MinIO.
Any other file that I send to MinIO, if the name does not contain characters such as (), then the upload works always.

Upload large files to Azure Media Services in Xamarin

I am trying to upload a .mp4 file, selected from the user's iOS or Android device, to my Azure Media Services account.
This code works for small files ( less than ~95MB):
public static async Task<string> UploadBlob(string blobContainerSasUri, string blobName, byte[] blobContent, string path)
{
string responseString;
int contentLength = blobContent.Length;
string queryString = (new Uri(blobContainerSasUri)).Query;
string blobContainerUri = blobContainerSasUri.Split('?')[0];
string requestUri = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/{1}{2}", blobContainerUri, blobName, queryString);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
request.Method = "PUT";
request.AllowWriteStreamBuffering = false;
request.Headers.Add("x-ms-blob-type", "BlockBlob");
request.ContentLength = contentLength;
request.Timeout = Int32.MaxValue;
request.KeepAlive = true;
int bufferLength = 1048576; //upload 1MB at time, useful for a simple progress bar.
Stream requestStream = request.GetRequestStream();
requestStream.WriteTimeout = Int32.MaxValue;
ProgressViewModel progressViewModel = App.Locator.GetProgressBar(App.Locator.MainViewModel.currentModuleItemId);
MyVideosPage myVideosPage = App.Locator.GetVideosPage(App.Locator.MainViewModel.currentModuleItemId);
FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
int nRead = 0;
int currentPos = 0;
while ((nRead = fileStream.Read(blobContent, currentPos, bufferLength)) > 0)
{
await requestStream.WriteAsync(blobContent, currentPos, nRead);
currentPos += nRead;
}
fileStream.Close();
requestStream.Close();
HttpWebResponse objHttpWebResponse = null;
try
{
// this is where it fails for large files
objHttpWebResponse = (HttpWebResponse)request.GetResponse();
Stream responseStream = objHttpWebResponse.GetResponseStream();
StreamReader stream = new StreamReader(responseStream);
responseString = stream.ReadToEnd();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (objHttpWebResponse != null)
objHttpWebResponse.Close();
}
return responseString;
}
An exception is thrown after this line is called:
(HttpWebResponse)request.GetResponse();
The exception message is "The request body is too large and exceeds the maximum permissible limit."
The exception StatusCode is "RequestEntityTooLarge".
How can I upload large files? Is this a problem with HttpWebRequest, or Azure Media Services?
Azure Storage supports one shot upload (aka PutBlob API) up to 256MB if you are using the new REST API versions. But since you are not specifying the REST API version, you're defaulting to a very old version where the maximum supported size of one shot upload is 100MB.
Use x-ms-version: 2018-03-28 header to be able to upload up to 256MB in one HTTP request.
If you have to deal with larger files, you will need to use block & commit upload. You will need to use PutBlock API to stage blocks from the source file. Blocks can be up to 100MB each. Then you need to commit all the blocks using the PutBlockList API. If you don't have to deal with this logic yourself, simply use the Azure Storage SDK for .NET (supports Xamarin) and use the uploadFromFile method. It is simple, and resilient.

Owin SSL firefox authenticity of the received data could not be verified error

I'm wondering whether anybody can run the code below and see why firefox cannot talk to the owin SSL server correctly? It worked well in Chrome & IE.
I've tried to manually import the pfx into firefox but it still cannot talk to the owin server correctly.
Refer to the code below, I created an owin http server with SSL support and it can generate & install the certificate & bind the certificate to port automatically.
However, I noticed an issue when I test in different browsers. The firefox browser always display an error message indicating that:
The connection to the server was reset while the page was loading.
The page you are trying to view cannot be shown because the
authenticity of the received data could not be verified. Please
contact the website owners to inform them of this problem.
Chrome & IE can display the page correctly.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using CERTENROLLLib;
using Microsoft.Owin.Hosting;
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
using System.Security.Permissions;
using System.Security.Policy;
namespace Owin.Startup
{
class Program
{
static void Main(string[] args)
{
int port = 7878;
var cert = GetCert("localhost", TimeSpan.FromDays(3650), "devpwd", AppDomain.CurrentDomain.BaseDirectory + "cert.pfx");
ActivateCert((X509Certificate2)cert, port, GetAppId());
StartOptions so = new StartOptions();
so.Urls.Add($"https://+:{port}/");
using (WebApp.Start<Startup>(so))
{
Console.WriteLine($"Hosted on port: {port}");
Console.ReadLine();
}
}
static private string GetAppId()
{
Assembly assembly = Assembly.GetExecutingAssembly();
//The following line (part of the original answer) is misleading.
//**Do not** use it unless you want to return the System.Reflection.Assembly type's GUID.
//Console.WriteLine(assembly.GetType().GUID.ToString());
// The following is the correct code.
var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
var id = attribute.Value;
return id;
}
static public X509Certificate GetCert(string cn, TimeSpan expirationLength, string pwd = "", string filename = null)
{
// http://stackoverflow.com/questions/18339706/how-to-create-self-signed-certificate-programmatically-for-wcf-service
// http://stackoverflow.com/questions/21629395/http-listener-with-https-support-coded-in-c-sharp
// https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.storename(v=vs.110).aspx
// create DN for subject and issuer
System.Security.Cryptography.X509Certificates.X509Certificate cert = null;
if (filename != null && File.Exists(filename))
{
cert = new X509Certificate2(filename, pwd, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
}
else
{
var base64encoded = string.Empty;
base64encoded = CreateCertContent(cn, expirationLength, pwd);
cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(
System.Convert.FromBase64String(base64encoded), pwd,
// mark the private key as exportable (this is usually what you want to do)
// mark private key to go into the Machine store instead of the current users store
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet
);
File.WriteAllBytes(filename, cert.Export(X509ContentType.Pfx, pwd));
}
// instantiate the target class with the PKCS#12 data (and the empty password)
return cert;
}
private static string CreateCertContent(string cn, TimeSpan expirationLength, string pwd)
{
string base64encoded = string.Empty;
var dn = new CX500DistinguishedName();
dn.Encode("CN=" + cn, X500NameFlags.XCN_CERT_NAME_STR_NONE);
CX509PrivateKey privateKey = new CX509PrivateKey();
privateKey.ProviderName = "Microsoft Strong Cryptographic Provider";
privateKey.Length = 2048;
privateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
privateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_DECRYPT_FLAG |
X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_KEY_AGREEMENT_FLAG;
privateKey.MachineContext = true;
privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
privateKey.Create();
// Use the stronger SHA512 hashing algorithm
var hashobj = new CObjectId();
hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
AlgorithmFlags.AlgorithmFlagsNone, "SHA512");
// Create the self signing request
var cert = new CX509CertificateRequestCertificate();
cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, "");
cert.Subject = dn;
cert.Issuer = dn; // the issuer and the subject are the same
cert.NotBefore = DateTime.Now.Date;
// this cert expires immediately. Change to whatever makes sense for you
cert.NotAfter = cert.NotBefore + expirationLength;
cert.HashAlgorithm = hashobj; // Specify the hashing algorithm
cert.Encode(); // encode the certificate
// Do the final enrollment process
var enroll = new CX509Enrollment();
enroll.InitializeFromRequest(cert); // load the certificate
enroll.CertificateFriendlyName = cn; // Optional: add a friendly name
string csr = enroll.CreateRequest(); // Output the request in base64
// and install it back as the response
enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
csr, EncodingType.XCN_CRYPT_STRING_BASE64, pwd); // no password
// output a base64 encoded PKCS#12 so we can import it back to the .Net security classes
base64encoded = enroll.CreatePFX(pwd, // no password, this is for internal consumption
PFXExportOptions.PFXExportChainWithRoot);
return base64encoded;
}
private static void ActivateCert(X509Certificate2 rlt, int port, string appId)
{
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
if (!store.Certificates.Contains(rlt))
{
store.Add(rlt);
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "netsh";
psi.Arguments = $"http delete sslcert ipport=0.0.0.0:{port}";
Process procDel = Process.Start(psi);
procDel.WaitForExit();
psi.Arguments = $"http add sslcert ipport=0.0.0.0:{port} certhash={rlt.Thumbprint} appid={{{appId}}}";
Process proc = Process.Start(psi);
proc.WaitForExit();
psi.Arguments = $"http delete sslcert ipport=[::]:{port}";
Process procDelV6 = Process.Start(psi);
procDelV6.WaitForExit();
psi.Arguments = $"http add sslcert ipport=[::]:{port} certhash={rlt.Thumbprint} appid={{{appId}}}";
Process procV6 = Process.Start(psi);
procV6.WaitForExit();
psi.Arguments = $"http add urlacl url=https://+:{port}/ user={Environment.UserDomainName}\\{Environment.UserName}";
Process procAcl = Process.Start(psi);
procAcl.WaitForExit();
}
store.Close();
}
}
public class Startup
{
private IAppBuilder app;
public void Configuration(IAppBuilder app)
{
#if DEBUG
app.UseErrorPage();
#endif
app.Use(new Func<AppFunc, AppFunc>(next => (async env =>
{
Console.WriteLine("Begin Request");
foreach (var i in env.Keys)
{
Console.WriteLine($"{i}\t={(env[i] == null ? "null" : env[i].ToString())}\t#\t{(env[i] == null ? "null" : env[i].GetType().FullName)}");
}
if (next != null)
{
await next.Invoke(env);
}
else
{
Console.WriteLine("Process Complete");
}
Console.WriteLine("End Request");
})));
app.UseWelcomePage("/");
this.app = app;
}
}
}
In firefox, type about:config and search for dhe.
The list shown all the cipher algorithms that firefox supported.
However, I noticed you used SHA512 which happened to be not supported by the latest firefox.
Please modify the algorithm to sha256 and it should work in firefox :)

square Connect API Batch

I am using .NET to list the payments from my square account.
I am able to get a list of the payments, but to get the description field I have to go one level deeper and make http end point calls for each payment. This is time consuming.
Question: Can anyone provide me with a sample in Visual C# or Java to make batch calls for retrieving payments (using multiple payment id's)?
Your help is greatly appreciated.
Thanks,
Prashant
#Andrew - Here's what I am using, I am just not sure how to add the headers for batch payments retrieval.
string res = string.Empty;
string qs = string.Empty;
foreach (string s in parameters.Keys)
{
if (qs == string.Empty)
qs = "?";
else
qs += "&";
qs += s + "=" + parameters[s];
}
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(_connectUrl + "/" + command + qs); ///
request.Proxy = null;
request.Headers.Add("Authorization", "Bearer " + _accessToken);// ");
request.ContentType = "application/json";
request.Method = method; // "GET";
try { HttpWebResponse responseGet = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(responseGet.GetResponseStream());
StringBuilder output = new StringBuilder();
output.Append(reader.ReadToEnd());
responseGet.Close();
request = null;
return output.ToString();
}
catch (Exception exp)
Looks like I've been able to answer my own query.
We need to be able to send the following POST to the HTTP Endpoint
{"requests":[{"method":"GET","relative_path":"/v1/me/payments/<payment_id>","access_token":"XXXX","request_id":"1"},{"method":"GET","relative_path":"/v1/me/payments/<payment_id>","access_token":"XXXX","request_id":"2"}]}
the following code in .NET achieves the above
//Convert the body of request into a byte array
byte[] byteArray = Encoding.UTF8.GetBytes(body);
//Set the length
request.ContentLength = byteArray.Length;
//Write the body to the request by using a datastream
//This line never returns....
Stream datastream = request.GetRequestStream();
datastream.Write(byteArray, 0, byteArray.Length);
datastream.Close();
And that's all there is to it.
Hope this helps anyone is is set out to use the batch mode.
Thanks

Yahoo PlaceFinder API Connection Issues

I'm trying to used Yahoo's PlaceFinder API to get longitude and latitude for a postcode. Everything works fine on my local machine but when I upload it to my production server I get a connection refused error from Yahoo. I've copied my code below:
Dictionary<string, decimal> GetYahooGeoCode(string postcode)
{
string url = "http://where.yahooapis.com/geocode?flags=J&appid=[My_App_ID]&location=";
decimal latitude = 0;
decimal longitude = 0;
try
{
dynamic yahooResults = new Uri(url + postcode).GetDynamicJsonObject();
foreach (var result in yahooResults.ResultSet.Results)
{
latitude = (decimal)result.latitude;
longitude = (decimal)result.longitude;
}
Dictionary<string, decimal> geoCode = new Dictionary<string, decimal>();
geoCode.Add("latitude", latitude);
geoCode.Add("longitude", longitude);
return geoCode;
}
catch (Exception ex)
{
Log4NetLogger logger = new Log4NetLogger();
logger.Error(ex);
return null;
}
}
The error I'm getting is: No connection could be made because the target machine actively refused it. Does anyone have any ideas on this? I have done a lot of searching and can't seem to find any information on this problem. I don't know if this makes any difference but my production server is a dedicated Cloud server. Any help would be appreciated.
I try to solve your problem here my solition:
Check your web.config:
<system.net>
<defaultProxy />
</system.net>
And my code (GetDynamicJsonObject() not parsed the response):
string postcode = "10003";
string url = String.Format("http://where.yahooapis.com/geocode?state={0}&postal={1}", "NY", postcode);
Dictionary<string, decimal> geoCode = new Dictionary<string, decimal>();
System.Xml.Linq.XDocument xDocument = XDocument.Load(url);
var latlon = (from r in xDocument.Descendants("Result")
select new { latitude = r.Element("latitude").Value, longitude = r.Element("longitude").Value }).FirstOrDefault();
if(null != latlon)
{
geoCode.Add("latitude", Convert.ToDecimal(latlon.latitude));
geoCode.Add("longitude", Convert.ToDecimal(latlon.longitude));
}
Check place finder yahoo api parameters here http://developer.yahoo.com/geo/placefinder/guide/requests.html#base-uri

Resources