Atrinik Client  4.0
updater.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
87 #include <global.h>
88 #include <toolkit/string.h>
89 #include <toolkit/curl.h>
90 #include <toolkit/sha1.h>
91 #include <toolkit/path.h>
92 #include <curl/curl.h>
93 
95 static curl_request_t *request = NULL;
99 static size_t download_packages_num = 0;
101 static size_t download_package_next = 0;
105 static bool download_package_process = false;
111 static button_struct button_close, button_retry, button_restart;
112 
123 static char *
124 updater_get_dir (char *buf, size_t len)
125 {
126  snprintf(buf, len, "%s/.atrinik/temp", get_config_dir());
127  return buf;
128 }
129 
134 static void
136 {
137  char dir_path[HUGE_BUF];
138  rmrf(updater_get_dir(VS(dir_path)));
139 }
140 
144 static void
146 {
147  /* Construct URL. */
148  CURL *curl = curl_easy_init();
149  char version[MAX_BUF];
150  package_get_version_full(VS(version));
151  char *version_escaped = curl_easy_escape(curl, version, 0);
152  char url[HUGE_BUF];
153  snprintf(VS(url), UPDATER_CHECK_URL "&version=%s", version_escaped);
154  curl_free(version_escaped);
155  curl_easy_cleanup(curl);
156 
157  /* Start downloading the list of available updates. */
158  request = curl_request_create(url, CURL_PKEY_TRUST_ULTIMATE);
159  curl_request_start_get(request);
160 
161  progress_dots_create(&progress);
162 }
163 
167 static void
169 {
170  /* Free data that is being downloaded, if the user quits mid-download.
171  * Also remove the temp directory, as the update has clearly not
172  * finished downloading its data */
173  if (request != NULL) {
175  curl_request_free(request);
176  request = NULL;
177  }
178 
179  /* Free the allocated filenames and SHA-1 sums. */
180  for (size_t i = 0; i < download_packages_num; i++) {
181  efree(download_packages[i].filename);
182  efree(download_packages[i].sha1);
183  }
184 
185  if (download_packages != NULL) {
186  efree(download_packages);
187  download_packages = NULL;
188  }
189 
190  download_packages_num = 0;
192  download_package_process = false;
194 }
195 
199 static void
201 {
202  char *body = curl_request_get_body(request, NULL);
203  if (body == NULL) {
204  return;
205  }
206 
207  char *cp = strtok(body, "\n");
208  while (cp != NULL) {
209  char *cps[2];
210  if (string_split(cp, cps, arraysize(cps), '\t') == arraysize(cps)) {
211  download_packages = erealloc(download_packages,
212  sizeof(*download_packages) *
213  (download_packages_num + 1));
214  download_packages[download_packages_num].filename = estrdup(cps[0]);
215  download_packages[download_packages_num].sha1 = estrdup(cps[1]);
217  }
218 
219  cp = strtok(NULL, "\n");
220  }
221 
222 #ifdef WIN32
223  if (download_packages_num != 0) {
226  }
227 #endif
228 }
229 
233 static void
235 {
236  /* Have we got anything to store yet, or are we just starting
237  * the download? */
238  if (download_package_next != 0 && request != NULL) {
239  size_t body_size;
240  char *body = curl_request_get_body(request, &body_size);
241 
242  if (body == NULL) {
244  curl_request_free(request);
245  request = NULL;
246  return;
247  }
248 
249  unsigned char sha1_output[20];
250  /* Calculate the SHA-1 sum of the downloaded data. */
251  sha1((unsigned char *) body, body_size, sha1_output);
252 
253  /* Create the ASCII SHA-1 sum. snprintf() is not
254  * needed, because no overflow can happen in this
255  * case. */
256  char sha1_output_ascii[sizeof(sha1_output) * 2 + 1];
257  for (size_t i = 0; i < sizeof(sha1_output); i++) {
258  sprintf(sha1_output_ascii + i * 2, "%02x", sha1_output[i]);
259  }
260 
261  /* Compare the SHA-1 sum. */
262  if (strcmp(download_packages[download_package_next - 1].sha1,
263  sha1_output_ascii) == 0) {
264  /* Get the temporary directory. */
265  char dir_path[HUGE_BUF];
266  updater_get_dir(VS(dir_path));
267 
268  /* Construct the path. */
269  char filename[HUGE_BUF];
270  snprintf(VS(filename), "%s/client_patch_%09" PRIu64 ".tar.gz",
271  dir_path,
272  (uint64_t) download_package_next - 1);
273 
274  path_ensure_directories(filename);
275  FILE *fp = fopen(filename, "wb");
276  if (fp != NULL) {
277  fwrite(body, 1, body_size, fp);
278  fclose(fp);
280  } else {
281  LOG(ERROR, "Failed to open file for writing: %s", filename);
282  }
283  } else {
284  /* Did not match, stop downloading, even if there are
285  * more. */
287  }
288 
289  curl_request_free(request);
290  request = NULL;
291  }
292 
293  /* Starting the download (possibly next package). */
295  /* Construct the URL. */
296  char url[HUGE_BUF];
297  snprintf(VS(url), UPDATER_PATH_URL "/%s",
298  download_packages[download_package_next].filename);
299  request = curl_request_create(url, CURL_PKEY_TRUST_ULTIMATE);
300  curl_request_start_get(request);
302  }
303 }
304 
306 static int
308 {
309  SDL_Rect box;
310 
311  box.x = popup->x;
312  box.y = popup->y;
313  box.w = popup->surface->w;
314  box.h = 38;
315 
317  FONT_SERIF20,
318  "Updater",
319  box.x,
320  box.y,
321  COLOR_HGOLD,
323  &box);
324  box.y += 60;
325 
326  /* Show the progress dots. */
327  progress_dots_show(&progress,
329  box.x + box.w / 2 - progress_dots_width(&progress) / 2,
330  box.y);
331  box.y += 30;
332 
333  /* Not done yet and downloading something, inform the user. */
334  if (!progress.done && request != NULL) {
335  /* Downloading list of updates? */
336  if (strncmp(curl_request_get_url(request),
338  strlen(UPDATER_CHECK_URL)) == 0) {
340  FONT_ARIAL11,
341  "Downloading list of updates...",
342  box.x,
343  box.y,
344  COLOR_WHITE,
345  COLOR_BLACK,
347  &box);
348  } else {
350  FONT_ARIAL11,
351  box.x,
352  box.y,
353  COLOR_WHITE,
354  COLOR_BLACK,
356  &box,
357  "Downloading update #%" PRIu64 " out of %"
358  PRIu64 "...",
359  (uint64_t) download_package_next,
360  (uint64_t) download_packages_num);
361  }
362  }
363 
364  /* Finished every request. */
365  if (request == NULL) {
366  progress.done = 1;
367 
368  /* No packages, so the client is up-to-date. */
369  if (download_packages_num == 0) {
371  FONT_ARIAL11,
372  "Your client is up-to-date.",
373  box.x,
374  box.y,
375  COLOR_WHITE,
376  COLOR_BLACK,
378  &box);
379  box.y += 60;
380 
381  button_close.x = box.x + box.w / 2 -
382  texture_surface(button_close.texture)->w / 2;
383  button_close.y = box.y;
384  button_show(&button_close, "Close");
385  } else {
386 #ifdef WIN32
388  FONT_ARIAL11,
389  box.x,
390  box.y,
391  COLOR_WHITE,
392  COLOR_BLACK,
394  &box,
395  "%" PRIu64 " update(s) downloaded "
396  "successfully.",
397  (uint64_t) download_packages_downloaded);
398  box.y += 20;
400  FONT_ARIAL11,
401  "Restart the client to complete the update.",
402  box.x,
403  box.y,
404  COLOR_WHITE,
405  COLOR_BLACK,
407  &box);
408 
409  if (download_packages_downloaded < download_packages_num) {
411  FONT_ARIAL11,
412  box.x,
413  box.y + 20,
414  COLOR_WHITE,
415  COLOR_BLACK,
417  &box,
418  "%" PRIu64 " update(s) failed to "
419  "download (possibly due to a "
420  "connection failure).",
421  (uint64_t) (download_packages_num -
422  download_packages_downloaded));
424  FONT_ARIAL11,
425  "You may need to retry updating after "
426  "restarting the client.",
427  box.x,
428  box.y + 40,
429  COLOR_WHITE,
430  COLOR_BLACK,
432  &box);
433  }
434 
435  box.y += 60;
436 
437  /* Show a restart button, which will call atrinik2.exe to
438  * apply the updates (using atrinik_updater.bat) and restart
439  * the client. */
440  button_restart.x = box.x + box.w / 2 -
441  texture_surface(button_restart.texture)->w / 2;
442  button_restart.y = box.y;
443  button_show(&button_restart, "Restart");
444 #else
446  FONT_ARIAL11,
447  "Updates are available; please use your "
448  "distribution's package/update",
449  box.x,
450  box.y,
451  COLOR_WHITE,
452  COLOR_BLACK,
454  &box);
455  box.y += 20;
457  FONT_ARIAL11,
458  "manager to update, or visit "
459  "[a=url:http://www.atrinik.org/]"
460  "www.atrinik.org[/a] for help.",
461  box.x,
462  box.y,
463  COLOR_WHITE,
464  COLOR_BLACK,
466  &box);
467 #endif
468  }
469 
470  return 1;
471  }
472 
473  curl_state_t state = curl_request_get_state(request);
474 
475  /* We are not done yet... */
476  progress.done = 0;
477 
478  /* Failed? */
479  if (state == CURL_STATE_ERROR) {
480  /* Remove the temporary directory. */
482  progress.done = 1;
483 
485  FONT_ARIAL11,
486  "Connection timed out.",
487  box.x,
488  box.y,
489  COLOR_WHITE,
490  COLOR_BLACK,
492  &box);
493 
494  box.y += 20;
495 
496  button_retry.x = box.x + box.w / 2 -
497  texture_surface(button_retry.texture)->w / 2;
498  button_retry.y = box.y;
499  button_show(&button_retry, "Retry");
500  } else if (state == CURL_STATE_OK) {
501  /* Finished downloading. */
502 
503  /* Is it the list of updates? */
504  if (strncmp(curl_request_get_url(request),
506  strlen(UPDATER_CHECK_URL)) == 0) {
508  curl_request_free(request);
509  request = NULL;
510  }
511 
512  /* Are we downloading packages? */
515  }
516  }
517 
518  return 1;
519 }
520 
522 static int
523 popup_event (popup_struct *popup, SDL_Event *event)
524 {
525  if (button_event(&button_close, event)) {
526  popup_destroy(popup);
527  return 1;
528  } else if (button_event(&button_retry, event)) {
531  return 1;
532 #ifdef WIN32
533  } else if (button_event(&button_restart, event)) {
534  char path[HUGE_BUF], wdir[HUGE_BUF];
535  snprintf(VS(path), "%s\\atrinik2.exe", getcwd(wdir, sizeof(wdir) - 1));
536  ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWNORMAL);
537  exit(0);
538  return 1;
539 #endif
540  }
541 
542  return -1;
543 }
544 
552 static int
554 {
556 
557  button_destroy(&button_close);
558  button_destroy(&button_retry);
559  button_destroy(&button_restart);
560 
561  return 1;
562 }
563 
567 void
569 {
570  /* Create the popup. */
571  popup_struct *popup = popup_create(texture_get(TEXTURE_TYPE_CLIENT,
572  "popup"));
575  popup->event_func = popup_event;
576 
577  button_create(&button_close);
578  button_create(&button_retry);
579  button_create(&button_restart);
580 
582 }
#define COLOR_BLACK
Definition: text.h:323
static char * updater_get_dir(char *buf, size_t len)
Definition: updater.c:124
int(* event_func)(struct popup_struct *popup, SDL_Event *event)
Definition: popup.h:148
static button_struct button_close
Definition: updater.c:111
static size_t download_packages_num
Definition: updater.c:99
int(* draw_post_func)(struct popup_struct *popup)
Definition: popup.h:137
SDL_Surface * texture_surface(texture_struct *texture)
Definition: texture.c:303
uint8_t done
Definition: progress.h:44
int button_event(button_struct *button, SDL_Event *event)
Definition: button.c:222
int(* destroy_callback_func)(struct popup_struct *popup)
Definition: popup.h:159
void button_destroy(button_struct *button)
Definition: button.c:94
static void updater_download_clean(void)
Definition: updater.c:168
void text_show(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box)
Definition: text.c:1983
int x
Definition: popup.h:87
#define COLOR_HGOLD
Definition: text.h:319
#define UPDATER_CHECK_URL
Definition: updater.h:38
static int popup_event(popup_struct *popup, SDL_Event *event)
Definition: updater.c:523
texture_struct * texture_get(texture_type_t type, const char *name)
Definition: texture.c:279
void progress_dots_create(progress_dots *progress)
Definition: progress.c:39
SDL_Surface * ScreenSurface
Definition: main.c:47
#define TEXT_MARKUP
Definition: text.h:224
#define TEXT_ALIGN_CENTER
Definition: text.h:230
static update_file_struct * download_packages
Definition: updater.c:97
static int popup_destroy_callback(popup_struct *popup)
Definition: updater.c:553
char * package_get_version_full(char *dst, size_t dstlen)
Definition: misc.c:69
void text_show_shadow_format(SDL_Surface *surface, font_struct *font, int x, int y, const char *color_notation, const char *color_shadow_notation, uint64_t flags, SDL_Rect *box, const char *format,...)
Definition: text.c:2305
void rmrf(const char *path)
Definition: wrapper.c:248
char * filename
Definition: updater.h:51
void button_create(button_struct *button)
Definition: button.c:65
#define UPDATER_PATH_URL
Definition: updater.h:44
void text_show_shadow(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, const char *color_shadow_notation, uint64_t flags, SDL_Rect *box)
Definition: text.c:2278
int progress_dots_width(progress_dots *progress)
Definition: progress.c:86
texture_struct * texture
Definition: button.h:61
static void updater_process_list(void)
Definition: updater.c:200
int y
Definition: popup.h:90
static void cleanup_patch_files(void)
Definition: updater.c:135
static void updater_process_packages(void)
Definition: updater.c:234
static progress_dots progress
Definition: updater.c:107
static curl_request_t * request
Definition: updater.c:95
SDL_Surface * surface
Definition: popup.h:65
static size_t download_package_next
Definition: updater.c:101
void button_show(button_struct *button, const char *text)
Definition: button.c:161
void progress_dots_show(progress_dots *progress, SDL_Surface *surface, int x, int y)
Definition: progress.c:57
#define TEXT_VALIGN_CENTER
Definition: text.h:246
static bool download_package_process
Definition: updater.c:105
const char * get_config_dir(void)
Definition: wrapper.c:303
void updater_open(void)
Definition: updater.c:568
static size_t download_packages_downloaded
Definition: updater.c:103
#define COLOR_WHITE
Definition: text.h:289
static void updater_download_start(void)
Definition: updater.c:145
static int popup_draw_post(popup_struct *popup)
Definition: updater.c:307