Files @ f2a6ba12fc29
Branch filter:

Location: libtransport.git/3rdparty/cpprestsdk/samples/WindowsLiveAuth/live_connect.h

Jan Kaluza
Slack frontend stub
  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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved. 
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* live_connect.h
*
* Simple API for connecting to Windows Live services, such as OneDrive and Hotmail.
* Only supported for App Store apps.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/

#pragma once

#include <collection.h>
#include "cpprest/http_client.h"
#include "cpprest/streams.h"
#include "cpprest/filestream.h"

namespace web { namespace live {

#if defined(__cplusplus_winrt)

    /// <summary>
    /// This namespace contains symbols for the standard Windows Live permission scopes, which
    /// determine what privileges the application has to access data. What operations are allowed
    /// and what details are returned will depend on privileges requested and granted.
    ///
    /// See MSDN documentation for Live Connect at http://msdn.microsoft.com/en-us/live/ for details
    /// on the precise meaning of these scopes.
    /// </summary>
    namespace scopes 
    {
        static const utility::string_t wl_birthday           = U("wl.birthday");
        static const utility::string_t wl_basic              = U("wl.basic");
        static const utility::string_t wl_calendars          = U("wl.calendars");
        static const utility::string_t wl_calendars_update   = U("wl.calendars_update");
        static const utility::string_t wl_contacts_birthday  = U("wl.contacts_birthday");
        static const utility::string_t wl_contacts_create    = U("wl.contacts_create");
        static const utility::string_t wl_contacts_calendars = U("wl.contacts_calendars");
        static const utility::string_t wl_contacts_photos    = U("wl.contacts_photos");
        static const utility::string_t wl_contacts_skydrive  = U("wl.contacts_skydrive");
        static const utility::string_t wl_emails             = U("wl.emails");
        static const utility::string_t wl_events_create      = U("wl.events_create");
        static const utility::string_t wl_messenger          = U("wl.messenger");
        static const utility::string_t wl_offline_access     = U("wl.offline_access");
        static const utility::string_t wl_phone_numbers      = U("wl.phone_numbers");
        static const utility::string_t wl_photos             = U("wl.photos");
        static const utility::string_t wl_postal_addresses   = U("wl.postal_addresses");
        static const utility::string_t wl_share              = U("wl.share");
        static const utility::string_t wl_signin             = U("wl.signin");
        static const utility::string_t wl_skydrive           = U("wl.skydrive");
        static const utility::string_t wl_skydrive_update    = U("wl.skydrive_update");
        static const utility::string_t wl_work_profile       = U("wl.work_profile");
    }

    /// <summary>
    /// Represents a session connected to Windows Live services like Calendar, Contacts, OneDrive, and the
    /// user profile, by using the Live Connect REST API. It is a thin layer on top of the Casablanca HTTP
    /// library, tailored to the usage scenarios for Windows Live clients.
    /// </summary>
    /// <remarks>
    ///   See http://msdn.microsoft.com/onedrive/ for details on using the OneDrive REST API.
    ///
    ///   Note: when passing resource paths into the functions in this class, it is not necessary to add the
    ///   leading '/' path character. It will be added automatically when constructing the HTTP request.
    ///
    ///   Most Windows Live requests will return data in the form of plain text or JSON. The APIs in this
    ///   class will typically return data as JSON. The most significant exception to this is download(),
    ///   which will not return any data to be extracted, it will place the result in the stream or file
    ///   passed into it. Also, remove() will return data only to provide error information: for non-error
    ///   cases, the JSON value will be <c>null</c>.
    ///
    ///   This class relies on PPL tasks to represent asynchrony: the examples contained within the comments
    ///   show synchronous forms of invoking these APIs. In real use, applications should be relying on 
    ///   .then() instead of .get() or .wait().
    /// </remarks>
    class live_client
    {
    public:
        /// <summary>
        /// Constructs a new Windows Live client
        /// </summary>
        live_client() :
            m_client(L"https://apis.live.net/v5.0/"),
            m_authenticator(ref new Windows::Security::Authentication::OnlineId::OnlineIdAuthenticator)
        {
        }

