diff options
Diffstat (limited to 'plugins/checksum/checksum.c')
-rw-r--r-- | plugins/checksum/checksum.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/plugins/checksum/checksum.c b/plugins/checksum/checksum.c new file mode 100644 index 00000000..2b5f801a --- /dev/null +++ b/plugins/checksum/checksum.c @@ -0,0 +1,351 @@ +/* XChat-WDK + * Copyright (c) 2010-2011 Berke Viktor. + * + * Use of OpenSSL SHA256 interface: http://adamlamers.com/?p=5 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <string.h> +#include <malloc.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <openssl/sha.h> + +#include "xchat-plugin.h" + +#define BUFSIZE 32768 +#define DEFAULT_MAX_HASH_SIZE 268435456 /* default size is 256 MB */ +#define FILE_BUF_SIZE 512 + +#ifndef snprintf +#define snprintf _snprintf +#endif +#ifndef stat64 +#define stat64 _stat64 +#endif + +static xchat_plugin *ph; /* plugin handle */ +static int config_fail; /* variable for config availability */ + +static void +sha256_hash_string (unsigned char hash[SHA256_DIGEST_LENGTH], char outputBuffer[65]) +{ + int i; + for (i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + sprintf (outputBuffer + (i * 2), "%02x", hash[i]); + } + outputBuffer[64] = 0; +} + +static void +sha256 (char *string, char outputBuffer[65]) +{ + int i; + unsigned char hash[SHA256_DIGEST_LENGTH]; + SHA256_CTX sha256; + + SHA256_Init (&sha256); + SHA256_Update (&sha256, string, strlen (string)); + SHA256_Final (hash, &sha256); + + for (i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + sprintf (outputBuffer + (i * 2), "%02x", hash[i]); + } + outputBuffer[64] = 0; +} + +static int +sha256_file (char *path, char outputBuffer[65]) +{ + int bytesRead; + unsigned char *buffer; + unsigned char hash[SHA256_DIGEST_LENGTH]; + SHA256_CTX sha256; + + FILE *file = fopen (path, "rb"); + if (!file) + { + return -534; + } + + SHA256_Init (&sha256); + buffer = malloc (BUFSIZE); + bytesRead = 0; + + if (!buffer) + { + return ENOMEM; + } + + while ((bytesRead = fread (buffer, 1, BUFSIZE, file))) + { + SHA256_Update (&sha256, buffer, bytesRead); + } + + SHA256_Final (hash, &sha256); + sha256_hash_string (hash, outputBuffer); + + fclose (file); + free (buffer); + return 0; +} + +static void +init () +{ + /* check whether the config file exists, if it doesn't, try to create it */ + FILE * file_in; + FILE * file_out; + char buffer[FILE_BUF_SIZE]; + + config_fail = 0; + snprintf (buffer, sizeof (buffer), "%s/checksum.conf", xchat_get_info (ph, "xchatdirfs")); + + if ((file_in = fopen (buffer, "r")) == NULL) + { + if ((file_out = fopen (buffer, "w")) == NULL) + { + config_fail = 1; + } else + { + fprintf (file_out, "%llu\n", (unsigned long long) DEFAULT_MAX_HASH_SIZE); + fclose (file_out); + } + } else + { + fclose (file_in); + } + + /* nasty easter egg: if FILE_BUF_SIZE is set to 1024 and you build for x86, you can do fclose () + at the end of init (), which is plain wrong as it will only work if fopen () != 0. */ +} + +static unsigned long long +get_max_hash_size () +{ + FILE * file_in; + char buffer[FILE_BUF_SIZE]; + unsigned long long max_hash_size; + + if (config_fail) + { + return (unsigned long long) DEFAULT_MAX_HASH_SIZE; + } else + { + snprintf (buffer, sizeof (buffer), "%s/checksum.conf", xchat_get_info (ph, "xchatdirfs")); + file_in = fopen (buffer, "r"); + fscanf (file_in, "%llu", &max_hash_size); + + fclose (file_in); + return max_hash_size; + } +} + +static void +print_size () +{ + unsigned long long size; + char suffix[3]; + + size = get_max_hash_size (); + + if (size >= 1073741824) + { + size /= 1073741824; + snprintf (suffix, sizeof (suffix), "GB"); + } else if (size >= 1048576) + { + size /= 1048576; + snprintf (suffix, sizeof (suffix), "MB"); + } else if (size >= 1024) + { + size /= 1024; + snprintf (suffix, sizeof (suffix), "kB"); + } else + { + snprintf (suffix, sizeof (suffix), "B"); + } + xchat_printf (ph, "File size limit for checksums: %llu %s\n", size, suffix); +} + +static void +increase_max_hash_size () +{ + unsigned long long size; + FILE * file_out; + char buffer[FILE_BUF_SIZE]; + + if (config_fail) + { + xchat_printf (ph, "Config file is unavailable, falling back to the default value\n"); + print_size (); + } else + { + size = get_max_hash_size (); + if (size <= ULLONG_MAX/2) + { + size *= 2; + } + + snprintf (buffer, sizeof (buffer), "%s/checksum.conf", xchat_get_info (ph, "xchatdirfs")); + file_out = fopen (buffer, "w"); + fprintf (file_out, "%llu\n", size); + fclose (file_out); + print_size (); + } +} + +static void +decrease_max_hash_size () +{ + unsigned long long size; + FILE * file_out; + char buffer[FILE_BUF_SIZE]; + + if (config_fail) + { + xchat_printf (ph, "Config file is unavailable, falling back to the default value\n"); + print_size (); + } else + { + size = get_max_hash_size (); + if (size >= 2) + { + size /= 2; + } + + snprintf (buffer, sizeof (buffer), "%s/checksum.conf", xchat_get_info (ph, "xchatdirfs")); + file_out = fopen (buffer, "w"); + fprintf (file_out, "%llu\n", size); + fclose (file_out); + print_size (); + } +} + +static int +dccrecv_cb (char *word[], void *userdata) +{ + int result; + struct stat64 buffer; /* buffer for storing file info */ + char sum[65]; /* buffer for checksum */ + + result = stat64 (word[2], &buffer); + if (result == 0) /* stat returns 0 on success */ + { + if (buffer.st_size <= get_max_hash_size ()) + { + sha256_file (word[2], sum); /* word[2] is the full filename */ + /* try to print the checksum in the privmsg tab of the sender */ + xchat_set_context (ph, xchat_find_context (ph, NULL, word[3])); + xchat_printf (ph, "SHA-256 checksum for %s (local): %s\n", word[1], sum); + } else + { + xchat_set_context (ph, xchat_find_context (ph, NULL, word[3])); + xchat_printf (ph, "SHA-256 checksum for %s (local): (size limit reached, no checksum calculated, you can increase it with /CHECKSUM INC)\n", word[1]); + } + } else + { + xchat_printf (ph, "File access error\n"); + } + + return XCHAT_EAT_NONE; +} + +static int +dccoffer_cb (char *word[], void *userdata) +{ + int result; + struct stat64 buffer; /* buffer for storing file info */ + char sum[65]; /* buffer for checksum */ + + result = stat64 (word[3], &buffer); + if (result == 0) /* stat returns 0 on success */ + { + if (buffer.st_size <= get_max_hash_size ()) + { + sha256_file (word[3], sum); /* word[3] is the full filename */ + xchat_commandf (ph, "quote PRIVMSG %s :SHA-256 checksum for %s (remote): %s", word[2], word[1], sum); + } else + { + xchat_set_context (ph, xchat_find_context (ph, NULL, word[3])); + xchat_printf (ph, "quote PRIVMSG %s :SHA-256 checksum for %s (remote): (size limit reached, no checksum calculated)", word[2], word[1]); + } + } else + { + xchat_printf (ph, "File access error\n"); + } + + return XCHAT_EAT_NONE; +} + +static void +checksum (char *word[], void *userdata) +{ + if (!stricmp ("GET", word[2])) + { + print_size (); + } else if (!stricmp ("INC", word[2])) + { + increase_max_hash_size (); + } else if (!stricmp ("DEC", word[2])) + { + decrease_max_hash_size (); + } else + { + xchat_printf (ph, "Usage: /CHECKSUM GET|INC|DEC\n"); + xchat_printf (ph, " GET - print the maximum file size to be hashed\n"); + xchat_printf (ph, " INC - double the maximum file size to be hashed\n"); + xchat_printf (ph, " DEC - halve the maximum file size to be hashed\n"); + } +} + +int +xchat_plugin_init (xchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg) +{ + ph = plugin_handle; + + *plugin_name = "Checksum"; + *plugin_desc = "Calculate checksum for DCC file transfers"; + *plugin_version = "2.0"; + + init (); + + xchat_hook_command (ph, "CHECKSUM", XCHAT_PRI_NORM, checksum, "Usage: /CHECKSUM GET|INC|DEC", 0); + xchat_hook_print (ph, "DCC RECV Complete", XCHAT_PRI_NORM, dccrecv_cb, NULL); + xchat_hook_print (ph, "DCC Offer", XCHAT_PRI_NORM, dccoffer_cb, NULL); + + xchat_print (ph, "Checksum plugin loaded\n"); + return 1; +} + +int +xchat_plugin_deinit (void) +{ + xchat_print (ph, "Checksum plugin unloaded\n"); + return 1; +} |