blob: 6895c62e09be80b95898ece4dec4a2068f7bf383 [file] [log] [blame]
Stefan Reinauerdfd556c2019-11-20 17:57:40 -08001/*
2 * Copyright 2019 Google LLC
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 2 of the License.
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
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <unistd.h>
18#include <curl/curl.h>
19#include "em100.h"
20
21/* For higher availability these binaries are hosted on
22 * Google Drive for convenience. You can create them by yourself
23 * from the installer tar ball.
24 *
25 * TODO: some sort of MD5 check / update check.
26 */
27const char *firmware_id = "1UmzGZbRkF9duwTLPi467EyfIZ6EhnMKA";
28const char *firmware_name = "firmware.tar.xz";
29
30const char *configs_id = "19jT6kNYV1TE6WNx6lUkgH0TYyKbxXcd4";
31const char *configs_name = "configs.tar.xz";
32
33const char *version_id = "1YC755W_c4nRN4qVgosegFrvfyWllqb0b";
34const char *version_name = "VERSION";
35
36#define TIMEOPT CURLINFO_TOTAL_TIME_T
37#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL 3000000
38
39static int xferinfo(void *p __unused,
40 curl_off_t dltotal __unused, curl_off_t dlnow __unused,
41 curl_off_t ultotal __unused, curl_off_t ulnow __unused)
42{
43 /* Google Drive API transfers no Content-Length, so
44 * instead of bloating this with Range: hacks, let's
45 * just print a spinning wheel.
46 */
47 static int pos = 0;
48 char cursor[4] = { '/', '-', '\\', '|' };
49 printf("%c\b", cursor[pos]);
50 fflush(stdout);
51 pos = (pos + 1) % 4;
52 return 0;
53}
54
55static size_t write_data(void *ptr, size_t size, size_t nmemb,
56 void *stream)
57{
58 return fwrite(ptr, size, nmemb, (FILE *) stream);
59}
60
61static int curl_get(const char *id, const char *filename, int progress)
62{
63 FILE *file;
64 CURLcode res;
65#define URL_BUFFER_SIZE 1024
66 char url[URL_BUFFER_SIZE] =
67 "https://drive.google.com/uc?export=download&id=";
68
69 file = fopen(filename, "wb");
70 if (!file) {
71 perror(filename);
72 return -1;
73 }
74
75 CURL *curl = curl_easy_init();
76 if (!curl) {
77 printf("CURL failed.\n");
78 fclose(file);
79 return -1;
80 }
81
82 strncat(url, id, URL_BUFFER_SIZE - 1);
83
84 /* Set URL to GET here */
85 curl_easy_setopt(curl, CURLOPT_URL, url);
86#ifdef DEBUG
87 /* Switch on protocol/debug output for testing */
88 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
89#endif
90
91 /* Some servers don't like requests without a user-agent field. */
92 curl_easy_setopt(curl, CURLOPT_USERAGENT, "em100-agent/1.0");
93
94 /* Write callback to write the data to disk */
95 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
96
97 /* Write data to this file handle */
98 curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
99
100 if (progress) {
101 /* Simple progress indicator function */
102 curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);
103
104 /* Enable progress indicator */
105 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
106 }
107
108 /* Follow redirections (as used by Google Drive) */
109 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION , 1L);
110
111 /* Fetch the file */
112 res = curl_easy_perform(curl);
113
114 /* Close file */
115 fclose(file);
116
117 /* Clean up */
118 curl_easy_cleanup(curl);
119
120 return 0;
121}
122
123void download(const char *name, const char *id)
124{
125 char *filename = get_em100_file(name);
126 printf("Downloading %s: ", name);
127 if (curl_get(id, filename, 1))
128 printf("FAILED.\n");
129 else
130 printf("OK\n");
131 free(filename);
132}
133
134int update_all_files(void)
135{
136 long old_time, new_time;
137 char old_version[256] = "<unknown>", new_version[256] = "<unknown>";
138
139 /* Read existing version and timestamp */
140 char *my_version_name = get_em100_file(version_name);
141 FILE *old = fopen(my_version_name, "r");
142 if (!old) {
143 free(my_version_name);
144 /* We probably don't have any files yet. Try to
145 * download everything.
146 */
147 goto download_all;
148 }
149
Stefan Reinauerd565ea82019-12-02 11:59:16 -0800150 if (fscanf(old, "Time: %ld\nVersion: %255s\n",
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800151 &old_time, old_version) != 2)
152 printf("Parse error in %s.\n", my_version_name);
153
154 free(my_version_name);
155 fclose(old);
156
157 /* Read upstream version and timestamp */
158 char *tmp_version = get_em100_file(".VERSION.new");
159 if (curl_get(version_id, tmp_version, 0)) {
160 printf("FAILED.\n");
161 }
162 FILE *new = fopen(tmp_version, "r");
163 if (!new) {
164 free(tmp_version);
165 return 1;
166 }
Stefan Reinauerd565ea82019-12-02 11:59:16 -0800167 if (fscanf(new, "Time: %ld\nVersion: %255s\n",
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800168 &new_time, new_version) != 2)
169 printf("Parse error in upstream VERSION.\n");
170 fclose(new);
171 unlink(tmp_version);
172 free(tmp_version);
173
174 /* Compare time stamps and bail out if we have the latest version */
175 if (old_time >= new_time) {
176 printf("Current version: %s. No newer version available.\n", old_version);
177 return 0;
178 }
179
180download_all:
181 /* Download everything */
182 printf("Update available: %s (installed: %s)\n", new_version, old_version);
183 download(version_name, version_id);
184 download(configs_name, configs_id);
185 download(firmware_name, firmware_id);
186
187 return 0;
188}