/* Basic History plugin Copyright (C) 2011-2012 Krzysztof Kral 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 version 2 of the License. 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, see . */ #include "StdAfx.h" #include "RichHtmlExport.h" #include "Options.h" #include "resource.h" #define EXP_FILE (*stream) RichHtmlExport::~RichHtmlExport() { } extern HINSTANCE hInst; extern HANDLE *hEventIcons; extern HANDLE hPlusExIcon, hMinusExIcon; extern bool g_SmileyAddAvail; std::wstring MakeTextHtmled(const std::wstring& message, std::queue >* positionMap = NULL) { std::wstring ret; std::wstring search = _T("&<>\t\r\n"); size_t start = 0; size_t find; size_t currentAdd = 0; while((find = message.find_first_of(search, start)) < message.length()) { ret += message.substr(start, find - start); switch(message[find]) { case _T('&'): ret += _T("&"); break; case _T('<'): ret += _T("<"); break; case _T('>'): ret += _T(">"); break; case _T('\t'): ret += _T(" "); break; case _T('\n'): ret += _T("
"); break; } start = find + 1; if(positionMap != NULL) { size_t len = ret.length() - start - currentAdd; if(len != 0) { positionMap->push(std::pair(start + currentAdd, len)); currentAdd += len; } } } ret += message.substr(start, message.length() - start); return ret; } std::wstring UrlHighlightHtml(const std::wstring& message, bool& isUrl) { std::wstring ret; std::wstring htmlStop = _T("\'\" []<>\r\n"); std::wstring search = _T("://"); size_t start = 0; size_t find; while((find = message.find(search, start)) < message.length()) { size_t urlStart = message.find_last_of(htmlStop, find); size_t urlEnd = message.find_first_of(htmlStop, find + 3); if(urlStart >= message.length()) urlStart = -1; if(urlEnd >= message.length()) urlEnd = message.length(); if(((int)urlEnd -3 - (int)find > 0) && ((int)find - (int)urlStart -1 > 0)) { ret += message.substr(start, (urlStart + 1) - start); std::wstring url = message.substr(urlStart + 1, urlEnd - urlStart - 1); start = urlEnd; ret += _T("") + url + _T(""); isUrl = true; } else { ret += message.substr(start, (find + 3) - start); start = find + 3; } } ret += message.substr(start, message.length() - start); return ret; } std::wstring RemoveExt(const std::wstring &fileName) { size_t find = fileName.find_last_of(L'.'); if(find < fileName.length()) { return fileName.substr(0, find); } return fileName; } std::wstring GetName(const std::wstring &path) { size_t find = path.find_last_of(L"\\/"); if(find < path.length()) { return path.substr(find + 1); } return path; } void ExtractFile(short int res, const std::wstring &fileName) { HRSRC rSrc = FindResource(hInst, MAKEINTRESOURCE(res), MAKEINTRESOURCE(CUSTOMRES)); if(rSrc != NULL) { HGLOBAL res = LoadResource(hInst, rSrc); int size = SizeofResource(hInst, rSrc); if(res != NULL) { char* resData = (char*)LockResource(res); if(resData != NULL) { std::ofstream stream (fileName.c_str(), std::ios_base::binary); if(stream.is_open()) { stream.write(resData, size); stream.close(); } } FreeResource(res); } } } #pragma pack(push, 2) typedef struct { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image BYTE bColorCount; // Number of colors in image (0 if >=8bpp) BYTE bReserved; // Reserved ( must be 0) WORD wPlanes; // Color Planes WORD wBitCount; // Bits per pixel DWORD dwBytesInRes; // How many bytes in this resource? DWORD dwImageOffset; // Where in the file is this image? } ICONDIRENTRY, *LPICONDIRENTRY; typedef struct { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource Type (1 for icons) WORD idCount; // How many images? //ICONDIRENTRY idEntries; // An entry for each image (idCount of 'em) } ICONDIR, *LPICONDIR; #pragma pack(pop) typedef struct tagMyBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[256]; } MYBITMAPINFO; void IcoSave(const std::wstring &fileName, HICON hicon) { std::ofstream store (fileName.c_str(), std::ios_base::binary); if(!store.is_open()) return; ICONINFO ii; if(!GetIconInfo(hicon,&ii)) { store.close(); return; } HBITMAP hbmMask = ii.hbmMask; HBITMAP hbmColor = ii.hbmColor; BITMAP bmiMask; BITMAP bmiColor; if( GetObject(hbmColor,sizeof(bmiColor),&bmiColor) && GetObject(hbmMask,sizeof(bmiMask),&bmiMask) && (bmiColor.bmWidth==bmiMask.bmWidth) && (bmiColor.bmHeight==bmiMask.bmHeight) && (bmiMask.bmHeight) > 0 && (bmiMask.bmWidth) > 0 ) { BITMAPINFOHEADER icobmi = {0}; MYBITMAPINFO info1 = {0}; MYBITMAPINFO info2 = {0}; HDC hDC = CreateCompatibleDC(NULL); info1.bmiHeader.biSize = sizeof(info1.bmiHeader); info1.bmiHeader.biWidth = bmiColor.bmWidth; info1.bmiHeader.biHeight = bmiColor.bmHeight; info1.bmiHeader.biPlanes = 1; info1.bmiHeader.biBitCount = bmiColor.bmBitsPixel; unsigned int size = GetDIBits(hDC,hbmColor,0,info1.bmiHeader.biHeight,NULL,(BITMAPINFO*)&info1,DIB_RGB_COLORS); char* bits1 = new char[info1.bmiHeader.biSizeImage]; size = GetDIBits(hDC,hbmColor,0,info1.bmiHeader.biHeight,bits1,(BITMAPINFO*)&info1,DIB_RGB_COLORS); info2.bmiHeader.biSize = sizeof(info2.bmiHeader); info2.bmiHeader.biWidth = bmiMask.bmWidth; info2.bmiHeader.biHeight = bmiMask.bmHeight; info2.bmiHeader.biPlanes = 1; info2.bmiHeader.biBitCount = bmiMask.bmBitsPixel; size = GetDIBits(hDC,hbmColor,0,info1.bmiHeader.biHeight,NULL,(BITMAPINFO*)&info2,DIB_RGB_COLORS); char* bits2 = new char[info2.bmiHeader.biSizeImage]; size = GetDIBits(hDC,hbmMask,0,info2.bmiHeader.biHeight,bits2,(BITMAPINFO*)&info2,DIB_RGB_COLORS); ICONDIR icodir; ICONDIRENTRY icoent; icodir.idReserved = 0; icodir.idType = 1; icodir.idCount = 1; icoent.bWidth = (unsigned char)bmiColor.bmWidth; icoent.bHeight = (unsigned char)bmiColor.bmHeight; icoent.bColorCount = 8<=bmiColor.bmBitsPixel?0:1<extCssHtml2.empty()) { cssCopied = CopyFile(Options::instance->extCssHtml2.c_str(), css.c_str(), FALSE); } if(!cssCopied) ExtractFile(IDR_CSS, css); ExtractFile(IDR_JS, folder + _T("\\history.js")); HICON ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hPlusExIcon); IcoSave(folder + _T("\\pnode.ico"), ico); CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0); ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hMinusExIcon); IcoSave(folder + _T("\\mnode.ico"), ico); CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0); ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hEventIcons[0]); IcoSave(folder + _T("\\event0.ico"), ico); CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0); ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hEventIcons[1]); IcoSave(folder + _T("\\event1.ico"), ico); CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0); EXP_FILE << _T("\n"); EXP_FILE << _T("\n\n"); EXP_FILE << _T("") << TranslateT("History Log") << _T(" [") << MakeTextHtmled(myName) << _T("] - [") << MakeTextHtmled(name1) << _T("]\n"); EXP_FILE << _T("\n"); EXP_FILE << _T("\n"); EXP_FILE << _T("\n"); EXP_FILE << _T("\n"); EXP_FILE << _T("") << TranslateT("Menu") << _T("\n"); EXP_FILE << _T("\n"); EXP_FILE << _T("\n"); EXP_FILE << _T("\n"); EXP_FILE << _T("
\n"); EXP_FILE << _T("\n"); EXP_FILE << _T("

