How to post data to a web server using wpf webbrowser - c#

I want to get data from a database and use it to login a user to a web site.
I have a wpf page that holds a web browser control. and I have this code that login user to web site which is written in php:
<form action='http://www.asite.net/index.php' method='post' name='frm'>
<?php
$user = $_GET['u'];
$pass = $_GET['p'];
echo "<input type='text' name='user' value='$user'>";
echo "<input type='text' name='pass' value='$pass'>";
?>
<input type='submit' name='submit' value='submit'>
</form>
How can I do this in wpf? As far as I can understand, I need to create an html and post it to site.
My questions:
1- How can I create such html in code?
2- How can I automatically submit it to the site (assuming I am doing this on constructor of a wpf user control).

As far as I understand, your goal is to log in and keep the session active inside the WebBrowser. If so, you have a few options:
First, navigate the WebBrowser to www.asite.net, to establish the session.
Then obtain the underlying WebBrowser ActiveX control and use IWebBrowser2::Navigate2 method, it has PostData parameter which allows to do an HTTP POST request.
Or, inject and execute some JavaScript which would use XHR to post the form the AJAX way.
Or, use WebBrowser.Document as dynamic to create a hidden form element, populate it and submit it, in the same way you'd do with JavaScript.
Or, use COM XMLHTTPobject to send a POST request, it shares the session with the WebBrowser.
You could also use some low level UrlMon API to send a POST request.
Updated, here is an example of creating and submitting a :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
NavigatedEventHandler handler = null;
handler = delegate
{
this.webBrowser.Navigated -= handler;
dynamic document = this.webBrowser.Document;
var form = document.createElement("form");
form.action = "http://requestb.in/tox7drto";
form.method = "post";
var input = document.createElement("input");
input.type = "text";
input.name = "name_1";
input.value = "value_1";
form.appendChild(input);
input = document.createElement("input");
input.type = "submit";
form.appendChild(input);
document.body.appendChild(form);
input.click();
};
this.webBrowser.Navigated += handler;
this.webBrowser.Navigate("about:blank");
}
}

Use the System.Net namespace, particularly the WebRequest and WebResponse objects.
See this previous answer, it should get you started:
How to programmatically fill a form and post a web page

Related

submit ajax aspforms req to modify viewstate