        /// <summary>
        /// Authenticates the current user and requests permission to access a listed Live services.
        /// </summary>
        /// <param name="services">A string containing a space-separated list of access scopes to request</param>
        /// <returns><c>true</c> if authentication succeeded, <c>false</c> otherwise.</returns>
        pplx::task<bool> login(utility::string_t scopes)
        {
            this->m_token.clear();

            auto request = ref new Windows::Security::Authentication::OnlineId::OnlineIdServiceTicketRequest(ref new Platform::String(scopes.c_str()), "DELEGATION");

            return pplx::create_task(m_authenticator->AuthenticateUserAsync(request))
            .then([this](Windows::Security::Authentication::OnlineId::UserIdentity^ ident)
            {      
                if ( ident->Tickets->Size > 0 )
                {
                    auto ticket = ident->Tickets->GetAt(0);
      
                    m_token = std::wstring(ticket->Value->Data());
                    return true;
                }
                return false;
            });
        }

        /// <summary>
        /// Authenticates the current user and requests permission to access a listed Live services.
        /// </summary>
        /// <param name="begin">
        ///   The starting point of an iterator over a std::string collection, (for example a vector of
        ///   strings), holding the names of scopes to get permission for.
        /// </param>
        /// <param name="end">The end point for the same collection.</param>
        /// <returns><c>true</c> if authentication succeeded, <c>false</c> otherwise.</returns>
        template<typename Iter>
        pplx::task<bool> login(Iter begin, Iter end)
        {
            utility::string_t services;
            
            for (Iter iter = begin; iter != end; ++iter)
            {
                services.append(*iter);
                services.append(utility::string_t(L" "));
            }

            return login(services);
        }

        /// <summary>
        /// Dismisses any and all permissions that have been granted to the user.
        /// </summary>
        /// <returns><c>true</c> if logout succeeded, <c>false</c> otherwise.</returns>
        /// <remarks>
        ///   Whether the logout attempt was successful or not, the application will
        ///   be required to log in again, since the access token will be cleared.
        /// </remarks>
        pplx::task<bool> logout()
        {
            this->m_token.clear();

            if (!m_authenticator->CanSignOut)
                return pplx::task_from_result(false);

            return pplx::create_task(m_authenticator->SignOutUserAsync()).then([this]
            {
                return true;
            });
        }

        /// <summary>
        /// Retrieves the access token in use by this client instance.
        /// </summary>
        /// <returns>The current token: a string. An invalid token is indicated by an empty string.</returns>
        const utility::string_t& access_token() const 
        {
                return m_token;
        }

        /// <summary>
        /// Retrieves data from Windows Live.
        /// </summary>
        /// <param name="resource">The Windows Live resource to retrieve.</param>
        /// <returns>The JSON value resulting from the request.</returns>
        /// <example>To get the current user profile: <code>json::value profile = live_clnt.get(L"me").get().extract_json().get();</code></example>
        pplx::task<web::json::value> get(const utility::string_t& resource)
        {
            return _make_request(web::http::methods::GET, resource).then([](web::http::http_response response) { return _json_extract(response); });
        }

        /// <summary>
        /// Deletes data from Windows Live.
        /// </summary>
        /// <param name="resource">The Windows Live resource to delete.</param>
        /// <returns>The JSON value resulting from the request.</returns>
        /// <example>To delete a file: <code>live_clnt.remove(L"file.NNNNNNNNNNNN").wait();</code>, where file.NNNNNNNNNNNN is a file identifier.</example>
        pplx::task<web::json::value> remove(const utility::string_t& resource)
        {
            return _make_request(web::http::methods::DEL, resource).then([](web::http::http_response response) { return _json_extract(response); });
        }

        /// <summary>
        /// Modifies data in Windows Live.
        /// </summary>
        /// <param name="resource">The Windows Live resource to modify.</param>
        /// <returns>The JSON value resulting from the request.</returns>
        /// <remarks>In most cases, the response will contain data about the modified resource.</remarks>
        pplx::task<web::json::value> put(const utility::string_t& resource, const web::json::value& data)
        {
            return _make_request(web::http::methods::PUT, resource, data).then([](web::http::http_response response) { return _json_extract(response); });
        }

        /// <summary>
        /// Adds data to Windows Live.
        /// </summary>
        /// <param name="resource">The Windows Live resource (such as a folder) where data should be added.</param>
        /// <returns>The JSON value resulting from the request.</returns>
        /// <remarks>In most cases, the response will contain data about the added resource.</remarks>
        /// <example>To add a contact: <code>json::value contact = ...; live_clnt.post(L"me/contacts", contact).wait();</code></example>
        pplx::task<web::json::value> post(const utility::string_t& resource, const web::json::value& data)
        {
            return _make_request(web::http::methods::POST, resource, data).then([](web::http::http_response response) { return _json_extract(response); });
        }