") << TranslateT("History Log") << _T("

\n

"); EXP_FILE << MakeTextHtmled(myName); if(proto1.length() || myId.length()) { EXP_FILE << _T(" (") << MakeTextHtmled(proto1) << _T(": ") << MakeTextHtmled(myId) << _T(") - "); } else { EXP_FILE << _T(" - "); } EXP_FILE << MakeTextHtmled(name1); if(proto1.length() || id1.length()) { EXP_FILE << _T(" (") << MakeTextHtmled(proto1) << _T(": ") << MakeTextHtmled(id1) << _T(")

\n"); } else { EXP_FILE << _T("\n"); } EXP_FILE << _T("
") << TranslateT("Filter:") << _T(" ") << MakeTextHtmled(filterName) << _T("
\n"); groupId = 0; } void RichHtmlExport::WriteFooter() { if(groupId > 0) { EXP_FILE << _T("\n"); } EXP_FILE << _T("
\n\n"); } void RichHtmlExport::WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText) { TCHAR *id = isMe ? _T("out") : _T("inc"); TCHAR* ev = (isMe ? _T("1") : _T("0")); if(groupId > 0) { EXP_FILE << _T("\n"); } bool isUrl = false; std::wstring& mes = ReplaceSmileys(isMe, eventText, isUrl); EXP_FILE << _T("
\n"); EXP_FILE << _T(""); EXP_FILE << _T("\n"); EXP_FILE << _T("") << time << _T("\n\n") << mes; EXP_FILE << _T("\n
\n"); EXP_FILE << _T("
\n"); ++groupId; } void RichHtmlExport::WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei) { TCHAR *id = isMe ? _T("out") : _T("inc"); TCHAR* ev = (isMe ? _T("1") : _T("0")); TCHAR* ev1 = ev; bool isUrl = false; std::wstring& mes = ReplaceSmileys(isMe, message, isUrl); if(isUrl) ev = _T("2"); EXP_FILE << _T("
\n"); EXP_FILE << _T("
") << _T("
\n"); EXP_FILE << _T("
") << (Options::instance->exportHtml2ShowDate ? longDate : shortDate) << _T("
\n"); EXP_FILE << _T("
") << MakeTextHtmled(user) << _T("
\n"); EXP_FILE << _T("
\n"); EXP_FILE << mes; EXP_FILE << _T("\n
\n"); EXP_FILE << _T("
\n"); } std::wstring RichHtmlExport::ReplaceSmileys(bool isMe, const std::wstring &msg, bool &isUrl) { if(Options::instance->exportHtml2UseSmileys && g_SmileyAddAvail) { TCHAR* msgbuf = new TCHAR[msg.length() + 1]; memcpy_s(msgbuf, (msg.length() + 1) * sizeof(TCHAR), msg.c_str(), (msg.length() + 1) * sizeof(TCHAR)); SMADD_BATCHPARSE2 sp = {0}; SMADD_BATCHPARSERES *spr; sp.cbSize = sizeof(sp); sp.Protocolname = baseProto.length() == 0 ? NULL : baseProto.c_str(); sp.str = msgbuf; sp.flag = SAFL_TCHAR | SAFL_PATH | (isMe ? SAFL_OUTGOING : 0); spr = (SMADD_BATCHPARSERES*)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp); delete[] msgbuf; if (spr == NULL || (INT_PTR)spr == CALLSERVICE_NOTFOUND) { // Did not find a simley return UrlHighlightHtml(MakeTextHtmled(msg), isUrl); } std::queue > positionMap; std::wstring newMsg = MakeTextHtmled(msg, &positionMap); std::wstring smileyMsg; size_t last_pos=0; std::pair pos(0, 0); size_t currentAdd = 0; if(!positionMap.empty()) { pos = positionMap.front(); positionMap.pop(); } for (unsigned i = 0; i < sp.numSmileys; ++i) { size_t startChar = spr[i].startChar + currentAdd; while(startChar >= pos.first && pos.second) { startChar += pos.second; currentAdd += pos.second; if(!positionMap.empty()) { pos = positionMap.front(); positionMap.pop(); } else { pos = std::pair(0, 0); } } size_t endChar = spr[i].startChar + spr[i].size + currentAdd; while(endChar >= pos.first && pos.second) { endChar += pos.second; currentAdd += pos.second; if(!positionMap.empty()) { pos = positionMap.front(); positionMap.pop(); } else { pos = std::pair(0, 0); } } size_t size = endChar - startChar; if (spr[i].filepath != NULL) // For deffective smileypacks { // Add text if (startChar - last_pos > 0) { smileyMsg += newMsg.substr(last_pos, startChar - last_pos); } std::wstring smileyName = GetName(spr[i].filepath); if(smileys.find(smileyName) == smileys.end()) { smileys.insert(smileyName); CopyFile(spr[i].filepath, (folder + _T("\\") + smileyName).c_str(), FALSE); } std::wstring smileyText = newMsg.substr(startChar, size); smileyMsg += _T("\"");"); } // Get next last_pos = endChar; } // Add rest of text if (last_pos < newMsg.length()) { smileyMsg += newMsg.substr(last_pos); } CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spr); return UrlHighlightHtml(smileyMsg, isUrl); } else { return UrlHighlightHtml(MakeTextHtmled(msg), isUrl); } }