1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
|
//-----------------------------------------------------------------------
// <copyright file="GoogleConsumer.cs" company="Outercurve Foundation">
// Copyright (c) Outercurve Foundation. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.ApplicationBlock {
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
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;
/// <summary>
/// A consumer capable of communicating with Google Data APIs.
/// </summary>
public static class GoogleConsumer {
/// <summary>
/// The Consumer to use for accessing Google data APIs.
/// </summary>
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() },
};
/// <summary>
/// A mapping between Google's applications and their URI scope values.
/// </summary>
private static readonly Dictionary<Applications, string> DataScopeUris = new Dictionary<Applications, string> {
{ Applications.Analytics, "https://www.google.com/analytics/feeds/" },
{ Applications.GoogleBase, "http://www.google.com/base/feeds/" },
{ Applications.Blogger, "http://www.blogger.com/feeds" },
{ Applications.BookSearch, "http://www.google.com/books/feeds/" },
{ Applications.Calendar, "http://www.google.com/calendar/feeds/" },
{ Applications.Contacts, "http://www.google.com/m8/feeds/" },
{ Applications.DocumentsList, "http://docs.google.com/feeds/" },
{ Applications.Finance, "http://finance.google.com/finance/feeds/" },
{ Applications.Gmail, "https://mail.google.com/mail/feed/atom" },
{ Applications.Health, "https://www.google.com/h9/feeds/" },
{ Applications.Maps, "http://maps.google.com/maps/feeds/" },
{ Applications.OpenSocial, "http://sandbox.gmodules.com/api/" },
{ Applications.PicasaWeb, "http://picasaweb.google.com/data/" },
{ Applications.Spreadsheets, "http://spreadsheets.google.com/feeds/" },
{ Applications.WebmasterTools, "http://www.google.com/webmasters/tools/feeds/" },
{ Applications.YouTube, "http://gdata.youtube.com" },
};
/// <summary>
/// The URI to get contacts once authorization is granted.
/// </summary>
private static readonly MessageReceivingEndpoint GetContactsEndpoint = new MessageReceivingEndpoint("http://www.google.com/m8/feeds/contacts/default/full/", HttpDeliveryMethods.GetRequest);
/// <summary>
/// The many specific authorization scopes Google offers.
/// </summary>
[Flags]
public enum Applications : long {
/// <summary>
/// The Gmail address book.
/// </summary>
Contacts = 0x1,
/// <summary>
/// Appointments in Google Calendar.
/// </summary>
Calendar = 0x2,
/// <summary>
/// Blog post authoring.
/// </summary>
Blogger = 0x4,
/// <summary>
/// Google Finance
/// </summary>
Finance = 0x8,
/// <summary>
/// Google Gmail
/// </summary>
Gmail = 0x10,
/// <summary>
/// Google Health
/// </summary>
Health = 0x20,
/// <summary>
/// Google OpenSocial
/// </summary>
OpenSocial = 0x40,
/// <summary>
/// Picasa Web
/// </summary>
PicasaWeb = 0x80,
/// <summary>
/// Google Spreadsheets
/// </summary>
Spreadsheets = 0x100,
/// <summary>
/// Webmaster Tools
/// </summary>
WebmasterTools = 0x200,
/// <summary>
/// YouTube service
/// </summary>
YouTube = 0x400,
/// <summary>
/// Google Docs
/// </summary>
DocumentsList = 0x800,
/// <summary>
/// Google Book Search
/// </summary>
BookSearch = 0x1000,
/// <summary>
/// Google Base
/// </summary>
GoogleBase = 0x2000,
/// <summary>
/// Google Analytics
/// </summary>
Analytics = 0x4000,
/// <summary>
/// Google Maps
/// </summary>
Maps = 0x8000,
}
/// <summary>
/// The service description to use for accessing Google data APIs using an X509 certificate.
/// </summary>
/// <param name="signingCertificate">The signing certificate.</param>
/// <returns>A service description that can be used to create an instance of
/// <see cref="DesktopConsumer"/> or <see cref="WebConsumer"/>. </returns>
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 RsaSha1ConsumerSigningBindingElement(signingCertificate) },
};
}
/// <summary>
/// Requests authorization from Google to access data from a set of Google applications.
/// </summary>
/// <param name="consumer">The Google consumer previously constructed using <see cref="CreateWebConsumer"/> or <see cref="CreateDesktopConsumer"/>.</param>
/// <param name="requestedAccessScope">The requested access scope.</param>
public static void RequestAuthorization(WebConsumer consumer, Applications requestedAccessScope) {
if (consumer == null) {
throw new ArgumentNullException("consumer");
}
var extraParameters = new Dictionary<string, string> {
{ "scope", GetScopeUri(requestedAccessScope) },
};
Uri callback = Util.GetCallbackUrlFromContext();
var request = consumer.PrepareRequestUserAuthorization(callback, extraParameters, null);
consumer.Channel.Send(request);
}
/// <summary>
/// Requests authorization from Google to access data from a set of Google applications.
/// </summary>
/// <param name="consumer">The Google consumer previously constructed using <see cref="CreateWebConsumer"/> or <see cref="CreateDesktopConsumer"/>.</param>
/// <param name="requestedAccessScope">The requested access scope.</param>
/// <param name="requestToken">The unauthorized request token assigned by Google.</param>
/// <returns>The request token</returns>
public static Uri RequestAuthorization(DesktopConsumer consumer, Applications requestedAccessScope, out string requestToken) {
if (consumer == null) {
throw new ArgumentNullException("consumer");
}
var extraParameters = new Dictionary<string, string> {
{ "scope", GetScopeUri(requestedAccessScope) },
};
return consumer.RequestUserAuthorization(extraParameters, null, out requestToken);
}
/// <summary>
/// Gets the Gmail address book's contents.
/// </summary>
/// <param name="consumer">The Google consumer.</param>
/// <param name="accessToken">The access token previously retrieved.</param>
/// <param name="maxResults">The maximum number of entries to return. If you want to receive all of the contacts, rather than only the default maximum, you can specify a very large number here.</param>
/// <param name="startIndex">The 1-based index of the first result to be retrieved (for paging).</param>
/// <returns>An XML document returned by Google.</returns>
public static XDocument GetContacts(ConsumerBase consumer, string accessToken, int maxResults/* = 25*/, int startIndex/* = 1*/) {
if (consumer == null) {
throw new ArgumentNullException("consumer");
}
var extraData = new Dictionary<string, string>() {
{ "start-index", startIndex.ToString(CultureInfo.InvariantCulture) },
{ "max-results", maxResults.ToString(CultureInfo.InvariantCulture) },
};
var request = consumer.PrepareAuthorizedRequest(GetContactsEndpoint, accessToken, extraData);
// Enable gzip compression. Google only compresses the response for recognized user agent headers. - Mike Lim
request.AutomaticDecompression = DecompressionMethods.GZip;
request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16";
var response = consumer.Channel.WebRequestHandler.GetResponse(request);
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!
}
}
}
/// <summary>
/// Gets the scope URI in Google's format.
/// </summary>
/// <param name="scope">The scope, which may include one or several Google applications.</param>
/// <returns>A space-delimited list of URIs for the requested Google applications.</returns>
public static string GetScopeUri(Applications scope) {
return string.Join(" ", Util.GetIndividualFlags(scope).Select(app => DataScopeUris[(Applications)app]).ToArray());
}
}
}
|