~ci-train-bot/unity-scope-click/unity-scope-click-ubuntu-yakkety-landing-072

« back to all changes in this revision

Viewing changes to src/download-manager.vala

  • Committer: Michael McCracken
  • Date: 2014-01-13 19:14:10 UTC
  • mto: This revision was merged to the branch mainline in revision 105.
  • Revision ID: mike.mccracken@canonical.com-20140113191410-0241f3ykr7k92e1p
re-add the vala sources and autotools stuff alongside cmake and CXX new stuff

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2013 Canonical, Ltd.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 */
 
16
 
 
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"};
 
20
 
 
21
[DBus (name = "com.canonical.applications.Download")]
 
22
interface Download : GLib.Object {
 
23
    [DBus (name = "start")]
 
24
    public abstract void start () throws IOError;
 
25
 
 
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);
 
34
 
 
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);
 
41
}
 
42
 
 
43
struct DownloadStruct {
 
44
    public string url;
 
45
    public string hash;
 
46
    public string hash_algorithm;
 
47
    GLib.HashTable<string, Variant> metadata;
 
48
    GLib.HashTable<string, string> headers;
 
49
}
 
50
 
 
51
[DBus (name = "com.canonical.applications.DownloadManager")]
 
52
interface DownloadManager : GLib.Object {
 
53
    public abstract GLib.ObjectPath createDownload (
 
54
        DownloadStruct download
 
55
    ) throws IOError;
 
56
 
 
57
    public abstract GLib.ObjectPath[] getAllDownloadsWithMetadata (string name, string value) throws IOError;
 
58
 
 
59
    [DBus (name = "downloadCreated")]
 
60
    public signal void downloadCreated (GLib.ObjectPath path);
 
61
}
 
62
 
 
63
DownloadManager? get_downloader () throws IOError {
 
64
    try {
 
65
        return Bus.get_proxy_sync (BusType.SESSION, "com.canonical.applications.Downloader",
 
66
            "/", DBusProxyFlags.DO_NOT_AUTO_START);
 
67
    } catch (IOError e) {
 
68
        warning ("Can't connect to DBus: %s", e.message);
 
69
        throw e;
 
70
    }
 
71
}
 
72
 
 
73
Download? get_download (GLib.ObjectPath object_path) throws IOError {
 
74
    try {
 
75
        return Bus.get_proxy_sync (BusType.SESSION, "com.canonical.applications.Downloader",
 
76
            object_path, DBusProxyFlags.DO_NOT_AUTO_START);
 
77
    } catch (IOError e) {
 
78
        warning ("Can't connect to DBus: %s", e.message);
 
79
        throw e;
 
80
    }
 
81
}
 
82
 
 
83
public errordomain DownloadError {
 
84
    DOWNLOAD_ERROR,
 
85
    INVALID_CREDENTIALS
 
86
}
 
87
 
 
88
public string? get_download_progress (string app_id) {
 
89
    try {
 
90
        var downloads = get_downloader().getAllDownloadsWithMetadata (DOWNLOAD_APP_ID_KEY, app_id);
 
91
        if (downloads.length > 0) {
 
92
            return downloads[0];
 
93
        } else {
 
94
            return null;
 
95
        }
 
96
    } catch (IOError e) {
 
97
        warning ("Cannot get download progress for %s, ignoring: %s", app_id, e.message);
 
98
        return null;
 
99
    }
 
100
}
 
101
 
 
102
public class SignedDownload : GLib.Object {
 
103
    const string CLICK_TOKEN_HEADER = "X-Click-Token";
 
104
 
 
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";
 
109
 
 
110
    HashTable<string, string> credentials;
 
111
    internal Soup.SessionAsync http_session;
 
112
 
 
113
    construct {
 
114
        http_session = WebClient.get_webclient ();
 
115
    }
 
116
 
 
117
    public SignedDownload (HashTable<string, string> credentials) {
 
118
        this.credentials = credentials;
 
119
    }
 
120
 
 
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]);
 
125
    }
 
126
 
 
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;
 
131
 
 
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");
 
137
            } else {
 
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);
 
141
                    debug (msg);
 
142
                    error = new DownloadError.DOWNLOAD_ERROR (msg);
 
143
                    break;
 
144
                case Soup.KnownStatusCode.UNAUTHORIZED:
 
145
                    var msg = "The Ubuntu One credentials are invalid, please log in again.";
 
146
                    debug (msg);
 
147
                    error = new DownloadError.INVALID_CREDENTIALS (msg);
 
148
                    break;
 
149
                default:
 
150
                    var msg = "Web request failed: HTTP %u %s - %s".printf(
 
151
                           message.status_code, message.reason_phrase, download_url);
 
152
                    debug (msg);
 
153
                    error = new DownloadError.DOWNLOAD_ERROR (message.reason_phrase);
 
154
                    break;
 
155
                }
 
156
            }
 
157
            fetch_click_token.callback ();
 
158
        });
 
159
        yield;
 
160
        if (error != null) {
 
161
            throw error;
 
162
        }
 
163
        return click_token;
 
164
    }
 
165
 
 
166
    public virtual async GLib.ObjectPath start_download (string uri, string app_id) throws DownloadError {
 
167
        debug ("Starting download");
 
168
 
 
169
        var click_token = yield fetch_click_token (uri);
 
170
 
 
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;
 
176
 
 
177
        try {
 
178
            var download_struct = DownloadStruct(){
 
179
                url = uri,
 
180
                hash = "",  // means no hash check
 
181
                hash_algorithm = "",  // will be ignored
 
182
                metadata = metadata,
 
183
                headers = headers
 
184
            };
 
185
            var download_object_path = get_downloader().createDownload(download_struct);
 
186
            debug ("Download created, path: %s", download_object_path);
 
187
 
 
188
            var download = get_download (download_object_path);
 
189
            download.start ();
 
190
            debug ("Download started");
 
191
 
 
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);
 
196
        }
 
197
    }
 
198
}