Atrinik Client 2.5
gui/updater.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00087 #include <global.h>
00088 
00090 static curl_data *dl_data = NULL;
00092 static update_file_struct *download_packages;
00094 static size_t download_packages_num = 0;
00096 static size_t download_package_next = 0;
00098 static size_t download_packages_downloaded = 0;
00100 static uint8 download_package_process = 0;
00102 static progress_dots progress;
00103 
00109 static char *updater_get_dir(char *buf, size_t len)
00110 {
00111     snprintf(buf, len, "%s/.atrinik/temp", get_config_dir());
00112     return buf;
00113 }
00114 
00118 static void cleanup_patch_files()
00119 {
00120     char dir_path[HUGE_BUF];
00121 
00122     rmrf(updater_get_dir(dir_path, sizeof(dir_path)));
00123 }
00124 
00127 static void popup_draw_func_post(popup_struct *popup)
00128 {
00129     SDL_Rect box;
00130 
00131     box.x = popup->x;
00132     box.y = popup->y + 10;
00133     box.w = popup->surface->w;
00134     box.h = popup->surface->h;
00135 
00136     string_blt(ScreenSurface, FONT_SERIF20, "<u>Settings</u>", box.x, box.y + 10, COLOR_HGOLD, TEXT_ALIGN_CENTER | TEXT_MARKUP, &box);
00137     box.y += 50;
00138 
00139     /* Show the progress dots. */
00140     progress_dots_show(&progress, ScreenSurface, box.x + box.w / 2 - progress_dots_width(&progress) / 2, box.y);
00141     box.y += 30;
00142 
00143     /* Not done yet and downloading something, inform the user. */
00144     if (!progress.done && dl_data)
00145     {
00146         /* Downloading list of updates? */
00147         if (!strncmp(dl_data->url, UPDATER_CHECK_URL, strlen(UPDATER_CHECK_URL)))
00148         {
00149             string_blt_shadow(ScreenSurface, FONT_ARIAL11, "Downloading list of updates...", box.x, box.y, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER, &box);
00150         }
00151         else
00152         {
00153             string_blt_shadow_format(ScreenSurface, FONT_ARIAL11, box.x, box.y, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER, &box, "Downloading update #%"FMT64" out of %"FMT64"...", (uint64) download_package_next, (uint64) download_packages_num);
00154         }
00155     }
00156 
00157     /* There is something being downloaded. */
00158     if (dl_data)
00159     {
00160         sint8 ret = curl_download_finished(dl_data);
00161 
00162         /* We are not done yet... */
00163         progress.done = 0;
00164 
00165         /* Failed? */
00166         if (ret == -1)
00167         {
00168             /* Remove the temporary directory. */
00169             cleanup_patch_files();
00170             progress.done = 1;
00171 
00172             string_blt_shadow(ScreenSurface, FONT_ARIAL11, "Connection timed out.", box.x, box.y, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER, &box);
00173 
00174             box.y += 20;
00175 
00176             /* Give the user a chance to retroy. */
00177             if (button_show(BITMAP_BUTTON, -1, BITMAP_BUTTON_DOWN, box.x + box.w / 2 - Bitmaps[BITMAP_BUTTON]->bitmap->w / 2, box.y, "Retry", FONT_ARIAL10, COLOR_WHITE, COLOR_BLACK, COLOR_HGOLD, COLOR_BLACK, 0))
00178             {
00179                 popup_destroy_visible();
00180                 updater_open();
00181                 return;
00182             }
00183         }
00184         /* Finished downloading. */
00185         else if (ret == 1)
00186         {
00187             /* Is it the list of updates? */
00188             if (!strncmp(dl_data->url, UPDATER_CHECK_URL, strlen(UPDATER_CHECK_URL)))
00189             {
00190                 if (dl_data->memory)
00191                 {
00192                     char *cp, *line, *tmp[2];
00193 
00194                     cp = strdup(dl_data->memory);
00195 
00196                     line = strtok(cp, "\n");
00197 
00198                     while (line)
00199                     {
00200                         if (split_string(line, tmp, arraysize(tmp), '\t') == 2)
00201                         {
00202                             download_packages = realloc(download_packages, sizeof(*download_packages) * (download_packages_num + 1));
00203                             download_packages[download_packages_num].filename = strdup(tmp[0]);
00204                             download_packages[download_packages_num].sha1 = strdup(tmp[1]);
00205                             download_packages_num++;
00206                         }
00207 
00208                         line = strtok(NULL, "\n");
00209                     }
00210 
00211                     free(cp);
00212                 }
00213 
00214 #ifdef WIN32
00215                 if (download_packages_num)
00216                 {
00217                     download_package_process = 1;
00218                     download_package_next = 0;
00219                 }
00220 #endif
00221 
00222                 curl_data_free(dl_data);
00223                 dl_data = NULL;
00224             }
00225 
00226             /* Are we downloading packages? */
00227             if (download_package_process)
00228             {
00229                 /* Have we got anything to store yet, or are we just starting the download? */
00230                 if (download_package_next != 0 && dl_data)
00231                 {
00232                     char sha1_output_ascii[41];
00233                     unsigned char sha1_output[20];
00234                     size_t i;
00235 
00236                     /* Calculate the SHA-1 sum of the downloaded data. */
00237                     sha1((unsigned char *) dl_data->memory, dl_data->size, sha1_output);
00238 
00239                     /* Create the ASCII SHA-1 sum. snprintf() is not
00240                      * needed, because no overflow can happen in this
00241                      * case. */
00242                     for (i = 0; i < 20; i++)
00243                     {
00244                         sprintf(sha1_output_ascii + i * 2, "%02x", sha1_output[i]);
00245                     }
00246 
00247                     /* Compare the SHA-1 sum. */
00248                     if (!strcmp(download_packages[download_package_next - 1].sha1, sha1_output_ascii))
00249                     {
00250                         char dir_path[HUGE_BUF], filename[HUGE_BUF];
00251                         FILE *fp;
00252 
00253                         /* Get the temporary directory. */
00254                         updater_get_dir(dir_path, sizeof(dir_path));
00255 
00256                         /* Create the temporary directory if it doesn't exist yet. */
00257                         if (access(dir_path, R_OK) != 0)
00258                         {
00259                             mkdir(dir_path, 0755);
00260                         }
00261 
00262                         /* Construct the path. */
00263                         snprintf(filename, sizeof(filename), "%s/client_patch_%09"FMT64".tar.gz", dir_path, (uint64) download_package_next - 1);
00264                         fp = fopen(filename, "wb");
00265 
00266                         if (fp)
00267                         {
00268                             fwrite(dl_data->memory, 1, dl_data->size, fp);
00269                             fclose(fp);
00270                             download_packages_downloaded++;
00271                         }
00272                     }
00273                     /* Did not match, stop downloading, even if there are more. */
00274                     else
00275                     {
00276                         download_package_next = download_packages_num;
00277                     }
00278 
00279                     curl_data_free(dl_data);
00280                     dl_data = NULL;
00281                 }
00282 
00283                 /* Starting the download (possibly next package). */
00284                 if (download_package_next < download_packages_num)
00285                 {
00286                     char url[HUGE_BUF];
00287 
00288                     /* Construct the URL. */
00289                     snprintf(url, sizeof(url), UPDATER_PATH_URL"/%s", download_packages[download_package_next].filename);
00290                     dl_data = curl_download_start(url);
00291                     download_package_next++;
00292                 }
00293             }
00294         }
00295     }
00296     /* Finished all downloads. */
00297     else
00298     {
00299         progress.done = 1;
00300 
00301         /* No packages, so the client is up-to-date. */
00302         if (!download_packages_num)
00303         {
00304             string_blt_shadow(ScreenSurface, FONT_ARIAL11, "Your client is up-to-date.", box.x, box.y, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER, &box);
00305             box.y += 60;
00306 
00307             if (button_show(BITMAP_BUTTON, -1, BITMAP_BUTTON_DOWN, box.x + box.w / 2 - Bitmaps[BITMAP_BUTTON]->bitmap->w / 2, box.y, "Close", FONT_ARIAL10, COLOR_WHITE, COLOR_BLACK, COLOR_HGOLD, COLOR_BLACK, 0))
00308             {
00309                 popup_destroy_visible();
00310                 return;
00311             }
00312         }
00313         else
00314         {
00315 #ifdef WIN32
00316             string_blt_shadow_format(ScreenSurface, FONT_ARIAL11, box.x, box.y, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER, &box, "%"FMT64" update(s) downloaded successfully.", (uint64) download_packages_downloaded);
00317             box.y += 20;
00318             string_blt_shadow(ScreenSurface, FONT_ARIAL11, "Restart the client to complete the update.", box.x, box.y, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER, &box);
00319 
00320             if (download_packages_downloaded < download_packages_num)
00321             {
00322                 string_blt_shadow_format(ScreenSurface, FONT_ARIAL11, box.x, box.y + 20, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER, &box, "%"FMT64" update(s) failed to download (possibly due to a connection failure).", (uint64) (download_packages_num - download_packages_downloaded));
00323                 string_blt_shadow(ScreenSurface, FONT_ARIAL11, "You may need to retry updating after restarting the client.", box.x, box.y + 40, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER, &box);
00324             }
00325 
00326             box.y += 60;
00327 
00328             /* Show a restart button, which will call up_dater.exe to
00329              * apply the updates (using atrinik_updater.bat) and restarts
00330              * the client. */
00331             if (button_show(BITMAP_BUTTON, -1, BITMAP_BUTTON_DOWN, box.x + box.w / 2 - Bitmaps[BITMAP_BUTTON]->bitmap->w / 2, box.y, "Restart", FONT_ARIAL10, COLOR_WHITE, COLOR_BLACK, COLOR_HGOLD, COLOR_BLACK, 0))
00332             {
00333                 char path[HUGE_BUF], wdir[HUGE_BUF];
00334 
00335                 snprintf(path, sizeof(path), "%s\\up_dater.exe", getcwd(wdir, sizeof(wdir) - 1));
00336                 ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWNORMAL);
00337                 system_end();
00338                 exit(0);
00339             }
00340 #else
00341             string_blt_shadow(ScreenSurface, FONT_ARIAL11, "Updates are available; please use your distribution's package/update", box.x, box.y, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER, &box);
00342             box.y += 20;
00343             string_blt_shadow(ScreenSurface, FONT_ARIAL11, "manager to update, or visit <a=url:http://www.atrinik.org/>www.atrinik.org</a> for help.", box.x, box.y, COLOR_WHITE, COLOR_BLACK, TEXT_ALIGN_CENTER | TEXT_MARKUP, &box);
00344 #endif
00345         }
00346     }
00347 }
00348 
00353 static int popup_destroy_callback(popup_struct *popup)
00354 {
00355     size_t i;
00356 
00357     (void) popup;
00358 
00359     /* Free data that is being downloaded, if the user quits mid-download.
00360      * Also remove the temp directory, as the update has clearly not
00361      * finished downloading its data */
00362     if (dl_data)
00363     {
00364         cleanup_patch_files();
00365         curl_data_free(dl_data);
00366         dl_data = NULL;
00367     }
00368 
00369     /* Free the allocated filenames and SHA-1 sums. */
00370     for (i = 0; i < download_packages_num; i++)
00371     {
00372         free(download_packages[i].filename);
00373         free(download_packages[i].sha1);
00374     }
00375 
00376     if (download_packages)
00377     {
00378         free(download_packages);
00379         download_packages = NULL;
00380     }
00381 
00382     download_packages_num = 0;
00383     download_package_next = 0;
00384     download_package_process = 0;
00385     download_packages_downloaded = 0;
00386 
00387     return 1;
00388 }
00389 
00392 void updater_open()
00393 {
00394     popup_struct *popup;
00395     CURL *curl;
00396     char url[HUGE_BUF], version[MAX_BUF], *version_escaped;
00397 
00398     /* Create the popup. */
00399     popup = popup_create(BITMAP_POPUP);
00400     popup->destroy_callback_func = popup_destroy_callback;
00401     popup->draw_func_post = popup_draw_func_post;
00402 
00403     /* Construct URL. */
00404     curl = curl_easy_init();
00405     package_get_version_full(version, sizeof(version));
00406     version_escaped = curl_easy_escape(curl, version, 0);
00407     snprintf(url, sizeof(url), UPDATER_CHECK_URL"&version=%s", version_escaped);
00408     curl_free(version_escaped);
00409     curl_easy_cleanup(curl);
00410 
00411     /* Start downloading the list of available updates. */
00412     dl_data = curl_download_start(url);
00413 
00414     progress_dots_create(&progress);
00415 }