diff -r fdd1f3a3c6df -r e83a87761544 libpurple/protocols/gg/lib/message.c
--- a/libpurple/protocols/gg/lib/message.c Thu Feb 13 04:27:27 2014 +0100
+++ b/libpurple/protocols/gg/lib/message.c Thu Feb 13 18:29:10 2014 +0100
@@ -164,7 +164,7 @@
GG_MESSAGE_CHECK(gm, (uin_t) -1);
if ((gm->recipients == NULL) || (gm->recipient_count < 1)) {
- // errno = XXX;
+ /* errno = XXX; */
return (uin_t) -1;
}
@@ -292,14 +292,14 @@
free(gm->html_converted);
- len = gg_message_text_to_html(NULL, gm->text, gm->attributes, gm->attributes_length);
+ len = gg_message_text_to_html(NULL, gm->text, GG_ENCODING_UTF8, gm->attributes, gm->attributes_length);
gm->html_converted = malloc(len + 1);
if (gm->html_converted == NULL)
return NULL;
- gg_message_text_to_html(gm->html_converted, gm->text, gm->attributes, gm->attributes_length);
+ gg_message_text_to_html(gm->html_converted, gm->text, GG_ENCODING_UTF8, gm->attributes, gm->attributes_length);
return gm->html_converted;
}
@@ -312,7 +312,7 @@
GG_MESSAGE_CHECK(gm, -1);
if (length > 0xfffd) {
- // errno = XXX;
+ /* errno = XXX; */
return -1;
}
@@ -361,7 +361,7 @@
* \param src Dodawany tekst
* \param len Długość dodawanego tekstu
*/
-static void gg_append(char *dst, size_t *pos, const void *src, int len)
+static void gg_append(char *dst, size_t *pos, const void *src, size_t len)
{
if (dst != NULL)
memcpy(&dst[*pos], src, len);
@@ -373,7 +373,8 @@
* \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML.
*
* \param dst Bufor wynikowy (może być \c NULL)
- * \param src Tekst źródłowy w UTF-8
+ * \param src Tekst źródłowy
+ * \param encoding Kodowanie tekstu źródłowego oraz wynikowego
* \param format Atrybuty tekstu źródłowego
* \param format_len Długość bloku atrybutów tekstu źródłowego
*
@@ -384,50 +385,50 @@
*
* \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
*/
-size_t gg_message_text_to_html(char *dst, const char *src, const char *format, size_t format_len)
+size_t gg_message_text_to_html(char *dst, const char *src, gg_encoding_t encoding, const unsigned char *format, size_t format_len)
{
const char span_fmt[] = "";
- const int span_len = 75;
+ const size_t span_len = 75;
const char img_fmt[] = "
";
- const int img_len = 29;
- int char_pos = 0;
- int format_idx = 0;
+ const size_t img_len = 29;
+ size_t char_pos = 0;
unsigned char old_attr = 0;
- const unsigned char *color = (const unsigned char*) "\x00\x00\x00";
- int i;
- size_t len;
- const unsigned char *format_ = (const unsigned char*) format;
-
- len = 0;
-
- /* Nie mamy atrybutów dla pierwsze znaku, a tekst nie jest pusty, więc
- * tak czy inaczej trzeba otworzyć . */
-
- if (src[0] != 0 && (format_idx + 3 > format_len || (format_[format_idx] | (format_[format_idx + 1] << 8)) != 0)) {
- if (dst != NULL)
- sprintf(&dst[len], span_fmt, 0, 0, 0);
-
- len += span_len;
- }
+ const unsigned char default_color[] = {'\x00', '\x00', '\x00'};
+ const unsigned char *old_color = NULL;
+ int in_span = 0;
+ unsigned int i;
+ size_t len = 0;
/* Pętla przechodzi też przez kończące \0, żeby móc dokleić obrazek
* na końcu tekstu. */
for (i = 0; ; i++) {
- /* Analizuj atrybuty tak długo jak dotyczą aktualnego znaku. */
+ int in_char = 0;
+ size_t format_idx = 0;
+
+ /* Sprawdź, czy bajt jest kontynuacją znaku UTF-8. */
+ if (encoding == GG_ENCODING_UTF8 && (src[i] & 0xc0) == 0x80)
+ in_char = 1;
+
+ /* GG_FONT_IMAGE powinno dotyczyć tylko jednego znaku, więc czyścimy stary atrybut */
+
+ if (!in_char && (old_attr & GG_FONT_IMAGE) != 0)
+ old_attr &= ~GG_FONT_IMAGE;
+
+ /* Analizuj wszystkie atrybuty dotyczące aktualnego znaku. */
for (;;) {
unsigned char attr;
- int attr_pos;
+ size_t attr_pos;
+
+ /* Nie wstawiamy niczego wewnątrz wielobajtowego znaku UTF-8. */
+ if (in_char)
+ break;
if (format_idx + 3 > format_len)
break;
- attr_pos = format_[format_idx] | (format_[format_idx + 1] << 8);
-
- if (attr_pos != char_pos)
- break;
-
- attr = format_[format_idx + 2];
+ attr_pos = format[format_idx] | (format[format_idx + 1] << 8);
+ attr = format[format_idx + 2];
/* Nie doklejaj atrybutów na końcu, co najwyżej obrazki. */
@@ -436,37 +437,49 @@
format_idx += 3;
- if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0 || (attr == 0 && old_attr != 0)) {
- if (char_pos != 0) {
- if ((old_attr & GG_FONT_UNDERLINE) != 0)
- gg_append(dst, &len, "", 4);
+ if (attr_pos != char_pos) {
+ if ((attr & GG_FONT_COLOR) != 0)
+ format_idx += 3;
+ if ((attr & GG_FONT_IMAGE) != 0)
+ format_idx += 10;
+
+ continue;
+ }
- if ((old_attr & GG_FONT_ITALIC) != 0)
- gg_append(dst, &len, "", 4);
+ if ((old_attr & GG_FONT_UNDERLINE) != 0)
+ gg_append(dst, &len, "", 4);
+
+ if ((old_attr & GG_FONT_ITALIC) != 0)
+ gg_append(dst, &len, "", 4);
- if ((old_attr & GG_FONT_BOLD) != 0)
- gg_append(dst, &len, "", 4);
+ if ((old_attr & GG_FONT_BOLD) != 0)
+ gg_append(dst, &len, "", 4);
- if (src[i] != 0)
- gg_append(dst, &len, "", 7);
- }
+ if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0) {
+ const unsigned char *color;
if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) {
- color = &format_[format_idx];
+ color = &format[format_idx];
format_idx += 3;
} else {
- color = (unsigned char*) "\x00\x00\x00";
+ color = default_color;
}
- if (src[i] != 0) {
- if (dst != NULL)
- sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
- len += span_len;
+ if (old_color == NULL || memcmp(color, old_color, 3) != 0) {
+ if (in_span) {
+ gg_append(dst, &len, "", 7);
+ in_span = 0;
+ }
+
+ if (src[i] != 0) {
+ if (dst != NULL)
+ sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
+
+ len += span_len;
+ in_span = 1;
+ old_color = color;
+ }
}
- } else if (char_pos == 0 && src[0] != 0) {
- if (dst != NULL)
- sprintf(&dst[len], span_fmt, 0, 0, 0);
- len += span_len;
}
if ((attr & GG_FONT_BOLD) != 0)
@@ -481,14 +494,14 @@
if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) {
if (dst != NULL) {
sprintf(&dst[len], img_fmt,
- format_[format_idx + 9],
- format_[format_idx + 8],
- format_[format_idx + 7],
- format_[format_idx + 6],
- format_[format_idx + 5],
- format_[format_idx + 4],
- format_[format_idx + 3],
- format_[format_idx + 2]);
+ format[format_idx + 9],
+ format[format_idx + 8],
+ format[format_idx + 7],
+ format[format_idx + 6],
+ format[format_idx + 5],
+ format[format_idx + 4],
+ format[format_idx + 3],
+ format[format_idx + 2]);
}
len += img_len;
@@ -498,6 +511,30 @@
old_attr = attr;
}
+ if (src[i] == 0)
+ break;
+
+ /* Znaki oznaczone jako GG_FONT_IMAGE nie są częścią wiadomości. */
+
+ if ((old_attr & GG_FONT_IMAGE) != 0) {
+ if (!in_char)
+ char_pos++;
+
+ continue;
+ }
+
+ /* Jesteśmy na początku tekstu i choć nie było atrybutów dla pierwszego
+ * znaku, ponieważ tekst nie jest pusty, trzeba otworzyć . */
+
+ if (!in_span) {
+ if (dst != NULL)
+ sprintf(&dst[len], span_fmt, default_color[0], default_color[1], default_color[2]);
+
+ len += span_len;
+ in_span = 1;
+ old_color = default_color;
+ }
+
/* Doklej znak zachowując htmlowe escapowanie. */
switch (src[i]) {
@@ -520,7 +557,6 @@
gg_append(dst, &len, "
", 4);
break;
case '\r':
- case 0:
break;
default:
if (dst != NULL)
@@ -528,13 +564,8 @@
len++;
}
- /* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */
-
- if ((src[i] & 0xc0) != 0xc0)
+ if (!in_char)
char_pos++;
-
- if (src[i] == 0)
- break;
}
/* Zamknij tagi. */
@@ -548,7 +579,7 @@
if ((old_attr & GG_FONT_BOLD) != 0)
gg_append(dst, &len, "", 4);
- if (src[0] != 0)
+ if (in_span)
gg_append(dst, &len, "", 7);
if (dst != NULL)
@@ -558,36 +589,104 @@
}
/**
+ * \internal Dokleja nowe atrybuty formatowania, jeśli konieczne, oraz inkrementuje pozycję znaku w tekście.
+ *
+ * \param pos Wskaźnik na zmienną przechowującą pozycję znaku w tekście
+ * \param attr_flag Aktualna flaga atrybutu formatowania
+ * \param old_attr_flag Wskaźnik na poprzednią flagę atrybutu formatowania
+ * \param color Wskaźnik na tablicę z aktualnym kolorem RGB (jeśli \p attr_flag nie zawiera flagi \c GG_FONT_COLOR, ignorowane)
+ * \param old_color Wskaźnik na tablicę z poprzednim kolorem RGB
+ * \param imgs_size Rozmiar atrybutów formatowania obrazków znajdujących się obecnie w tablicy atrybutów formatowania, w bajtach
+ * \param format Wskaźnik na wskaźnik do tablicy atrybutów formatowania
+ * \param format_len Wskaźnik na zmienną zawierającą długość tablicy atrybutów formatowania, w bajtach (może być \c NULL)
+ */
+static void gg_after_append_formatted_char(uint16_t *pos, unsigned char attr_flag, unsigned char *old_attr_flag, const unsigned char *color, unsigned char *old_color, size_t imgs_size, unsigned char **format, size_t *format_len)
+{
+ const size_t color_size = 3;
+ int has_color = 0;
+
+ if ((attr_flag & GG_FONT_COLOR) != 0)
+ has_color = 1;
+
+ if (*old_attr_flag != attr_flag || (has_color && memcmp(old_color, color, color_size) != 0)) {
+ size_t attr_size = sizeof(*pos) + sizeof(attr_flag) + (has_color ? color_size : 0);
+
+ if (*format != NULL) {
+ /* Staramy się naśladować oryginalnego klienta i atrybuty obrazków trzymamy na końcu */
+
+ *format -= imgs_size;
+ memmove(*format + attr_size, *format, imgs_size);
+
+ **format = (unsigned char) (*pos & (uint16_t) 0x00ffU);
+ *format += 1;
+ **format = (unsigned char) ((*pos & (uint16_t) 0xff00U) >> 8);
+ *format += 1;
+
+ **format = attr_flag;
+ *format += 1;
+
+ if (has_color) {
+ memcpy(*format, color, color_size);
+ *format += color_size;
+ }
+
+ *format += imgs_size;
+ }
+
+ if (format_len != NULL)
+ *format_len += attr_size;
+
+ *old_attr_flag = attr_flag;
+ if (has_color)
+ memcpy(old_color, color, color_size);
+ }
+
+ *pos += 1;
+}
+
+/**
* \internal Zamienia tekst w formacie HTML na czysty tekst.
*
* \param dst Bufor wynikowy (może być \c NULL)
+ * \param format Bufor wynikowy z atrybutami formatowania (może być \c NULL)
+ * \param format_len Wskaźnik na zmienną, do której zostanie zapisana potrzebna wielkość bufora wynikowego z atrybutami formatowania, w bajtach (może być \c NULL)
* \param html Tekst źródłowy
+ * \param encoding Kodowanie tekstu źródłowego oraz wynikowego
*
* \note Dokleja \c \\0 na końcu bufora wynikowego.
*
- * \note Funkcja służy do zachowania kompatybilności przy przesyłaniu
- * wiadomości HTML do klientów, które tego formatu nie obsługują. Z tego
- * powodu funkcja nie zachowuje formatowania, a jedynie usuwa tagi i
- * zamienia podstawowe encje na ich odpowiedniki ASCII.
- *
- * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
+ * \return Długość bufora wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
*/
-size_t gg_message_html_to_text(char *dst, const char *html)
+size_t gg_message_html_to_text(char *dst, unsigned char *format, size_t *format_len, const char *html, gg_encoding_t encoding)
{
- const char *src, *entity, *tag;
- int in_tag, in_entity;
- size_t len;
+ const char *src, *entity = NULL, *tag = NULL;
+ int in_tag = 0, in_entity = 0, in_bold = 0, in_italic = 0, in_underline = 0;
+ unsigned char color[3] = { 0 }, old_color[3] = { 0 };
+ unsigned char attr_flag = 0, old_attr_flag = 0;
+ uint16_t pos = 0;
+ size_t len = 0, imgs_size = 0;
- len = 0;
- in_tag = 0;
- tag = NULL;
- in_entity = 0;
- entity = NULL;
+ if (format_len != NULL)
+ *format_len = 0;
for (src = html; *src != 0; src++) {
if (in_entity && !(isalnum(*src) || *src == '#' || *src == ';')) {
+ int first = 1;
+ size_t i, append_len = src - entity;
+
+ gg_append(dst, &len, entity, append_len);
+ for (i = 0; i < append_len; i++) {
+ if (encoding != GG_ENCODING_UTF8 || (entity[i] & 0xc0) != 0x80) {
+ if (first) {
+ gg_after_append_formatted_char(&pos, attr_flag, &old_attr_flag, color, old_color, imgs_size, &format, format_len);
+ first = 0;
+ } else {
+ pos++;
+ }
+ }
+ }
+
in_entity = 0;
- gg_append(dst, &len, entity, src - entity);
}
if (*src == '<') {
@@ -601,7 +700,138 @@
if (dst != NULL)
dst[len] = '\n';
len++;
+
+ gg_after_append_formatted_char(&pos, attr_flag, &old_attr_flag, color, old_color, imgs_size, &format, format_len);
+ } else if (strncmp(tag, "
'9') && (tolower(tag[i]) < 'a' || tolower(tag[i]) > 'f')) {
+ ok = 0;
+ break;
+ }
+ }
+
+ if (ok) {
+ unsigned char img_attr[13];
+
+ if (format != NULL) {
+ char buf[3] = { 0 };
+
+ img_attr[0] = (unsigned char) (pos & (uint16_t) 0x00ffU);
+ img_attr[1] = (unsigned char) ((pos & (uint16_t) 0xff00U) >> 8);
+ img_attr[2] = GG_FONT_IMAGE;
+ img_attr[3] = '\x09';
+ img_attr[4] = '\x01';
+ for (i = 0; i < 16; i += 2) {
+ buf[0] = tag[i];
+ buf[1] = tag[i + 1];
+ /* buf[2] to '\0' */
+ img_attr[12 - i / 2] = (unsigned char) strtoul(buf, NULL, 16);
+ }
+
+ memcpy(format, img_attr, sizeof(img_attr));
+ format += sizeof(img_attr);
+ }
+
+ if (format_len != NULL)
+ *format_len += sizeof(img_attr);
+ imgs_size += sizeof(img_attr);
+
+ if (dst != NULL) {
+ if (encoding == GG_ENCODING_UTF8)
+ dst[len++] = '\xc2';
+ dst[len++] = '\xa0';
+ } else {
+ len += 2;
+ }
+
+ /* Nie używamy tutaj gg_after_append_formatted_char(). Po pierwsze to praktycznie niczego
+ * by nie zmieniło, a po drugie nie wszystkim klientom mogłaby się spodobać redefinicja
+ * atrybutów formatowania dla jednego znaku (bo np. najpierw byśmy zdefiniowali bolda od
+ * znaku 10, a potem by się okazało, że znak 10 to obrazek).
+ */
+
+ pos++;
+
+ /* Resetujemy atrybuty, aby je w razie czego redefiniować od następnego znaku, co by sobie
+ * nikt przypadkiem nie pomyślał, że GG_FONT_IMAGE dotyczy więcej, niż jednego znaku.
+ * Tak samo robi oryginalny klient.
+ */
+
+ old_attr_flag = -1;
+ }
+ }
+ } else if (strncmp(tag, "", 3) == 0) {
+ in_bold++;
+ attr_flag |= GG_FONT_BOLD;
+ } else if (strncmp(tag, "", 4) == 0) {
+ if (in_bold > 0) {
+ in_bold--;
+ if (in_bold == 0)
+ attr_flag &= ~GG_FONT_BOLD;
+ }
+ } else if (strncmp(tag, "", 3) == 0) {
+ in_italic++;
+ attr_flag |= GG_FONT_ITALIC;
+ } else if (strncmp(tag, "", 4) == 0) {
+ if (in_italic > 0) {
+ in_italic--;
+ if (in_italic == 0)
+ attr_flag &= ~GG_FONT_ITALIC;
+ }
+ } else if (strncmp(tag, "", 3) == 0) {
+ in_underline++;
+ attr_flag |= GG_FONT_UNDERLINE;
+ } else if (strncmp(tag, "", 4) == 0) {
+ if (in_underline > 0) {
+ in_underline--;
+ if (in_underline == 0)
+ attr_flag &= ~GG_FONT_UNDERLINE;
+ }
+ } else if (strncmp(tag, " src)
+ break;
+
+ for (i = 0; i < 6; i++) {
+ if ((tag[i] < '0' || tag[i] > '9') && (tolower(tag[i]) < 'a' || tolower(tag[i]) > 'f')) {
+ ok = 0;
+ break;
+ }
+ }
+
+ if (!ok)
+ break;
+
+ for (i = 0; i < 6; i += 2) {
+ buf[0] = tag[i];
+ buf[1] = tag[i + 1];
+ /* buf[2] to '\0' */
+ color[i / 2] = (unsigned char) strtoul(buf, NULL, 16);
+ }
+
+ attr_flag |= GG_FONT_COLOR;
+ }
+ }
+ }
+ } else if (strncmp(tag, "", 6);
+
+ for (i = 0; i < (size_t)text_len; i++)
+ {
+ char c = text[i];
+ if (c == '<')
+ gg_append(dst, &dst_len, "<", 4);
+ else if (c == '>')
+ gg_append(dst, &dst_len, ">", 4);
+ else if (c == '&')
+ gg_append(dst, &dst_len, "&", 5);
+ else if (c == '"')
+ gg_append(dst, &dst_len, """, 6);
+ else if (c == '\'')
+ gg_append(dst, &dst_len, "'", 6);
+ else if (c == '\n')
+ gg_append(dst, &dst_len, "
", 4);
+ else if (c == '\r')
+ continue;
+ else if (c == '\xc2' && text[i + 1] == '\xa0') {
+ gg_append(dst, &dst_len, " ", 6);
+ i++;
+ } else {
+ if (dst)
+ dst[dst_len] = c;
+ dst_len++;
+ }
+ }
+
+ gg_append(dst, &dst_len, "", 7);
+
+ if (dst)
+ dst[dst_len] = '\0';
+
+ return dst_len;
+}
+
+char *gg_message_html_to_text_110(const char *html)
+{
+ size_t dst_len;
+ char *dst;
+
+ dst_len = gg_message_html_to_text_110_buff(NULL, html) + 1;
+ dst = malloc(dst_len);
+ if (!dst)
+ return NULL;
+ gg_message_html_to_text_110_buff(dst, html);
+
+ return dst;
+}
+
+char *gg_message_text_to_html_110(const char *text, ssize_t text_len)
+{
+ size_t dst_len;
+ char *dst;
+
+ dst_len = gg_message_text_to_html_110_buff(NULL, text, text_len) + 1;
+ dst = malloc(dst_len);
+ if (!dst)
+ return NULL;
+ gg_message_text_to_html_110_buff(dst, text, text_len);
+
+ return dst;
+}
+