2
* (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU Lesser General Public License Version
6
* 2.1 as published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public
14
* License along with this program; if not, write to the Free Software
15
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
22
* \brief Obsługa wiadomości
24
* Plik zawiera funkcje dotyczące obsługi "klasy" gg_message_t, które
25
* w przyszłości zostaną dołączone do API. Obecnie używane są funkcje
26
* konwersji między tekstem z atrybutami i HTML.
39
gg_message_t *gg_message_new(void)
43
gm = malloc(sizeof(gg_message_t));
48
memset(gm, 0, sizeof(gg_message_t));
50
gm->msgclass = GG_CLASS_CHAT;
51
gm->seq = (uint32_t) -1;
56
int gg_message_init(gg_message_t *gm, int msgclass, int seq, uin_t *recipients, size_t recipient_count, char *text, char *html, char *attributes, size_t attributes_length, int auto_convert)
58
GG_MESSAGE_CHECK(gm, -1);
60
memset(gm, 0, sizeof(gg_message_t));
61
gm->recipients = recipients;
62
gm->recipient_count = recipient_count;
65
gm->attributes = attributes;
66
gm->attributes_length = attributes_length;
67
gm->msgclass = msgclass;
69
gm->auto_convert = auto_convert;
74
void gg_message_free(gg_message_t *gm)
82
free(gm->text_converted);
84
free(gm->html_converted);
91
int gg_message_set_auto_convert(gg_message_t *gm, int auto_convert)
93
GG_MESSAGE_CHECK(gm, -1);
95
gm->auto_convert = !!auto_convert;
97
if (!gm->auto_convert) {
98
free(gm->text_converted);
99
free(gm->html_converted);
100
gm->text_converted = NULL;
101
gm->html_converted = NULL;
107
int gg_message_get_auto_convert(gg_message_t *gm)
109
GG_MESSAGE_CHECK(gm, -1);
111
return gm->auto_convert;
114
int gg_message_set_recipients(gg_message_t *gm, const uin_t *recipients, size_t recipient_count)
116
GG_MESSAGE_CHECK(gm, -1);
118
if (recipient_count >= INT_MAX / sizeof(uin_t)) {
123
if ((recipients == NULL) || (recipient_count == 0)) {
124
free(gm->recipients);
125
gm->recipients = NULL;
126
gm->recipient_count = 0;
130
tmp = realloc(gm->recipients, recipient_count * sizeof(uin_t));
135
memcpy(tmp, recipients, recipient_count * sizeof(uin_t));
137
gm->recipients = tmp;
138
gm->recipient_count = recipient_count;
144
int gg_message_set_recipient(gg_message_t *gm, uin_t recipient)
146
return gg_message_set_recipients(gm, &recipient, 1);
149
int gg_message_get_recipients(gg_message_t *gm, const uin_t **recipients, size_t *recipient_count)
151
GG_MESSAGE_CHECK(gm, -1);
153
if (recipients != NULL)
154
*recipients = gm->recipients;
156
if (recipient_count != NULL)
157
*recipient_count = gm->recipient_count;
162
uin_t gg_message_get_recipient(gg_message_t *gm)
164
GG_MESSAGE_CHECK(gm, (uin_t) -1);
166
if ((gm->recipients == NULL) || (gm->recipient_count < 1)) {
171
return gm->recipients[0];
174
int gg_message_set_class(gg_message_t *gm, uint32_t msgclass)
176
GG_MESSAGE_CHECK(gm, -1);
178
gm->msgclass = msgclass;
183
uint32_t gg_message_get_class(gg_message_t *gm)
185
GG_MESSAGE_CHECK(gm, (uint32_t) -1);
190
int gg_message_set_seq(gg_message_t *gm, uint32_t seq)
192
GG_MESSAGE_CHECK(gm, -1);
199
uint32_t gg_message_get_seq(gg_message_t *gm)
201
GG_MESSAGE_CHECK(gm, (uint32_t) -1);
206
int gg_message_set_text(gg_message_t *gm, const char *text)
208
GG_MESSAGE_CHECK(gm, -1);
225
free(gm->html_converted);
226
gm->html_converted = NULL;
231
const char *gg_message_get_text(gg_message_t *gm)
233
GG_MESSAGE_CHECK(gm, NULL);
235
if (gm->text_converted != NULL)
236
return gm->text_converted;
238
if (gm->text == NULL && gm->html != NULL && gm->auto_convert) {
241
free(gm->text_converted);
243
len = gg_message_html_to_text(NULL, gm->html);
245
gm->text_converted = malloc(len + 1);
247
if (gm->text_converted == NULL)
250
gg_message_html_to_text(gm->text_converted, gm->html);
252
return gm->text_converted;
258
int gg_message_set_html(gg_message_t *gm, const char *html)
260
GG_MESSAGE_CHECK(gm, -1);
277
free(gm->text_converted);
278
gm->text_converted = NULL;
283
const char *gg_message_get_html(gg_message_t *gm)
285
GG_MESSAGE_CHECK(gm, NULL);
287
if (gm->html_converted != NULL)
288
return gm->html_converted;
290
if (gm->html == NULL && gm->text != NULL && gm->auto_convert) {
293
free(gm->html_converted);
295
len = gg_message_text_to_html(NULL, gm->text, gm->attributes, gm->attributes_length);
297
gm->html_converted = malloc(len + 1);
299
if (gm->html_converted == NULL)
302
gg_message_text_to_html(gm->html_converted, gm->text, gm->attributes, gm->attributes_length);
304
return gm->html_converted;
310
int gg_message_set_attributes(gg_message_t *gm, const char *attributes, size_t length)
312
GG_MESSAGE_CHECK(gm, -1);
314
if (length > 0xfffd) {
319
if ((attributes == NULL) || (length == 0)) {
320
free(gm->attributes);
321
gm->attributes = NULL;
322
gm->attributes_length = 0;
326
tmp = realloc(gm->attributes, length);
331
gm->attributes = tmp;
332
gm->attributes_length = length;
335
free(gm->html_converted);
336
gm->html_converted = NULL;
341
int gg_message_get_attributes(gg_message_t *gm, const char **attributes, size_t *attributes_length)
343
GG_MESSAGE_CHECK(gm, -1);
345
if (attributes != NULL)
346
*attributes = gm->attributes;
348
if (attributes_length != NULL)
349
*attributes_length = gm->attributes_length;
357
* \internal Dodaje tekst na koniec bufora.
359
* \param dst Wskaźnik na bufor roboczy
360
* \param pos Wskaźnik na aktualne położenie w buforze roboczym
361
* \param src Dodawany tekst
362
* \param len Długość dodawanego tekstu
364
static void gg_append(char *dst, size_t *pos, const void *src, int len)
367
memcpy(&dst[*pos], src, len);
373
* \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML.
375
* \param dst Bufor wynikowy (może być \c NULL)
376
* \param src Tekst źródłowy w UTF-8
377
* \param format Atrybuty tekstu źródłowego
378
* \param format_len Długość bloku atrybutów tekstu źródłowego
380
* \note Wynikowy tekst nie jest idealnym kodem HTML, ponieważ ma jak
381
* dokładniej odzwierciedlać to, co wygenerowałby oryginalny klient.
383
* \note Dokleja \c \\0 na końcu bufora wynikowego.
385
* \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
387
size_t gg_message_text_to_html(char *dst, const char *src, const char *format, size_t format_len)
389
const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">";
390
const int span_len = 75;
391
const char img_fmt[] = "<img name=\"%02x%02x%02x%02x%02x%02x%02x%02x\">";
392
const int img_len = 29;
395
unsigned char old_attr = 0;
396
const unsigned char *color = (const unsigned char*) "\x00\x00\x00";
399
const unsigned char *format_ = (const unsigned char*) format;
403
/* Nie mamy atrybutów dla pierwsze znaku, a tekst nie jest pusty, więc
404
* tak czy inaczej trzeba otworzyć <span>. */
406
if (src[0] != 0 && (format_idx + 3 > format_len || (format_[format_idx] | (format_[format_idx + 1] << 8)) != 0)) {
408
sprintf(&dst[len], span_fmt, 0, 0, 0);
413
/* Pętla przechodzi też przez kończące \0, żeby móc dokleić obrazek
414
* na końcu tekstu. */
417
/* Analizuj atrybuty tak długo jak dotyczą aktualnego znaku. */
422
if (format_idx + 3 > format_len)
425
attr_pos = format_[format_idx] | (format_[format_idx + 1] << 8);
427
if (attr_pos != char_pos)
430
attr = format_[format_idx + 2];
432
/* Nie doklejaj atrybutów na końcu, co najwyżej obrazki. */
435
attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR);
439
if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0 || (attr == 0 && old_attr != 0)) {
441
if ((old_attr & GG_FONT_UNDERLINE) != 0)
442
gg_append(dst, &len, "</u>", 4);
444
if ((old_attr & GG_FONT_ITALIC) != 0)
445
gg_append(dst, &len, "</i>", 4);
447
if ((old_attr & GG_FONT_BOLD) != 0)
448
gg_append(dst, &len, "</b>", 4);
451
gg_append(dst, &len, "</span>", 7);
454
if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) {
455
color = &format_[format_idx];
458
color = (unsigned char*) "\x00\x00\x00";
463
sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
466
} else if (char_pos == 0 && src[0] != 0) {
468
sprintf(&dst[len], span_fmt, 0, 0, 0);
472
if ((attr & GG_FONT_BOLD) != 0)
473
gg_append(dst, &len, "<b>", 3);
475
if ((attr & GG_FONT_ITALIC) != 0)
476
gg_append(dst, &len, "<i>", 3);
478
if ((attr & GG_FONT_UNDERLINE) != 0)
479
gg_append(dst, &len, "<u>", 3);
481
if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) {
483
sprintf(&dst[len], img_fmt,
484
format_[format_idx + 9],
485
format_[format_idx + 8],
486
format_[format_idx + 7],
487
format_[format_idx + 6],
488
format_[format_idx + 5],
489
format_[format_idx + 4],
490
format_[format_idx + 3],
491
format_[format_idx + 2]);
501
/* Doklej znak zachowując htmlowe escapowanie. */
505
gg_append(dst, &len, "&", 5);
508
gg_append(dst, &len, "<", 4);
511
gg_append(dst, &len, ">", 4);
514
gg_append(dst, &len, "'", 6);
517
gg_append(dst, &len, """, 6);
520
gg_append(dst, &len, "<br>", 4);
531
/* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */
533
if ((src[i] & 0xc0) != 0xc0)
542
if ((old_attr & GG_FONT_UNDERLINE) != 0)
543
gg_append(dst, &len, "</u>", 4);
545
if ((old_attr & GG_FONT_ITALIC) != 0)
546
gg_append(dst, &len, "</i>", 4);
548
if ((old_attr & GG_FONT_BOLD) != 0)
549
gg_append(dst, &len, "</b>", 4);
552
gg_append(dst, &len, "</span>", 7);
561
* \internal Zamienia tekst w formacie HTML na czysty tekst.
563
* \param dst Bufor wynikowy (może być \c NULL)
564
* \param html Tekst źródłowy
566
* \note Dokleja \c \\0 na końcu bufora wynikowego.
568
* \note Funkcja służy do zachowania kompatybilności przy przesyłaniu
569
* wiadomości HTML do klientów, które tego formatu nie obsługują. Z tego
570
* powodu funkcja nie zachowuje formatowania, a jedynie usuwa tagi i
571
* zamienia podstawowe encje na ich odpowiedniki ASCII.
573
* \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
575
size_t gg_message_html_to_text(char *dst, const char *html)
577
const char *src, *entity, *tag;
578
int in_tag, in_entity;
587
for (src = html; *src != 0; src++) {
588
if (in_entity && !(isalnum(*src) || *src == '#' || *src == ';')) {
590
gg_append(dst, &len, entity, src - entity);
599
if (in_tag && (*src == '>')) {
600
if (strncmp(tag, "<br", 3) == 0) {
618
if (in_entity && *src == ';') {
621
if (strncmp(entity, "<", 4) == 0)
623
else if (strncmp(entity, ">", 4) == 0)
625
else if (strncmp(entity, """, 6) == 0)
627
else if (strncmp(entity, "'", 6) == 0)
629
else if (strncmp(entity, "&", 5) == 0)
631
else if (strncmp(entity, " ", 6) == 0) {
637
if (strncmp(entity, " ", 6) == 0)
646
if (in_entity && !(isalnum(*src) || *src == '#'))