/* IEView Plugin for Miranda IM Copyright (C) 2005-2010 Piotr Piastucki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "ieview_common.h" TextToken::TextToken(int type, const char *text, int len) { next = NULL; tag = 0; end = false; this->type = type; this->text = mir_strndup(text, len); this->wtext = mir_a2t(this->text); this->link = NULL; this->wlink = NULL; } TextToken::TextToken(int type, const wchar_t *wtext, int len) { next = NULL; tag = 0; end = false; this->type = type; this->wtext = mir_tstrndup(wtext, len); this->text = mir_t2a(this->wtext); this->link = NULL; this->wlink = NULL; } TextToken::~TextToken() { if (text!=NULL) { mir_free(text); } if (wtext!=NULL) { mir_free(wtext); } if (link!=NULL) { mir_free(link); } if (wlink!=NULL) { mir_free(wlink); } } TextToken * TextToken::getNext() { return next; } void TextToken::setNext(TextToken *ptr) { next = ptr; } int TextToken::getType() { return type; } const char *TextToken::getText() { return text; } const wchar_t *TextToken::getTextW() { return wtext; } int TextToken::getTag() { return tag; } void TextToken::setTag(int tag) { this->tag = tag; } bool TextToken::isEnd() { return end; } void TextToken::setEnd(bool b) { this->end = b; } const char *TextToken::getLink() { return link; } const wchar_t *TextToken::getLinkW() { return wlink; } void TextToken::setLink(const char *link) { if (this->link != NULL) { delete this->link; } if (this->wlink != NULL) { delete this->wlink; } this->link = mir_strdup(link); this->wlink = mir_a2t(link); } void TextToken::setLink(const wchar_t *link) { if (this->link != NULL) { delete this->link; } if (this->wlink != NULL) { delete this->wlink; } this->link = mir_t2a(link); this->wlink = mir_tstrdup(link); } static int countNoWhitespace(const wchar_t *str) { int c; for (c=0; *str!='\n' && *str!='\r' && *str!='\t' && *str!=' ' && *str!='\0'; str++, c++); return c; } TextToken* TextToken::tokenizeBBCodes(const wchar_t *text) { return tokenizeBBCodes(text, (int)wcslen(text)); } TextToken* TextToken::tokenizeMath(const wchar_t *text) { TextToken *firstToken = NULL, *lastToken = NULL, *mathToken = NULL; static bool mathModInitialized = false; static wchar_t *mathTagName[] = {NULL, NULL}; static int mathTagLen[] = {0, 0}; int i; if (!mathModInitialized) { if (ServiceExists(MATH_GET_PARAMS)) { char* mthDelStart = (char *)CallService(MATH_GET_PARAMS, (WPARAM)MATH_PARAM_STARTDELIMITER, 0); char* mthDelEnd = (char *)CallService(MATH_GET_PARAMS, (WPARAM)MATH_PARAM_ENDDELIMITER, 0); if (mthDelStart!=NULL) { mathTagName[0] = mir_a2t(mthDelStart); mathTagLen[0] = (int)wcslen(mathTagName[0]); } if (mthDelEnd!=NULL) { mathTagName[1] = mir_a2t(mthDelEnd); mathTagLen[1] = (int)wcslen(mathTagName[1]); } CallService(MTH_FREE_MATH_BUFFER,0, (LPARAM) mthDelStart); CallService(MTH_FREE_MATH_BUFFER,0, (LPARAM) mthDelEnd); } mathModInitialized = true; } int textLen = 0; int l = (int)wcslen(text); for (i=0; i<=l;) { bool mathFound = false; int k = 0, tagDataStart=0, newTokenType = 0, newTokenSize = 0; if (mathTagName[0] != NULL && mathTagName[1] != NULL) { if (!wcsnicmp(text+i, mathTagName[0], mathTagLen[0])) { k = tagDataStart = i + mathTagLen[0]; for (; k < l; k++) { if (!wcsnicmp(text+k, mathTagName[1], mathTagLen[1])) { k += mathTagLen[1]; mathFound = true; break; } } } } if (mathFound) { mathToken = new TextToken(MATH, text + tagDataStart, k - mathTagLen[1] - tagDataStart); char* mathPath=(char*)CallService(MTH_GET_GIF_UNICODE, 0, (LPARAM) mathToken->getTextW()); if (mathPath!=NULL) { mathToken->setLink(mathPath); CallService(MTH_FREE_GIFPATH, 0, (LPARAM) mathPath); } else { mathToken->setLink(""); } mathToken->setEnd(false); newTokenType = MATH; newTokenSize = k - i; } else { if (i==l) { newTokenType = END; newTokenSize = 1; } else { newTokenType = TEXT; newTokenSize = 1; } } if (newTokenType != TEXT) { if (textLen >0 ) { TextToken *newToken = new TextToken(TEXT, text+i-textLen, textLen); textLen = 0; if (lastToken == NULL) { firstToken = newToken; } else { lastToken->setNext(newToken); } lastToken = newToken; } if (newTokenType == MATH) { if (lastToken == NULL) { firstToken = mathToken; } else { lastToken->setNext(mathToken); } lastToken = mathToken; } } else { textLen += newTokenSize; } i += newTokenSize; } return firstToken; } // TODO: Add the following BBCodes: code #define BB_TAG_NUM 10 TextToken* TextToken::tokenizeBBCodes(const wchar_t *text, int l) { static const wchar_t *bbTagName[] = {L"b", L"i", L"u", L"s", L"img", L"color", L"size", L"bimg", L"url", L"code"}; static int bbTagNameLen[] = {1, 1, 1, 1, 3, 5, 4, 4, 3, 4}; static int bbTagArg[] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0}; static int bbTagId[] = {BB_B, BB_I, BB_U, BB_S, BB_IMG, BB_COLOR, BB_SIZE, BB_BIMG, BB_URL, BB_CODE}; static int bbTagEnd[BB_TAG_NUM]; static int bbTagCount[BB_TAG_NUM]; int i,j; TextToken *firstToken = NULL, *lastToken = NULL, * bbToken = NULL; int textLen = 0; for (j = 0; j < BB_TAG_NUM; j++) { bbTagCount[j] = 0; bbTagEnd[j] = 0; } for (i = 0; i <= l;) { int k, tagArgStart=0, tagArgEnd=0, tagDataStart=0, newTokenType = 0, newTokenSize = 0; bool bbFound = false; if (text[i] == '[') { if (text[i+1] != '/') { for (j = 0; j < BB_TAG_NUM; j++) { k = i + 1; if (!wcsnicmp(text+k, bbTagName[j], bbTagNameLen[j])) { tagArgStart = tagArgEnd = 0; k += bbTagNameLen[j]; if (bbTagArg[j]) { if (text[k] != '=') continue; k++; tagArgStart = k; for (; text[k]!='\0'; k++) { if (text[k]==']') break; } tagArgEnd = k; } if (text[k] == ']') { k++; tagDataStart = k; if (k < bbTagEnd[j]) k = bbTagEnd[j]; for (; k < l; k++) { if (text[k] == '[' && text[k+1] == '/') { k += 2; if (!wcsnicmp(text+k, bbTagName[j], bbTagNameLen[j])) { k += bbTagNameLen[j]; if (text[k] == ']') { k++; bbFound = true; break; } } } } if (bbFound) break; } } } if (bbFound) { bbTagEnd[j] = k; switch (bbTagId[j]) { case BB_B: case BB_I: case BB_U: case BB_S: case BB_CODE: case BB_COLOR: case BB_SIZE: bbTagCount[j]++; if (bbTagArg[j]) { bbToken = new TextToken(BBCODE, text + tagArgStart, tagArgEnd - tagArgStart); } else { bbToken = new TextToken(BBCODE, bbTagName[j], bbTagNameLen[j]); } bbToken->setTag(bbTagId[j]); bbToken->setEnd(false); newTokenType = BBCODE; newTokenSize = tagDataStart - i; break; case BB_URL: case BB_IMG: case BB_BIMG: bbToken = new TextToken(BBCODE, text + tagDataStart, k - bbTagNameLen[j] - 3 - tagDataStart); bbToken->setTag(bbTagId[j]); bbToken->setEnd(false); newTokenType = BBCODE; newTokenSize = k - i; if (bbTagArg[j]) { wchar_t *urlLink = mir_tstrndup(text + tagArgStart, tagArgEnd - tagArgStart); bbToken->setLink(urlLink); delete urlLink; } break; } } } else { for (j = 0; j < BB_TAG_NUM; j++) { k = i + 2; if (bbTagCount[j]>0 && !wcsnicmp(text+k, bbTagName[j], bbTagNameLen[j])) { k += bbTagNameLen[j]; if (text[k] == ']') { k++; bbFound = true; break; } } } if (bbFound) { bbTagCount[j]--; bbToken = new TextToken(BBCODE, bbTagName[j], bbTagNameLen[j]); bbToken->setEnd(true); bbToken->setTag(bbTagId[j]); newTokenType = BBCODE; newTokenSize = k - i; } } } if (!bbFound) { if (i==l) { newTokenType = END; newTokenSize = 1; } else { newTokenType = TEXT; newTokenSize = 1; } } if (newTokenType != TEXT) { if (textLen >0 ) { TextToken *newToken = new TextToken(TEXT, text+i-textLen, textLen); textLen = 0; if (lastToken == NULL) { firstToken = newToken; } else { lastToken->setNext(newToken); } lastToken = newToken; } if (newTokenType == BBCODE) { if (lastToken == NULL) { firstToken = bbToken; } else { lastToken->setNext(bbToken); } lastToken = bbToken; } } else { textLen += newTokenSize; } i += newTokenSize; } return firstToken; } TextToken* TextToken::tokenizeLinks(const wchar_t *text) { TextToken *firstToken = NULL, *lastToken = NULL; int textLen = 0; int l = (int)wcslen(text); for (int i=0; i<=l;) { int newTokenType, newTokenSize; int urlLen = Utils::detectURL(text+i); if (i == l) { newTokenType = END; newTokenSize = 1; } else if (urlLen > 0) { newTokenType = LINK; newTokenSize = urlLen; } else if (!wcsncmp(text+i, L"www.", 4)) { newTokenType = WWWLINK; newTokenSize = countNoWhitespace(text+i); } else if (!wcsncmp(text+i, L"mailto:", 7)) { newTokenType = LINK; newTokenSize = countNoWhitespace(text+i); } else { newTokenType = TEXT; newTokenSize = 1; } if (newTokenType != TEXT) { if (textLen >0 ) { TextToken *newToken = new TextToken(TEXT, text+i-textLen, textLen); textLen = 0; if (lastToken == NULL) { firstToken = newToken; } else { lastToken->setNext(newToken); } lastToken = newToken; } if (newTokenType == WWWLINK || newTokenType == LINK) { TextToken *newToken = new TextToken(newTokenType, text+i, newTokenSize); newToken->setLink(newToken->getText()); if (lastToken == NULL) { firstToken = newToken; } else { lastToken->setNext(newToken); } lastToken = newToken; } } else { textLen += newTokenSize; } i += newTokenSize; } return firstToken; } TextToken* TextToken::tokenizeSmileys(HANDLE hContact, const char *proto, const wchar_t *text, bool isSent) { TextToken *firstToken = NULL, *lastToken = NULL; SMADD_BATCHPARSE2 sp; SMADD_BATCHPARSERES *spRes; int l = (int)wcslen(text); if (!Options::isSmileyAdd()) { return new TextToken(TEXT, text, l); } sp.cbSize = sizeof(sp); sp.Protocolname = proto; sp.flag = SAFL_PATH | SAFL_UNICODE | (isSent ? SAFL_OUTGOING : 0); sp.wstr = (wchar_t *)text; sp.hContact = hContact; spRes = (SMADD_BATCHPARSERES *) CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp); int last_pos = 0; if (spRes != NULL) { for (int i = 0; i < (int)sp.numSmileys; i++) { if (spRes[i].filepath != NULL && strlen((char *)spRes[i].filepath) > 0) { if ((int)spRes[i].startChar - last_pos > 0) { TextToken *newToken = new TextToken(TEXT, text+last_pos, spRes[i].startChar-last_pos); if (lastToken == NULL) { firstToken = newToken; } else { lastToken->setNext(newToken); } lastToken = newToken; } TextToken *newToken = new TextToken(SMILEY, text+spRes[i].startChar, spRes[i].size); if (sp.oflag & SAFL_UNICODE) { newToken->setLink((wchar_t *)spRes[i].filepath); } else { newToken->setLink((char *)spRes[i].filepath); } if (lastToken == NULL) { firstToken = newToken; } else { lastToken->setNext(newToken); } lastToken = newToken; last_pos = spRes[i].startChar + spRes[i].size; } } CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spRes); } if (last_pos < l) { TextToken *newToken = new TextToken(TEXT, text+last_pos, l-last_pos); if (lastToken == NULL) { firstToken = newToken; } else { lastToken->setNext(newToken); } lastToken = newToken; } return firstToken; } TextToken* TextToken::tokenizeChatFormatting(const wchar_t *text) { TextToken *firstToken = NULL, *lastToken = NULL; int textLen = 0; int l = (int)wcslen(text); wchar_t* tokenBuffer = new wchar_t[l + 1]; for (int i=0; i<=l;) { int newTokenType = TEXT; int newTokenSize = 1; int newTokenTag = 0; int newTokenTextLen = 0; const wchar_t * newTokenText = NULL; bool endToken = false; if (i==l) { newTokenType = END; } else { if (text[i] == '%') { newTokenSize = 2; switch (text[i + 1]) { case '%': break; case 'B': endToken = true; case 'b': newTokenType = BBCODE; newTokenTag = BB_B; break; case 'U': endToken = true; case 'u': newTokenType = BBCODE; newTokenTag = BB_U; break; case 'I': endToken = true; case 'i': newTokenType = BBCODE; newTokenTag = BB_I; break; case 'C': endToken = true; case 'c': newTokenType = BBCODE; newTokenTag = BB_COLOR; if (!endToken) { newTokenText = text + i + 2; newTokenTextLen = 7; newTokenSize = 9; } break; case 'F': endToken = true; case 'f': newTokenType = BBCODE; newTokenTag = BB_BACKGROUND; if (!endToken) { newTokenText = text + i + 2; newTokenTextLen = 7; newTokenSize = 9; } break; default: newTokenSize = 1; } } } if (newTokenType != TEXT) { if (textLen >0 ) { TextToken *newToken = new TextToken(TEXT, tokenBuffer, textLen); textLen = 0; if (lastToken == NULL) { firstToken = newToken; } else { lastToken->setNext(newToken); } lastToken = newToken; } if (newTokenType == BBCODE) { TextToken *newToken = new TextToken(newTokenType, newTokenText, newTokenTextLen); newToken->setEnd(endToken); newToken->setTag(newTokenTag); if (lastToken == NULL) { firstToken = newToken; } else { lastToken->setNext(newToken); } lastToken = newToken; } } else { tokenBuffer[textLen] = text[i]; textLen++; } i += newTokenSize; } return firstToken; } wchar_t *TextToken::htmlEncode(const wchar_t *str) { wchar_t *out; const wchar_t *ptr; bool wasSpace; int c; c = 0; wasSpace = false; for (ptr=str; *ptr!='\0'; ptr++) { if (*ptr==' ' && wasSpace) { wasSpace = true; c += 6; } else { wasSpace = false; switch (*ptr) { case '\n': c += 4; break; case '\r': break; case '&': c += 5; break; case '>': c += 4; break; case '<': c += 4; break; case '"': c += 6; break; case ' ': wasSpace = true; default: c += 1; break; } } } wchar_t *output = new wchar_t[c+1]; wasSpace = false; for (out=output, ptr=str; *ptr!='\0'; ptr++) { if (*ptr==' ' && wasSpace) { wcscpy(out, L" "); out += 6; } else { wasSpace = false; switch (*ptr) { case '\n': wcscpy(out, L"
"); out += 4; break; case '\r': break; case '&': wcscpy(out, L"&"); out += 5; break; case '>': wcscpy(out, L">"); out += 4; break; case '<': wcscpy(out, L"<"); out += 4; break; case '"': wcscpy(out, L"""); out += 6; break; case ' ': wasSpace = true; default: *out = *ptr; out += 1; break; } } } *out = '\0'; return output; } void TextToken::toString(wchar_t **str, int *sizeAlloced) { wchar_t *eText = NULL, *eLink = NULL; switch (type) { case TEXT: eText = htmlEncode(wtext); Utils::appendText(str, sizeAlloced, L"%s", eText); break; case WWWLINK: case LINK: { eText = htmlEncode(wtext); eLink = htmlEncode(wlink); const wchar_t *linkPrefix = type == WWWLINK ? L"http://" : L""; if ((Options::getGeneralFlags()&Options::GENERAL_ENABLE_EMBED)) { wchar_t *match = wcsstr(wlink, L"youtube.com"); if (match != NULL) { match = wcsstr(match + 11, L"v="); if (match != NULL) { match += 2; wchar_t *match2 = wcsstr(match, L"&"); int len = match2 != NULL ? match2 - match : (int)wcslen(match); match = mir_wstrdup(match); match[len] = 0; int width ; int height; int Embedsize = Options::getEmbedsize(); switch (Embedsize){ case 0: width = 320; height = 205; break; case 1: width = 480; height = 385; break; case 2: width = 560; height = 349; break; case 3: width = 640; height = 390; break; }; Utils::appendText(str, sizeAlloced, L"
\ \ \ \ \
", width, height, match, match, width, height); mir_free(match); break; } } } Utils::appendText(str, sizeAlloced, L"%s", linkPrefix, eLink, eText); } break; case SMILEY: eText = htmlEncode(wtext); if ((Options::getGeneralFlags()&Options::GENERAL_ENABLE_FLASH) && (wcsstr(wlink, L".swf")!=NULL)) { Utils::appendText(str, sizeAlloced, L"\ %s", eText, wlink, eText); } else if ((Options::getGeneralFlags()&Options::GENERAL_ENABLE_PNGHACK) && (wcsstr(wlink, L".png")!=NULL)) { Utils::appendText(str, sizeAlloced, L"\"%s\"/%s", wlink, eText, eText, eText); } else { Utils::appendText(str, sizeAlloced, L"\"%s\"%s", wlink, eText, eText, eText); } break; case MATH: eText = htmlEncode(wtext); if ((Options::getGeneralFlags()&Options::GENERAL_ENABLE_PNGHACK) && (wcsstr(wlink, L".png")!=NULL)) { Utils::appendText(str, sizeAlloced, L"\"%s\"%s",wlink , eText, eText); } else { Utils::appendText(str, sizeAlloced, L"\"%s\"%s", wlink, eText, eText); } break; case BBCODE: if (!end) { switch (tag) { case BB_B: //Utils::appendText(str, sizeAlloced, L""); Utils::appendText(str, sizeAlloced, L""); break; case BB_I: //Utils::appendText(str, sizeAlloced, L""); Utils::appendText(str, sizeAlloced, L""); break; case BB_U: //Utils::appendText(str, sizeAlloced, L""); Utils::appendText(str, sizeAlloced, L""); break; case BB_S: //Utils::appendText(str, sizeAlloced, L""); Utils::appendText(str, sizeAlloced, L""); break; case BB_CODE: //Utils::appendText(str, sizeAlloced, L""); Utils::appendText(str, sizeAlloced, L"
");
					break;
				case BB_IMG:
					eText = htmlEncode(wtext);
					if ((Options::getGeneralFlags()&Options::GENERAL_ENABLE_FLASH) && (wcsstr(eText, L".swf")!=NULL)) {
						Utils::appendText(str, sizeAlloced,
		L"
\
", eText); } else if ((Options::getGeneralFlags()&Options::GENERAL_ENABLE_PNGHACK) && (wcsstr(eText, L".png")!=NULL)) { Utils::appendText(str, sizeAlloced, L"", eText); } else { if (wcsncmp(eText, L"http://", 7)) { Utils::appendText(str, sizeAlloced, L"
this.width ? 'auto' : maxw);\" src=\"file://%s\" />
", eText); } else { Utils::appendText(str, sizeAlloced, L"
this.width ? 'auto' : maxw);\" src=\"%s\" />
", eText); } } break; case BB_BIMG: { wchar_t *absolutePath = Utils::toAbsolute(wtext); eText = htmlEncode(absolutePath); delete absolutePath; } if ((Options::getGeneralFlags()&Options::GENERAL_ENABLE_FLASH) && (wcsstr(eText, L".swf")!=NULL)) { Utils::appendText(str, sizeAlloced, L"
\
", eText); } else if ((Options::getGeneralFlags()&Options::GENERAL_ENABLE_PNGHACK) && (wcsstr(eText, L".png")!=NULL)) { Utils::appendText(str, sizeAlloced, L"", eText); } else { Utils::appendText(str, sizeAlloced, L"
this.width ? 'auto' : maxw);\" src=\"file://%s\" />
", eText); } break; case BB_URL: eText = htmlEncode(wtext); eLink = htmlEncode(wlink); Utils::appendText(str, sizeAlloced, L"%s", eLink, eText); break; case BB_COLOR: eText = htmlEncode(wtext); //Utils::appendText(str, sizeAlloced, L"", eText); Utils::appendText(str, sizeAlloced, L"", eText); break; case BB_BACKGROUND: eText = htmlEncode(wtext); Utils::appendText(str, sizeAlloced, L"", eText); break; case BB_SIZE: eText = htmlEncode(wtext); Utils::appendText(str, sizeAlloced, L"", eText); break; } } else { switch (tag) { case BB_B: Utils::appendText(str, sizeAlloced, L"
"); break; case BB_I: Utils::appendText(str, sizeAlloced, L""); break; case BB_U: Utils::appendText(str, sizeAlloced, L""); break; case BB_S: Utils::appendText(str, sizeAlloced, L"
"); break; case BB_CODE: Utils::appendText(str, sizeAlloced, L"
"); break; case BB_COLOR: Utils::appendText(str, sizeAlloced, L""); break; case BB_SIZE: Utils::appendText(str, sizeAlloced, L"
"); break; case BB_BACKGROUND: Utils::appendText(str, sizeAlloced, L""); break; } } break; } if (eText!=NULL) delete eText; if (eLink!=NULL) delete eLink; }