I am trying to develop a unity application that, among other things, controls a PTZ camera remotely. I'm having trouble actually getting the connection set up though.
I am using the Onvif Unity library made by Oneiros90: https://github.com/Oneiros90/UnityOnvif
When I go into the test scene in Unity-OnvifMain > core > test > scenes (which only has an empty game object and the test script) and hit play, the script initializes all clients without errors but upon reaching the mediaClient.getProfiles() line, it throws an error "WebException: There was an error on processing web request: Status code 400(BadRequest): Bad Request"
Here is the relevant test script code for reference:
using System.Linq;
using Onvif.Core;
using UnityEngine;
public class OnvifClientTest : MonoBehaviour
{
[SerializeField]
private string host = "";
[SerializeField]
private string username = "admin";
[SerializeField]
private string password = "";
[SerializeField]
private string ptzServiceAddress = "";
[SerializeField]
private string mediaServiceAddress = "";
private Vector2 ptzPanSpeedLimits;
private Vector2 ptzTiltSpeedLimits;
private Vector2 ptzZoomSpeedLimits;
private OnvifClient onvif;
private string currentProfile;
private PTZClient ptzClient;
private MediaClient mediaClient;
private async void Start()
{
onvif = new OnvifClient(host, username, password);
var success = await onvif.Init();
if (!success)
{
Debug.LogError($"Cannot connect to {host}");
return;
}
ptzClient = await onvif.CreatePTZClient(ptzServiceAddress);
mediaClient = await onvif.CreateMediaClient(mediaServiceAddress);
var profiles = mediaClient.GetProfiles();
currentProfile = profiles.First().token;
var ptzConf = mediaClient.GetProfile(currentProfile).PTZConfiguration;
Space2DDescription panTiltSpace = null;
Space1DDescription zoomSpace = null;
//other stuff that does not matter yet because it doesn't get this far
}
}
And the OnvifClient code also:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;
using Onvif.Security;
using UnityEngine;
namespace Onvif.Core
{
public class OnvifClient
{
private DeviceClient device;
private Binding binding;
private SoapSecurityHeaderBehavior securityHeader;
public Uri OnvifUri { get; private set; }
public string Host { get; private set; }
public string Username { get; private set; }
public string Password { get; private set; }
public Capabilities Capabilities { get; private set; }
public OnvifClient(string host, string username, string password)
{
Host = host;
OnvifUri = new Uri(host); //($"http://{host}/onvif/device_service");
Username = username;
Password = password;
}
public async Task<bool> Init()
{
try
{
//Have to set http continue to false since the server is not expecting it: https://forum.unity.com/threads/http-error-417-expectation-failed-unitywebrequest.651256/
System.Net.ServicePointManager.Expect100Continue = false;
binding = CreateBinding();
var endpoint = new EndpointAddress(OnvifUri);
device = new DeviceClient(binding, endpoint);
var timeShift = await GetDeviceTimeShift(device);
securityHeader = new SoapSecurityHeaderBehavior(Username, Password, timeShift);
device = new DeviceClient(binding, endpoint);
device.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
device.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
device.Open();
Capabilities = device.GetCapabilities(new CapabilityCategory[] { CapabilityCategory.All });
return true;
}
catch (Exception e)
{
Debug.LogError(e.Message);
return false;
}
}
public void Ping()
{
device.GetCapabilities(new CapabilityCategory[] { CapabilityCategory.All });
}
public void Close()
{
if (device != null)
device.Close();
}
public async Task<DeviceClient> GetDeviceClient()
{
if (device == null)
await Init();
return device;
}
//original
public async Task<MediaClient> CreateMediaClient()
{
if (device == null)
await Init();
var media = new MediaClient(binding, new EndpointAddress(new Uri(Capabilities.Media.XAddr)));
media.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
media.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return media;
}
//custom
public async Task<MediaClient> CreateMediaClient(string endpoint)
{
if (device == null)
await Init();
var media = new MediaClient(binding, new EndpointAddress(new Uri(endpoint)));
media.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
media.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return media;
}
//original
public async Task<PTZClient> CreatePTZClient()
{
if (device == null)
await Init();
var ptz = new PTZClient(binding, new EndpointAddress(new Uri(Capabilities.PTZ.XAddr)));
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return ptz;
}
//custom
public async Task<PTZClient> CreatePTZClient(string endpoint)
{
if (device == null)
await Init();
var ptz = new PTZClient(binding, new EndpointAddress(new Uri(endpoint)));
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return ptz;
}
public async Task<ImagingPortClient> CreateImagingClient()
{
if (device == null)
await Init();
var imaging = new ImagingPortClient(binding, new EndpointAddress(new Uri(Capabilities.Imaging.XAddr)));
imaging.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
imaging.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return imaging;
}
private static Binding CreateBinding()
{
var binding = new CustomBinding();
var textBindingElement = new TextMessageEncodingBindingElement
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
};
var httpBindingElement = new HttpTransportBindingElement
{
AllowCookies = true,
MaxBufferSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue
};
binding.Elements.Add(textBindingElement);
binding.Elements.Add(httpBindingElement);
return binding;
}
private static Task<TimeSpan> GetDeviceTimeShift(DeviceClient device)
{
return Task.Run(() =>
{
var utc = device.GetSystemDateAndTime().UTCDateTime;
var dt = new System.DateTime(utc.Date.Year, utc.Date.Month, utc.Date.Day,
utc.Time.Hour, utc.Time.Minute, utc.Time.Second);
return dt - System.DateTime.UtcNow;
});
}
}
}
I'm very new to .net client connection and so forth, so I'm really not sure why it would throw a 400 error, as the username and password are both correct. I also know for a fact that connecting to a camera works as I am working off of an existing project that does it through c# (outside of unity though).
Alternatively, I have tried avoiding using the helper scripts provided by Oneiros90 and connecting to the devices using the core onfiv scripts, as per this post: C# .NET Framework 4.52 Zoom/Focus for PELCO Camera via ONVIF
But when I do that, it reaches the mediaClient.getProfiles() line and instead produces an "InvalidOperationException: Use ClientCredentials to specify a user name for required HTTP Digest authentication." error, even though I am clearly setting the http digest client credentials just above. This is also how it works in the aforementioned existing project, although there isn't as much going on with the security headers and so on in that.
My Code in unity:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Onvif.Core;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
public class PTZController_Local : MonoBehaviour
{
public string hostAddress = "http://<secret>/onvif/device_service";
public string mediaAddress = "http://<secret>/onvif/media_service";
public string PTZAddress = "http://<secret>/onvif/ptz_service";
public string username = "";
public string password = "";
private OnvifClient onvifClient;
private string currentProfile;
private PTZClient ptzClient;
private MediaClient mediaClient;
private DeviceClient device;
private CustomBinding binding;
private CustomBinding mediaBinding;
private void Start()
{
////connect to onvif device service
//onvifClient = new OnvifClient(hostAddress, username, password);
//var success = await onvifClient.Init();
//if (!success)
//{
// Debug.LogError($"PTZController: Cannot connect to {hostAddress}");
// return;
//}
//Debug.Log($"PTZController: Successfully connected to {hostAddress}");
//device = await onvifClient.GetDeviceClient();
System.Net.ServicePointManager.Expect100Continue = false;
HttpTransportBindingElement httpTransport = new HttpTransportBindingElement();
httpTransport.AuthenticationScheme = System.Net.AuthenticationSchemes.Digest;
httpTransport.MaxReceivedMessageSize = Int32.MaxValue;
httpTransport.MaxBufferSize = Int32.MaxValue;
httpTransport.ProxyAddress = null;
httpTransport.BypassProxyOnLocal = true;
httpTransport.UseDefaultWebProxy = false;
httpTransport.TransferMode = TransferMode.StreamedResponse;
binding = new CustomBinding(new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, System.Text.Encoding.UTF8), httpTransport);
binding.CloseTimeout = TimeSpan.FromSeconds(30.0);
binding.OpenTimeout = TimeSpan.FromSeconds(30.0);
binding.SendTimeout = TimeSpan.FromMinutes(10.0);
binding.ReceiveTimeout = TimeSpan.FromMinutes(3.0);
device = new DeviceClient(binding, new EndpointAddress(hostAddress));
device.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
device.ClientCredentials.HttpDigest.ClientCredential.UserName = username;
device.ClientCredentials.HttpDigest.ClientCredential.Password = password;
var messageElement = new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
};
HttpTransportBindingElement mediaHttpTransport = new HttpTransportBindingElement()
{
AuthenticationScheme = System.Net.AuthenticationSchemes.Digest
};
mediaBinding = new CustomBinding(messageElement, mediaHttpTransport);
mediaClient = new MediaClient(mediaBinding, new EndpointAddress(mediaAddress));
mediaClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.UserName = username;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.Password = password;
var profiles = mediaClient.GetProfiles();
currentProfile = profiles.First().token;
//other code that is never reached
}
}
Original project code (i've tried copying this into unity exactly and get the same error about setting the http credentials):
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.ServiceModel;
using OnvifPipeClient.OnvifMedia10;
using OnvifPipeClient.OnvifPTZService;
using System.Timers;
using System.Diagnostics;
using System.Threading;
namespace OnvifPipeClient
{
public class PTZConnection
{
public static PTZClient ptzClient;
private static MediaClient mediaClient;
public static Profile profile;
// establish connection to PTZ
public static void OnvifLogin()
{
// create binding
System.Net.ServicePointManager.Expect100Continue = false;
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;
// connect to PTZ interface
ptzClient = new PTZClient(binding, new EndpointAddress("http://<secret>/onvif/ptz_service"));
ptzClient.ClientCredentials.HttpDigest.ClientCredential.UserName = "secret";
ptzClient.ClientCredentials.HttpDigest.ClientCredential.Password = "secret";
ptzClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
// connect to media client
mediaClient = new MediaClient(binding, new EndpointAddress("http://<secret>/onvif/media_service"));
mediaClient.ClientCredentials.HttpDigest.ClientCredential.UserName = "secret";
mediaClient.ClientCredentials.HttpDigest.ClientCredential.Password = "secret";
mediaClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
// load profiles and configurations
var profiles = mediaClient.GetProfiles();
profile = mediaClient.GetProfile(profiles[0].token);
//other code that is reached without issues
}
}
The only thread of a possible explanation I could find is this github issue(https://github.com/jamidon/ONVIF-test/issues/1) saying that another onvif c# library that oneiros90 had previously tried did not work with pelco cameras for some reason (the camera I'm trying to connect to is a pelco camera).
So, yeah, sorry for the big code dumps here but it's very difficult to find resources about this. Thanks in advance for any help.
Related
I'm having a problem with my Discord Bot where the only method of interfacing with the bot is by tagging it in a message. I've setup the code for how I thought the prefix functionality was supposed to work, but I've done something wrong. I don't get any error messages when in use and the bot functions perfectly fine if its tagged, but doesn't react at all if you try to use the prefix instead. Can someone explain please? (I'm using DSharp for the discord api)
Here's the exact part of the code I think there's a problem with:
var commandsConfig = new CommandsNextConfiguration
{
StringPrefixes = new List<string>() { configJson.Prefix },
EnableDms = false,
EnableMentionPrefix = true,
DmHelp = false,
EnableDefaultHelp = true,
UseDefaultCommandHandler = true
};
Console.WriteLine($"HELLO! {configJson.Prefix}"); //Testing it could read the desired prefix (it can)
Commands = Client.UseCommandsNext(commandsConfig);
The configJson.cs looks like this:
public struct ConfigJson
{
[JsonProperty("token")]
public string Token { get; private set; }
[JsonProperty("prefix")]
public string Prefix { get; private set; }
}
The config.Json file looks like this (The token is removed for security)
{
"token": "#######",
"prefix": "!"
}
I'll also post the entire .cs if you want to recreate the conditions
using DSharpPlus;
using DSharpPlus.CommandsNext;
using DSharpPlus.EventArgs;
using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Extensions;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using VLB.Commands.VLBCommands;
namespace VLB
{
public class Bot
{
public DiscordClient Client { get; private set; }
public InteractivityExtension Interactivity { get; private set; }
public CommandsNextExtension Commands { get; private set; }
public async Task RunAsync()
{
var json = string.Empty;
using (var fs = File.OpenRead("config.json"))
using (var sr = new StreamReader(fs, new UTF8Encoding(false)))
json = await sr.ReadToEndAsync().ConfigureAwait(false);
var configJson = JsonConvert.DeserializeObject<ConfigJson>(json);
var config = new DiscordConfiguration
{
Token = configJson.Token,
TokenType = TokenType.Bot,
AutoReconnect = true,
MinimumLogLevel = LogLevel.Debug,
};
Client = new DiscordClient(config);
Client.Ready += OnClientReady;
Client.UseInteractivity(new InteractivityConfiguration
{
Timeout = TimeSpan.FromSeconds(30)
});
var commandsConfig = new CommandsNextConfiguration
{
StringPrefixes = new List<string>() { configJson.Prefix },
EnableDms = false,
EnableMentionPrefix = true,
DmHelp = false,
EnableDefaultHelp = true,
UseDefaultCommandHandler = true
};
Console.WriteLine($"HELLO! {configJson.Prefix}"); //Testing it could read the desired prefix (it can)
Commands = Client.UseCommandsNext(commandsConfig);
Commands.RegisterCommands<VLBCommands>();
await Client.ConnectAsync();
await Task.Delay(-1);
}
private Task OnClientReady(DiscordClient client, ReadyEventArgs e)
{
return Task.CompletedTask;
}
}
}
I've tried changing the prefix to various conventional characters.
I've also tried changing the assignment to "StringPrefixes" to a string array instead of a list.
I'm only trying to use one prefix, but after scouring DSharp documentation I'm not sure CommandsNextConfiguration has a way to indicate only one prefix.
Thanks for reading :)
I have a class library written in C# that uses the google drive v3 API to keep a local file repository updated. This code works perfectly fine in a console application, however when I add a reference in the VBA tools to this DLL after applying the necessary measures (registering it etc), it does not run the updateFiles Method in VBA. It does however run other functions that do not depend on the google drive API. My question is, how can I get this dll working in VBA. If anyone could point me in the right direction that would be great.
Thanks in advance
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using Google.Apis.Services;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace fileUpdater
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Class1
{
private static DriveService service;
private static Dictionary<string, Google.Apis.Drive.v3.Data.File> files = new Dictionary<string, Google.Apis.Drive.v3.Data.File>();
private const string FOLDER_MIME_TYPE = "application/vnd.google-apps.folder";
private static double percentComplete;
private string AbsPath(Google.Apis.Drive.v3.Data.File file)
{
var name = file.Name;
if (file.Parents.Count() == 0)
{
return name;
}
var path = new List<string>();
while (true)
{
var parent = GetParent(file.Parents[0]);
if (parent.Parents == null || parent.Parents.Count() == 0)
{
break;
}
path.Insert(0, parent.Name);
file = parent;
}
path.Add(name);
return path.Aggregate((current, next) => Path.Combine(current, next));
}
private Google.Apis.Drive.v3.Data.File GetParent(string id)
{
if (files.ContainsKey(id))
{
return files[id];
}
var request = service.Files.Get(id);
request.Fields = "name,parents";
var parent = request.Execute();
files[id] = parent;
return parent;
}
public void updateFiles()
{
service = AuthenticateServiceAccount("serviceAccountEmail", #"serviceAccountCredentials");
if (service == null)
{
return;
}
FilesResource.ListRequest listRequest = service.Files.List();
listRequest.Fields = "files(id, name, mimeType, parents, createdTime)";
listRequest.PageSize = 1000;
IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute()
.Files;
double counter = 0.0f;
foreach (var file in files)
{
percentComplete = Math.Round((counter / files.Count) * 100, 2);
if (file.Parents != null)
{
string path = "downloadFile/" + AbsPath(file);
if (file.MimeType == FOLDER_MIME_TYPE)
{
if (!System.IO.Directory.Exists(path))
{
System.IO.Directory.CreateDirectory(path);
}
}
else
{
if (System.IO.File.Exists(path))
{
if (DateTime.Compare((DateTime)file.CreatedTime, System.IO.File.GetCreationTime(path)) > 0)
{
System.IO.File.Delete(path);
downloadFile(file, path);
}
}
else
{
downloadFile(file, path);
}
}
}
counter++;
}
}
private void downloadFile(Google.Apis.Drive.v3.Data.File file, string path)
{
var stream = new System.IO.MemoryStream();
service.Files.Get(file.Id).Download(stream);
System.IO.FileStream fileToWrite = new System.IO.FileStream(path, System.IO.FileMode.Create, System.IO.FileAccess.Write);
stream.WriteTo(fileToWrite);
fileToWrite.Close();
}
private DriveService AuthenticateServiceAccount(string serviceAccountEmail, string keyFilePath)
{
if (!System.IO.File.Exists(keyFilePath))
{
return null;
}
string[] scopes = new string[] { DriveService.Scope.Drive };
var certificate = new X509Certificate2(keyFilePath, "password", X509KeyStorageFlags.Exportable);
try
{
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
DriveService service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample"
});
return service;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
}
public double getPercentComplete()
{
return percentComplete;
}
}
}
I need a C# configuration which would generate envelope shown on the third part of the screenshot. Currently I can only get as close as shown on the first two. My current config:
using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Security;
using System.ServiceModel.Security.Tokens;
using System.Text;
public class CustomAlgorithmSuite : SecurityAlgorithmSuite
{
public override string DefaultAsymmetricKeyWrapAlgorithm { get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }}
public override string DefaultAsymmetricSignatureAlgorithm { get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }}
public override string DefaultCanonicalizationAlgorithm { get { return "http://www.w3.org/2001/10/xml-exc-c14n#"; }}
public override string DefaultDigestAlgorithm { get { return "http://www.w3.org/2000/09/xmldsig#sha1"; }}
public override string DefaultEncryptionAlgorithm { get { return "http://www.w3.org/2001/04/xmlenc#aes256-cbc"; }}
public override int DefaultEncryptionKeyDerivationLength { get { return SecurityAlgorithmSuite.Default.DefaultEncryptionKeyDerivationLength; }}
public override int DefaultSignatureKeyDerivationLength { get { return SecurityAlgorithmSuite.Default.DefaultSignatureKeyDerivationLength; }}
public override int DefaultSymmetricKeyLength { get { return SecurityAlgorithmSuite.Default.DefaultSymmetricKeyLength; }}
public override string DefaultSymmetricKeyWrapAlgorithm { get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }}
public override string DefaultSymmetricSignatureAlgorithm { get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }}
public override bool IsAsymmetricKeyLengthSupported(int length) { return true; }
public override bool IsSymmetricKeyLengthSupported(int length) { return true; }
}
class Program
{
static void Main()
{
X509SecurityTokenParameters x509Params = new X509SecurityTokenParameters()
{
X509ReferenceStyle = X509KeyIdentifierClauseType.RawDataKeyIdentifier,
InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient,
ReferenceStyle = SecurityTokenReferenceStyle.External,
RequireDerivedKeys = false
};
SecurityBindingElement security = new TransportSecurityBindingElement()
{
MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10,
DefaultAlgorithmSuite = new CustomAlgorithmSuite()
};
security.EndpointSupportingTokenParameters.Endorsing.Add(x509Params);
security.SetKeyDerivation(false);
//security.IncludeTimestamp = false;
TextMessageEncodingBindingElement encoding = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
HttpsTransportBindingElement transport = new HttpsTransportBindingElement();
//transport.RequireClientCertificate = true;
CustomBinding customBinding = new CustomBinding(security, encoding, transport);
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
var twoCertificatesInOneFile = new X509Certificate2Collection();
twoCertificatesInOneFile.Import("foo path", "foo cert pass", X509KeyStorageFlags.Exportable);
someGeneratedServiceClass client = new someGeneratedServiceClass(customBinding, new EndpointAddress(new Uri("foo webservice address"), EndpointIdentity.CreateDnsIdentity(twoCertificatesInOneFile[0].FriendlyName)));
client.ClientCredentials.ServiceCertificate.DefaultCertificate = twoCertificatesInOneFile[0];
client.ClientCredentials.ClientCertificate.Certificate = twoCertificatesInOneFile[1];
//client.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None;
client.ClientCredentials.UserName.UserName = "foo user";
client.ClientCredentials.UserName.Password = "foo pass";
client.someServiceCall("foo", "foo", false, out i1, out i2);
}
}
Is there any property I'm missing, or must I implement a custom token / encoder for this job?
This has the same answer as this question.
There was no standard way of achieving this nonstandard implementation, so I ended up rewriting parts of the generated SOAP message manually.
When I run my c# console app the windows firewall pops up requesting access for vshost32 (my app listens for incomming messages over port 1234 using TCP and UDP). I accept the offered suggestion (private network). The console app then works fine.
I dont want the user to deal with this, so I have added the code below. However when I investigate what this has done in Control Panel > Firewall, it seems to have enabled it for the 'public network' rather than the private network. This is no use as far as allowing my app to work.
Is there an adjustment in the code below to force it to the private network?
INetFwOpenPorts ports3;
INetFwOpenPort port3 = (INetFwOpenPort)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FWOpenPort"));
port3.Port = 1234;
port3.Name = "vshost32.exe";
port3.Enabled = true;
//**UPDATE** added for suggestion in answer below - still doesnt change anything though
port3.Scope = NetFwTypeLib.NET_FW_SCOPE_.NET_FW_SCOPE_LOCAL_SUBNET;
Type NetFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false);
INetFwMgr mgr3 = (INetFwMgr)Activator.CreateInstance(NetFwMgrType);
ports3 = (INetFwOpenPorts)mgr3.LocalPolicy.CurrentProfile.GloballyOpenPorts;
ports3.Add(port3);
INetFwRule firewallRule = (INetFwRule)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FWRule"));
INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
firewallRule.ApplicationName = "<path to your app>";
firewallRule.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW;
firewallRule.Description = " My Windows Firewall Rule";
firewallRule.Enabled = true;
firewallRule.InterfaceTypes = "All";
firewallRule.Name = "<your rule name>";
// Should really check that rule is not already present before add in
firewallPolicy.Rules.Add(firewallRule);
Refer to my answer to your previous question.
Have a look at the following lines:
private static int Main (string [] args)
{
var application = new NetFwAuthorizedApplication()
{
Name = "MyService",
Enabled = true,
RemoteAddresses = "*",
Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL,
IpVersion = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY,
ProcessImageFileName = "ServiceAssemblyName.dll",
};
return (FirewallUtilities.AddApplication(application, out exception) ? 0 : -1);
}
The NET_FW_SCOPE_ enumeration has the following values:
NET_FW_SCOPE_ALL = 0,
NET_FW_SCOPE_LOCAL_SUBNET = 1,
NET_FW_SCOPE_CUSTOM = 2,
NET_FW_SCOPE_MAX = 3,
You can further limit the ports, protocol as well as remote addresses to the rule.
UPDATE:
Here is the missing ReleaseComObject function. Place it whatever namespace and remove the reference to ComUtilities.
public static void ReleaseComObject (object o)
{
try
{
if (o != null)
{
if (Marshal.IsComObject(o))
{
Marshal.ReleaseComObject(o);
}
}
}
finally
{
o = null;
}
}
Here is the NetFwAuthorizedApplication class:
namespace MySolution.Configurator.Firewall
{
using System;
using System.Linq;
using NetFwTypeLib;
public sealed class NetFwAuthorizedApplication:
INetFwAuthorizedApplication
{
public string Name { get; set; }
public bool Enabled { get; set; }
public NET_FW_SCOPE_ Scope { get; set; }
public string RemoteAddresses { get; set; }
public string ProcessImageFileName { get; set; }
public NET_FW_IP_VERSION_ IpVersion { get; set; }
public NetFwAuthorizedApplication ()
{
this.Name = "";
this.Enabled = false;
this.RemoteAddresses = "";
this.ProcessImageFileName = "";
this.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
this.IpVersion = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY;
}
public NetFwAuthorizedApplication (string name, bool enabled, string remoteAddresses, NET_FW_SCOPE_ scope, NET_FW_IP_VERSION_ ipVersion, string processImageFileName)
{
this.Name = name;
this.Scope = scope;
this.Enabled = enabled;
this.IpVersion = ipVersion;
this.RemoteAddresses = remoteAddresses;
this.ProcessImageFileName = processImageFileName;
}
public static NetFwAuthorizedApplication FromINetFwAuthorizedApplication (INetFwAuthorizedApplication application)
{
return (new NetFwAuthorizedApplication(application.Name, application.Enabled, application.RemoteAddresses, application.Scope, application.IpVersion, application.ProcessImageFileName));
}
}
}
I'm new to C# and I'm having some difficulty with saving the XML Atom feed from Gmail to an xml file. I'm certain I'm miles off of where I need to be and I'm embarassed to be asking this but I'm not getting anywhere on my own:(
I'm using the GmailHandler class that's been floating around for some time.
GmailHandler.cs
using System;
using System.Data;
using System.Xml;
using System.Net;
using System.IO;
/*
* this code made by Ahmed Essawy
* AhmedEssawy#gmail.com
* http://fci-h.blogspot.com
*/
/// <summary>
/// Summary description for Class1
/// </summary>
public class GmailHandler
{
private string username;
private string password;
private string gmailAtomUrl;
public string GmailAtomUrl
{
get { return gmailAtomUrl; }
set { gmailAtomUrl = value; }
}
public string Password
{
get { return password; }
set { password = value; }
}
public string Username
{
get { return username; }
set { username = value; }
}
public GmailHandler(string _Username, string _Password, string _GmailAtomUrl)
{
Username = _Username;
Password = _Password;
GmailAtomUrl = _GmailAtomUrl;
}
public GmailHandler(string _Username, string _Password)
{
Username = _Username;
Password = _Password;
GmailAtomUrl = "https://mail.google.com/mail/feed/atom";
}
public XmlDocument GetGmailAtom()
{
byte[] buffer = new byte[8192];
int byteCount = 0;
XmlDocument _feedXml = null;
try
{
System.Text.StringBuilder sBuilder = new System.Text.StringBuilder();
WebRequest webRequest = WebRequest.Create(GmailAtomUrl);
webRequest.PreAuthenticate = true;
System.Net.NetworkCredential credentials = new NetworkCredential(this.Username, this.Password);
webRequest.Credentials = credentials;
WebResponse webResponse = webRequest.GetResponse();
Stream stream = webResponse.GetResponseStream();
while ((byteCount = stream.Read(buffer, 0, buffer.Length)) > 0)
sBuilder.Append(System.Text.Encoding.ASCII.GetString(buffer, 0, byteCount));
_feedXml = new XmlDocument();
_feedXml.LoadXml(sBuilder.ToString());
}
catch (Exception ex)
{
//add error handling
throw ex;
}
return _feedXml;
}
}
Then I've got my Program.cs here:
I'm assuming the issue is with the code below since I'm responsible for that, and not what's above.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace GmailAtom
{
class Program
{
static void Main()
{
//Create the object from GmailHandler class
GmailHandler gmailFeed = new GmailHandler("username", "password");
//get the feed
XmlDocument myXml = gmailFeed.GetGmailAtom();
XmlTextWriter writer = new XmlTextWriter("data.xml", null);
writer.Formatting = Formatting.Indented;
myXml.Save(writer);
}
}
}
When I run the program I get a "WebException was unhandled - The remote server returned an error: (407) Proxy Authentication Required."
Any advice would be appreciated!
I have tried the code and it works fine (but I have no proxy in my network).
I have changed the GmailHandler.cs, the constructor now accepts a internet proxy.
using System;
using System.Data;
using System.Xml;
using System.Net;
using System.IO;
/*
* this code made by Ahmed Essawy
* AhmedEssawy#gmail.com
* http://fci-h.blogspot.com
*/
/// <summary>
/// Summary description for Class1
/// </summary>
public class GmailHandler
{
private string username;
private string password;
private string gmailAtomUrl;
private string proxy;
public string GmailAtomUrl
{
get { return gmailAtomUrl; }
set { gmailAtomUrl = value; }
}
public string Password
{
get { return password; }
set { password = value; }
}
public string Username
{
get { return username; }
set { username = value; }
}
public string Proxy
{
get { return proxy; }
set { proxy = value; }
}
public GmailHandler(string _Username, string _Password, string _GmailAtomUrl, string _proxy = null)
{
Username = _Username;
Password = _Password;
GmailAtomUrl = _GmailAtomUrl;
Proxy = _proxy;
}
public GmailHandler(string _Username, string _Password, string _proxy = null)
{
Username = _Username;
Password = _Password;
GmailAtomUrl = "https://mail.google.com/mail/feed/atom";
Proxy = _proxy;
}
public XmlDocument GetGmailAtom()
{
byte[] buffer = new byte[8192];
int byteCount = 0;
XmlDocument _feedXml = null;
try
{
System.Text.StringBuilder sBuilder = new System.Text.StringBuilder();
WebRequest webRequest = WebRequest.Create(GmailAtomUrl);
if(!String.IsNullOrWhiteSpace(Proxy))
webRequest.Proxy = new WebProxy(Proxy, true);
webRequest.PreAuthenticate = true;
System.Net.NetworkCredential credentials = new NetworkCredential(this.Username, this.Password);
webRequest.Credentials = credentials;
WebResponse webResponse = webRequest.GetResponse();
Stream stream = webResponse.GetResponseStream();
while ((byteCount = stream.Read(buffer, 0, buffer.Length)) > 0)
sBuilder.Append(System.Text.Encoding.ASCII.GetString(buffer, 0, byteCount));
_feedXml = new XmlDocument();
_feedXml.LoadXml(sBuilder.ToString());
}
catch (Exception ex)
{
//add error handling
throw ex;
}
return _feedXml;
}
}
Use this in your console application:
//Create the object from GmailHandler class
GmailHandler gmailFeed = new GmailHandler("username", "password", "http://proxyserver:80/");
//get the feed
XmlDocument myXml = gmailFeed.GetGmailAtom();
XmlTextWriter writer = new XmlTextWriter("data.xml", null);
writer.Formatting = Formatting.Indented;
myXml.Save(writer);