I'm working on a very specific web-scraping application, and it needs to login to several websites and retrieve some data from them.
I am using a WebClient that has been made aware of cookies by overriding the following method:
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
var castRequest = request as HttpWebRequest;
if (castRequest != null)
{
castRequest.CookieContainer = this.CookieContainer;
}
return request;
}
I can login to the sites fine with regular POST/GET requests (via the appropriate download/upload methods on the webclient)
The targetted websites use ajax ASP.Net top-level forms, and there is a state variable that gets enabled after you click a button on the page. That is, when you click the button, the form gets submitted, the state gets changed, and then when it loads the response it has the information I need. The state modification at this point is also persistent. If i reload the page, or even close the tab and re-open it, the data i need will still be there because it is associated with the ASP session. As soon as the ASP session expires, you have to login and click the button again before the server will send the data I need.
I have watched the submitted form via the Chrome developer tools when clicking the button, and i re-created the form submit exactly as I saw it in the chrome network watch window, but it still does not correctly modify the viewstate.
So my question is, how can i simulate clicking this button so that the server will modify the viewstate and return the value i need.
I can not use a web-browser control for this, but I could use the html agility pack if it makes things substantially easier (although I really would like not to use an external library)
The button is defined as this:
<form name="aspnetForm" method="post" action="enterurlhere..." id="aspnetForm">
<input type="image" name="ctl00$....." id="ctl00...." title="...." src="...." style="height:50px;border-width:0px;">
if your target is ASP.NET WebForms site which:
1) you must login first to navigate to the required page
2) on the required page there is an UpdatePanel that has, let's say a textbox into which you need to enter something and then submit that information and if that information is correct, you will get "what you expect"
I've done previously various crawlers, thus took one as the base but stripped down quite, well, a lot, no error logging, validation that you are logged in, validation that you are still logged in when requesting the page, HtmlAgilityPack, structure, code cleanness, user agent string randomization etc. to keep it simple for you, but you of course can enhance it :) Anyway, I've created a web project (Web Forms) in Visual Studio 2013. As you may know it has some landing pages including user registration etc. Then you have "Manage account" page, which obviously requires user to be authenticated. On that page I added another div, then inside of it I placed UpdatePanel (that makes postback ajaxified). Inside UpdatePanel I placed textbox, a button and a literal server controls. In code behind I added a click event handler for that button: if user input is equal to, let's say "secret" then put some text into the literal to indicate that operation was successful. Thus the application had to login first then get that secret text by submitting the secret phrase to "Manage account" page.
Actual fetcher:
using Pokemon.BL.Utils;
using System;
using System.Text;
using System.Web;
namespace Pokemon.BL
{
sealed class UrlFetcher : IDisposable
{
private static readonly UrlFetcher _instance;
private CGWebClient _cgWebClient;
private string loginPostString = "__EVENTTARGET={0}&__EVENTARGUMENT={1}&__VIEWSTATE={2}&__VIEWSTATEGENERATOR={3}&__EVENTVALIDATION={4}&ctl00$MainContent$Email={5}&ctl00$MainContent$Password={6}&ctl00$MainContent$ctl05={7}";
private string secretPhrasePostString = "__EVENTTARGET={0}&__EVENTARGUMENT={1}&__VIEWSTATE={2}&__VIEWSTATEGENERATOR={3}&__EVENTVALIDATION={4}&__ASYNCPOST=true&ctl00$MainContent$btnGetSecretPhrase=Button&ctl00$ctl08=ctl00$MainContent$UpdatePanel1|ctl00$MainContent$btnGetSecretPhrase&ctl00$MainContent$txtSecret={5}";
private UrlFetcher()
{
_cgWebClient = new CGWebClient();
}
static UrlFetcher()
{
_instance = new UrlFetcher();
}
#region Methods
public void LoginToSite(string email, string password)
{
var loginUrl = "http://localhost:53998/Account/Login";
byte[] response = _cgWebClient.DownloadData(loginUrl);
var content = Encoding.UTF8.GetString(response);
string eventTarget = ExtractToken("__EVENTTARGET", content);
string eventArg = ExtractToken("__EVENTARGUMENT", content);
string viewState = ExtractToken("__VIEWSTATE", content);
string viewStateGen = ExtractToken("__VIEWSTATEGENERATOR", content);
string eventValidation = ExtractToken("__EVENTVALIDATION", content);
string postData = string.Format(
loginPostString,
eventTarget,
eventArg,
viewState,
viewStateGen,
eventValidation,
email,
password,
"Log in"
);
_cgWebClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
response = _cgWebClient.UploadData(loginUrl, "POST", Encoding.UTF8.GetBytes(postData));
_cgWebClient.Headers.Remove("Content-Type");
}
public void GetSecretPhrase()
{
var loginUrl = "http://localhost:53998/Account/Manage";
byte[] response = _cgWebClient.DownloadData(loginUrl);
var content = Encoding.UTF8.GetString(response);
string eventTarget = ExtractToken("__EVENTTARGET", content);
string eventArg = ExtractToken("__EVENTARGUMENT", content);
string viewState = ExtractToken("__VIEWSTATE", content);
string viewStateGen = ExtractToken("__VIEWSTATEGENERATOR", content);
string eventValidation = ExtractToken("__EVENTVALIDATION", content);
string postData = string.Format(
secretPhrasePostString,
eventTarget,
eventArg,
viewState,
viewStateGen,
eventValidation,
"secret"
);
_cgWebClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
_cgWebClient.Headers.Add("X-Requested-With", "XMLHttpRequest");
response = _cgWebClient.UploadData(loginUrl, "POST", Encoding.UTF8.GetBytes(postData));
_cgWebClient.Headers.Remove("Content-Type");
_cgWebClient.Headers.Remove("X-Requested-With");
Console.WriteLine(Encoding.UTF8.GetString(response));
}
#region IDisposable Members
public void Dispose()
{
if (_cgWebClient != null)
{
_cgWebClient.Dispose();
}
}
#endregion
private string ExtractToken(string whatToExtract, string content)
{
string viewStateNameDelimiter = whatToExtract;
string valueDelimiter = "value=\"";
int viewStateNamePosition = content.IndexOf(viewStateNameDelimiter);
int viewStateValuePosition = content.IndexOf(valueDelimiter, viewStateNamePosition);
int viewStateStartPosition = viewStateValuePosition + valueDelimiter.Length;
int viewStateEndPosition = content.IndexOf("\"", viewStateStartPosition);
return HttpUtility.UrlEncode(
content.Substring(
viewStateStartPosition,
viewStateEndPosition - viewStateStartPosition
)
);
}
#endregion
#region Properties
public static UrlFetcher Instance { get { return _instance; } }
#endregion
}
}
WebClient wrapper:
using System;
using System.Collections.Generic;
using System.Net;
namespace Pokemon.BL.Utils
{
// http://codehelp.smartdev.eu/2009/05/08/improve-webclient-by-adding-useragent-and-cookies-to-your-requests/
public class CGWebClient : WebClient
{
private System.Net.CookieContainer cookieContainer;
private string userAgent;
private int timeout;
public System.Net.CookieContainer CookieContainer
{
get { return cookieContainer; }
set { cookieContainer = value; }
}
public string UserAgent
{
get { return userAgent; }
set { userAgent = value; }
}
public int Timeout
{
get { return timeout; }
set { timeout = value; }
}
public CGWebClient()
{
timeout = -1;
userAgent = "Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0";
cookieContainer = new CookieContainer();
}
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
if (request.GetType() == typeof(HttpWebRequest))
{
((HttpWebRequest)request).CookieContainer = cookieContainer;
((HttpWebRequest)request).UserAgent = userAgent;
((HttpWebRequest)request).Timeout = timeout;
}
return request;
}
}
}
and finally run it:
UrlFetcher.Instance.LoginToSite("username", "password");
UrlFetcher.Instance.GetSecretPhrase();
UrlFetcher.Instance.Dispose();
this outputs the secret phrase into console application. Of course you will need to tweak this to make it work, for example depending on the ASP.NET version your target site is running and so on :)
Hope this helps :)
I don't think this will work server-side, because the client needs the session information. To do this you could implement an Iframe control that you could load the form in and call a server side or client side call to click the button in the Iframe and load the session information.

