//-----------------------------------------------------------------------
//
// Copyright (c) Andrew Arnott. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.ApplicationBlock {
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
///
/// A consumer capable of communicating with Google Data APIs.
///
public static class GoogleConsumer {
///
/// The Consumer to use for accessing Google data APIs.
///
public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription {
RequestTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthAuthorizeToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
AccessTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetAccessToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
};
///
/// A mapping between Google's applications and their URI scope values.
///
private static readonly Dictionary DataScopeUris = new Dictionary {
{ Applications.Contacts, "http://www.google.com/m8/feeds/" },
{ Applications.Calendar, "http://www.google.com/calendar/feeds/" },
{ Applications.Blogger, "http://www.blogger.com/feeds" },
};
///
/// The URI to get contacts once authorization is granted.
///
private static readonly MessageReceivingEndpoint GetContactsEndpoint = new MessageReceivingEndpoint("http://www.google.com/m8/feeds/contacts/default/full/", HttpDeliveryMethods.GetRequest);
///
/// The many specific authorization scopes Google offers.
///
[Flags]
public enum Applications : long {
///
/// The Gmail address book.
///
Contacts = 0x1,
///
/// Appointments in Google Calendar.
///
Calendar = 0x2,
///
/// Blog post authoring.
///
Blogger = 0x4,
}
///
/// The service description to use for accessing Google data APIs using an X509 certificate.
///
/// The signing certificate.
/// A service description that can be used to create an instance of
/// or .
public static ServiceProviderDescription CreateRsaSha1ServiceDescription(X509Certificate2 signingCertificate) {
if (signingCertificate == null) {
throw new ArgumentNullException("signingCertificate");
}
return new ServiceProviderDescription {
RequestTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthAuthorizeToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
AccessTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetAccessToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new RsaSha1SigningBindingElement(signingCertificate) },
};
}
///
/// Requests authorization from Google to access data from a set of Google applications.
///
/// The Google consumer previously constructed using or .
/// The requested access scope.
public static void RequestAuthorization(WebConsumer consumer, Applications requestedAccessScope) {
if (consumer == null) {
throw new ArgumentNullException("consumer");
}
var extraParameters = new Dictionary {
{ "scope", GetScopeUri(requestedAccessScope) },
};
Uri callback = Util.GetCallbackUrlFromContext();
var request = consumer.PrepareRequestUserAuthorization(callback, extraParameters, null);
consumer.Channel.Send(request);
}
///
/// Requests authorization from Google to access data from a set of Google applications.
///
/// The Google consumer previously constructed using or .
/// The requested access scope.
/// The unauthorized request token assigned by Google.
/// The request token
public static Uri RequestAuthorization(DesktopConsumer consumer, Applications requestedAccessScope, out string requestToken) {
if (consumer == null) {
throw new ArgumentNullException("consumer");
}
var extraParameters = new Dictionary {
{ "scope", GetScopeUri(requestedAccessScope) },
};
return consumer.RequestUserAuthorization(extraParameters, null, out requestToken);
}
///
/// Gets the Gmail address book's contents.
///
/// The Google consumer previously constructed using or .
/// The access token previously retrieved.
/// An XML document returned by Google.
public static XDocument GetContacts(ConsumerBase consumer, string accessToken) {
if (consumer == null) {
throw new ArgumentNullException("consumer");
}
var response = consumer.PrepareAuthorizedRequestAndSend(GetContactsEndpoint, accessToken);
string body = response.GetResponseReader().ReadToEnd();
XDocument result = XDocument.Parse(body);
return result;
}
public static void PostBlogEntry(ConsumerBase consumer, string accessToken, string blogUrl, string title, XElement body) {
string feedUrl;
var getBlogHome = WebRequest.Create(blogUrl);
using (var blogHomeResponse = getBlogHome.GetResponse()) {
using (StreamReader sr = new StreamReader(blogHomeResponse.GetResponseStream())) {
string homePageHtml = sr.ReadToEnd();
Match m = Regex.Match(homePageHtml, @"http://www.blogger.com/feeds/\d+/posts/default");
Debug.Assert(m.Success, "Posting operation failed.");
feedUrl = m.Value;
}
}
const string Atom = "http://www.w3.org/2005/Atom";
XElement entry = new XElement(
XName.Get("entry", Atom),
new XElement(XName.Get("title", Atom), new XAttribute("type", "text"), title),
new XElement(XName.Get("content", Atom), new XAttribute("type", "xhtml"), body),
new XElement(XName.Get("category", Atom), new XAttribute("scheme", "http://www.blogger.com/atom/ns#"), new XAttribute("term", "oauthdemo")));
MemoryStream ms = new MemoryStream();
XmlWriterSettings xws = new XmlWriterSettings() {
Encoding = Encoding.UTF8,
};
XmlWriter xw = XmlWriter.Create(ms, xws);
entry.WriteTo(xw);
xw.Flush();
WebRequest request = consumer.PrepareAuthorizedRequest(new MessageReceivingEndpoint(feedUrl, HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), accessToken);
request.ContentType = "application/atom+xml";
request.Method = "POST";
request.ContentLength = ms.Length;
ms.Seek(0, SeekOrigin.Begin);
using (Stream requestStream = request.GetRequestStream()) {
ms.CopyTo(requestStream);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
if (response.StatusCode == HttpStatusCode.Created) {
// Success
} else {
// Error!
}
}
}
///
/// Gets the scope URI in Google's format.
///
/// The scope, which may include one or several Google applications.
/// A space-delimited list of URIs for the requested Google applications.
private static string GetScopeUri(Applications scope) {
return string.Join(" ", Util.GetIndividualFlags(scope).Select(app => DataScopeUris[(Applications)app]).ToArray());
}
}
}