/* * SkypeWeb Plugin for libpurple/Pidgin * Copyright (c) 2014-2020 Eion Robb * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "skypeweb_util.h" gchar * skypeweb_string_get_chunk(const gchar *haystack, gsize len, const gchar *start, const gchar *end) { const gchar *chunk_start, *chunk_end; g_return_val_if_fail(haystack && start, NULL); if (len > 0) { chunk_start = g_strstr_len(haystack, len, start); } else { chunk_start = strstr(haystack, start); } g_return_val_if_fail(chunk_start, NULL); chunk_start += strlen(start); if (end != NULL) { if (len > 0) { chunk_end = g_strstr_len(chunk_start, len - (chunk_start - haystack), end); } else { chunk_end = strstr(chunk_start, end); } g_return_val_if_fail(chunk_end, NULL); return g_strndup(chunk_start, chunk_end - chunk_start); } else { return g_strdup(chunk_start); } } gchar * skypeweb_jsonobj_to_string(JsonObject *jsonobj) { JsonGenerator *generator; JsonNode *root; gchar *string; root = json_node_new(JSON_NODE_OBJECT); json_node_set_object(root, jsonobj); generator = json_generator_new(); json_generator_set_root(generator, root); string = json_generator_to_data(generator, NULL); g_object_unref(generator); json_node_free(root); return string; } /** turn https://bay-client-s.gateway.messenger.live.com/v1/users/ME/contacts/8:eionrobb or https://bay-client-s.gateway.messenger.live.com/v1/users/8:eionrobb/presenceDocs/messagingService into eionrobb */ const gchar * skypeweb_contact_url_to_name(const gchar *url) { static gchar *tempname = NULL; const gchar *start, *end; start = g_strrstr(url, "/8:"); if (!start) start = g_strrstr(url, "/1:"); if (!start) start = g_strrstr(url, "/4:"); if (start) start = start + 2; if (!start) start = g_strrstr(url, "/2:"); if (!start) start = g_strrstr(url, "/28:"); if (start) start = start + 1; if (!start) return NULL; if ((end = strchr(start, '/'))) { g_free(tempname); tempname = g_strndup(start, end - start); return tempname; } g_free(tempname); tempname = g_strdup(start); return tempname; } /** turn https://bay-client-s.gateway.messenger.live.com/v1/users/ME/conversations/19:blah@thread.skype into 19:blah@thread.skype */ const gchar * skypeweb_thread_url_to_name(const gchar *url) { static gchar *tempname = NULL; const gchar *start, *end; start = g_strrstr(url, "/19:"); if (!start) return NULL; start = start + 1; if ((end = strchr(start, '/'))) { g_free(tempname); tempname = g_strndup(start, end - start); return tempname; } return start; } /** Blatantly stolen from MSN prpl, with super-secret SHA256 change! */ #define BUFSIZE 256 char * skypeweb_hmac_sha256(char *input) { GChecksum *hash; const guchar productKey[] = SKYPEWEB_LOCKANDKEY_SECRET; const guchar productID[] = SKYPEWEB_LOCKANDKEY_APPID; const char hexChars[] = "0123456789abcdef"; char buf[BUFSIZE]; unsigned char sha256Hash[32]; gsize sha256HashLen = sizeof(sha256Hash); unsigned char *newHash; unsigned int *sha256Parts; unsigned int *chlStringParts; unsigned int newHashParts[5]; gchar *output; long long nHigh = 0, nLow = 0; int len; int i; hash = g_checksum_new(G_CHECKSUM_SHA256); g_checksum_update(hash, (guchar *)input, strlen(input)); g_checksum_update(hash, productKey, sizeof(productKey) - 1); g_checksum_get_digest(hash, (guchar *)sha256Hash, &sha256HashLen); g_checksum_free(hash); /* Split it into four integers */ sha256Parts = (unsigned int *)sha256Hash; for (i = 0; i < 4; i++) { /* adjust endianess */ sha256Parts[i] = GUINT_TO_LE(sha256Parts[i]); /* & each integer with 0x7FFFFFFF */ /* and save one unmodified array for later */ newHashParts[i] = sha256Parts[i]; sha256Parts[i] &= 0x7FFFFFFF; } /* make a new string and pad with '0' to length that's a multiple of 8 */ snprintf(buf, BUFSIZE - 5, "%s%s", input, productID); len = strlen(buf); if ((len % 8) != 0) { int fix = 8 - (len % 8); memset(&buf[len], '0', fix); buf[len + fix] = '\0'; len += fix; } /* split into integers */ chlStringParts = (unsigned int *)buf; /* this is magic */ for (i = 0; i < (len / 4); i += 2) { long long temp; chlStringParts[i] = GUINT_TO_LE(chlStringParts[i]); chlStringParts[i + 1] = GUINT_TO_LE(chlStringParts[i + 1]); temp = (0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF; temp = (sha256Parts[0] * (temp + nLow) + sha256Parts[1]) % 0x7FFFFFFF; nHigh += temp; temp = ((long long)chlStringParts[i + 1] + temp) % 0x7FFFFFFF; nLow = (sha256Parts[2] * temp + sha256Parts[3]) % 0x7FFFFFFF; nHigh += nLow; } nLow = (nLow + sha256Parts[1]) % 0x7FFFFFFF; nHigh = (nHigh + sha256Parts[3]) % 0x7FFFFFFF; newHashParts[0] ^= nLow; newHashParts[1] ^= nHigh; newHashParts[2] ^= nLow; newHashParts[3] ^= nHigh; /* adjust endianness */ for(i = 0; i < 4; i++) newHashParts[i] = GUINT_TO_LE(newHashParts[i]); /* make a string of the parts */ newHash = (unsigned char *)newHashParts; /* convert to hexadecimal */ output = g_new0(gchar, 33); for (i = 0; i < 16; i++) { output[i * 2] = hexChars[(newHash[i] >> 4) & 0xF]; output[(i * 2) + 1] = hexChars[newHash[i] & 0xF]; } output[32] = '\0'; return output; } gint64 skypeweb_get_js_time() { #if GLIB_CHECK_VERSION(2, 28, 0) return (g_get_real_time() / 1000); #else GTimeVal val; g_get_current_time (&val); return (((gint64) val.tv_sec) * 1000) + (val.tv_usec / 1000); #endif } /* copied from oscar.c to be libpurple 2.1 compatible */ PurpleAccount * find_acct(const char *prpl, const char *acct_id) { PurpleAccount *acct = NULL; /* If we have a specific acct, use it */ if (acct_id && *acct_id) { acct = purple_accounts_find(acct_id, prpl); if (acct && !purple_account_is_connected(acct)) acct = NULL; } else { /* Otherwise find an active account for the protocol */ GList *l = purple_accounts_get_all(); while (l) { if (!strcmp(prpl, purple_account_get_protocol_id(l->data)) && purple_account_is_connected(l->data)) { acct = l->data; break; } l = l->next; } } return acct; } const gchar * skypeweb_user_url_prefix(const gchar *who) { if(SKYPEWEB_BUDDY_IS_S4B(who) || SKYPEWEB_BUDDY_IS_BOT(who)) { return ""; // already has a prefix } else if (SKYPEWEB_BUDDY_IS_MSN(who)) { return "1:"; } else if(SKYPEWEB_BUDDY_IS_PHONE(who)) { return "4:"; } else { return "8:"; } } const gchar * skypeweb_strip_user_prefix(const gchar *who) { if (who[1] == ':') { if (who[0] != '2') { return who + 2; } } return who; }