Using OnClientClick and OnClick events?

I have situation where a client goes to a website to complete a webform and upon submission/click of an asp button performs two acts:
1) runs a jquery script that opens an iframe for an embedded form to a thirdparty service that gets information via a url created by the jquery also (in this case docusign)
2) runs a method to insert specific data about the submission to an sql database table.
It is my understanding that the OnClient has to fire prior to the OnClick as well as some sort of return/postback if I understand correctly is needed to fire the onclick/severside method. So my difficulty has been trying understand how best to get the serverside method to fire... Any thought would be greatly appreciated. At first thought adding some sort of return true function but do not see how this would work. Thanks for any input and forward guidance.
So currently my asp button OnClientClick calls the following jquery that opens an iframe... I cannot figure how to also have it postback to cause the Onclick event to fire as well? Unless I can have the jquery fire/called from behind?
(jquery used to open/embed form)
function $$(elem) {return document.getElementById(elem);} //simple id reference
//Powerform/Iframe Specifics
number_messages = 0;
var last_id = "";
function check_messages() { //check for messages from iframe
if (location.hash != last_id) {
last_id = location.hash;
number_messages++;
last_id = last_id.replace("_"," ");
last_id = last_id.substr(1);
last_id = last_id.substr(0,last_id.indexOf("&"));
var message_color;
var extra_text;
if(last_id=="Signing Complete" || last_id=="Viewing Complete"){
message_color="green";
extra_text = "";
}else{
message_color="red";
extra_text = "<p style='text-align:center'><button type='form.button' onclick='window.location=\"embedded.html\"' style='display:inline;'>Reload form?</button></p>";
}
document.getElementById("powerform").innerHTML = "<center><h3 style='color:"+message_color+";border:none;font-size:20px;text-align:center;'>" + last_id + "</h3><br/>"+extra_text+"</center>";
}
}
function open_embeddedform(form) {
var form_url = "https://demo.docusign.net/MEMBER/PowerFormSigning.aspx";
form_url += "?PowerFormId=" + $$("Powerformid").value;
form_url += "&UserName=" + $$("UserName").value;
form_url += "&UserEmail=" + $$("UserEmail").value;
//alert(form_url);
$$("powerform").innerHTML = '<iframe id="document" src="' + form_url + '" border="0"></iframe>';
}
setInterval(check_messages, 200);
Could you run the server side as a PageMethod or Web Service which the client side sends an Ajax request to with the information about the submission? That way you could also return a success to the user to display some completion message.
Make sure whatever your Onclientclick calls returns true. Check out This post for more detail.

Drupal Views not returning a field for WP7's WebClient?