        /// <summary>
        /// Copies data in Windows Live.
        /// </summary>
        /// <param name="resource">The Windows Live resource to copy.</param>
        /// <param name="destination">The location where the resource copy should be placed.</param>
        /// <returns>The JSON value resulting from the request.</returns>
        /// <remarks>In most cases, the response will contain data about the added resource.</remarks>
        /// <example>To copy a file: <code>live_clnt.copy(L"file.NNNNNNNNNNNN", L"folder.MMMMMMMMMMMM").wait();</code></example>
        pplx::task<web::json::value> copy(const utility::string_t& resource, const utility::string_t& destination)
        {
            return _make_request(U("COPY"), resource, destination).then([](web::http::http_response response) { return _json_extract(response); });
        }

        /// <summary>
        /// Moves data in Windows Live.
        /// </summary>
        /// <param name="resource">The Windows Live resource to move.</param>
        /// <param name="destination">The location where the resource should be placed.</param>
        /// <returns>The JSON value resulting from the request.</returns>
        /// <remarks>In most cases, the response will contain data about the resource in its new location.</remarks>
        /// <example>To copy a file: <code>live_clnt.copy(L"file.NNNNNNNNNNNN", L"folder.MMMMMMMMMMMM").wait();</code></example>
        pplx::task<web::json::value> move(const utility::string_t& resource, const utility::string_t& destination)
        {
            return _make_request(U("MOVE"), resource, destination).then([](web::http::http_response response) { return _json_extract(response); });
        }

        /// <summary>
        /// Download a file from OneDrive.
        /// </summary>
        /// <param name="file_id">The OneDrive file id to download.</param>
        /// <param name="stream">A stream into which the contents of the file should be placed.</param>
        /// <returns>The size of the downloaded resource.</returns>
        /// <remarks></remarks>
        /// <example>To download a file: <code>ostream stream = ...; live_clnt.download(L"file.NNNNNNNNNNNN", ostream).get().content_ready().wait();</code></example>
        pplx::task<size_t> download(const utility::string_t& file_id, concurrency::streams::ostream stream)
        {
            web::http::uri_builder bldr;
            bldr.append(file_id);
            bldr.append_path(U("content"));

            web::http::http_request req(web::http::methods::GET);
            req.set_request_uri(bldr.to_string());

            return _make_request(req)
                .then([stream](web::http::http_response response) -> pplx::task<size_t>
                {
                    if ( response.status_code() >= 400 )
                    {
                        return response.extract_string().then(
                            [](utility::string_t message) -> pplx::task<size_t>
                            {
                                return pplx::task_from_exception<size_t>(std::exception(utility::conversions::to_utf8string(message).c_str()));
                            });
                    }
                    return response.body().read_to_end(stream.streambuf());
                });
        }

        /// <summary>
        /// Download a file from OneDrive.
        /// </summary>
        /// <param name="file_id">The OneDrive file id to download.</param>
        /// <param name="file">A StorageFile reference identifying the target for the downloaded data.</param>
        /// <returns>The size of the downloaded resource.</returns>
        /// <remarks></remarks>
        pplx::task<size_t> download(const utility::string_t& file_id, Windows::Storage::StorageFile^ file)
        {
            if ( file == nullptr )
                throw std::invalid_argument("file reference cannot be null");

            return concurrency::streams::file_stream<uint8_t>::open_ostream(file).then(
                [file_id,this](concurrency::streams::ostream stream)
                {
                    web::http::uri_builder bldr;
                    bldr.append(file_id);
                    bldr.append_path(U("content"));

                    web::http::http_request req(web::http::methods::GET);
                    req.set_request_uri(bldr.to_string());

                    return _make_request(req)
                        .then([stream](web::http::http_response response) -> pplx::task<size_t>
                        {
                            if ( response.status_code() >= 400 )
                            {
                                return response.extract_string().then(
                                    [](utility::string_t message) -> pplx::task<size_t>
                                    {
                                        return pplx::task_from_exception<size_t>(std::exception(utility::conversions::to_utf8string(message).c_str()));
                                    });
                            }
                            return response.body().read_to_end(stream.streambuf());
                        })
                        .then([stream](pplx::task<size_t> ret_task)
                        {
                            return stream.flush().then([stream, ret_task]()
                            {
                                return stream.close();
                            }).then([ret_task]()
                            {
                                return ret_task;    
                            });
                        });
                });
        }

