Atrinik Server 2.5
socket/updates.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 
00030 #include <global.h>
00031 #include "zlib.h"
00032 
00036 static update_file_struct *update_files;
00038 static size_t update_files_num;
00039 
00044 static void updates_file_new(const char *filename, struct stat *sb)
00045 {
00046     char *contents, *compressed;
00047     size_t st_size, numread;
00048     FILE *fp;
00049 
00050     update_files = realloc(update_files, sizeof(update_file_struct) * (update_files_num + 1));
00051 
00052     if (!update_files)
00053     {
00054         LOG(llevError, "updates_file_new(): Out of memory.\n");
00055         return;
00056     }
00057 
00058     st_size = sb->st_size;
00059     /* Allocate a buffer to hold the whole file. */
00060     contents = malloc(st_size);
00061 
00062     if (!contents)
00063     {
00064         LOG(llevError, "updates_file_new(): Out of memory.\n");
00065         return;
00066     }
00067 
00068     fp = fopen(filename, "rb");
00069 
00070     if (!fp)
00071     {
00072         LOG(llevError, "updates_file_new(): Could not open file '%s' for reading.\n", filename);
00073         return;
00074     }
00075 
00076     numread = fread(contents, 1, st_size, fp);
00077     fclose(fp);
00078 
00079     update_files[update_files_num].filename = strdup(filename + strlen(UPDATES_DIR_NAME) + 1);
00080     update_files[update_files_num].checksum = crc32(1L, (const unsigned char FAR *) contents, numread);
00081     update_files[update_files_num].ucomp_len = numread;
00082     /* Calculate the upper bound of the compressed size. */
00083     numread = compressBound(st_size);
00084     /* Allocate a buffer to hold the compressed file. */
00085     compressed = malloc(numread);
00086 
00087     if (!compressed)
00088     {
00089         LOG(llevError, "updates_file_new(): Out of memory.\n");
00090         return;
00091     }
00092 
00093     compress2((Bytef *) compressed, (uLong *) &numread, (const unsigned char FAR *) contents, st_size, Z_BEST_COMPRESSION);
00094     update_files[update_files_num].contents = malloc(numread);
00095 
00096     if (!update_files[update_files_num].contents)
00097     {
00098         LOG(llevError, "updates_file_new(): Out of memory.\n");
00099         return;
00100     }
00101 
00102     memcpy(update_files[update_files_num].contents, compressed, numread);
00103     update_files[update_files_num].len = numread;
00104 
00105     /* Free temporary buffers. */
00106     free(contents);
00107     free(compressed);
00108 
00109     /* 1 for the command type, xxx for the filename, 1 for trailing newline
00110      * of the filename, 4 for original uncompressed length. */
00111     update_files[update_files_num].sl.buf = malloc(1 + strlen(filename) + 1 + 4 + update_files[update_files_num].len);
00112     /* Set the type. */
00113     SOCKET_SET_BINARY_CMD(&update_files[update_files_num].sl, BINARY_CMD_FILE_UPD);
00114     /* Add the filename. */
00115     SockList_AddString(&update_files[update_files_num].sl, update_files[update_files_num].filename);
00116     /* The uncompressed length. */
00117     SockList_AddInt(&update_files[update_files_num].sl, update_files[update_files_num].ucomp_len);
00118     /* Add the file contents. */
00119     memcpy(update_files[update_files_num].sl.buf + update_files[update_files_num].sl.len, update_files[update_files_num].contents, update_files[update_files_num].len);
00120     update_files[update_files_num].sl.len += update_files[update_files_num].len;
00121 
00122     LOG(llevDebug, "  Loaded '%s': ucomp: %"FMT64U", comp: %"FMT64U" (%3.1f%%), CRC32: %lx.\n", filename, (uint64) update_files[update_files_num].ucomp_len, (uint64) numread, (float) (numread * 100) / update_files[update_files_num].ucomp_len, update_files[update_files_num].checksum);
00123     update_files_num++;
00124 }
00125 
00131 static int updates_file_compare(const void *a, const void *b)
00132 {
00133     return strcmp(((update_file_struct *) a)->filename, ((update_file_struct *) b)->filename);
00134 }
00135 
00140 static update_file_struct *updates_file_find(const char *filename)
00141 {
00142     update_file_struct key;
00143 
00144     key.filename = (char *) filename;
00145     return bsearch((void *) &key, (void *) update_files, update_files_num, sizeof(update_file_struct), updates_file_compare);
00146 }
00147 
00151 static void updates_traverse(const char *path)
00152 {
00153     DIR *dir = opendir(path);
00154     struct dirent *d;
00155     char filename[HUGE_BUF];
00156     struct stat sb;
00157 
00158     if (!dir)
00159     {
00160         LOG(llevError, "traverse_updates(): Could not open directory '%s'.\n", path);
00161         return;
00162     }
00163 
00164     while ((d = readdir(dir)))
00165     {
00166         /* Ignore . and .. entries. */
00167         if (!strncmp(d->d_name, ".", NAMLEN(d)) || !strncmp(d->d_name, "..", NAMLEN(d)))
00168         {
00169             continue;
00170         }
00171 
00172         snprintf(filename, sizeof(filename), "%s/%s", path, d->d_name);
00173         stat(filename, &sb);
00174 
00175         /* Go recursively through directories. */
00176         if (S_ISDIR(sb.st_mode))
00177         {
00178             updates_traverse(filename);
00179             continue;
00180         }
00181 
00182         updates_file_new(filename, &sb);
00183     }
00184 
00185     closedir(dir);
00186 }
00187 
00191 void updates_init()
00192 {
00193     char path[HUGE_BUF];
00194     FILE *fp;
00195     size_t i;
00196 
00197     update_files = NULL;
00198     update_files_num = 0;
00199 
00200     LOG(llevDebug, "Loading client updates...\n");
00201     updates_traverse(UPDATES_DIR_NAME);
00202     /* Sort the entries. */
00203     qsort((void *) update_files, update_files_num, sizeof(update_file_struct), (void *) (int (*)()) updates_file_compare);
00204 
00205     snprintf(path, sizeof(path), "%s/%s", settings.localdir, UPDATES_FILE_NAME);
00206     LOG(llevDebug, "Creating '%s'...\n", path);
00207     fp = fopen(path, "wb");
00208 
00209     if (!fp)
00210     {
00211         LOG(llevError, "updates_init(): Could not open file '%s' for writing.\n", path);
00212         return;
00213     }
00214 
00215     for (i = 0; i < update_files_num; i++)
00216     {
00217         update_file_struct *tmp = &update_files[i];
00218 
00219         fprintf(fp, "%s %"FMT64U" %lx\n", tmp->filename, (uint64) tmp->ucomp_len, tmp->checksum);
00220     }
00221 
00222     fclose(fp);
00223 }
00224 
00230 void cmd_request_update(char *buf, int len, socket_struct *ns)
00231 {
00232     update_file_struct *tmp;
00233 
00234     /* We assume all the update files will have at least 2 letters
00235      * (including directories they are in). */
00236     if (ns->status != Ns_Add || !buf || len < 2)
00237     {
00238         return;
00239     }
00240 
00241     /* Try to find the file. */
00242     tmp = updates_file_find(buf);
00243 
00244     /* Invalid file. */
00245     if (!tmp)
00246     {
00247         return;
00248     }
00249 
00250     Send_With_Handling(ns, &tmp->sl);
00251 }