I have a Drupal Views 2 view that returns a number of fields for some nodes as JSON. I can view it in Chrome, and it has everything I expect.
When I download it on my Windows Phone 7 app, one field, "field_images_nid" is mysteriously missing from the response. What could have caused this? Does Drupal not send that field because of some user agent data on WP7's WebClient?
WebClient data = new WebClient();
data.DownloadStringCompleted += new DownloadStringCompletedEventHandler(onComplete);
data.DownloadStringAsync(new Uri(dataUri));
The field in question is a link to an image associated with the node. Views refers to it as "Content: Images Full Node".
Update: I made a new test app that does nothing but make the request so I can view the result in a debugger:
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri("http://path/to/service"));
}
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string result = e.Result;
}
}
I made a new view that does nothing but return "Title" and "Content: Images (full node)" for a given nid. I am able to observe the same incongruence between the Chrome and WP7 request.
Update 2: When I go to the views url in Chrome incognito, the field is not returned. Perhaps there is a permissions issue at work? I thought the field should be anonymously available.
Turns out the problem was server side. When I was testing it in my browser, I was always logged in. Making the content type available anonymously solved the issue.

Passing javascript variables to server-side C# logic

Is there a way to assign/pass/copy a javascript variable to a server side variable in C#? For the sake of argument, let's say that I was able to parse some JSON for variables that I want to store and assign them on the client (ie. var = FirstName and var = 25 and var = someDateTime, etc) .
Javascript variables exist on the client so in order to get those values into the server you'll need to execute a request from the client. You probably want an approach called AJAX. AJAX involves Javascript making requests to the server in the background of your page. You'll set up a C# web page that expects these background requests. If you use a GET request then then place the variables in the query string of your AJAX request to your new C# page. If you want to use a POST request then you'll set parameters in the data that you post to your page.
Libraries like jQuery make this kind of thing pretty simple.
There's no direct way to access variables in client-side code from your server-side code.
An easy way, without writing handlers, ajax posts, etc., to accomplish this is to simply store the java script variable in a hidden text box and retrieve it on your post. You can also write back to the hidden field and feed your script with the value, e.g.
Markup
<asp:HiddenField runat="server" Id="JavascriptValue" value="0">
Script
<script>
var myValue = <%=JavascriptValue.Value%>
</script>
Server-Side
protected void Page_Load(object sender, EventArgs e)
{
string val = JavascriptValue.Value;
}
Write the value to a control (e.g. HiddenField) via JS and read that on the postback.
You can register hidden fields from code-behind on the Page_Load
if (this.Request.Form["myHiddenField"] == null) {
ClientScript.RegisterHiddenField("myHiddenField", ""); }
populate it with a script
ClientScript.RegisterOnSubmitStatement(this.GetType(),
MethodBase.GetCurrentMethod().DeclaringType.Name + "_myHiddenField",
"var res=document.getElementById('myHiddenField');if(res!=null){res.value='some value';}");
and read it on postbacks (also Page_Load)
var myValue = (!IsPostBack)
? null
: this.Request.Form["myHiddenField"];
what I did is save the javaScript variable in a cookie and then read it from C#.
JavaScript Code:
<script>
$(document).ready(function () {
createCookie("height", $(window).height(), "10");
});
function createCookie(name, value, days) {
var expires;
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toGMTString();
} else {
expires = "";
}
document.cookie = escape(name) + "=" + escape(value) + expires + "; path=/";
}
</script>
C# Code:
height of browser:#Request.Cookies["height"].Value;

Redirecting to AND posting data to an external page

I have a search page on my .NET 3.5 Web Forms site that redirects a user to an external site based on the user's search parameters. I would redirect to: http://www.site.com/search.aspx?searchterm=Hello.
But now they are changing the site so that the search parameter is passed as a POST parameter, and not in the query string. So the page is expecting "searchterm".
So not only do I need to redirect to the external page, I have to post data to the page as well. I have no idea how to do this and I don't know where to start.
Is this something I can do in Web Forms without some glitchy workaround? Or maybe it can be done using jQuery?
Most browsers will explicitely deny this. Doing a cross server post like this would lead to security issues.
You can create simple JavaScript function for execute POST redirect to external page (dynamicaly generate and initilaze form object and submit it).
For example (values pattern: a=1&b=2&c=3 ...):
function bind(pageURL, values) {
var form=document.createElement('form');
form.action= pageURL;
form.target='_blank';
form.style.display = 'none';
form.method = 'POST';
var valuesSplit = node.get_value().toString().split("&");
for (var i = 0; i < valuesSplit.length - 1; i++) {
var p = valuesSplit[i];
var ps = p.split('=');
addParam(form, ps[0], ps[1]);
}
document.body.appendChild(form);
form.submit();
}
function addParam(form,key,value){
var input= document.createElement('input');
input.name=key;
input.value=value;
form.appendChild(input);
}

Categories

Resources