I am trying to post an album to picasa, but always get "bad request" response.
Should I use HttpRequest class instead?
System.Net.WebClient wc = new System.Net.WebClient();
wc.Headers.Add("Authorization", "AuthSub token=\"" + token + "\"");
wc.Headers.Add("GData-Version", "2");
string data = "<entry xmlns='http://www.w3.org/2005/Atom' " +
"xmlns:media='http://search.yahoo.com/mrss/' " +
"xmlns:gphoto='http://schemas.google.com/photos/2007'>" +
"<title type='text'>" + name + "</title>" +
"<summary type='text'>" + descr + "</summary>" +
"<gphoto:location>asd</gphoto:location>" +
"<gphoto:access>" + access + "</gphoto:access>" +
"<gphoto:timestamp>1152255600000</gphoto:timestamp>" +
"<media:group>" +
"<media:keywords>adds</media:keywords>" +
"</media:group>" +
"<category scheme='http://schemas.google.com/g/2005#kind' " +
"term='http://schemas.google.com/photos/2007#album'></category>" +
"</entry>";
try
{
string response = wc.UploadString("https://picasaweb.google.com/data/feed/api/user/default", "post", data);
return response;
}
catch (Exception e)
{
return e.ToString();
}
Google makes a handy api for picasa [.net] integration:
http://code.google.com/apis/picasaweb/docs/1.0/developers_guide_dotnet.html
No sense writing all that code by hand!
Here is some code (vb.net, but it's straightforward):
Public Shared Function CreateAlbum(ByVal albumTitle As String) As AlbumAccessor
Dim newAlbum As New AlbumEntry()
newAlbum.Title.Text = albumTitle
Dim ac As New AlbumAccessor(newAlbum)
ac.Access = "public"
Dim feedUri As New Uri(PicasaQuery.CreatePicasaUri(ConfigurationManager.AppSettings("GData_Email")))
Dim albumEntry As PicasaEntry = CreateAuthenticatedRequest().Insert(feedUri, newAlbum)
Return New AlbumAccessor(albumEntry)
End Function
Public Shared Function CreateAuthenticatedRequest() As PicasaService
Dim service As New PicasaService(ConfigurationManager.AppSettings("GData_AppName"))
service.setUserCredentials(ConfigurationManager.AppSettings("GData_Email"), ConfigurationManager.AppSettings("GData_Password"))
Return service
End Function
I know this is older so you may already have an answer. I also know that Google does make an API but with .net it only works for the first version of Picasa and you are trying to work with version two, as am I. I came across your post and thought I would offer an answer for you in case you are still trying to work this out or someone else comes across the post and wants an answer.
I see a couple of things that may be causing your issue. The first is that you seem to be mixing and matching the authentication protocol with the version. For the second version of the Google Picasa API, I believe you need to be using OAuth2 protocol, not AuthSub protocol. I haven't tried with AuthSub. The second issue is that I do not believe you have enough information in your headers (missing content-length, content-type, and host[although you may not need host when using a webclient]). One way that I've found to make sure that my requests are working well (and honestly has been a lifesaver) is to go to the OAuth2Playground on Google: Oauth2Playground. Here you can create your tokens and requests and easily see their headers and post information when successful requests are made.
Here is a snippet of code that I wrote which allows for creation of an album. In order to create, you must have an authenticated token with an access code (you would want to first get user permissions and store their refresh token and then refresh to get the session access_token) The access_token is passed in the authorization line of the header. It also parses the response and gets a success variable from the response as well as the albumid. The entire xml feed for the album is returned on response, so you could go into the detail of reading that in and working with it directly if you wanted)
public bool CreatePicasaAlbum(GoogleUtility.Picasa.AlbumEntry.entry a, IGoogleOauth2AccessToken token)
{
TcpClient client = new TcpClient(picasaweb.google.com, 443);
Stream netStream = client.GetStream();
SslStream sslStream = new SslStream(netStream);
sslStream.AuthenticateAsClient(picasaweb.google.com);
byte[] contentAsBytes = Encoding.ASCII.GetBytes(a.toXmlPostString());
string data = a.toXmlPostString();
StringBuilder msg = new StringBuilder();
msg.AppendLine("POST /data/feed/api/user/default HTTP/1.1");
msg.AppendLine("Host: picasaweb.google.com");
msg.AppendLine("Gdata-version: 2");
msg.AppendLine("Content-Length: " + data.Length);
msg.AppendLine("Content-Type: application/atom+xml");
msg.AppendLine(string.Format(GetUserInfoDataString(), token.access_token));
msg.AppendLine("");
byte[] headerAsBytes = Encoding.ASCII.GetBytes(msg.ToString());
sslStream.Write(headerAsBytes);
sslStream.Write(contentAsBytes);
StreamReader reader = new StreamReader(sslStream);
bool success = false;
string albumID = "";
while (reader.Peek() > 0)
{
string line = reader.ReadLine();
if (line.Contains("HTTP/1.1 201 Created")) { success = true; }
if (line.Contains("Location: https") && string.IsNullOrWhiteSpace(albumID))
{
var aiIndex = line.LastIndexOf("/");
albumID = line.Substring(aiIndex + 1);
}
System.Diagnostics.Debug.WriteLine(line);
if (line == null) break;
}
return success;
}
/// <summary>
/// User Info Data String for Authorization on TCP requests
/// [Authorization: OAuth {0}"]
/// </summary>
/// <returns></returns>
private string GetUserInfoDataString()
{
return "Authorization: OAuth {0}";
}
Sorry, I should add that I created an object that returns the feed string for the album entry xml as you had above. The feed xml matches the documentation. I leave the timestamp blank as the default stamp is when you create it, and I've not figured out what if anything can be put in category so I leave that blank too.
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/' xmlns:gphoto='http://schemas.google.com/photos/2007'>
<title type='text'>Created from code</title>
<summary type='text'>Code created this album</summary>
<gphoto:location>somewhere</gphoto:location>
<gphoto:access>public</gphoto:access>
<gphoto:timestamp></gphoto:timestamp>
<media:group>
<media:keywords>test, album, fun</media:keywords>
</media:group>
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/photos/2007#album'>
</category>
</entry>
One other edit: The IGoogleOauth2AccessToken is another class I created to house the token details. What you really need passed in is the access_token string that you get when you refresh the OAuth2 token. My token housing code just has the access_code, token_type, and expires as part of the object. You just need the access token string for the authorization.
Related
I'm finding very little documentation online for implementing the PayPal REST API in C#.
I have gotten past the first step of getting an access token, but I keep seeing conflicting methods for sending API calls and nothing I have tried works.
Here's my current code:
private async void cmdRefund_Click(object sender, EventArgs e)
{
//var apiContext = Configuration.GetAPIContext();
string AccessToken;
string Nonce;
string AppID;
string TokenType;
try
{
// ClientId of your Paypal app API
//This is the live ID
string APIClientId = "AZxxxx-8";
//this is the live secret Key
string APISecret = "Exxxx39";
using (var client = new HttpClient())
{
var byteArray = Encoding.UTF8.GetBytes(APIClientId + ":" + APISecret);
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
//this is the live url
var url = new Uri("https://api.paypal.com/v1/oauth2/token", UriKind.Absolute);
client.DefaultRequestHeaders.IfModifiedSince = DateTime.UtcNow;
var requestParams = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "client_credentials")
};
var content = new FormUrlEncodedContent(requestParams);
var webresponse = await client.PostAsync(url, content);
var resp = await webresponse.Content.ReadAsStringAsync();
MessageBox.Show(resp);
if (resp.IndexOf("access_token") == -1)
{
MessageBox.Show("PayPal Authorization Failed.");
return;
}
AccessToken = resp.Substring(resp.IndexOf("access_token") + 15);
AccessToken = AccessToken.Substring(0, AccessToken.IndexOf("\""));
Nonce = resp.Substring(resp.IndexOf("nonce") + 8);
Nonce = Nonce.Substring(0, Nonce.IndexOf("\""));
AppID = resp.Substring(resp.IndexOf("app_id") + 9);
AppID = AppID.Substring(0, AppID.IndexOf("\""));
TokenType = resp.Substring(resp.IndexOf("token_type") + 13);
TokenType = TokenType.Substring(0, TokenType.IndexOf("\""));
MessageBox.Show("Access Token: \r\n" + AccessToken + "\r\nNonce:\r\n" + Nonce + "\r\nAppID:\r\n" + AppID + "\r\nToken Type:\r\n" + TokenType);
// response will deserialized using Jsonconver
//return JsonConvert.DeserializeObject<PayPalGetTokenResponse>(resp);
}
}
catch (System.Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
//Authorization has been achieved here - now I want to process a refund
var apiContext = new APIContext(AccessToken);
Amount refundAmount = new Amount();
refundAmount.total = "0.01";
refundAmount.currency = "USD";
Refund refund = new Refund();
refund.amount = refundAmount;
string saleId = "9XY489008U7836633";
//*************THIS NEXT LINE CAUSES AN ERROR
Refund refundforreal = Sale.Refund(apiContext, saleId, refund);
//*************
string refundId = refund.id;
}
The last line causes the Error: "PayPal.IdentityException: 'The remote server returned an error: (401) Unauthorized.'"
As far as I can tell, my access token is completely valid, but this does not work. I should note that I the transactions I'm trying to get information on and refund are NOT placed via the REST API, but are simply placed through the regular PayPal interface integrated on our website. I don't know if that causes a problem or not, but that is what I need to do.
I am using a Windows Forms App written in C# in Visual Studios 2017 because I'm replacing an old VB6 program that required that the user log into a PayPal session in a browser in the program and need to replace that program with something that is both usable and familiar for our employees, AND is more forward thinking by using the REST API instead of the old method of filling in fields in a WebBrowser control.
***********EDIT************ - I added this as a follow up:
I took #shamanthGowdraShankaramurthy's advice and used Postman and managed to do what I wanted, so thank you - that did help me to know that at least what I want to do is possible.
I still don't know how to do the POST in C#. I think perhaps I'll stay away from the built in "Refund" object in the SDK and instead try to POST some other way.
The url I'm using is in Postman is: https://api.paypal.com/v1/payments/sale/9XY489008U7836633/refund
I sent this as the body to do a $0.01 refund on my test transaction:
{
"amount": {
"total": "0.01",
"currency": "USD"
},
"invoice_number": "INV-1234567"
}'
I added a Bearer Token authorization to the POST with my Access Token that I had from my working code.
Finally, in Postman, I changed the body from "Text" to "JSON (application/json).
How do I incorporate all these elements (the URL, my bearer token, the body, and the information that the body is json) into a POST in a C# winforms application?
In case anyone else is looking for this, the answer turned out to be much simpler than I figured. I think I was messing myself up by not using the AccessTokens that are easily obtained by the PayPal SDK.
First, make sure that you have the App.Config file set up properly:
<!-- PayPal SDK settings -->
<paypal>
<settings>
//specify sandbox or live
<add name="mode" value="sandbox" />
<add name="clientId" value="insert_clientid_key_here" />
<add name="clientSecret" value="Insert_client_secret_key_here" />
</settings>
</paypal>
Then this is all the code I needed to make the refunds work:
private void cmdRefund_Click(object sender, EventArgs e)
{
var config = ConfigManager.Instance.GetProperties();
var accessToken = new OAuthTokenCredential(config).GetAccessToken();
var apiContext = new APIContext(accessToken);
Amount refundAmount = new Amount();
refundAmount.total = "0.01";
refundAmount.currency = "USD";
RefundRequest refund = new RefundRefundRequest();
refund.amount = refundAmount;
string saleId = "9XY489008U7836633";
Refund refundforreal = Sale.Refund(apiContext, saleId, refund);
MessageBox.Show("Refund status:" + refundforreal.state + "\r\n" + "Txn #:" + refundforreal.id);
}
I want to show my user feed on my website and what I intend to do is to authenticate my own user account each time a user visits the page, and in that way buypass that the user have to log in to his instagram account.
My problem is that I'm having a hard time retrieving the instagram access token through a HttpWebRequest..
See the following NON working code sample:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.instagram.com/oauth/authorize?client_id=xxxxxxxxxxxxxxxxxxxxxx&redirect_uri=http://mywebsite.com&response_type=token");
request.Method = "POST";
request.AllowAutoRedirect = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string redirectUrl = response.ResponseUri.ToString();
HttpContext.Current.Response.Write(redirectUrl);
HttpContext.Current.Response.End();
If I paste the url in my browser I get a redirect to http://mysite.com/#access_token=xxxxxxxxxxxxxx and everything seems fine, but when I try to execute the code above, I can't retrieve the correct uri due to some in between redirects before the final url.
Any help would be much appriciated..
I recommend you to use Instasharp library. InstaSharp is a C# library that wraps the Instagram API and makes it easy to write applications with Instagram data. It has a very easy method to get access token for a user. Check its API.
Unfortunately the documentation for Instasharp currently provided has a few errors. I.e. The documentation says OAuthInfo, when such a class does not exist.
Here is some code that works for me.
Notice you don't seem to need to pass a User Object at all (not sure why you would need to anyway)
Also note, that the authenticated and non authenticated methods allow you pass different params, count being the most important one. I've noticed that regardless of the count you pass, an arbitrary number of results is returned, e.g. 33 for authenticated and 13 for authenticated for the same search term. InstagramResult is my wrapper class for the object and Config holds the InstagramAuthorisationModel and InstagramAuthorisationModel holds the static keys created when signing up for a developer account.
public class InstagramService : IInstagramService
...
public InstagramConfig Config
{
get{return new InstagramConfig("https://api.instagram.com/v1", "https://api.instagram.com/oauth", InstagramAuthorisationModel.ApplicationId, InstagramAuthorisationModel.Secret, InstagramAuthorisationModel.RedirectUri);}
}
private AuthInfo UserAuthInfo()
{
return new AuthInfo()
{
// User =new UserInfo(){},
Access_Token = GetInstagramAccessToken()
};
}
public string GetInstagramAccessToken()
{
return _socialMediaRepository.GetInstagramAccessToken(_userApiKey);
}
public List<InstagramResult> Search(string searchTag, int count)
{
var auth = UserAuthInfo();
var tags = new InstaSharp.Endpoints.Tags.Authenticated(Config, auth);
var searchresult = tags.Recent(searchTag);
return searchresult.Data.Select(media => new InstagramResult()
{
Media = media,
image = media.Images.LowResolution.Url
})
.ToList();
}
..
Good day all.
Does anyone has a working example how to make Google Plus post through .NET (C#).
I have already tried google and stackoverflow search, but did not manage to find the solution.
I successfully get posts:
public void Post(string text)
{
PlusService plus = new PlusService {Key = "MYVERYSECRETKEY"};
ActivitiesResource ar = new ActivitiesResource(plus, null);
ActivitiesResource.ListRequest list = ar.List("108055870103885325249", new ActivitiesResource.Collection());
ActivityFeed feed = list.Fetch();
string activityKey = "";
foreach (var a in feed.Items)
if (a.Url == "https://plus.google.com/108055870103885325249/posts/EtvvUgn8eKz")
{
activityKey = a.Id;
break;
}
ActivitiesResource.GetRequest get = ar.Get(activityKey);
Activity act = get.Fetch();
var sb = new System.Text.StringBuilder();
sb.AppendLine("Title: " + act.Title);
sb.AppendLine("URL:" + act.Url);
sb.AppendLine("Published:" + act.Published);
sb.AppendLine("By:" + act.Actor.DisplayName);
sb.AppendLine("Annotation:" + act.Annotation);
sb.AppendLine("Content:" + act.Object.Content);
sb.AppendLine("Type:" + act.Object.ObjectType);
sb.AppendLine("# of +1s:" + act.Object.Plusoners.TotalItems);
sb.AppendLine("# of reshares:" + act.Object.Resharers.TotalItems);
}
But I cannot find any method for making posts.
Thanks in advance.
Currently, the Google+ API does not allow writing of posts to a user's activity stream. You can use the moments.insert method in the Google+ REST API to write App Activities to the user's profile, which the user can choose whether to share publicly or to their circles.
You would work with the REST API in a similar manner to other REST APIs in .NET by POSTing to the moments.insert end-point.
This feature is now available to apps that request the https://www.googleapis.com/auth/plus.login scope and specify the type of moments that the app wants to write in the request_visible_actions parameter either in the Google+ Sign-In button or directly in the OAuth query parameters.
I'm developing a desktop application in C#.
After visiting https://www.facebook.com/dialog/oauth?client_id=123 the user logs in and the user access token is attached to the redirect uri. Its no problem, when the loginpage is displayed in a webbrowser control in my form, so i can extract the token from the url.
But this is not the way i want to get the token. My question is, is there a way to obtain the freshly created token via an Graph API call?
Because i want to display the login page in the user's standard browser and not in this embedded webbrowser. All my efforts to get the user access token have been resulted in getting the app access token, which is useless in this case.
Any hints are appreciated.
// This is very raw, And Note it is a MVC3 solution, but it is in C# and I hope it helps.
// It is basically a C# version of the PHP example on FB for 'Server Side Flow'
// I have been at it for a while and had to go through some pain
// Please note the bug I read about that states the redirect_uri must be the same for both //requests
// Also read that someone had an issue if the redirect_uri did not end in '/'
// Post back if you hae any ?s as I am just starting this project and am going to try and //incorporate the C# FaceBook SDK
public class AccountController : Controller
{
// LoginWithFaceBook
// First Contact with FB - oauth?client_id ... redirect_uri = /Account/FacebookLinker
// according to a bug files on FB redirect_uri MUST BE SAME FOR both trips ( to get the 'code' then exchange the code for 'access_token'
public ActionResult ConnectFaceBookAccount()
{
string APP_ID = HttpContext.Application["FacebookAppId"].ToString();
string redirect_uri = HttpContext.Application["FacebookOAuthRedirect"].ToString();
string state = HttpContext.Application["state_guid"].ToString();
// in this View I simply link to this URL
ViewBag.FaceBookOAuthUrl = "https://www.facebook.com/dialog/oauth?client_id=" + APP_ID + "&redirect_uri="+redirect_uri+"&state=" + state+"&display=popup";
return View();
}
// Account/FacebookLinker
// redirect_uri for both getting 'code' and exchanging for 'access_token'
public ActionResult FacebookLinker()
{
if (!Request.IsAuthenticated)
{
Response.Redirect("/Account/LogOn");
}
// Per FB DOC, Make sure 'state' var returned is same one you sent to reduce chance of Cross Site Forgery
if (Request.QueryString["state"].ToString() == HttpContext.Application["state_guid"].ToString())
{
try
{
string FBcode = Request.QueryString["code"].ToString();
string APP_ID = HttpContext.Application["FacebookAppId"].ToString();
string APP_SECRET = HttpContext.Application["FacebookAppSecret"].ToString();
string redirect_uri = HttpContext.Application["FacebookOAuthRedirect"].ToString();
string FBAccessUrl = "https://graph.facebook.com/oauth/access_token?client_id=" + APP_ID + "&redirect_uri=" + redirect_uri + "&client_secret=" + APP_SECRET + "&code=" + FBcode;
string accessToken = null;
// Send the request to exchange the code for access_token
var accessTokenRequest = System.Net.HttpWebRequest.Create(FBAccessUrl);
HttpWebResponse response = (HttpWebResponse) accessTokenRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using(StreamReader sr = new StreamReader(response.GetResponseStream(),rEncoding))
{
// parse the response to get the value of the 'access_token'
accessToken = HttpUtility.ParseQueryString(sr.ReadToEnd()).Get("access_token");
}
//TODO
// Add to the accessToken for the Logged in User.Identity to a FBUSERS Model
// WHen someone Logs in Check to see if they are also in FB
// ON Login Page add option to login with FaceBook
return View();
}
catch (Exception exp)
{
// try to get token failed
}
}
else
{
// state var form FB did not match state var sent
}
return View();
}
I think this is achievable via URL protocol handlers;
Create a custom URL protocol handler (MSDN: Registering an Application to a URL Protocol)
Create a facebook page that passes user access token to your url handler (ex. myfbapp://accesstoken/{token})
Set oauth redirect_uri to your facebook page
Parse access token in your application
Any idea of how to upload a file to Google site from c#?
I am trying to upload but getting a 403 error. However, I am using the same credentials to connect to the site and get the list of attachments and pages present on the site.
Any help would be greatly appreciated!!
They most likely have an anti-CSRF scheme that stores temporal identifiers in the page and/or cookies, this is specifically to hinder bots.
You are most likely submitting a request without the proper CSRF tokens and get rejected. I would recommend analyzing how they handle CSRF, after this point it will most likely boil down to making a WebRequest to the page and so you can get any cookies they get back, along with having the form so you can scrape out any hidden fields that are relevant. Then move those over to your post request that you're attempting to the send the file to.
I figured out the problem and resolved it. Below is the complete function:
public bool UploadAttachment()
{
try
{
//AsyncSendData data = new AsyncSendData();
string parentUrl = Cabinets["Cabinet1"].ToString();
string parentID = parentUrl.Split('/')[7];
AtomEntry entry = new AtomEntry();
entry.Title.Text = "abc.jpg";
AtomCategory cat = new AtomCategory();
cat.Term = ATTACHMENT_TERM;
cat.Label = "attachment";
cat.Scheme = KIND_SCHEME;
entry.Categories.Add(cat);
AtomLink link = new AtomLink();
link.Rel = PARENT_REL;
link.HRef = parentUrl;
entry.Links.Add(link);
AtomContent content = new AtomContent();
FileInfo info = new FileInfo("C:\\Bluehills.txt");
FileStream stream = info.Open(FileMode.Open,FileAccess.ReadWrite,FileShare.ReadWrite);
this.setUserCredentials(userName, password);
Uri postUri = new Uri(makeFeedUri("content"));
entry.Source = new AtomSource();
//this.EntrySend(postUri, entry, GDataRequestType.Insert);
// Send the request and receive the response:
AtomEntry insertedEntry = this.Insert(postUri, stream, (string)DocumentTypes["TXT"], "bluehills");
return true;
}
catch (Exception ex)
{
return false;
}
}