compress.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /*
  2. * Copyright (C) 1997 Robey Pointer
  3. * Copyright (C) 1999 - 2002 Eggheads Development Team
  4. * Copyright (C) 2002 - 2014 Bryan Drewery
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. */
  20. /*
  21. * compress.c -- part of compress.mod
  22. * uses the compression library libz to compress and uncompress the
  23. * userfiles during the sharing process
  24. *
  25. * Written by Fabian Knittel <fknittel@gmx.de>. Based on zlib examples
  26. * by Jean-loup Gailly and Miguel Albrecht.
  27. *
  28. */
  29. /*
  30. * Copyright (C) 2000, 2001, 2002 Eggheads Development Team
  31. *
  32. * This program is free software; you can redistribute it and/or
  33. * modify it under the terms of the GNU General Public License
  34. * as published by the Free Software Foundation; either version 2
  35. * of the License, or (at your option) any later version.
  36. *
  37. * This program is distributed in the hope that it will be useful,
  38. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  39. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  40. * GNU General Public License for more details.
  41. *
  42. * You should have received a copy of the GNU General Public License
  43. * along with this program; if not, write to the Free Software
  44. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  45. */
  46. #define MODULE_NAME "compress"
  47. #include "src/common.h"
  48. #include "src/misc_file.h"
  49. #include "src/misc.h"
  50. #define free_func zlib_free_func
  51. #include <zlib.h>
  52. #undef free_func
  53. #include <string.h>
  54. #include <errno.h>
  55. #include "src/mod/share.mod/share.h"
  56. #ifdef HAVE_MMAP
  57. # include <sys/types.h>
  58. # include <sys/mman.h>
  59. # include <sys/stat.h>
  60. #endif /* HAVE_MMAP */
  61. #include "compress.h"
  62. #define BUFLEN 512
  63. static unsigned int compressed_files; /* Number of files compressed. */
  64. static unsigned int uncompressed_files; /* Number of files uncompressed. */
  65. static unsigned int share_compressed; /* Compress userfiles when sharing? */
  66. static unsigned int compress_level = 9; /* Default compression used. */
  67. static int uncompress_to_file(char *f_src, char *f_target);
  68. static int compress_to_file(char *f_src, char *f_target, int mode_num);
  69. int compress_file(char *filename, int mode_num);
  70. int uncompress_file(char *filename);
  71. static int is_compressedfile(char *filename);
  72. /*
  73. * Misc functions.
  74. */
  75. static int is_compressedfile(char *filename)
  76. {
  77. if (!is_file(filename))
  78. return COMPF_FAILED;
  79. char buf1[50] = "", buf2[50] = "";
  80. FILE *fin = NULL;
  81. register int len1, len2, i;
  82. /* Read data with zlib routines.
  83. */
  84. fin = gzopen(filename, "rb");
  85. if (!fin)
  86. return COMPF_FAILED;
  87. len1 = gzread(fin, buf1, sizeof(buf1));
  88. if (len1 < 0)
  89. return COMPF_FAILED;
  90. if (gzclose(fin) != Z_OK)
  91. return COMPF_FAILED;
  92. /* Read raw data.
  93. */
  94. fin = fopen(filename, "rb");
  95. if (!fin)
  96. return COMPF_FAILED;
  97. len2 = fread(buf2, 1, sizeof(buf2), fin);
  98. if (ferror(fin)) {
  99. fclose(fin);
  100. return COMPF_FAILED;
  101. }
  102. fclose(fin);
  103. /* Compare what we found.
  104. */
  105. if (len1 != len2)
  106. return COMPF_COMPRESSED;
  107. for (i = 0; i < sizeof(buf1); i++)
  108. if (buf1[i] != buf2[i])
  109. return COMPF_COMPRESSED;
  110. return COMPF_UNCOMPRESSED;
  111. }
  112. /*
  113. * General compression / uncompression functions
  114. */
  115. /* Uncompresses a file `f_src' and saves it as `f_target'.
  116. */
  117. static int uncompress_to_file(char *f_src, char *f_target)
  118. {
  119. if (!is_file(f_src)) {
  120. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': not a file.",
  121. f_src);
  122. return COMPF_ERROR;
  123. }
  124. char buf[BUFLEN] = "";
  125. int len;
  126. FILE *fin = NULL, *fout = NULL;
  127. fin = gzopen(f_src, "rb");
  128. if (!fin) {
  129. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': gzopen failed.",
  130. f_src);
  131. return COMPF_ERROR;
  132. }
  133. fout = fopen(f_target, "wb");
  134. if (!fout) {
  135. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': open failed: %s.",
  136. f_src, strerror(errno));
  137. return COMPF_ERROR;
  138. }
  139. while (1) {
  140. len = gzread(fin, buf, sizeof(buf));
  141. if (len < 0) {
  142. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': gzread failed.",
  143. f_src);
  144. return COMPF_ERROR;
  145. }
  146. if (!len)
  147. break;
  148. if ((int) fwrite(buf, (unsigned int) len, 1, fout) != 1) {
  149. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': fwrite failed: %s.",
  150. f_src, strerror(errno));
  151. return COMPF_ERROR;
  152. }
  153. }
  154. if (fclose(fout)) {
  155. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': fclose failed: %s.",
  156. f_src, strerror(errno));
  157. return COMPF_ERROR;
  158. }
  159. if (gzclose(fin) != Z_OK) {
  160. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': gzclose failed.",
  161. f_src);
  162. return COMPF_ERROR;
  163. }
  164. uncompressed_files++;
  165. return COMPF_SUCCESS;
  166. }
  167. /* Enforce limits.
  168. */
  169. inline static void adjust_mode_num(int *mode)
  170. {
  171. if (*mode > 9)
  172. *mode = 9;
  173. else if (*mode < 0)
  174. *mode = 0;
  175. }
  176. #ifdef HAVE_MMAP
  177. /* Attempt to compress in one go, by mmap'ing the file to memory.
  178. */
  179. static int compress_to_file_mmap(FILE *fout, FILE *fin)
  180. {
  181. size_t len;
  182. int ifd = fileno(fin);
  183. char *buf = NULL;
  184. struct stat st;
  185. /* Find out size of file */
  186. if (fstat(ifd, &st) < 0)
  187. return COMPF_ERROR;
  188. if (st.st_size <= 0)
  189. return COMPF_ERROR;
  190. /* mmap file contents to memory */
  191. buf = mmap(0, st.st_size, PROT_READ, MAP_SHARED, ifd, 0);
  192. if (buf < 0)
  193. return COMPF_ERROR;
  194. /* Compress the whole file in one go */
  195. len = gzwrite(fout, buf, st.st_size);
  196. if (len != (int) st.st_size)
  197. return COMPF_ERROR;
  198. munmap(buf, st.st_size);
  199. fclose(fin);
  200. if (gzclose(fout) != Z_OK)
  201. return COMPF_ERROR;
  202. return COMPF_SUCCESS;
  203. }
  204. #endif /* HAVE_MMAP */
  205. /* Compresses a file `f_src' and saves it as `f_target'.
  206. */
  207. static int compress_to_file(char *f_src, char *f_target, int mode_num)
  208. {
  209. char buf[BUFLEN] = "", mode[5] = "";
  210. FILE *fin = NULL, *fout = NULL;
  211. size_t len;
  212. int ret = COMPF_ERROR;
  213. adjust_mode_num(&mode_num);
  214. simple_snprintf(mode, sizeof mode, "wb%d", mode_num);
  215. if (!is_file(f_src)) {
  216. putlog(LOG_MISC, "*", "Failed to compress file `%s': not a file.",
  217. f_src);
  218. goto err;
  219. }
  220. fin = fopen(f_src, "rb");
  221. if (!fin) {
  222. putlog(LOG_MISC, "*", "Failed to compress file `%s': open failed: %s.",
  223. f_src, strerror(errno));
  224. goto err;
  225. }
  226. fout = gzopen(f_target, mode);
  227. if (!fout) {
  228. putlog(LOG_MISC, "*", "Failed to compress file `%s': gzopen failed.",
  229. f_src);
  230. goto err;
  231. }
  232. #ifdef HAVE_MMAP
  233. if (compress_to_file_mmap(fout, fin) == COMPF_SUCCESS) {
  234. compressed_files++;
  235. return COMPF_SUCCESS;
  236. } else {
  237. /* To be on the safe side, close the file before attempting
  238. * to write again.
  239. */
  240. gzclose(fout);
  241. fout = gzopen(f_target, mode);
  242. }
  243. #endif /* HAVE_MMAP */
  244. while (1) {
  245. len = fread(buf, 1, sizeof(buf), fin);
  246. if (ferror(fin)) {
  247. putlog(LOG_MISC, "*", "Failed to compress file `%s': fread failed: %s",
  248. f_src, strerror(errno));
  249. goto err;
  250. }
  251. if (!len)
  252. break;
  253. if (gzwrite(fout, buf, (unsigned int) len) != len) {
  254. putlog(LOG_MISC, "*", "Failed to compress file `%s': gzwrite failed.",
  255. f_src);
  256. goto err;
  257. }
  258. }
  259. if (gzclose(fout) != Z_OK) {
  260. putlog(LOG_MISC, "*", "Failed to compress file `%s': gzclose failed.",
  261. f_src);
  262. goto err;
  263. return COMPF_ERROR;
  264. }
  265. compressed_files++;
  266. ret = COMPF_SUCCESS;
  267. err:
  268. if (fin)
  269. fclose(fin);
  270. return ret;
  271. }
  272. /* Compresses a file `filename' and saves it as `filename'.
  273. */
  274. int compress_file(char *filename, int mode_num)
  275. {
  276. char *temp_fn = NULL, randstr[5] = "";
  277. int ret;
  278. /* Create temporary filename. */
  279. temp_fn = (char *) my_calloc(1, strlen(filename) + 5);
  280. make_rand_str(randstr, 4);
  281. strcpy(temp_fn, filename);
  282. strcat(temp_fn, randstr);
  283. /* Compress file. */
  284. ret = compress_to_file(filename, temp_fn, mode_num);
  285. /* Overwrite old file with compressed version. Only do so
  286. * if the compression routine succeeded.
  287. */
  288. if (ret == COMPF_SUCCESS)
  289. movefile(temp_fn, filename);
  290. free(temp_fn);
  291. return ret;
  292. }
  293. /* Uncompresses a file `filename' and saves it as `filename'.
  294. */
  295. int uncompress_file(char *filename)
  296. {
  297. char *temp_fn = NULL, randstr[5] = "";
  298. int ret;
  299. /* Create temporary filename. */
  300. temp_fn = (char *) my_calloc(1, strlen(filename) + 5);
  301. make_rand_str(randstr, 4);
  302. strcpy(temp_fn, filename);
  303. strcat(temp_fn, randstr);
  304. /* Uncompress file. */
  305. ret = uncompress_to_file(filename, temp_fn);
  306. /* Overwrite old file with uncompressed version. Only do so
  307. * if the uncompression routine succeeded.
  308. */
  309. if (ret == COMPF_SUCCESS)
  310. movefile(temp_fn, filename);
  311. free(temp_fn);
  312. return ret;
  313. }
  314. /*
  315. * Userfile feature releated functions
  316. */
  317. static int uff_comp(int idx, char *filename)
  318. {
  319. putlog(LOG_BOTS, "*", "Compressing user file for %s.", dcc[idx].nick);
  320. return compress_file(filename, compress_level);
  321. }
  322. static int uff_uncomp(int idx, char *filename)
  323. {
  324. putlog(LOG_BOTS, "*", "Uncompressing user file from %s.", dcc[idx].nick);
  325. return uncompress_file(filename);
  326. }
  327. static int uff_ask_compress(int idx)
  328. {
  329. if (share_compressed)
  330. return 1;
  331. else
  332. return 0;
  333. }
  334. static uff_table_t compress_uff_table[] = {
  335. {"compress", UFF_COMPRESS, uff_ask_compress, 100, uff_comp, uff_uncomp},
  336. {NULL, 0, NULL, 0, NULL, NULL}
  337. };
  338. /*
  339. * Compress module related code
  340. */
  341. int compress_report(int idx, int details)
  342. {
  343. if (details) {
  344. dprintf(idx, " %u file%s compressed\n", compressed_files,
  345. (compressed_files != 1) ? "s" : "");
  346. dprintf(idx, " %u file%s uncompressed\n", uncompressed_files,
  347. (uncompressed_files != 1) ? "s" : "");
  348. }
  349. return 0;
  350. }
  351. void compress_init()
  352. {
  353. uff_addtable(compress_uff_table);
  354. }