Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 1 | /* |
| 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 | */ |
| 27 | const char *firmware_id = "1UmzGZbRkF9duwTLPi467EyfIZ6EhnMKA"; |
| 28 | const char *firmware_name = "firmware.tar.xz"; |
| 29 | |
| 30 | const char *configs_id = "19jT6kNYV1TE6WNx6lUkgH0TYyKbxXcd4"; |
| 31 | const char *configs_name = "configs.tar.xz"; |
| 32 | |
| 33 | const char *version_id = "1YC755W_c4nRN4qVgosegFrvfyWllqb0b"; |
| 34 | const char *version_name = "VERSION"; |
| 35 | |
| 36 | #define TIMEOPT CURLINFO_TOTAL_TIME_T |
| 37 | #define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL 3000000 |
| 38 | |
Alexander Amelkin | 5a9d0e0 | 2020-06-26 17:27:07 +0300 | [diff] [blame] | 39 | #if LIBCURL_VERSION_NUM >= 0x073200 |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 40 | static 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 Amelkin | 5a9d0e0 | 2020-06-26 17:27:07 +0300 | [diff] [blame] | 55 | #endif |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 56 | |
| 57 | static 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 Amelkin | 5a9d0e0 | 2020-06-26 17:27:07 +0300 | [diff] [blame] | 63 | static int curl_get(const char *id, const char *filename, int progress __unused) |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 64 | { |
| 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 Amelkin | 5a9d0e0 | 2020-06-26 17:27:07 +0300 | [diff] [blame] | 102 | #if LIBCURL_VERSION_NUM >= 0x073200 |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 103 | 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 Amelkin | 5a9d0e0 | 2020-06-26 17:27:07 +0300 | [diff] [blame] | 110 | #endif |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 111 | |
| 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 Reinauer | 8f7e223 | 2019-12-04 14:31:11 -0800 | [diff] [blame] | 117 | if (res != CURLE_OK) |
| 118 | printf("Error while downloading %s: %s\n", filename, |
| 119 | curl_easy_strerror(res)); |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 120 | |
| 121 | /* Close file */ |
| 122 | fclose(file); |
| 123 | |
| 124 | /* Clean up */ |
| 125 | curl_easy_cleanup(curl); |
| 126 | |
Stefan Reinauer | 8f7e223 | 2019-12-04 14:31:11 -0800 | [diff] [blame] | 127 | return (res == CURLE_OK) ? 0 : -1; |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | void 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 | |
| 141 | int update_all_files(void) |
| 142 | { |
Stefan Reinauer | bb6415a | 2019-12-04 17:52:13 -0800 | [diff] [blame] | 143 | long old_time = 0, new_time = 0; |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 144 | 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 Reinauer | bb6415a | 2019-12-04 17:52:13 -0800 | [diff] [blame] | 149 | 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 Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 154 | } |
| 155 | |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 156 | free(my_version_name); |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 157 | |
| 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 Reinauer | d565ea8 | 2019-12-02 11:59:16 -0800 | [diff] [blame] | 168 | if (fscanf(new, "Time: %ld\nVersion: %255s\n", |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 169 | &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 Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 181 | /* Download everything */ |
Stefan Reinauer | bb6415a | 2019-12-04 17:52:13 -0800 | [diff] [blame] | 182 | 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 Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 186 | download(configs_name, configs_id); |
| 187 | download(firmware_name, firmware_id); |
Stefan Reinauer | c1c4b6b | 2020-11-18 17:56:12 -0800 | [diff] [blame] | 188 | download(version_name, version_id); |
Stefan Reinauer | dfd556c | 2019-11-20 17:57:40 -0800 | [diff] [blame] | 189 | |
| 190 | return 0; |
| 191 | } |