Reading browser cookies in safari extension - safari-extension

I am new to safari extension development. I need to access browser cookies specific to a web site. I know that this is possible with in chrome extension development. How can do this with safari extension development.

From an injected script you can send the cookies to the extension. So when building the extension you have global.html and injected.js.
injected.js:
var cookies = document.cookie;
safari.self.tab.dispatchMessage("setCookies",cookies);
global.html (without the HTML tags):
var cookies = null;
function getCookies(incMsg) {
cookies = incMsg.message;
alert('I received them :) \n\n'+cookies);
}
safari.application.addEventListener("message",getCookies,false);
The code above will send all the cookies to the extension each time you load a page. Then of course you still need to read out the cookies. I used something like the following function (in global.html):
function readCookie(name) {
if(cookies) {
var nameEQ = name + "=";
var ca = cookies.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
}
Good luck!

Related

non-invocable member 'File' cannot be used like a method error message- what am I missing?

I have a Blazor Application which had files uploaded to a upload folder on the web server. I am in the process of trying to figure out the code to download an uploaded file in the browser for retrieval and viewing. Right now the code is as below (the download part from code examples on the internet)
public void FileDetailsToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
{
string path = null;
string uploads = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot\\uploads");
path = uploads + "\\" + SelectedFileName;
if (args.Item.Text == "Delete")
{
//Code for Deleting goes here
//UploadRef.Remove();
if (System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
FileDetailsService.FileDetailsDelete(SelectedFileId); //NavigationManager.NavigateTo($"/ServiceRequestNotes/servicerequestnoteadd");
NavigationManager.NavigateTo($"/ServiceRequests/serviceRequestsaddedit2/{Id}", forceLoad: true);
}
else
{
// its a download
IFileProvider provider = new PhysicalFileProvider(uploads);
IFileInfo fileinfo = provider.GetFileInfo(path + SelectedFileName);
var readStream = fileinfo.CreateReadStream();
var mimeType = "application/pdf";
return File(readStream, mimeType, SelectedFileName);
}
}
On the last statement I am a getting the following error message
non-invocable member 'File' cannot be used like a method error message
What am I missing or do I need to change or add to have the output from the readstream render to the browser?
The blazor application is a blazor server app not WASM. It does not make use of API controllers.
Any advice?
This is a void method. You can't return anything at all. Also, if you're trying to instantiate a File object, you'd have to use the new keyword.

Ktor custom feature swap url

Im trying to add a custom feature in Ktor. It's basically a url swapper (we have a scenario where domains might be changed during anytime & can't update the client everytime).
We get the swapper list available and need a CustomFeature in Ktor to swap the url based on list. However, the context.request or request.url - everything is val and Im not able to assign new url to the request.
In Retrofit, it used to work like this
if (currentUrl.contains(urlSwapper.oldUrl)) {
val newUrl = currentUrl.replace(urlSwapper.oldUrl, urlSwapper.newUrl)
val newHttpUrl = request.url.newBuilder(newUrl)!!.build()
// build a new request with the new url. replace it
request = request.newBuilder().url(newHttpUrl).build()
break
}
}
In Ktor feature, Im trying something like this
scope.requestPipeline.intercept(HttpRequestPipeline.Transform) {
val currentUrl =
context.url.protocol.name + "://" + context.url.host + context.url.encodedPath
for (urlSwapper in feature.urlSwappers) {
if (currentUrl.contains(urlSwapper.oldUrl)) {
val newUrl = currentUrl.replace(urlSwapper.oldUrl, urlSwapper.newUrl)
val newHttpUrl = Url(newUrl)
context.url(url = newHttpUrl)
break
}
}
proceedWith(subject)
}
}
Is this the right way to do this ?
Generally yes, this is the right way. I have a few recommendations:
Intercept the sendPipeline instead of the requestPipeline.
An example:
client.sendPipeline.intercept(HttpSendPipeline.State) {
context.url(url = newUrl)
}
Get rid of proceedWith(subject) call because it's redundant.
Try to use Url objects instead of strings. You can get the current URL without affecting context by cloning UrlBuilder and building Url from it: context.url.clone().build()

ETrade API unattended authentication

