2
* Copyright (C) 2013 Canonical, Ltd.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; version 3.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
const string DOWNLOAD_APP_ID_KEY = "app_id";
18
const string DOWNLOAD_COMMAND_KEY = "post-download-command";
19
const string[] DOWNLOAD_CMDLINE = {"pkcon", "-p", "install-local", "$file"};
21
[DBus (name = "com.canonical.applications.Download")]
22
interface Download : GLib.Object {
23
[DBus (name = "start")]
24
public abstract void start () throws IOError;
26
[DBus (name = "started")]
27
public signal void started (bool success);
28
[DBus (name = "paused")]
29
public signal void paused (bool success);
30
[DBus (name = "resumed")]
31
public signal void resumed (bool success);
32
[DBus (name = "canceled")]
33
public signal void canceled (bool success);
35
[DBus (name = "finished")]
36
public signal void finished (string path);
37
[DBus (name = "error")]
38
public signal void error (string error);
39
[DBus (name = "progress")]
40
public signal void progress (uint64 received, uint64 total);
43
struct DownloadStruct {
46
public string hash_algorithm;
47
GLib.HashTable<string, Variant> metadata;
48
GLib.HashTable<string, string> headers;
51
[DBus (name = "com.canonical.applications.DownloadManager")]
52
interface DownloadManager : GLib.Object {
53
public abstract GLib.ObjectPath createDownload (
54
DownloadStruct download
57
public abstract GLib.ObjectPath[] getAllDownloadsWithMetadata (string name, string value) throws IOError;
59
[DBus (name = "downloadCreated")]
60
public signal void downloadCreated (GLib.ObjectPath path);
63
DownloadManager? get_downloader () throws IOError {
65
return Bus.get_proxy_sync (BusType.SESSION, "com.canonical.applications.Downloader",
66
"/", DBusProxyFlags.DO_NOT_AUTO_START);
68
warning ("Can't connect to DBus: %s", e.message);
73
Download? get_download (GLib.ObjectPath object_path) throws IOError {
75
return Bus.get_proxy_sync (BusType.SESSION, "com.canonical.applications.Downloader",
76
object_path, DBusProxyFlags.DO_NOT_AUTO_START);
78
warning ("Can't connect to DBus: %s", e.message);
83
public errordomain DownloadError {
88
public string? get_download_progress (string app_id) {
90
var downloads = get_downloader().getAllDownloadsWithMetadata (DOWNLOAD_APP_ID_KEY, app_id);
91
if (downloads.length > 0) {
97
warning ("Cannot get download progress for %s, ignoring: %s", app_id, e.message);
102
public class SignedDownload : GLib.Object {
103
const string CLICK_TOKEN_HEADER = "X-Click-Token";
105
const string CONSUMER_KEY = "consumer_key";
106
const string CONSUMER_SECRET = "consumer_secret";
107
const string TOKEN = "token";
108
const string TOKEN_SECRET = "token_secret";
110
HashTable<string, string> credentials;
111
internal Soup.SessionAsync http_session;
114
http_session = WebClient.get_webclient ();
117
public SignedDownload (HashTable<string, string> credentials) {
118
this.credentials = credentials;
121
string sign_url (string method, string url) {
122
return OAuth.sign_url2(url, null, OAuth.Method.HMAC, method,
123
credentials[CONSUMER_KEY], credentials[CONSUMER_SECRET],
124
credentials[TOKEN], credentials[TOKEN_SECRET]);
127
async string fetch_click_token(string download_url) throws DownloadError {
128
string click_token = null;
129
const string HEAD = "HEAD";
130
DownloadError error = null;
132
var message = new Soup.Message (HEAD, sign_url(HEAD, download_url));
133
http_session.queue_message (message, (session, message) => {
134
click_token = message.response_headers[CLICK_TOKEN_HEADER];
135
if (message.status_code == Soup.KnownStatusCode.OK && click_token != null) {
136
debug ("Click token received");
138
switch (message.status_code) {
139
case Soup.KnownStatusCode.OK:
140
var msg = "No X-Click-Token header received. Url was: %s".printf(download_url);
142
error = new DownloadError.DOWNLOAD_ERROR (msg);
144
case Soup.KnownStatusCode.UNAUTHORIZED:
145
var msg = "The Ubuntu One credentials are invalid, please log in again.";
147
error = new DownloadError.INVALID_CREDENTIALS (msg);
150
var msg = "Web request failed: HTTP %u %s - %s".printf(
151
message.status_code, message.reason_phrase, download_url);
153
error = new DownloadError.DOWNLOAD_ERROR (message.reason_phrase);
157
fetch_click_token.callback ();
166
public virtual async GLib.ObjectPath start_download (string uri, string app_id) throws DownloadError {
167
debug ("Starting download");
169
var click_token = yield fetch_click_token (uri);
171
var metadata = new HashTable<string, Variant> (str_hash, str_equal);
172
metadata[DOWNLOAD_COMMAND_KEY] = DOWNLOAD_CMDLINE;
173
metadata[DOWNLOAD_APP_ID_KEY] = app_id;
174
var headers = new HashTable<string, string> (str_hash, str_equal);
175
headers[CLICK_TOKEN_HEADER] = click_token;
178
var download_struct = DownloadStruct(){
180
hash = "", // means no hash check
181
hash_algorithm = "", // will be ignored
185
var download_object_path = get_downloader().createDownload(download_struct);
186
debug ("Download created, path: %s", download_object_path);
188
var download = get_download (download_object_path);
190
debug ("Download started");
192
return download_object_path;
193
} catch (GLib.IOError e) {
194
warning ("Cannot start download: %s", e.message);
195
throw new DownloadError.DOWNLOAD_ERROR(e.message);