+
+/* Formats arguments to a string and returns it after allocating memory
+ for it. It must be remembered to free it later. */
+
+char *silc_format(char *fmt, ...)
+{
+ va_list args;
+ static char buf[8192];
+
+ memset(buf, 0, sizeof(buf));
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf) - 1, fmt, args);
+ va_end(args);
+
+ return strdup(buf);
+}
+
+/* Renders ID to suitable to print for example to log file. */
+
+static char rid[256];
+
+char *silc_id_render(void *id, uint16 type)
+{
+ char tmp[100];
+ unsigned char tmps[2];
+
+ memset(rid, 0, sizeof(rid));
+ switch(type) {
+ case SILC_ID_SERVER:
+ {
+ SilcServerID *server_id = (SilcServerID *)id;
+ struct in_addr ipv4;
+
+ if (server_id->ip.data_len > 4) {
+
+ } else {
+ SILC_GET32_MSB(ipv4.s_addr, server_id->ip.data);
+ strcat(rid, inet_ntoa(ipv4));
+ }
+
+ memset(tmp, 0, sizeof(tmp));
+ snprintf(tmp, sizeof(tmp), ",%d,", ntohs(server_id->port));
+ strcat(rid, tmp);
+ SILC_PUT16_MSB(server_id->rnd, tmps);
+ memset(tmp, 0, sizeof(tmp));
+ snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
+ strcat(rid, tmp);
+ }
+ break;
+ case SILC_ID_CLIENT:
+ {
+ SilcClientID *client_id = (SilcClientID *)id;
+ struct in_addr ipv4;
+
+ if (client_id->ip.data_len > 4) {
+
+ } else {
+ SILC_GET32_MSB(ipv4.s_addr, client_id->ip.data);
+ strcat(rid, inet_ntoa(ipv4));
+ }
+
+ memset(tmp, 0, sizeof(tmp));
+ snprintf(tmp, sizeof(tmp), ",%02x,", client_id->rnd);
+ strcat(rid, tmp);
+ memset(tmp, 0, sizeof(tmp));
+ snprintf(tmp, sizeof(tmp), "[%02x %02x %02x %02x...]",
+ client_id->hash[0], client_id->hash[1],
+ client_id->hash[2], client_id->hash[3]);
+ strcat(rid, tmp);
+ }
+ break;
+ case SILC_ID_CHANNEL:
+ {
+ SilcChannelID *channel_id = (SilcChannelID *)id;
+ struct in_addr ipv4;
+
+ if (channel_id->ip.data_len > 4) {
+
+ } else {
+ SILC_GET32_MSB(ipv4.s_addr, channel_id->ip.data);
+ strcat(rid, inet_ntoa(ipv4));
+ }
+
+ memset(tmp, 0, sizeof(tmp));
+ snprintf(tmp, sizeof(tmp), ",%d,", ntohs(channel_id->port));
+ strcat(rid, tmp);
+ SILC_PUT16_MSB(channel_id->rnd, tmps);
+ memset(tmp, 0, sizeof(tmp));
+ snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
+ strcat(rid, tmp);
+ }
+ break;
+ }
+
+ return rid;
+}
+
+/* Compares two strings. Strings may include wildcards * and ?.
+ Returns TRUE if strings match. */
+
+int silc_string_compare(char *string1, char *string2)
+{
+ int i;
+ int slen1 = strlen(string1);
+ int slen2 = strlen(string2);
+ char *tmpstr1, *tmpstr2;
+
+ if (!string1 || !string2)
+ return FALSE;
+
+ /* See if they are same already */
+ if (!strncmp(string1, string2, strlen(string2)))
+ return TRUE;
+
+ if (slen2 < slen1)
+ if (!strchr(string1, '*'))
+ return FALSE;
+
+ /* Take copies of the original strings as we will change them */
+ tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
+ memcpy(tmpstr1, string1, slen1);
+ tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
+ memcpy(tmpstr2, string2, slen2);
+
+ for (i = 0; i < slen1; i++) {
+
+ /* * wildcard. Only one * wildcard is possible. */
+ if (tmpstr1[i] == '*')
+ if (!strncmp(tmpstr1, tmpstr2, i)) {
+ memset(tmpstr2, 0, slen2);
+ strncpy(tmpstr2, tmpstr1, i);
+ break;
+ }
+
+ /* ? wildcard */
+ if (tmpstr1[i] == '?') {
+ if (!strncmp(tmpstr1, tmpstr2, i)) {
+ if (!(slen1 < i + 1))
+ if (tmpstr1[i + 1] != '?' &&
+ tmpstr1[i + 1] != tmpstr2[i + 1])
+ continue;
+
+ if (!(slen1 < slen2))
+ tmpstr2[i] = '?';
+ }
+ }
+ }
+
+ /* if using *, remove it */
+ if (strchr(tmpstr1, '*'))
+ *strchr(tmpstr1, '*') = 0;
+
+ if (!strcmp(tmpstr1, tmpstr2)) {
+ memset(tmpstr1, 0, slen1);
+ memset(tmpstr2, 0, slen2);
+ silc_free(tmpstr1);
+ silc_free(tmpstr2);
+ return TRUE;
+ }
+
+ memset(tmpstr1, 0, slen1);
+ memset(tmpstr2, 0, slen2);
+ silc_free(tmpstr1);
+ silc_free(tmpstr2);
+ return FALSE;
+}
+
+/* Basic has function to hash strings. May be used with the SilcHashTable.
+ Note that this lowers the characters of the string (with tolower()) so
+ this is used usually with nicknames, channel and server names to provide
+ case insensitive keys. */
+
+uint32 silc_hash_string(void *key, void *user_context)
+{
+ char *s = (char *)key;
+ uint32 h = 0, g;
+
+ while (*s != '\0') {
+ h = (h << 4) + tolower(*s);
+ if ((g = h & 0xf0000000)) {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ s++;
+ }
+
+ return h;
+}
+
+/* Basic hash function to hash integers. May be used with the SilcHashTable. */
+
+uint32 silc_hash_uint(void *key, void *user_context)
+{
+ return *(uint32 *)key;
+}
+
+/* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
+
+uint32 silc_hash_ptr(void *key, void *user_context)
+{
+ return (uint32)key;
+}
+
+/* Hash a ID. The `user_context' is the ID type. */
+
+uint32 silc_hash_id(void *key, void *user_context)
+{
+ SilcIdType id_type = (SilcIdType)(uint32)user_context;
+ uint32 h = 0;
+ int i;
+
+ switch (id_type) {
+ case SILC_ID_CLIENT:
+ {
+ SilcClientID *id = (SilcClientID *)key;
+ uint32 g;
+
+ /* The client ID is hashed by hashing the hash of the ID
+ (which is a truncated MD5 hash of the nickname) so that we
+ can access the entry from the cache with both Client ID but
+ with just a hash from the ID as well. */
+
+ for (i = 0; i < sizeof(id->hash); i++) {
+ h = (h << 4) + id->hash[i];
+ if ((g = h & 0xf0000000)) {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ }
+
+ return h;
+ }
+ break;
+ case SILC_ID_SERVER:
+ {
+ SilcServerID *id = (SilcServerID *)key;
+
+ h = id->port * id->rnd;
+ for (i = 0; i < id->ip.data_len; i++)
+ h ^= id->ip.data[i];
+
+ return h;
+ }
+ break;
+ case SILC_ID_CHANNEL:
+ {
+ SilcChannelID *id = (SilcChannelID *)key;
+
+ h = id->port * id->rnd;
+ for (i = 0; i < id->ip.data_len; i++)
+ h ^= id->ip.data[i];
+
+ return h;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return h;
+}
+
+/* Hash binary data. The `user_context' is the data length. */
+
+uint32 silc_hash_data(void *key, void *user_context)
+{
+ uint32 len = (uint32)user_context, h = 0;
+ unsigned char *data = (unsigned char *)key;
+ int i;
+
+ h = (data[0] * data[len - 1] + 1) * len;
+ for (i = 0; i < len; i++)
+ h ^= data[i];
+
+ return h;
+}
+
+/* Compares two strings. May be used as SilcHashTable comparison function. */
+
+bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
+{
+ return !strcasecmp((char *)key1, (char *)key2);
+}
+
+/* Compares two ID's. May be used as SilcHashTable comparison function.
+ The Client ID's compares only the hash of the Client ID not any other
+ part of the Client ID. Other ID's are fully compared. */
+
+bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
+{
+ SilcIdType id_type = (SilcIdType)(uint32)user_context;
+ return (id_type == SILC_ID_CLIENT ?
+ SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
+ SILC_ID_COMPARE_TYPE(key1, key2, id_type));
+}
+
+/* Compare two Client ID's entirely and not just the hash from the ID. */
+
+bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
+{
+ return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
+}
+
+/* Compares binary data. May be used as SilcHashTable comparison function. */
+
+bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
+{
+ uint32 len = (uint32)user_context;
+ return !memcmp(key1, key2, len);
+}
+
+/* Parses mode mask and returns the mode as string. */
+
+char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac)
+{
+ char string[100];
+
+ if (!mode)
+ return NULL;
+
+ memset(string, 0, sizeof(string));
+
+ if (mode & SILC_CHANNEL_MODE_PRIVATE)
+ strncat(string, "p", 1);
+
+ if (mode & SILC_CHANNEL_MODE_SECRET)
+ strncat(string, "s", 1);
+
+ if (mode & SILC_CHANNEL_MODE_PRIVKEY)
+ strncat(string, "k", 1);
+
+ if (mode & SILC_CHANNEL_MODE_INVITE)
+ strncat(string, "i", 1);
+
+ if (mode & SILC_CHANNEL_MODE_TOPIC)
+ strncat(string, "t", 1);
+
+ if (mode & SILC_CHANNEL_MODE_ULIMIT)
+ strncat(string, "l", 1);
+
+ if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
+ strncat(string, "a", 1);
+
+ if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
+ strncat(string, "f", 1);
+
+ if (mode & SILC_CHANNEL_MODE_CIPHER)
+ strncat(string, cipher, strlen(cipher));
+
+ if (mode & SILC_CHANNEL_MODE_HMAC)
+ strncat(string, hmac, strlen(hmac));
+
+ /* Rest of mode is ignored */
+
+ return strdup(string);
+}
+
+/* Parses channel user mode mask and returns te mode as string */
+
+char *silc_client_chumode(uint32 mode)
+{
+ char string[4];
+
+ if (!mode)
+ return NULL;
+
+ memset(string, 0, sizeof(string));
+
+ if (mode & SILC_CHANNEL_UMODE_CHANFO)
+ strncat(string, "f", 1);
+
+ if (mode & SILC_CHANNEL_UMODE_CHANOP)
+ strncat(string, "o", 1);
+
+ return strdup(string);
+}
+
+/* Parses channel user mode and returns it as special mode character. */
+
+char *silc_client_chumode_char(uint32 mode)
+{
+ char string[4];
+
+ if (!mode)
+ return NULL;
+
+ memset(string, 0, sizeof(string));
+
+ if (mode & SILC_CHANNEL_UMODE_CHANFO)
+ strncat(string, "*", 1);
+
+ if (mode & SILC_CHANNEL_UMODE_CHANOP)
+ strncat(string, "@", 1);
+
+ return strdup(string);
+}