Mercurial > pidgin.yaz
comparison libpurple/util.c @ 27577:6d26258e9f1d
propagate from branch 'im.pidgin.pidgin' (head ac87c285c7056f86005dc157b9870745de471f74)
to branch 'im.pidgin.cpw.darkrain42.roster' (head 976d874853ac9745edb77d3cf107b92ebc037c10)
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Mon, 06 Jul 2009 04:37:41 +0000 |
parents | e9279f0e551a |
children | 70e64287f790 |
comparison
equal
deleted
inserted
replaced
27576:b9da56683499 | 27577:6d26258e9f1d |
---|---|
66 gboolean has_explicit_data_len; | 66 gboolean has_explicit_data_len; |
67 char *webdata; | 67 char *webdata; |
68 unsigned long len; | 68 unsigned long len; |
69 unsigned long data_len; | 69 unsigned long data_len; |
70 gssize max_len; | 70 gssize max_len; |
71 gboolean chunked; | |
71 }; | 72 }; |
72 | 73 |
73 static char *custom_user_dir = NULL; | 74 static char *custom_user_dir = NULL; |
74 static char *user_dir = NULL; | 75 static char *user_dir = NULL; |
75 | 76 |
217 "0123456789abcdef"; | 218 "0123456789abcdef"; |
218 | 219 |
219 gchar * | 220 gchar * |
220 purple_base64_encode(const guchar *data, gsize len) | 221 purple_base64_encode(const guchar *data, gsize len) |
221 { | 222 { |
223 #if GLIB_CHECK_VERSION(2,12,0) | |
224 return g_base64_encode(data, len); | |
225 #else | |
222 char *out, *rv; | 226 char *out, *rv; |
223 | 227 |
224 g_return_val_if_fail(data != NULL, NULL); | 228 g_return_val_if_fail(data != NULL, NULL); |
225 g_return_val_if_fail(len > 0, NULL); | 229 g_return_val_if_fail(len > 0, NULL); |
226 | 230 |
251 } | 255 } |
252 | 256 |
253 *out = '\0'; | 257 *out = '\0'; |
254 | 258 |
255 return rv; | 259 return rv; |
260 #endif /* GLIB < 2.12.0 */ | |
256 } | 261 } |
257 | 262 |
258 guchar * | 263 guchar * |
259 purple_base64_decode(const char *str, gsize *ret_len) | 264 purple_base64_decode(const char *str, gsize *ret_len) |
260 { | 265 { |
266 #if GLIB_CHECK_VERSION(2,12,0) | |
267 /* | |
268 * We want to allow ret_len to be NULL for backward compatibility, | |
269 * but g_base64_decode() requires a valid length variable. So if | |
270 * ret_len is NULL then pass in a dummy variable. | |
271 */ | |
272 gsize unused; | |
273 return g_base64_decode(str, ret_len != NULL ? ret_len : &unused); | |
274 #else | |
261 guchar *out = NULL; | 275 guchar *out = NULL; |
262 char tmp = 0; | 276 char tmp = 0; |
263 const char *c; | 277 const char *c; |
264 gint32 tmp2 = 0; | 278 gint32 tmp2 = 0; |
265 int len = 0, n = 0; | 279 int len = 0, n = 0; |
317 | 331 |
318 if (ret_len != NULL) | 332 if (ret_len != NULL) |
319 *ret_len = len; | 333 *ret_len = len; |
320 | 334 |
321 return out; | 335 return out; |
336 #endif /* GLIB < 2.12.0 */ | |
322 } | 337 } |
323 | 338 |
324 /************************************************************************** | 339 /************************************************************************** |
325 * Quoted Printable Functions (see RFC 2045). | 340 * Quoted Printable Functions (see RFC 2045). |
326 **************************************************************************/ | 341 **************************************************************************/ |
962 static char buf[7]; | 977 static char buf[7]; |
963 int buflen = g_unichar_to_utf8((gunichar)pound, buf); | 978 int buflen = g_unichar_to_utf8((gunichar)pound, buf); |
964 buf[buflen] = '\0'; | 979 buf[buflen] = '\0'; |
965 pln = buf; | 980 pln = buf; |
966 | 981 |
967 len = 2; | 982 len = (*(text+2) == 'x' ? 3 : 2); |
968 while(isdigit((gint) text[len])) len++; | 983 while(isxdigit((gint) text[len])) len++; |
969 if(text[len] == ';') len++; | 984 if(text[len] == ';') len++; |
970 } | 985 } |
971 else | 986 else |
972 return NULL; | 987 return NULL; |
973 | 988 |
2382 | 2397 |
2383 } | 2398 } |
2384 return g_string_free(ret, FALSE); | 2399 return g_string_free(ret, FALSE); |
2385 } | 2400 } |
2386 | 2401 |
2387 char * | 2402 char *purple_unescape_html(const char *html) |
2388 purple_unescape_html(const char *html) { | 2403 { |
2389 if (html != NULL) { | 2404 GString *ret; |
2390 const char *c = html; | 2405 const char *c = html; |
2391 GString *ret = g_string_new(""); | 2406 |
2392 while (*c) { | 2407 if (html == NULL) |
2393 int len; | 2408 return NULL; |
2394 const char *ent; | 2409 |
2395 | 2410 ret = g_string_new(""); |
2396 if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) { | 2411 while (*c) { |
2397 ret = g_string_append(ret, ent); | 2412 int len; |
2398 c += len; | 2413 const char *ent; |
2399 } else if (!strncmp(c, "<br>", 4)) { | 2414 |
2400 ret = g_string_append_c(ret, '\n'); | 2415 if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) { |
2401 c += 4; | 2416 g_string_append(ret, ent); |
2402 } else { | 2417 c += len; |
2403 ret = g_string_append_c(ret, *c); | 2418 } else if (!strncmp(c, "<br>", 4)) { |
2404 c++; | 2419 g_string_append_c(ret, '\n'); |
2405 } | 2420 c += 4; |
2406 } | 2421 } else { |
2407 return g_string_free(ret, FALSE); | 2422 g_string_append_c(ret, *c); |
2408 } | 2423 c++; |
2409 | 2424 } |
2410 return NULL; | 2425 } |
2426 | |
2427 return g_string_free(ret, FALSE); | |
2411 } | 2428 } |
2412 | 2429 |
2413 char * | 2430 char * |
2414 purple_markup_slice(const char *str, guint x, guint y) | 2431 purple_markup_slice(const char *str, guint x, guint y) |
2415 { | 2432 { |
3712 } | 3729 } |
3713 | 3730 |
3714 return TRUE; | 3731 return TRUE; |
3715 } | 3732 } |
3716 | 3733 |
3717 static size_t | 3734 static const char * |
3718 parse_content_len(const char *data, size_t data_len) | 3735 find_header_content(const char *data, size_t data_len, const char *header, size_t header_len) |
3719 { | 3736 { |
3720 size_t content_len = 0; | |
3721 const char *p = NULL; | 3737 const char *p = NULL; |
3722 | 3738 |
3723 /* This is still technically wrong, since headers are case-insensitive | 3739 if (header_len <= 0) |
3724 * [RFC 2616, section 4.2], though this ought to catch the normal case. | 3740 header_len = strlen(header); |
3725 * Note: data is _not_ nul-terminated. | 3741 |
3726 */ | 3742 /* Note: data is _not_ nul-terminated. */ |
3727 if(data_len > 16) { | 3743 if (data_len > header_len) { |
3728 p = (strncmp(data, "Content-Length: ", 16) == 0) ? data : NULL; | 3744 if (header[0] == '\n') |
3729 if(!p) | 3745 p = (g_strncasecmp(data, header + 1, header_len - 1) == 0) ? data : NULL; |
3730 p = (strncmp(data, "CONTENT-LENGTH: ", 16) == 0) | 3746 if (!p) |
3731 ? data : NULL; | 3747 p = purple_strcasestr(data, header); |
3732 if(!p) { | 3748 if (p) |
3733 p = g_strstr_len(data, data_len, "\nContent-Length: "); | 3749 p += header_len; |
3734 if (p) | 3750 } |
3735 p++; | 3751 |
3736 } | 3752 /* If we can find the header at all, try to sscanf it. |
3737 if(!p) { | |
3738 p = g_strstr_len(data, data_len, "\nCONTENT-LENGTH: "); | |
3739 if (p) | |
3740 p++; | |
3741 } | |
3742 | |
3743 if(p) | |
3744 p += 16; | |
3745 } | |
3746 | |
3747 /* If we can find a Content-Length header at all, try to sscanf it. | |
3748 * Response headers should end with at least \r\n, so sscanf is safe, | 3753 * Response headers should end with at least \r\n, so sscanf is safe, |
3749 * if we make sure that there is indeed a \n in our header. | 3754 * if we make sure that there is indeed a \n in our header. |
3750 */ | 3755 */ |
3751 if (p && g_strstr_len(p, data_len - (p - data), "\n")) { | 3756 if (p && g_strstr_len(p, data_len - (p - data), "\n")) { |
3757 return p; | |
3758 } | |
3759 | |
3760 return NULL; | |
3761 } | |
3762 | |
3763 static size_t | |
3764 parse_content_len(const char *data, size_t data_len) | |
3765 { | |
3766 size_t content_len = 0; | |
3767 const char *p = NULL; | |
3768 | |
3769 p = find_header_content(data, data_len, "\nContent-Length: ", sizeof("\nContent-Length: ") - 1); | |
3770 if (p) { | |
3752 sscanf(p, "%" G_GSIZE_FORMAT, &content_len); | 3771 sscanf(p, "%" G_GSIZE_FORMAT, &content_len); |
3753 purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len); | 3772 purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len); |
3754 } | 3773 } |
3755 | 3774 |
3756 return content_len; | 3775 return content_len; |
3757 } | 3776 } |
3758 | 3777 |
3778 static gboolean | |
3779 content_is_chunked(const char *data, size_t data_len) | |
3780 { | |
3781 const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ", sizeof("\nTransfer-Encoding: ") - 1); | |
3782 if (p && g_strncasecmp(p, "chunked", 7) == 0) | |
3783 return TRUE; | |
3784 | |
3785 return FALSE; | |
3786 } | |
3787 | |
3788 /* Process in-place */ | |
3789 static void | |
3790 process_chunked_data(char *data, gsize *len) | |
3791 { | |
3792 gsize sz; | |
3793 gsize newlen = 0; | |
3794 char *p = data; | |
3795 char *s = data; | |
3796 | |
3797 while (*s) { | |
3798 /* Read the size of this chunk */ | |
3799 if (sscanf(s, "%" G_GSIZE_MODIFIER "x\r\n", &sz) != 1 && | |
3800 sscanf(s, "%" G_GSIZE_MODIFIER "x;", &sz) != 1) | |
3801 { | |
3802 purple_debug_error("util", "Error processing chunked data: " | |
3803 "Expected data length, found: %s\n", s); | |
3804 break; | |
3805 } | |
3806 if (sz == 0) { | |
3807 /* We've reached the last chunk */ | |
3808 /* | |
3809 * TODO: The spec allows "footers" to follow the last chunk. | |
3810 * If there is more data after this line then we should | |
3811 * treat it like a header. | |
3812 */ | |
3813 break; | |
3814 } | |
3815 | |
3816 /* Advance to the start of the data */ | |
3817 s = strstr(s, "\r\n"); | |
3818 if (s == NULL) | |
3819 break; | |
3820 s += 2; | |
3821 | |
3822 if (s + sz > data + *len) { | |
3823 purple_debug_error("util", "Error processing chunked data: " | |
3824 "Chunk size %" G_GSIZE_FORMAT " bytes was longer " | |
3825 "than the data remaining in the buffer (%" | |
3826 G_GSIZE_FORMAT " bytes)\n", sz, data + *len - s); | |
3827 } | |
3828 | |
3829 /* Move all data overtop of the chunk length that we read in earlier */ | |
3830 g_memmove(p, s, sz); | |
3831 p += sz; | |
3832 s += sz; | |
3833 newlen += sz; | |
3834 if (*s != '\r' && *(s + 1) != '\n') { | |
3835 purple_debug_error("util", "Error processing chunked data: " | |
3836 "Expected \\r\\n, found: %s\n", s); | |
3837 break; | |
3838 } | |
3839 s += 2; | |
3840 } | |
3841 | |
3842 /* NULL terminate the data */ | |
3843 *p = 0; | |
3844 | |
3845 *len = newlen; | |
3846 } | |
3759 | 3847 |
3760 static void | 3848 static void |
3761 url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond) | 3849 url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond) |
3762 { | 3850 { |
3763 PurpleUtilFetchUrlData *gfud = url_data; | 3851 PurpleUtilFetchUrlData *gfud = url_data; |
3814 | 3902 |
3815 gfud->got_headers = TRUE; | 3903 gfud->got_headers = TRUE; |
3816 | 3904 |
3817 /* No redirect. See if we can find a content length. */ | 3905 /* No redirect. See if we can find a content length. */ |
3818 content_len = parse_content_len(gfud->webdata, header_len); | 3906 content_len = parse_content_len(gfud->webdata, header_len); |
3907 gfud->chunked = content_is_chunked(gfud->webdata, header_len); | |
3819 | 3908 |
3820 if(content_len == 0) { | 3909 if(content_len == 0) { |
3821 /* We'll stick with an initial 8192 */ | 3910 /* We'll stick with an initial 8192 */ |
3822 content_len = 8192; | 3911 content_len = 8192; |
3823 } else { | 3912 } else { |
3886 | 3975 |
3887 if((len == 0) || got_eof) { | 3976 if((len == 0) || got_eof) { |
3888 gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1); | 3977 gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1); |
3889 gfud->webdata[gfud->len] = '\0'; | 3978 gfud->webdata[gfud->len] = '\0'; |
3890 | 3979 |
3980 if (!gfud->include_headers && gfud->chunked) { | |
3981 /* Process only if we don't want the headers. */ | |
3982 process_chunked_data(gfud->webdata, &gfud->len); | |
3983 } | |
3984 | |
3891 gfud->callback(gfud, gfud->user_data, gfud->webdata, gfud->len, NULL); | 3985 gfud->callback(gfud, gfud->user_data, gfud->webdata, gfud->len, NULL); |
3892 purple_util_fetch_url_cancel(gfud); | 3986 purple_util_fetch_url_cancel(gfud); |
3893 } | 3987 } |
3894 } | 3988 } |
3895 | 3989 |
3896 static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) | 3990 static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) |
3897 { | 3991 { |
3898 url_fetch_recv_cb(data, -1, cond); | 3992 url_fetch_recv_cb(data, -1, cond); |
3899 } | 3993 } |
3900 | 3994 |
3901 /* | 3995 /** |
3902 * This function is called when the socket is available to be written | 3996 * This function is called when the socket is available to be written |
3903 * to. | 3997 * to. |
3904 * | 3998 * |
3905 * @param source The file descriptor that can be written to. This can | 3999 * @param source The file descriptor that can be written to. This can |
3906 * be an http connection or it can be the SSL connection of an | 4000 * be an http connection or it can be the SSL connection of an |
3946 (gfud->http11 ? "1.1" : "1.0"), | 4040 (gfud->http11 ? "1.1" : "1.0"), |
3947 (gfud->website.address ? gfud->website.address : "")); | 4041 (gfud->website.address ? gfud->website.address : "")); |
3948 } | 4042 } |
3949 } | 4043 } |
3950 | 4044 |
3951 if(g_getenv("PURPLE_UNSAFE_DEBUG")) | 4045 if(purple_debug_is_unsafe()) |
3952 purple_debug_misc("util", "Request: '%s'\n", gfud->request); | 4046 purple_debug_misc("util", "Request: '%s'\n", gfud->request); |
3953 else | 4047 else |
3954 purple_debug_misc("util", "request constructed\n"); | 4048 purple_debug_misc("util", "request constructed\n"); |
3955 | 4049 |
3956 total_len = strlen(gfud->request); | 4050 total_len = strlen(gfud->request); |
4063 PurpleUtilFetchUrlData *gfud; | 4157 PurpleUtilFetchUrlData *gfud; |
4064 | 4158 |
4065 g_return_val_if_fail(url != NULL, NULL); | 4159 g_return_val_if_fail(url != NULL, NULL); |
4066 g_return_val_if_fail(callback != NULL, NULL); | 4160 g_return_val_if_fail(callback != NULL, NULL); |
4067 | 4161 |
4068 if(g_getenv("PURPLE_UNSAFE_DEBUG")) | 4162 if(purple_debug_is_unsafe()) |
4069 purple_debug_info("util", | 4163 purple_debug_info("util", |
4070 "requested to fetch (%s), full=%d, user_agent=(%s), http11=%d\n", | 4164 "requested to fetch (%s), full=%d, user_agent=(%s), http11=%d\n", |
4071 url, full, user_agent?user_agent:"(null)", http11); | 4165 url, full, user_agent?user_agent:"(null)", http11); |
4072 else | 4166 else |
4073 purple_debug_info("util", "requesting to fetch a URL\n"); | 4167 purple_debug_info("util", "requesting to fetch a URL\n"); |