Background
The ETrade authentication system has me creating a RequestToken, then executing an Authorization URL, which opens an ETrade page.
The user logs in to authorize the activity on their account.
They receive a pin, which they enter in my app.
I call ExchangeRequestTokenForAccessToken with the RequestToken and the Pin.
Then we are off and running.
Question
The problem is I'm creating a service that runs continuously in the background. There won't be any user to log in. Conversely, I won't be making any trades. Just crunching numbers, looking for stocks that meet certain criteria.
I can't figure how to get this to work unattended.
Thanks, Brad.
Previously, I have used a series of WebRequests and manually added headers to simulate the authorization pages. This worked until about a year ago when ETrade complicated their headers with something that appears to be tracking information. I now use http://watin.org/ to log in, and to strip the Auth Code.
Sloppy code looks like this:
using WatiN.Core; // IE Automation
...
// verify current thread in STA.
Settings.Instance.MakeNewIeInstanceVisible = false;
var ieStaticInstanceHelper = new IEStaticInstanceHelper();
Settings.AutoStartDialogWatcher = false;
using (ieStaticInstanceHelper.IE = new IE())
{
string authCode = "";
ieStaticInstanceHelper.IE.GoTo(GetAuthorizationLink());
if (ieStaticInstanceHelper.IE.ContainsText("Scheduled System Maintenance"))
{
throw new ApplicationException("eTrade down for maintenance.");
}
TextField user = ieStaticInstanceHelper.IE.TextField(Find.ByName("USER"));
TextField pass = ieStaticInstanceHelper.IE.TextField(Find.ById("txtPassword"));
TextField pass2 = ieStaticInstanceHelper.IE.TextField(Find.ByName("PASSWORD"));
Button btn = ieStaticInstanceHelper.IE.Button(Find.ByClass("log-on-btn"));
Button btnAccept = ieStaticInstanceHelper.IE.Button(Find.ByValue("Accept"));
TextField authCodeBox = ieStaticInstanceHelper.IE.TextField(Find.First());
if (user != null && pass != null && btn != null &&
user.Exists && pass2.Exists && btn.Exists)
{
user.Value = username;
pass2.Value = password;
btn.Click();
}
btnAccept.WaitUntilExists(30);
btnAccept.Click();
authCodeBox.WaitUntilExists(30);
authCode = authCodeBox.Value;
SavePin(authCode);
}
Current version of Brad Melton's code.
WatiN has changed and no longer contains the IE.AttachToIE function.
So, IEStaticInstanceHelper is now called StaticBrowserInstanceHelper, but that code is hard to find, so I've included it here.
class StaticBrowserInstanceHelper<T> where T : Browser {
private Browser _browser;
private int _browserThread;
private string _browserHwnd;
public Browser Browser {
get {
int currentThreadId = GetCurrentThreadId();
if (currentThreadId != _browserThread) {
_browser = Browser.AttachTo<T>(Find.By("hwnd", _browserHwnd));
_browserThread = currentThreadId;
}
return _browser;
}
set {
_browser = value;
_browserHwnd = _browser.hWnd.ToString();
_browserThread = GetCurrentThreadId();
}
}
private int GetCurrentThreadId() {
return Thread.CurrentThread.GetHashCode();
}
}
ETrade's login pages have changed as well. They have several. All the login pages I checked consistently had a USER field and a PASSWORD field, but the login buttons had various names that look fragile. So if this doesn't work, that's the first thing I'd check.
Second, if I go directly to the auth page, it prompts to log in, but then it frequently doesn't take you to the auth page.
I got more consistent results by going to the home page to log in, then going to the auth page.
static public string GetPin(string username, string password, string logonLink, string authLink) {
// Settings.Instance.MakeNewIeInstanceVisible = false;
var StaticInstanceHelper = new StaticBrowserInstanceHelper<IE>();
Settings.AutoStartDialogWatcher = false;
// This code doesn't always handle it well when IE is already running, but it won't be in my case. You may need to attach to existing, depending on your context.
using (StaticInstanceHelper.Browser = new IE(logonLink)) {
string authCode = "";
// Browser reference was failing because IE hadn't started up yet.
// I'm in the background, so I don't care how long it takes.
// You may want to do a WaitFor to make it snappier.
Thread.Sleep(5000);
if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
throw new ApplicationException("eTrade down for maintenance.");
}
TextField user = StaticInstanceHelper.Browser.TextField(Find.ByName("USER"));
TextField pass2 = StaticInstanceHelper.Browser.TextField(Find.ByName("PASSWORD"));
// Class names of the Logon and Logoff buttons vary by page, so I find by text. Seems likely to be more stable.
Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ByText("Log On"));
Element btnLogOff = StaticInstanceHelper.Browser.Element(Find.ByText("Log Off"));
Button btnAccept = StaticInstanceHelper.Browser.Button(Find.ByValue("Accept"));
TextField authCodeBox = StaticInstanceHelper.Browser.TextField(Find.First());
if (user != null && btnLogOn != null &&
user.Exists && pass2.Exists && btnLogOn.Exists) {
user.Value = username;
pass2.Value = password;
btnLogOn.Click();
}
Thread.Sleep(1000);
if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
Element btnContinue = StaticInstanceHelper.Browser.Element(Find.ByName("continueButton"));
if (btnContinue.Exists)
btnContinue.Click();
}
btnLogOff.WaitUntilExists(30);
// Here we go, finally.
StaticInstanceHelper.Browser.GoTo(authLink);
btnAccept.WaitUntilExists(30);
btnAccept.Click();
authCodeBox.WaitUntilExists(30);
authCode = authCodeBox.Value;
StaticInstanceHelper.Browser.Close();
return authCode;
}
}
Being able to automate it like this means that I no longer care about how long the token is valid. Thanks BradM!
This was amazingly helpful. I used your code plus what was posted here to automate this (because tokens expire daily): E*Trade API frequently returns HTTP 401 Unauthorized when fetching an access token but not always
I made two edits:
Changed the authorize URL to what was posted here: https://seansoper.com/blog/connecting_etrade.html
For the log on button, changed it to search by ID: Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ById("logon_button"));
I ran into issues with Watin and setting up the Apartmentstate. So did this:
static void Main(string[] args)
{
System.Threading.Thread th = new Thread(new ThreadStart(TestAuth));
th.SetApartmentState(ApartmentState.STA);
th.Start();
th.Join();
}
Then put the code in the TestAuth method.

