I'm trying to use google apps script to login to an ASP.Net website and scrape some data that I typically have to retrieve manually. I've used Chrome Developer tools to get the correct payload names (TEXT_Username, TEXT_Password, _VIEWSTATE, _VIEWSTATEGENERATOR), I also got a ASP Net session Id to send along with my Post request.
When I run my function(s) it returns a Response Code = 200 if followRedirects is set to false and returns Response Code = 302 if followRedirects is set to true. Unfortunately in neither case do the functions successfully authenticate the website. Instead the HTML returned is that of the Login Page.
I've tried different header variants and parameters, but I can't seem to successfully login.
Couple of other points. When I do the login in Chrome using the Developer tools, the response code appears to be 302 Found.
Does anyone have any suggestions on how I can successfully login to this site. Do you see any errors in my functions that could be the cause of my problems. I'm open to any and all suggestions.
My GAS functions follow:
function login(cookie, viewState,viewStateGenerator) {
var payload =
{
"__VIEWSTATE" : viewState,
"__VIEWSTATEGENERATOR" : viewStateGenerator,
"TEXT_Username" : "myUserName",
"TEXT_Password" : "myPassword",
};
var header = {'Cookie':cookie};
Logger.log(header);
var options =
{
"method" : "post",
"payload" : payload,
"followRedirects" : false,
"headers" : header
};
var browser = UrlFetchApp.fetch("http://tnetwork.trakus.com/tnet/Login.aspx?" , options);
Utilities.sleep(1000);
var html = browser.getContentText();
var response = browser.getResponseCode();
var cookie2 = browser.getAllHeaders()['Set-Cookie'];
Logger.log(response);
Logger.log(html);
}
function loginPage() {
var options =
{
"method" : "get",
"followRedirects" : false,
};
var browser = UrlFetchApp.fetch("http://tnetwork.trakus.com/tnet/Login.aspx?" , options);
var html = browser.getContentText();
// Utilities.sleep(500);
var response = browser.getResponseCode();
var cookie = browser.getAllHeaders()['Set-Cookie'];
login(cookie);
var regExpGen = new RegExp("<input type=\"hidden\" name=\"__VIEWSTATEGENERATOR\" id=\"__VIEWSTATEGENERATOR\" value=\"(.*)\" \/>");
var viewStateGenerator = regExpGen.exec(html)[1];
var regExpView = new RegExp("<input type=\"hidden\" name=\"__VIEWSTATE\" id=\"__VIEWSTATE\" value=\"(.*)\" \/>");
var viewState = regExpView.exec(html)[1];
var response = login(cookie,viewState,viewStateGenerator);
return response
}
I call the script by running the loginPage() function. This function obtains the cookie (session id) and then calls the login function and passes along the session id (cookie).
Here is what I see in the Google Developer tools Network section when I login using Google's Chrome browser:
Remote Address: 66.92.89.141:80
Request URL: http://tnetwork.trakus.com/tnet/Login.aspx
Request Method: POST
Status Code:302 Found
**Request Headers** view source
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate
Accept-Language: en-US,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Content-Length: 252
Content-Type:application/x-www-form-urlencoded
Cookie: ASP.NET_SessionId=jayaejut5hopr43xkp0vhzu4; userCredentials=username=myUsername; .ASPXAUTH=A54B65A54A850901437E07D8C6856B7799CAF84C1880EEC530074509ADCF40456FE04EC9A4E47D1D359C1645006B29C8A0A7D2198AA1E225C636E7DC24C9DA46072DE003EFC24B9FF2941755F2F290DC1037BB2B289241A0E30AF5CB736E6E1A7AF52630D8B31318A36A4017893452B29216DCF2; __utma=260442568.1595796669.1421539534.1425211879.1425214489.16; __utmc=260442568; __utmz=260442568.1421539534.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utma=190106350.1735963725.1421539540.1425152706.1425212185.18; __utmc=190106350; __utmz=190106350.1421539540.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Host:tnetwork.trakus.com
Origin:http://tnetwork.trakus.com
Referer:http://tnetwork.trakus.com/tnet/Login.aspx?
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36
**Form Dataview** sourceview URL encoded
__VIEWSTATE: O7YCnq5e471jHLqfPre/YW+dxYxyhoQ/VetOBeA1hqMubTAAUfn+j9HDyVeEgfAdHMl+2DG/9Gw2vAGWYvU97gml5OXiR9E/9ReDaw9EaQg836nBvMMIjE4lVfU=
__VIEWSTATEGENERATOR:F4425990
TEXT_Username:myUsername
TEXT_Password:myPassword
BUTTON_Submit: Log In
Update: It appears that the website is using an HttpOnly cookie. As a result, I don't think I am capturing the whole cookie and therefore my header is not correct. As a result, I believe I need to set followRedirects to false and handle the redirect and cookie manually. I'm currently researching this process, but welcome input from anyone who has been down this road.
I was finally able to successfully login to the page. The issue seems to be that the urlFetchApp was unable to follow the redirect. I credit this stackoverflow post: how to fetch a wordpress admin page using google apps script
This post described the following process that led to my successful login:
Set followRedirect to false
Submit the post and capture the cookies
Use the captured cookie to issue a get with the appropriate url.
Here is the relevant code:
var url = "http://myUrl.com/";
var options = {
"method": "post",
"payload": {
"TEXT_Username" : "myUserName",
"TEXT_Password" : "myPassword",
"BUTTON_Submit" : "Log In",
},
"testcookie": 1,
"followRedirects": false
};
var response = UrlFetchApp.fetch(url, options);
if ( response.getResponseCode() == 200 ) {
// Incorrect user/pass combo
} else if ( response.getResponseCode() == 302 ) {
// Logged-in
var headers = response.getAllHeaders();
if ( typeof headers['Set-Cookie'] !== 'undefined' ) {
// Make sure that we are working with an array of cookies
var cookies = typeof headers['Set-Cookie'] == 'string' ? [ headers['Set-Cookie'] ] : headers['Set-Cookie'];
for (var i = 0; i < cookies.length; i++) {
// We only need the cookie's value - it might have path, expiry time, etc here
cookies[i] = cookies[i].split( ';' )[0];
};
url = "http://myUrl/Calendar.aspx";
options = {
"method": "get",
// Set the cookies so that we appear logged-in
"headers": {
"Cookie": cookies.join(';')
}
}
...
I notice that the provided Chrome payload includes BUTTON_Submit: Log In but your POST payload does not. I have found that for POSTs in GAS things go much more smoothly if I explicitly set a submit variable in my payload objects. In any case, if you're trying to emulate what Chrome is doing, this is a good first step.
So in your case, it's a one line change:
var payload =
{
"__VIEWSTATE" : viewState,
"__VIEWSTATEGENERATOR" : viewStateGenerator,
"TEXT_Username" : "myUserName",
"TEXT_Password" : "myPassword",
"BUTTON_Submit" : "Log In"
};
Related
I'm trying to send images to a whatsapp user via Twilio, but I'm always getting 400 error
(400) Bad Request. -> {"code": 21620, "message": "Media urls: https://image-charts.com/chart?cht=pd&chd=a:10238,10200&chs=400x300&chdls=9e9e9e,10&chco=FFC00C,03A9F4&chtt=Ocupaci%C3%B3n%20en%20almac%C3%A9n&chdl=Contratado%7COcupado&chli=100.37%25&chl=10238%7C10200&chdlp=b&chof=.png are invalid. Please use only valid http and https urls", "more_info": "https://www.twilio.com/docs/errors/21620", "status": 400}
The image works fine in browser or when is send directly to the user directly in whatsapp
I'm encoding the image like this (js):
img_url = encodeURI(img_url);
Any ideas or workaround?
thanks
It seems not every parameter was encoded, example: chd=a:10238,10200 should be chd=a%3A10238%2C10200.
encodeURI won't produce URL safe, you will need to rely on encodeURIComponent but it will encode the whole url ('/chart?' included) so it's not an option either.
If you are doing this server-side from NodeJS:
// node v10+
const url = require('url');
const querystring = require('querystring');
function encode_uri(chart_url) {
const parsed_url = url.parse(chart_url);
// parse the querytring and then encode every parameter values
parsed_url.query = querystring.stringify(querystring.parse(parsed_url.query));
// generate the full url
return url.format({
...parsed_url,
href:undefined,
path:undefined,
search:'?' + parsed_url.query,
});
}
let chart_url = 'https://image-charts.com/chart?cht=pd&chd=a:10238,10200&chs=400x300&chdlp=b&chdls=9e9e9e,10&chco=FFC00C,03A9F4&chtt=Ocupación en almacén&chli=100.37%&chl=10238|10200&chdl=Contratado|Ocupado&chof=.png';
console.log(encode_uri(chart_url));
// Fully encoded URL
// https://image-charts.com/chart?cht=pd&chd=a%3A10238%2C10200&chs=400x300&chdlp=b&chdls=9e9e9e%2C10&chco=FFC00C%2C03A9F4&chtt=Ocupaci%C3%B3n%20en%20almac%C3%A9n&chli=100.37%25&chl=10238%7C10200&chdl=Contratado%7COcupado&chof=.png
If you are doing this on the browser side then use:
function encode_uri(chart_url) {
// use https://developer.mozilla.org/en-US/docs/Web/API/URL
// not supported in IE10
const parsed_url = new URL(chart_url);
// encode every parameter values
[...parsed_url.searchParams.keys()].forEach(key => parsed_url.searchParams.set(key, encodeURIComponent(parsed_url.searchParams.get(key))));
// generate back the full url
return parsed_url.toString();
}
let chart_url = 'https://image-charts.com/chart?cht=pd&chd=a:10238,10200&chs=400x300&chdlp=b&chdls=9e9e9e,10&chco=FFC00C,03A9F4&chtt=Ocupación en almacén&chli=100.37%&chl=10238|10200&chdl=Contratado|Ocupado&chof=.png';
console.log(encode_uri(chart_url));
// https://image-charts.com/chart?cht=pd&chd=a%253A10238%252C10200&chs=400x300&chdlp=b&chdls=9e9e9e%252C10&chco=FFC00C%252C03A9F4&chtt=Ocupaci%25C3%25B3n%2520en%2520almac%25C3%25A9n&chli=100.37%2525&chl=10238%257C10200&chdl=Contratado%257COcupado&chof=.png
I have a form in googlescript where I can add a user to a sheet.
Is there a way to implement some lines in that code so the script adds a post on a wordpress page?
I read that it's possible via wp_insert_post , but I have no idea how that works in my case.
EDIT:
As Spencer suggested I tried to do it via WP REST API.
The following code seems to be working .............
function httpPostTemplate() {
// URL for target web API
var url = 'http://example.de/wp-json/wp/v2/posts';
// For POST method, API parameters will be sent in the
// HTTP message payload.
// Start with an object containing name / value tuples.
var apiParams = {
// Relevant parameters would go here
'param1' : 'value1',
'param2' : 'value2' // etc.
};
// All 'application/json' content goes as a JSON string.
var payload = JSON.stringify(apiParams);
// Construct `fetch` params object
var params = {
'method': 'POST',
'contentType': 'application/json',
'payload': payload,
'muteHttpExceptions' : true
};
var response = UrlFetchApp.fetch(url, params)
// Check return code embedded in response.
var rc = response.getResponseCode();
var responseText = response.getContentText();
if (rc !== 200) {
// Log HTTP Error
Logger.log("Response (%s) %s",
rc,
responseText );
// Could throw an exception yourself, if appropriate
}
else {
// Successful POST, handle response normally
Logger.log( responseText );
}
}
But I get the error:
[16-09-28 21:24:29:475 CEST] Response (401.0)
{"code":"rest_cannot_create","message":"Sorry, you are not allowed to
create new posts.","data":{"status":401}}
Means: I have to authenticate first.
I installed the plugin: WP REST API - OAuth 1.0a Server
I setup a new user and got a client key and client user.
But from here I have no clue what to do : /
It is possible. Wordpress has a REST API. I can be found at:
http://v2.wp-api.org/
You will use the UrlFetchApp Service to access this api. Documentation can be found at:
https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app
Read the docs and try to write some code. It you get stuck post the code that is confusing you here and I'll update this answer.
You should add you authentification in the header :
var headers = {
... ,
'Authorization' : 'Basic ' + Utilities.base64Encode('USERNAME:PASSWORD'),
};
And then add your header in your parameters :
var params = {
'method': 'POST',
'headers': headers,
'payload': JSON.stringify(payload),
'muteHttpExceptions': true
}
And then use UrlfetchApp.fetch
var response = UrlFetchApp.fetch("https://.../wp-json/wp/v2/posts/", params)
Logger.log(response);
You need to pass the basic auth, like this:
// Construct `fetch` params object
var params = {
'method': 'POST',
'contentType': 'application/json',
'payload': payload,
'muteHttpExceptions' : true,
"headers" : {
"Authorization" : "Basic " + Utilities.base64Encode(username + ":" + password)+"",
"cache-control": "no-cache"
}
};
thank you for giving me these important links.
<3
I installed WP REST API and the OAuth plugin.
In the documentation is written:
Once you have WP API and the OAuth server plugins activated on your
server, you’ll need to create a “client”. This is an identifier for
the application, and includes a “key” and “secret”, both needed to
link to your site.
I couldn't find out how to setup a client?
In my GoogleScriptCode according to the WP API I get the error:
{"code":"rest_cannot_create","message":"Sorry, you are not allowed to create new posts.","data":{"status":401}}
Edit: I found it - it's under User/Application
I'll try to figure it out and get back to you later.
I am trying to construct an email for some users. Code is running server-side. In the email I would like to have a link for the users to click on, but I am not having much luck.
I am trying to use Router.url() to set the href of an anchor. I can do console.log() and see the Router object is at least defined, but the link ends up being strange.
The code looks like this:
Meteor.methods({
sendSubmissionEmail: function(responseId) {
// Let other method calls from the same client start running,
// without waiting for the email sending to complete.
this.unblock();
var formResponse = FormResponses.findOne({_id: responseId});
var toEmails = [];
_.each(Roles.getUsersInRole('ADMIN').fetch(), function(user) {
if (user.profile && user.profile.receivesResponseEmails) {
var email = _.findWhere(user.emails, {verified: true});
if (!email) {
console.log('No verified email address was found for ' + user.username + '. Using unverified email instead.');
email = _.first(user.emails);
}
if (email) {
toEmails.push(email.address);
}
}
});
if (toEmails && toEmails.length > 0) {
console.log('Sending an email to the following Admins: ' + toEmails);
console.log('Router: ', Router);
Email.send({
from: 'noreply#strataconsulting.us',
to: toEmails,
subject: 'Form Response for Form "' + formResponse.form_title + '" Ready For Approval',
html: '<p>Form Response for Form ' + formResponse.formTitle + ' is now ready for your approval.</p>'
});
}
}
});
And the resulting email:
====== BEGIN MAIL #0 ======
MIME-Version: 1.0
From: noreply#strataconsulting.us
To: testuser4#codechimp.net
Subject: Form Response for Form "undefined" Ready For Approval
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<p>Form Response for Form Test Form One is now ready for your approval.</p>
====== END MAIL #0 ======
First, there is a strange "3D" appearing prior to the first " of the href, then the return or Router.url() is always undefined. Just to make sure the call was right, I simulated it in Chrome's dev tools console by doing the following:
var fr = FormResponses.findOne({_id: '1234567890'});
Router.url('editResponse', fr);
As expected this spits out the full URL path to my editResponse route with the correct ID set. Is Router.url() a client-only call? If so, how do I get the URL to a route server-side? All routes are defined for both client and server.
Here:
var fr = FormResponses.findOne({_id: '1234567890'});
Router.url('editResponse', fr);
You're passing the result of the Find as a parameter. It'll look like
{ _id: ..., otherStuff: ...}
But in your code you're not passing an object, you're just passing a string:
Router.url('editResponse', formResponse.id)
That explains the "undefined".
The 3D is very odd.
I'm writing a simple log in to website, navigate to page and parse app for iOS. I've managed to generate a post request, but it doesn't seem to want to log in. I don't know why it could be failing, because I get status 200. Admittedly I get this even if I deliberately enter wrong credentials. Any ideas? (code is in Swift)
var url = NSURL(string:"https://www.example.com/ps/signin.html")
var request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
var dataString = "timezoneOffset=-600&userid1=xxx&userid=xxx&pwd=xxx&x=31&y=12"
let data = (dataString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPBody = data
var connection = NSURLConnection(request: request, delegate: self, startImmediately: false)
println("sending request : \(request)")
connection.start()
So I get a response back, with status 200, and seemingly the html code for the login page again.
The problem is that you are POSTing to the login form: https://www.example.com/ps/signin.html. You need to POST to the same place the HTML form posts to, namely: https://www.example.com/psp/ps/?cmd=login&languageCd=ENG
Try changing your URL:
var url = NSURL(string:"https://www.example.com/psp/ps/?cmd=login&languageCd=ENG")
I need to fetch a page inside my Wordpress blog admin area. The following script:
function fetchAdminPage() {
var url = "http://www.mydomain.invalid/wp/wp-admin/wp-login.php";
var options = {
"method": "post",
"payload": {
"log": "admin",
"pwd": "password",
"wp-submit": "Login",
"redirect_to":"http://www.mydomain.invalid/wp/wp-admin/edit-comments.php",
"testcookie": 1
}
};
var response = UrlFetchApp.fetch(url, options);
...
}
is executed without errors. Anyway, response.getContentText() returns the login page, and I am not able to access the page http://www.mydomain.invalid/wp/wp-admin/edit-comments.php which is the one I want to fetch.
Any idea on how to do this?
There might be an issue with Google Apps Scripts and post-ing to a URL that gives you back a redirection header.
It seems like it might not be possible to follow the redirect with a post - here's a discussion on the issue -
https://issuetracker.google.com/issues/36754794
Would it be possible, if you modify your code to not follow redirects, capture the cookies and then do a second request to your page? I haven't actually used GAS, but here's my best guess from reading the documentation:
function fetchAdminPage() {
var url = "http://www.mydomain.invalid/wp/wp-admin/wp-login.php";
var options = {
"method": "post",
"payload": {
"log": "admin",
"pwd": "password",
"wp-submit": "Login",
"testcookie": 1
},
"followRedirects": false
};
var response = UrlFetchApp.fetch(url, options);
if ( response.getResponseCode() == 200 ) {
// Incorrect user/pass combo
} else if ( response.getResponseCode() == 302 ) {
// Logged-in
var headers = response.getAllHeaders();
if ( typeof headers['Set-Cookie'] !== 'undefined' ) {
// Make sure that we are working with an array of cookies
var cookies = typeof headers['Set-Cookie'] == 'string' ? [ headers['Set-Cookie'] ] : headers['Set-Cookie'];
for (var i = 0; i < cookies.length; i++) {
// We only need the cookie's value - it might have path, expiry time, etc here
cookies[i] = cookies[i].split( ';' )[0];
};
url = "http://www.mydomain.invalid/wp/wp-admin/edit-comments.php";
options = {
"method": "get",
// Set the cookies so that we appear logged-in
"headers": {
"Cookie": cookies.join(';')
}
};
response = UrlFetchApp.fetch(url, options);
};
};
...
}
You would obviously need to add some debugging and error handling, but it should get you through.
What happens here is that we first post to the log-in form. Assuming that everything goes correctly, that should give us back a response code of 302(Found). If that's the case, we will then process the headers and look specifically for the "Set-Cookie" header. If it's set, we'll get rid of the un-needed stuff and store the cookies values.
Finally we make a new get request to the desired page on the admin( in this case /wp/wp-admin/edit-comments.php ), but this time we attach the "Cookie" header which contains all of the cookies acquired in the previous step.
If everything works as expected, you should get your admin page :)
I would advise on storing the cookies information(in case you're going to make multiple requests to your page) in order to save time, resources and requests.
Again - I haven't actually tested the code, but in theory it should work. Please test it and come back to me with any findings you make.