blob: 5fad8452122848460e44a58401afce75bc28f38c [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
Alexander Amelkin5a9d0e02020-06-26 17:27:07 +030039#if LIBCURL_VERSION_NUM >= 0x073200
Stefan Reinauerdfd556c2019-11-20 17:57:40 -080040static int xferinfo(void *p __unused,
41 curl_off_t dltotal __unused, curl_off_t dlnow __unused,
42 curl_off_t ultotal __unused, curl_off_t ulnow __unused)
43{
44 /* Google Drive API transfers no Content-Length, so
45 * instead of bloating this with Range: hacks, let's
46 * just print a spinning wheel.
47 */
48 static int pos = 0;
49 char cursor[4] = { '/', '-', '\\', '|' };
50 printf("%c\b", cursor[pos]);
51 fflush(stdout);
52 pos = (pos + 1) % 4;
53 return 0;
54}
Alexander Amelkin5a9d0e02020-06-26 17:27:07 +030055#endif
Stefan Reinauerdfd556c2019-11-20 17:57:40 -080056
57static size_t write_data(void *ptr, size_t size, size_t nmemb,
58 void *stream)
59{
60 return fwrite(ptr, size, nmemb, (FILE *) stream);
61}
62
Alexander Amelkin5a9d0e02020-06-26 17:27:07 +030063static int curl_get(const char *id, const char *filename, int progress __unused)
Stefan Reinauerdfd556c2019-11-20 17:57:40 -080064{
65 FILE *file;
66 CURLcode res;
67#define URL_BUFFER_SIZE 1024
68 char url[URL_BUFFER_SIZE] =
69 "https://drive.google.com/uc?export=download&id=";
70
71 file = fopen(filename, "wb");
72 if (!file) {
73 perror(filename);
74 return -1;
75 }
76
77 CURL *curl = curl_easy_init();
78 if (!curl) {
79 printf("CURL failed.\n");
80 fclose(file);
81 return -1;
82 }
83
84 strncat(url, id, URL_BUFFER_SIZE - 1);
85
86 /* Set URL to GET here */
87 curl_easy_setopt(curl, CURLOPT_URL, url);
88#ifdef DEBUG
89 /* Switch on protocol/debug output for testing */
90 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
91#endif
92
93 /* Some servers don't like requests without a user-agent field. */
94 curl_easy_setopt(curl, CURLOPT_USERAGENT, "em100-agent/1.0");
95
96 /* Write callback to write the data to disk */
97 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
98
99 /* Write data to this file handle */
100 curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
101
Alexander Amelkin5a9d0e02020-06-26 17:27:07 +0300102#if LIBCURL_VERSION_NUM >= 0x073200
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800103 if (progress) {
104 /* Simple progress indicator function */
105 curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);
106
107 /* Enable progress indicator */
108 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
109 }
Alexander Amelkin5a9d0e02020-06-26 17:27:07 +0300110#endif
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800111
112 /* Follow redirections (as used by Google Drive) */
113 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION , 1L);
114
115 /* Fetch the file */
116 res = curl_easy_perform(curl);
Stefan Reinauer8f7e2232019-12-04 14:31:11 -0800117 if (res != CURLE_OK)
118 printf("Error while downloading %s: %s\n", filename,
119 curl_easy_strerror(res));
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800120
121 /* Close file */
122 fclose(file);
123
124 /* Clean up */
125 curl_easy_cleanup(curl);
126
Stefan Reinauer8f7e2232019-12-04 14:31:11 -0800127 return (res == CURLE_OK) ? 0 : -1;
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800128}
129
130void download(const char *name, const char *id)
131{
132 char *filename = get_em100_file(name);
133 printf("Downloading %s: ", name);
134 if (curl_get(id, filename, 1))
135 printf("FAILED.\n");
136 else
137 printf("OK\n");
138 free(filename);
139}
140
141int update_all_files(void)
142{
Stefan Reinauerbb6415a2019-12-04 17:52:13 -0800143 long old_time = 0, new_time = 0;
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800144 char old_version[256] = "<unknown>", new_version[256] = "<unknown>";
145
146 /* Read existing version and timestamp */
147 char *my_version_name = get_em100_file(version_name);
148 FILE *old = fopen(my_version_name, "r");
Stefan Reinauerbb6415a2019-12-04 17:52:13 -0800149 if (old) {
150 if (fscanf(old, "Time: %ld\nVersion: %255s\n",
151 &old_time, old_version) != 2)
152 printf("Parse error in %s.\n", my_version_name);
153 fclose(old);
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800154 }
155
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800156 free(my_version_name);
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800157
158 /* Read upstream version and timestamp */
159 char *tmp_version = get_em100_file(".VERSION.new");
160 if (curl_get(version_id, tmp_version, 0)) {
161 printf("FAILED.\n");
162 }
163 FILE *new = fopen(tmp_version, "r");
164 if (!new) {
165 free(tmp_version);
166 return 1;
167 }
Stefan Reinauerd565ea82019-12-02 11:59:16 -0800168 if (fscanf(new, "Time: %ld\nVersion: %255s\n",
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800169 &new_time, new_version) != 2)
170 printf("Parse error in upstream VERSION.\n");
171 fclose(new);
172 unlink(tmp_version);
173 free(tmp_version);
174
175 /* Compare time stamps and bail out if we have the latest version */
176 if (old_time >= new_time) {
177 printf("Current version: %s. No newer version available.\n", old_version);
178 return 0;
179 }
180
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800181 /* Download everything */
Stefan Reinauerbb6415a2019-12-04 17:52:13 -0800182 if (old_time == 0)
183 printf("Downloading latest version: %s\n", new_version);
184 else
185 printf("Update available: %s (installed: %s)\n", new_version, old_version);
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800186 download(configs_name, configs_id);
187 download(firmware_name, firmware_id);
Stefan Reinauerc1c4b6b2020-11-18 17:56:12 -0800188 download(version_name, version_id);
Stefan Reinauerdfd556c2019-11-20 17:57:40 -0800189
190 return 0;
191}