compress.c 9.9 KB

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