compress.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. * Copyright (C) 1997 Robey Pointer
  3. * Copyright (C) 1999 - 2002 Eggheads Development Team
  4. * Copyright (C) 2002 - 2008 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. return COMPF_FAILED;
  100. fclose(fin);
  101. /* Compare what we found.
  102. */
  103. if (len1 != len2)
  104. return COMPF_COMPRESSED;
  105. for (i = 0; i < sizeof(buf1); i++)
  106. if (buf1[i] != buf2[i])
  107. return COMPF_COMPRESSED;
  108. return COMPF_UNCOMPRESSED;
  109. }
  110. /*
  111. * General compression / uncompression functions
  112. */
  113. /* Uncompresses a file `f_src' and saves it as `f_target'.
  114. */
  115. static int uncompress_to_file(char *f_src, char *f_target)
  116. {
  117. if (!is_file(f_src)) {
  118. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': not a file.",
  119. f_src);
  120. return COMPF_ERROR;
  121. }
  122. char buf[BUFLEN] = "";
  123. int len;
  124. FILE *fin = NULL, *fout = NULL;
  125. fin = gzopen(f_src, "rb");
  126. if (!fin) {
  127. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': gzopen failed.",
  128. f_src);
  129. return COMPF_ERROR;
  130. }
  131. fout = fopen(f_target, "wb");
  132. if (!fout) {
  133. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': open failed: %s.",
  134. f_src, strerror(errno));
  135. return COMPF_ERROR;
  136. }
  137. while (1) {
  138. len = gzread(fin, buf, sizeof(buf));
  139. if (len < 0) {
  140. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': gzread failed.",
  141. f_src);
  142. return COMPF_ERROR;
  143. }
  144. if (!len)
  145. break;
  146. if ((int) fwrite(buf, (unsigned int) len, 1, fout) != 1) {
  147. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': fwrite failed: %s.",
  148. f_src, strerror(errno));
  149. return COMPF_ERROR;
  150. }
  151. }
  152. if (fclose(fout)) {
  153. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': fclose failed: %s.",
  154. f_src, strerror(errno));
  155. return COMPF_ERROR;
  156. }
  157. if (gzclose(fin) != Z_OK) {
  158. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': gzclose failed.",
  159. f_src);
  160. return COMPF_ERROR;
  161. }
  162. uncompressed_files++;
  163. return COMPF_SUCCESS;
  164. }
  165. /* Enforce limits.
  166. */
  167. inline static void adjust_mode_num(int *mode)
  168. {
  169. if (*mode > 9)
  170. *mode = 9;
  171. else if (*mode < 0)
  172. *mode = 0;
  173. }
  174. #ifdef HAVE_MMAP
  175. /* Attempt to compress in one go, by mmap'ing the file to memory.
  176. */
  177. static int compress_to_file_mmap(FILE *fout, FILE *fin)
  178. {
  179. size_t len;
  180. int ifd = fileno(fin);
  181. char *buf = NULL;
  182. struct stat st;
  183. /* Find out size of file */
  184. if (fstat(ifd, &st) < 0)
  185. return COMPF_ERROR;
  186. if (st.st_size <= 0)
  187. return COMPF_ERROR;
  188. /* mmap file contents to memory */
  189. buf = mmap(0, st.st_size, PROT_READ, MAP_SHARED, ifd, 0);
  190. if (buf < 0)
  191. return COMPF_ERROR;
  192. /* Compress the whole file in one go */
  193. len = gzwrite(fout, buf, st.st_size);
  194. if (len != (int) st.st_size)
  195. return COMPF_ERROR;
  196. munmap(buf, st.st_size);
  197. fclose(fin);
  198. if (gzclose(fout) != Z_OK)
  199. return COMPF_ERROR;
  200. return COMPF_SUCCESS;
  201. }
  202. #endif /* HAVE_MMAP */
  203. /* Compresses a file `f_src' and saves it as `f_target'.
  204. */
  205. static int compress_to_file(char *f_src, char *f_target, int mode_num)
  206. {
  207. char buf[BUFLEN] = "", mode[5] = "";
  208. FILE *fin = NULL, *fout = NULL;
  209. size_t len;
  210. adjust_mode_num(&mode_num);
  211. simple_snprintf(mode, sizeof mode, "wb%d", mode_num);
  212. if (!is_file(f_src)) {
  213. putlog(LOG_MISC, "*", "Failed to compress file `%s': not a file.",
  214. f_src);
  215. return COMPF_ERROR;
  216. }
  217. fin = fopen(f_src, "rb");
  218. if (!fin) {
  219. putlog(LOG_MISC, "*", "Failed to compress file `%s': open failed: %s.",
  220. f_src, strerror(errno));
  221. return COMPF_ERROR;
  222. }
  223. fout = gzopen(f_target, mode);
  224. if (!fout) {
  225. putlog(LOG_MISC, "*", "Failed to compress file `%s': gzopen failed.",
  226. f_src);
  227. return COMPF_ERROR;
  228. }
  229. #ifdef HAVE_MMAP
  230. if (compress_to_file_mmap(fout, fin) == COMPF_SUCCESS) {
  231. compressed_files++;
  232. return COMPF_SUCCESS;
  233. } else {
  234. /* To be on the safe side, close the file before attempting
  235. * to write again.
  236. */
  237. gzclose(fout);
  238. fout = gzopen(f_target, mode);
  239. }
  240. #endif /* HAVE_MMAP */
  241. while (1) {
  242. len = fread(buf, 1, sizeof(buf), fin);
  243. if (ferror(fin)) {
  244. putlog(LOG_MISC, "*", "Failed to compress file `%s': fread failed: %s",
  245. f_src, strerror(errno));
  246. return COMPF_ERROR;
  247. }
  248. if (!len)
  249. break;
  250. if (gzwrite(fout, buf, (unsigned int) len) != len) {
  251. putlog(LOG_MISC, "*", "Failed to compress file `%s': gzwrite failed.",
  252. f_src);
  253. return COMPF_ERROR;
  254. }
  255. }
  256. fclose(fin);
  257. if (gzclose(fout) != Z_OK) {
  258. putlog(LOG_MISC, "*", "Failed to compress file `%s': gzclose failed.",
  259. f_src);
  260. return COMPF_ERROR;
  261. }
  262. compressed_files++;
  263. return COMPF_SUCCESS;
  264. }
  265. /* Compresses a file `filename' and saves it as `filename'.
  266. */
  267. int compress_file(char *filename, int mode_num)
  268. {
  269. char *temp_fn = NULL, randstr[5] = "";
  270. int ret;
  271. /* Create temporary filename. */
  272. temp_fn = (char *) my_calloc(1, strlen(filename) + 5);
  273. make_rand_str(randstr, 4);
  274. strcpy(temp_fn, filename);
  275. strcat(temp_fn, randstr);
  276. /* Compress file. */
  277. ret = compress_to_file(filename, temp_fn, mode_num);
  278. /* Overwrite old file with compressed version. Only do so
  279. * if the compression routine succeeded.
  280. */
  281. if (ret == COMPF_SUCCESS)
  282. movefile(temp_fn, filename);
  283. free(temp_fn);
  284. return ret;
  285. }
  286. /* Uncompresses a file `filename' and saves it as `filename'.
  287. */
  288. int uncompress_file(char *filename)
  289. {
  290. char *temp_fn = NULL, randstr[5] = "";
  291. int ret;
  292. /* Create temporary filename. */
  293. temp_fn = (char *) my_calloc(1, strlen(filename) + 5);
  294. make_rand_str(randstr, 4);
  295. strcpy(temp_fn, filename);
  296. strcat(temp_fn, randstr);
  297. /* Uncompress file. */
  298. ret = uncompress_to_file(filename, temp_fn);
  299. /* Overwrite old file with uncompressed version. Only do so
  300. * if the uncompression routine succeeded.
  301. */
  302. if (ret == COMPF_SUCCESS)
  303. movefile(temp_fn, filename);
  304. free(temp_fn);
  305. return ret;
  306. }
  307. /*
  308. * Userfile feature releated functions
  309. */
  310. static int uff_comp(int idx, char *filename)
  311. {
  312. putlog(LOG_BOTS, "*", "Compressing user file for %s.", dcc[idx].nick);
  313. return compress_file(filename, compress_level);
  314. }
  315. static int uff_uncomp(int idx, char *filename)
  316. {
  317. putlog(LOG_BOTS, "*", "Uncompressing user file from %s.", dcc[idx].nick);
  318. return uncompress_file(filename);
  319. }
  320. static int uff_ask_compress(int idx)
  321. {
  322. if (share_compressed)
  323. return 1;
  324. else
  325. return 0;
  326. }
  327. static uff_table_t compress_uff_table[] = {
  328. {"compress", UFF_COMPRESS, uff_ask_compress, 100, uff_comp, uff_uncomp},
  329. {NULL, 0, NULL, 0, NULL, NULL}
  330. };
  331. /*
  332. * Compress module related code
  333. */
  334. int compress_report(int idx, int details)
  335. {
  336. if (details) {
  337. dprintf(idx, " %u file%s compressed\n", compressed_files,
  338. (compressed_files != 1) ? "s" : "");
  339. dprintf(idx, " %u file%s uncompressed\n", uncompressed_files,
  340. (uncompressed_files != 1) ? "s" : "");
  341. }
  342. return 0;
  343. }
  344. void compress_init()
  345. {
  346. uff_addtable(compress_uff_table);
  347. }