-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchromium-downloader.c
204 lines (194 loc) · 5.8 KB
/
chromium-downloader.c
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
/*
* chromium-downloader version 1.2
*
* This is a simple utility that allows you to download
* the latest Chromium build for your platform.
*
* It runs on macOS and Linux and depends on libcurl
* (please ensure you have it installed on your system).
*
* This utility may be useful if you wish to update your web browser
* to the latest nightly build. Chromium daily builds can be found at
* "https://storage.googleapis.com/chromium-browser-snapshots"
* but may be difficult to retrieve.
*
* The program works as follows:
*
* - It checks which build is the latest one, by querying the website
* commondatastorage.googleapis.com.
* - It builds the correct URL for downloading the Chromium zip file.
* - It downloads the zip file and saves it to your current working directory.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <curl/curl.h>
#define BASE_URL "https://storage.googleapis.com/chromium-browser-snapshots"
#define ROW_LENGTH 70
#define BUFSIZE 1024
#ifdef __APPLE__
#define DEFAULT_FILENAME "chrome-mac.zip"
#define PLATFORM "Mac"
#endif
#ifdef __linux__
#define DEFAULT_FILENAME "chrome-linux.zip"
#define PLATFORM "Linux"
#endif
/*
* The following buffer will be used to store information
* while performing requests using libcurl.
*/
typedef struct {
char *s;
size_t l;
} buffer_t;
/*
* This function creates a new buffer and returns a pointer to it.
* The function returns NULL if memory allocation fails.
*/
buffer_t *newBuffer() {
buffer_t *buf = malloc(sizeof(buffer_t));
if (buf) {
buf -> l = 0;
buf -> s = calloc(1, sizeof(char));
if (!(buf -> s)) {
free(buf);
return NULL;
}
return buf;
}
return NULL;
}
/*
* This callback function will be passed to 'curl_easy_setopt' in order
* to write curl output to a variable.
*/
size_t write_f(void *ptr, size_t size, size_t nmemb, buffer_t *buf) {
if (!ptr || !buf) return 0;
size_t buf_len = buf -> l, new_len = buf_len + (size * nmemb);
buf -> s = realloc(buf -> s, new_len + 1);
if (!(buf -> s)) return 0;
memcpy((buf -> s) + buf_len, ptr, size * nmemb);
buf -> s[new_len] = '\0';
buf -> l = new_len;
return (size * nmemb);
}
/*
* The procedure performs a request to the website
* "https://storage.googleapis.com/chromium-browser-snapshots"
* to obtain a string containing Chromium latest version number.
*/
char *getLatestVersion(CURLcode *reply_code) {
CURL *curl = NULL;
char request_url[BUFSIZE], *reply_msg = NULL;
if ((curl = curl_easy_init())) {
buffer_t *reply_buf = newBuffer();
if (!reply_buf) {
curl_easy_cleanup(curl);
curl_global_cleanup();
return NULL;
}
snprintf(request_url, sizeof(request_url), "%s/%s/%s",
(char *) BASE_URL, (char *) PLATFORM, (char *) "LAST_CHANGE");
curl_easy_setopt(curl, CURLOPT_URL, request_url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_f);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, reply_buf);
*reply_code = curl_easy_perform(curl);
if (*reply_code == CURLE_OK) reply_msg = strdup(reply_buf -> s);
if (reply_buf -> s) free(reply_buf -> s);
free(reply_buf);
curl_easy_cleanup(curl);
curl_global_cleanup();
return reply_msg;
}
return NULL;
}
/*
* Callback function for libcurl that displays the current download
* progress percentage.
*/
int display_progress(void *ptr, double download_size, double downloaded,
double upload_size, double uploaded) {
if (download_size != 0) {
double ratio = downloaded / download_size;
int i = 0, bar_width = ROW_LENGTH, bar_fill = round(ratio * bar_width);
fprintf(stdout, "Downloading Chromium... Total progress: %.01lf%%",
100 * ratio);
fprintf(stdout, "\r");
fflush(stdout);
}
return 0;
}
/*
* This function downloads the zip file containing Chromium latest build
* using libcurl. A progress meter is also displayed to keep track of
* the download status.
*/
int download(char *version, CURLcode *reply_code) {
char download_url[BUFSIZE];
CURL *curl = NULL;
FILE *fp;
if ((curl = curl_easy_init())) {
if (!(fp = fopen(DEFAULT_FILENAME, "wb"))) {
curl_easy_cleanup(curl);
curl_global_cleanup();
return 1;
}
snprintf(download_url, sizeof(download_url), "%s/%s/%s/%s",
(char *) BASE_URL, (char *) PLATFORM, version, DEFAULT_FILENAME);
curl_easy_setopt(curl, CURLOPT_URL, download_url);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, display_progress);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
*reply_code = curl_easy_perform(curl);
fclose(fp);
curl_easy_cleanup(curl);
curl_global_cleanup();
return 0;
}
return 1;
}
/*
* Prints a separator row made of '*' to stdout.
* The row contains ROW_LENGTH characters.
*/
void print_row() {
for (int i = 0; i < ROW_LENGTH; i++) fprintf(stdout, "*");
fprintf(stdout, "\n");
}
int main(int argc, char **argv) {
char *version = NULL;
CURLcode version_code, download_code;
fprintf(stdout, "chromium-downloader (version 1.2)\n");
print_row();
if (!(version = getLatestVersion(&version_code))) {
fprintf(stdout, "Error: version request initialization failed.\n");
return EXIT_FAILURE;
}
if (version_code == CURLE_OK) {
fprintf(stdout, "Chromium latest version for your platform is: %s\n",
version);
print_row();
}
else {
fprintf(stderr, "Could not retrieve Chromium version number: %s\n",
curl_easy_strerror(version_code));
return EXIT_FAILURE;
}
if (download(version, &download_code)) {
fprintf(stderr, "Error: download initialization failed.\n");
return EXIT_FAILURE;
}
if (download_code == CURLE_OK) {
fprintf(stdout, "Chromium version %s has been successfully downloaded.\n",
version);
}
else {
fprintf(stderr, "Server returned an error code while downloading: %s\n",
curl_easy_strerror(download_code));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}