getting user info google plus api

How can I get public info of a user from google plus login button integrated on the site, here is the code which is giving me email, I need more info which is provide by google plus :
<div id="signin-button" class="show">
<div class="g-signin" data-callback="loginFinishedCallback"
data-approvalprompt="force"
data-clientid="9076269517.apps.googleusercontent.com"
data-scope="https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email"
data-height="short"
data-cookiepolicy="single_host_origin">
</div>
java script :
function loginFinishedCallback(authResult) {
if (authResult) {
if (authResult['error'] == undefined){
gapi.auth.setToken(authResult); // Store the returned token.
toggleElement('signin-button'); // Hide the sign-in button after successfully signing in the user.
getEmail(); // Trigger request to get the email address.
} else {
console.log('An error occurred');
}
} else {
console.log('Empty authResult'); // Something went wrong
}
}
function getEmail(){
// Load the oauth2 libraries to enable the userinfo methods.
gapi.client.load('oauth2', 'v2', function() {
var request = gapi.client.oauth2.userinfo.get();
request.execute(getEmailCallback);
});
}
function getEmailCallback(obj){
var el = document.getElementById('email');
var email = '';
if (obj['email']) {
email = 'Email: ' + obj['email'];
}
//console.log(obj); // Uncomment to inspect the full object.
el.innerHTML = email;
toggleElement('email');
}
function toggleElement(id) {
var el = document.getElementById(id);
if (el.getAttribute('class') == 'hide') {
el.setAttribute('class', 'show');
} else {
el.setAttribute('class', 'hide');
}
}
I tried replacing email with name, userId but getting nothing from these variables.
How can I get basic information of a user when he is logged in through google plus.
Similar to how you have loaded the oauth2 v2 client package using gapi.client.load, you will use this again to load the plus v1 client package. This will give you a number of packages and methods under the gapi.client.plus namespace.
The Plus API includes a package to load information about People, including getting them by their User ID or, since they have authenticated with you, you can use the special identifier "me".
Full details and an example are given at https://developers.google.com/+/api/latest/people/get, but here is an (untested) similar function to your getEmail() method that would get their full name:
function getFullName(){
// Load the Plus library to get the People package and methods
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get('me');
request.execute(getFullNameCallback);
});
};
function getFullNameCallback(obj){
var el = document.getElementById('email');
var name = '';
if (obj['displayName']) {
name = 'Name: '+obj.displayName;
}
el.innerHTML = name;
toggleElement('name');
};
The above code snippet no longer seems to work.
Once again we are chasing our tails over something
google now changed......
error "Access Not Configured. Please use Google Developers Console to activate the API for your project."
I assumed it it might be the "Google+ API" so it is switched on in the developer console,
still no working however.
Yet api explorer shows promisingly that some sort of code can work,
however its a dogs breakfast trying to discern what javascript code is working there.
So so useful api explorer...., but how about google show a simple WORKING example in code that we can look at for this same request?

File Upload Control in Domino

I need to post a file to Domino Server from a PhoneGap Application.
Here is the PhoneGap File Transfer example
// !! Assumes variable fileURI contains a valid URI to a text file on the device
var win = function(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function(error) {
alert("An error has occurred: Code = " = error.code);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURI.substr(fileURI.lastIndexOf('/')+1);
options.mimeType="text/plain";
var params = new Object();
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURI, "http://some.server.com/upload.php", win, fail, options);
//This is a PHP example - Domino would be like
// ft.upload(fileURI, "http://some.server.com/database.nsf/attachmentForm? createDocument", win, fail, options);
Does anyone know what needs to be done in Domino to get the file attachment that is being posted?
The easiest thing to do would be to create a form in Domino containing a File Upload control. You should be able to open the Domino form with a browser and see the generated html form that would normally be used. In there you will find all the info you need. This is of course dependent on the ft.upload method acting like an http multipart/form-data POST.