        /// <summary>
        /// Upload a file to OneDrive.
        /// </summary>
        /// <param name="path">The path of the file location in OneDrive. It should be of the form "folder.NNNNNNNNNN/files/file_name.ext"</param>
        /// <param name="stream">A stream from which data for the file will be read.</param>
        /// <param name="content_length">The size of the data to upload.</param>
        /// <returns>The JSON value resulting from the request, containing metadata about the uploaded file.</returns>
        /// <remarks>
        ///   The stream must contain at least as many bytes as indicated by 'content_length', starting from its current read position. 
        /// </remarks>
        pplx::task<web::json::value> upload(const utility::string_t& path, concurrency::streams::istream stream, size_t content_length)
        {
            web::http::uri_builder bldr;
            bldr.append(path);

            web::http::http_request req(web::http::methods::PUT);
            req.set_request_uri(bldr.to_string());
            req.set_body(stream, content_length, U(""));

            return _make_request(req)
                .then([](web::http::http_response response)
                {
                    return _json_extract(response);
                });
        }

        /// <summary>
        /// Upload a file to OneDrive.
        /// </summary>
        /// <param name="path">The path of the file location in OneDrive. It should be of the form "folder.NNNNNNNNNN/files/file_name.ext"</param>
        /// <param name="file">A StorageFile reference identifying the source of the uploaded data.</param>
        /// <param name="content_length">The size of the data to upload.</param>
        /// <returns>The JSON value resulting from the request, containing metadata about the uploaded file.</returns>
        /// <remarks>The entire file will be uploaded.</remarks>
        pplx::task<web::json::value> upload(const utility::string_t& path, Windows::Storage::StorageFile^ file)
        {
            if ( file == nullptr )
                throw std::invalid_argument("file reference cannot be null");

            return pplx::create_task(file->GetBasicPropertiesAsync()).then(
                [path,this,file](Windows::Storage::FileProperties::BasicProperties^ props)
                {
                    if (props == nullptr)
                        throw std::exception("failed to retrieve file properties; cannot determine its size");
                    size_t size = (size_t)props->Size;

                    return concurrency::streams::file_stream<uint8_t>::open_istream(file).then(
                        [path,this,size](concurrency::streams::istream stream)
                        {
                            web::http::uri_builder bldr;
                            bldr.append(path);

                            web::http::http_request req(web::http::methods::PUT);
                            req.set_request_uri(bldr.to_string());
                            req.set_body(stream, size, U(""));

                            return _make_request(req)
                                .then([](web::http::http_response response)
                                {
                                    return response.content_ready(); 
                                })
                                .then([stream](pplx::task<web::http::http_response> response)
                                {
                                    return stream.close().then([response]()
                                    {
                                        return _json_extract(response.get());
                                    });
                                });
                        });
                });
        }

    private:

        static pplx::task<web::json::value> _json_extract(web::http::http_response response)
        {
            switch(response.status_code())
            {
            case web::http::status_codes::NoContent:
                return pplx::task_from_result(web::json::value::null());
            default:
                if ( response.status_code() >= 400 )
                {
                    return response.extract_string().then(
                        [](utility::string_t message) -> pplx::task<web::json::value>
                        {
                            return pplx::task_from_exception<web::json::value>(std::exception(utility::conversions::to_utf8string(message).c_str()));
                        });
                }
                return response.extract_json();
            }
        }

        pplx::task<web::http::http_response> _make_request(web::http::method method, const utility::string_t& path)
        {
            web::http::uri_builder bldr;
            bldr.append(path);

            web::http::http_request req(method);
            req.set_request_uri(bldr.to_string());
            return _make_request(req);
        }


        pplx::task<web::http::http_response> _make_request(web::http::method method, const utility::string_t& path, const web::json::value& data)
        {
            web::http::uri_builder bldr;
            bldr.append(path);

            web::http::http_request req(method);
            req.set_request_uri(bldr.to_string());
            req.set_body(data);
            return _make_request(req);
        }

        pplx::task<web::http::http_response> _make_request(web::http::method method, const utility::string_t& path, const utility::string_t& destination)
        {
            web::http::uri_builder bldr;
            bldr.append(path);

            web::json::value data;
            data[U("destination")] = web::json::value::string(destination);

            web::http::http_request req(method);
            req.set_request_uri(bldr.to_string());
            req.set_body(data);
            return _make_request(req);
        }

        pplx::task<web::http::http_response> _make_request(web::http::http_request req)
        {
            if (!m_token.empty())
                req.headers().add(U("Authorization"), U("Bearer ")+m_token);
            return m_client.request(req);
        }

        Windows::Security::Authentication::OnlineId::OnlineIdAuthenticator^ m_authenticator;
        web::http::client::http_client m_client;
        utility::string_t m_token;
    };
#endif
}}