diff --git a/telegram-adium/TelegramAccount.m b/telegram-adium/TelegramAccount.m index ec9a230..e5afd3d 100644 --- a/telegram-adium/TelegramAccount.m +++ b/telegram-adium/TelegramAccount.m @@ -22,6 +22,7 @@ #import #import #import +#import #import #include "telegram-purple.h" @@ -182,6 +183,24 @@ [super cancelFileTransfer:fileTransfer]; } +- (NSString *)encodedAttributedString:(NSAttributedString *)inAttributedString forListObject:(AIListObject *)inListObject +{ + static AIHTMLDecoder *htmlEncoder = nil; + if (!htmlEncoder) { + htmlEncoder = [[AIHTMLDecoder alloc] init]; + [htmlEncoder setIncludesHeaders:NO]; + [htmlEncoder setIncludesFontTags:NO]; + [htmlEncoder setClosesFontTags:NO]; + [htmlEncoder setIncludesStyleTags:YES]; + [htmlEncoder setIncludesColorTags:NO]; + [htmlEncoder setEncodesNonASCII:NO]; + [htmlEncoder setPreservesAllSpaces:NO]; + [htmlEncoder setUsesAttachmentTextEquivalents:YES]; + } + + return [htmlEncoder encodeHTML:inAttributedString imagesPath:nil]; +} + #pragma mark Group Chats /*! diff --git a/telegram-purple.c b/telegram-purple.c index b795d4d..07c5d88 100644 --- a/telegram-purple.c +++ b/telegram-purple.c @@ -607,34 +607,36 @@ static int tgprpl_send_im (PurpleConnection *gc, const char *who, const char *me return 1; } - // Make sure that to only send messages to an existing peer by searching it in the peer tree, to give immediate - // feedback by returning an error-code in case the peer doesn't exist. + // check receiver to give immediate feedback in case sending a message is not possible tgl_peer_t *peer = tgp_blist_lookup_peer_get (gc_get_tls (gc), who); - if (peer) { - // give a proper error message when attempting to send to a secret chat that is not usable - if (tgl_get_peer_type (peer->id) == TGL_PEER_ENCR_CHAT && peer->encr_chat.state != sc_ok) { - const char *msg; - if (peer->encr_chat.state == sc_deleted) { - msg = _("Secret chat was already deleted"); - } else { - msg = _("Secret chat is not ready"); - } - tgp_msg_special_out (gc_get_tls (gc), msg, peer->id, PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_ERROR); - return -1; - } + if (! peer) { + warning ("peer not found"); + return -1; + } - // give a proper error message when attempting to send to a secret chat you don't own - if (tgl_get_peer_type (peer->id) == TGL_PEER_CHANNEL && ! (peer->flags & TGLCHF_CREATOR)) { - tgp_msg_special_out (gc_get_tls (gc), _("Only the creator of a channel can post messages."), peer->id, - PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_ERROR); - return -1; + // secret chat not yet usable + if (tgl_get_peer_type (peer->id) == TGL_PEER_ENCR_CHAT && peer->encr_chat.state != sc_ok) { + const char *msg; + if (peer->encr_chat.state == sc_deleted) { + msg = _("Secret chat was already deleted"); + } else { + msg = _("Secret chat is not ready"); } + tgp_msg_special_out (gc_get_tls (gc), msg, peer->id, PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_ERROR); + return -1; + } - return tgp_msg_send (gc_get_tls (gc), message, peer->id); + // channel owned by someone else (TEST: why does it work with supergroups?) + if (tgl_get_peer_type (peer->id) == TGL_PEER_CHANNEL && ! (peer->flags & TGLCHF_CREATOR)) { + tgp_msg_special_out (gc_get_tls (gc), _("Only the creator of a channel can post messages."), peer->id, + PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_ERROR); + return -1; } - warning ("peer not found"); - return -1; + // when the other peer receives a message it is obvious that the previous messages were read + pending_reads_send_user (gc_get_tls (gc), peer->id); + + return tgp_msg_send (gc_get_tls (gc), message, peer->id); } static unsigned int tgprpl_send_typing (PurpleConnection *gc, const char *who, PurpleTypingState typing) { diff --git a/tgp-chat.c b/tgp-chat.c index 265c29e..de7d5a5 100644 --- a/tgp-chat.c +++ b/tgp-chat.c @@ -209,6 +209,9 @@ int tgprpl_send_chat (PurpleConnection *gc, int id, const char *message, PurpleM } g_return_val_if_fail(P != NULL, -1); + // when the group receives a message it is obvious that the previous messages were read + pending_reads_send_user (gc_get_tls (gc), P->id); + return tgp_msg_send (gc_get_tls (gc), message, P->id); } diff --git a/tgp-msg.c b/tgp-msg.c index 276debb..d9204be 100644 --- a/tgp-msg.c +++ b/tgp-msg.c @@ -220,34 +220,14 @@ static gboolean tgp_msg_send_schedule_cb (gpointer data) { return FALSE; } -static void tgp_msg_send_schedule (struct tgl_state *TLS, gchar *chunk, tgl_peer_id_t to) { - g_queue_push_tail (tls_get_data (TLS)->out_messages, tgp_msg_sending_init (TLS, chunk, to)); +static void tgp_msg_send_schedule (struct tgl_state *TLS, const char *chunk, tgl_peer_id_t to) { + g_queue_push_tail (tls_get_data (TLS)->out_messages, tgp_msg_sending_init (TLS, g_strdup (chunk), to)); if (tls_get_data (TLS)->out_timer) { purple_timeout_remove (tls_get_data (TLS)->out_timer); } tls_get_data (TLS)->out_timer = purple_timeout_add (0, tgp_msg_send_schedule_cb, tls_get_data (TLS)); } -static int tgp_msg_send_split (struct tgl_state *TLS, const char *message, tgl_peer_id_t to) { - int size = (int)g_utf8_strlen (message, -1), start = 0; - - if (size > TGP_MAX_MSG_SIZE * TGP_DEFAULT_MAX_MSG_SPLIT_COUNT) { - return -E2BIG; - } - - while (size > start) { - int end = start + (int)TGP_MAX_MSG_SIZE; - if (end > size) { - end = size; - } - gchar *chunk = g_utf8_substring (message, start, end); - tgp_msg_send_schedule (TLS, chunk, to); - start = end; - } - - return 1; -} - void tgp_msg_special_out (struct tgl_state *TLS, const char *msg, tgl_peer_id_t to_id, int flags) { if (tgl_get_peer_type (to_id) == TGL_PEER_CHAT) { tgp_chat_got_in (TLS, tgl_peer_get (TLS, to_id), to_id, msg, flags, time(0)); @@ -271,90 +251,132 @@ void send_inline_picture_done (struct tgl_state *TLS, void *extra, int success, char *errormsg = g_strdup_printf ("%d: %s", TLS->error_code, TLS->error); failure (errormsg); purple_notify_message (_telegram_protocol, PURPLE_NOTIFY_MSG_ERROR, _("Sending image failed."), - errormsg, NULL, NULL, NULL); + errormsg, NULL, NULL, NULL); g_free (errormsg); return; } } -int tgp_msg_send (struct tgl_state *TLS, const char *message, tgl_peer_id_t to) { +static GList *tgp_msg_imgs_parse (const char *msg) { + GList *imgs = NULL; -#ifndef __ADIUM_ - // search for outgoing embedded image tags and send them - gchar *img = NULL; - gchar *stripped = NULL; - debug ("tgp_msg_send='%s'", message); - - if ((img = g_strrstr (message, " 0) { - PurpleStoredImage *psi = purple_imgstore_find_by_id (imgid); - if (! psi) { - failure ("Img %d not found in imgstore", imgid); - return -1; - } - gchar *tmp = g_build_filename (g_get_tmp_dir(), purple_imgstore_get_filename (psi), NULL) ; - GError *err = NULL; - gconstpointer data = purple_imgstore_get_data (psi); - g_file_set_contents (tmp, data, purple_imgstore_get_size (psi), &err); - if (! err) { + int i; + int len = (int) strlen (msg); + for (i = 0; i < len; i ++) { + if (len - i >= 4 && (! memcmp (msg + i, " 0) { + PurpleStoredImage *psi = purple_imgstore_find_by_id (img); + if (psi) { + imgs = g_list_append (imgs, psi); + } else { + g_warn_if_reached(); } - - stripped = g_strstrip(purple_markup_strip_html (message)); - tgl_do_send_document (TLS, to, tmp, stripped, (int)strlen (stripped), flags, send_inline_picture_done, NULL); - g_free (stripped); - - // return 0 to assure that the picture is not echoed, since - // it will already be echoed with the outgoing message - return 0; - } else { - failure ("Storing %s in imagestore failed: %s\n", tmp, err->message); - g_error_free (err); - return -1; } + } else { + g_warn_if_reached(); } + + i = e; + } + } + return imgs; +} + +static char *tgp_msg_markdown_convert (const char *msg) { + int len = (int) strlen (msg); + char *html = g_new0(gchar, 3 * len); + + int open = FALSE; + int i, j; + for (i = 0, j = 0; i < len; i ++) { + + // markdown for bold and italic doesn't seem to work with non-bots, + // therefore only parse code-tags + if (len - i < 3 || (memcmp (msg + i, "```", 3))) { + html[j ++] = msg[i]; + } else { + i += 2; + if (! open) { + assert(j + 6 < 3 * len); + memcpy(html + j, "", 6); + j += 6; + } else { + assert(j + 7 < 3 * len); + memcpy(html + j, "", 7); + j += 7; + } + open = ! open; } - // no image id found in image - return -1; } - /* - Adium won't escape any HTML markup and just pass any user-input through, - while Pidgin will replace special chars with the escape chars and also add - additional markup for RTL languages and such. + html[j] = 0; + return html; +} - First, we remove any HTML markup added by Pidgin, since Telegram won't handle it properly. - User-entered HTML is still escaped and therefore won't be harmed. - */ - stripped = purple_markup_strip_html (message); +int tgp_msg_send (struct tgl_state *TLS, const char *message, tgl_peer_id_t to) { + + // send all inline images + GList *imgs = tgp_msg_imgs_parse (message); + debug ("parsed %d images in messages", g_list_length (imgs)); + while (imgs) { + PurpleStoredImage *psi = imgs->data; + gchar *tmp = g_build_filename (g_get_tmp_dir(), purple_imgstore_get_filename (psi), NULL) ; + GError *err = NULL; + gconstpointer data = purple_imgstore_get_data (psi); + g_file_set_contents (tmp, data, purple_imgstore_get_size (psi), &err); + if (! err) { + debug ("sending img='%s'", tmp); + tgl_do_send_document (TLS, to, tmp, NULL, 0, + TGL_SEND_MSG_FLAG_DOCUMENT_AUTO | (tgl_get_peer_type (to) == TGL_PEER_CHANNEL) ? TGLMF_POST_AS_CHANNEL : 0, + send_inline_picture_done, NULL); + } else { + failure ("error=%s", err->message); + g_warn_if_reached(); + } + imgs = g_list_next(imgs); + } - // now unescape the markup, so that html special chars will still show - // up properly in Telegram - gchar *unescaped = purple_unescape_text (stripped); - int ret = tgp_msg_send_split (TLS, stripped, to); + // replace markdown with html + char *html = g_strstrip(tgp_msg_markdown_convert (message)); - g_free (unescaped); - g_free (stripped); - return ret; -#endif + // check message length + int size = (int) g_utf8_strlen (html, -1); + if (size == 0) { + g_free (html); + return 0; // fail quietly on empty messages + } + if (size > TGP_MAX_MSG_SIZE * TGP_DEFAULT_MAX_MSG_SPLIT_COUNT) { + g_free (html); + return -E2BIG; + } + + // send big message as multiple chunks + int start = 0; + while (size > start) { + int end = start + (int)TGP_MAX_MSG_SIZE; + if (end > size) { + end = size; + } + char *chunk = g_utf8_substring (html, start, end); + tgp_msg_send_schedule (TLS, chunk, to); + start = end; + } - // when the other peer receives a message it is obvious that the previous messages were read - pending_reads_send_user (TLS, to); + g_free (html); - return tgp_msg_send_split (TLS, message, to); + // return 0 to assure that the picture is not echoed, since + // it will already be echoed with the outgoing message + // FIXME: Eventually never display outgoing messages? What about Adium??? + return 1; } static char *tgp_msg_photo_display (struct tgl_state *TLS, const char *filename, int *flags) { diff --git a/tgp-structs.c b/tgp-structs.c index 2a41479..f80dbf6 100644 --- a/tgp-structs.c +++ b/tgp-structs.c @@ -79,7 +79,7 @@ struct tgp_msg_loading *tgp_msg_loading_init (struct tgl_message *M) { return C; } -struct tgp_msg_sending *tgp_msg_sending_init (struct tgl_state *TLS, gchar *M, tgl_peer_id_t to) { +struct tgp_msg_sending *tgp_msg_sending_init (struct tgl_state *TLS, char *M, tgl_peer_id_t to) { struct tgp_msg_sending *C = malloc (sizeof (struct tgp_msg_sending)); C->TLS = TLS; C->msg = M; @@ -89,7 +89,9 @@ struct tgp_msg_sending *tgp_msg_sending_init (struct tgl_state *TLS, gchar *M, t void tgp_msg_sending_free (gpointer data) { struct tgp_msg_sending *C = data; - g_free (C->msg); + if (C->msg) { + g_free (C->msg); + } free (C); }