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");