|
1 | 1 | #include <debug.h> |
| 2 | +#include <zlib.h> |
2 | 3 |
|
3 | 4 | #include "slack-api.h" |
4 | 5 | #include "slack-json.h" |
@@ -42,21 +43,47 @@ static void api_error(SlackAPICall *call, const char *error) { |
42 | 43 |
|
43 | 44 | static gboolean api_retry(SlackAPICall *call); |
44 | 45 | static void api_run(SlackAccount *sa); |
| 46 | +gchar *api_gunzip(const guchar *gzip_data, gsize *len_ptr); |
45 | 47 |
|
46 | | -static void api_cb(PurpleUtilFetchUrlData *fetch, gpointer data, const gchar *buf, gsize len, const gchar *error) { |
| 48 | +static void api_cb(PurpleUtilFetchUrlData *fetch, gpointer data, const gchar *buf_h, gsize len_h, const gchar *error) { |
| 49 | + gboolean free_buf = FALSE; |
47 | 50 | SlackAccount *sa = data; |
48 | 51 | SlackAPICall *call = g_queue_pop_head(&sa->api_calls); |
49 | 52 | g_return_if_fail(call && (call->fetch == fetch || (call->fetch == NULL && error))); |
50 | 53 | call->fetch = NULL; |
51 | 54 |
|
| 55 | + gsize len = len_h; |
| 56 | + const gchar *buf = g_strstr_len(buf_h, len_h, "\r\n\r\n"); |
| 57 | + if (buf) { |
| 58 | + buf += 4; // skip the headers |
| 59 | + len = len_h - (buf - buf_h); |
| 60 | + } else { |
| 61 | + buf = buf_h; |
| 62 | + len = len_h; |
| 63 | + } |
| 64 | + |
| 65 | + if (g_strstr_len(buf_h, len_h - len, "Content-Encoding: gzip") != NULL || |
| 66 | + g_strstr_len(buf_h, len_h - len, "content-encoding: gzip") != NULL) { |
| 67 | + gchar *gunzip = api_gunzip((const guchar *)buf, &len); |
| 68 | + if (!gunzip) { |
| 69 | + api_error(call, "Failed to gunzip response"); |
| 70 | + api_run(sa); |
| 71 | + return; |
| 72 | + } |
| 73 | + free_buf = TRUE; |
| 74 | + buf = gunzip; |
| 75 | + } |
| 76 | + |
52 | 77 | purple_debug_misc("slack", "api response: %s\n", error ?: buf); |
53 | 78 | if (error) { |
| 79 | + if (free_buf) g_free((gchar *)buf); |
54 | 80 | api_error(call, error); |
55 | 81 | api_run(sa); |
56 | 82 | return; |
57 | 83 | } |
58 | 84 |
|
59 | 85 | json_value *json = json_parse(buf, len); |
| 86 | + if (free_buf) g_free((gchar *)buf); |
60 | 87 | if (!json) { |
61 | 88 | api_error(call, "Invalid JSON response"); |
62 | 89 | api_run(sa); |
@@ -94,7 +121,7 @@ static gboolean api_retry(SlackAPICall *call) { |
94 | 121 | purple_debug_misc("slack", "api call: %s\n%s\n", call->url, call->request ?: ""); |
95 | 122 | PurpleUtilFetchUrlData *fetch = |
96 | 123 | purple_util_fetch_url_request_len_with_account(call->sa->account, |
97 | | - call->url, TRUE, NULL, TRUE, call->request, FALSE, 4096*1024, |
| 124 | + call->url, TRUE, NULL, TRUE, call->request, TRUE, 4096*1024, |
98 | 125 | api_cb, call->sa); |
99 | 126 | if (fetch) |
100 | 127 | call->fetch = fetch; |
@@ -149,6 +176,7 @@ Content-Length: %" G_GSIZE_FORMAT "\r\n", |
149 | 176 | if (sa->d_cookie) { |
150 | 177 | g_string_append_printf(request, "Cookie: d=%s\r\n", sa->d_cookie); |
151 | 178 | } |
| 179 | + g_string_append(request, "Accept-Encoding: gzip\r\n"); |
152 | 180 | g_string_append(request, "\r\n"); |
153 | 181 | g_string_append(request, postdata->str); |
154 | 182 |
|
@@ -194,3 +222,81 @@ void slack_api_disconnect(SlackAccount *sa) { |
194 | 222 | while ((call = g_queue_pop_head(&sa->api_calls))) |
195 | 223 | api_error(call, "disconnected"); |
196 | 224 | } |
| 225 | + |
| 226 | + |
| 227 | +#include <zlib.h> |
| 228 | + |
| 229 | +gchar * |
| 230 | +api_gunzip(const guchar *gzip_data, gsize *len_ptr) |
| 231 | +{ |
| 232 | + gsize gzip_data_len = *len_ptr; |
| 233 | + z_stream zstr; |
| 234 | + int gzip_err = 0; |
| 235 | + gchar *data_buffer; |
| 236 | + gulong gzip_len = G_MAXUINT16; |
| 237 | + GString *output_string = NULL; |
| 238 | + |
| 239 | + data_buffer = g_new0(gchar, gzip_len); |
| 240 | + |
| 241 | + zstr.next_in = NULL; |
| 242 | + zstr.avail_in = 0; |
| 243 | + zstr.zalloc = Z_NULL; |
| 244 | + zstr.zfree = Z_NULL; |
| 245 | + zstr.opaque = 0; |
| 246 | + gzip_err = inflateInit2(&zstr, MAX_WBITS+32); |
| 247 | + if (gzip_err != Z_OK) |
| 248 | + { |
| 249 | + g_free(data_buffer); |
| 250 | + purple_debug_error("slack", "no built-in gzip support in zlib\n"); |
| 251 | + return NULL; |
| 252 | + } |
| 253 | + |
| 254 | + zstr.next_in = (Bytef *)gzip_data; |
| 255 | + zstr.avail_in = gzip_data_len; |
| 256 | + |
| 257 | + zstr.next_out = (Bytef *)data_buffer; |
| 258 | + zstr.avail_out = gzip_len; |
| 259 | + |
| 260 | + gzip_err = inflate(&zstr, Z_SYNC_FLUSH); |
| 261 | + |
| 262 | + if (gzip_err == Z_DATA_ERROR) |
| 263 | + { |
| 264 | + inflateEnd(&zstr); |
| 265 | + gzip_err = inflateInit2(&zstr, -MAX_WBITS); |
| 266 | + if (gzip_err != Z_OK) |
| 267 | + { |
| 268 | + g_free(data_buffer); |
| 269 | + purple_debug_error("slack", "Cannot decode gzip header\n"); |
| 270 | + return NULL; |
| 271 | + } |
| 272 | + zstr.next_in = (Bytef *)gzip_data; |
| 273 | + zstr.avail_in = gzip_data_len; |
| 274 | + zstr.next_out = (Bytef *)data_buffer; |
| 275 | + zstr.avail_out = gzip_len; |
| 276 | + gzip_err = inflate(&zstr, Z_SYNC_FLUSH); |
| 277 | + } |
| 278 | + output_string = g_string_new(""); |
| 279 | + while (gzip_err == Z_OK) |
| 280 | + { |
| 281 | + //append data to buffer |
| 282 | + output_string = g_string_append_len(output_string, data_buffer, gzip_len - zstr.avail_out); |
| 283 | + //reset buffer pointer |
| 284 | + zstr.next_out = (Bytef *)data_buffer; |
| 285 | + zstr.avail_out = gzip_len; |
| 286 | + gzip_err = inflate(&zstr, Z_SYNC_FLUSH); |
| 287 | + } |
| 288 | + if (gzip_err == Z_STREAM_END) |
| 289 | + { |
| 290 | + output_string = g_string_append_len(output_string, data_buffer, gzip_len - zstr.avail_out); |
| 291 | + } else { |
| 292 | + purple_debug_error("slack", "gzip inflate error\n"); |
| 293 | + } |
| 294 | + inflateEnd(&zstr); |
| 295 | + |
| 296 | + g_free(data_buffer); |
| 297 | + |
| 298 | + if (len_ptr) |
| 299 | + *len_ptr = output_string->len; |
| 300 | + |
| 301 | + return g_string_free(output_string, FALSE); |
| 302 | +} |
0 commit comments