From: Jon K. <em...@us...> - 2005-05-20 02:35:46
|
Update of /cvsroot/licq/licq/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17398/src Modified Files: icqd-srv.cpp icqd-tcp.cpp icqpacket.cpp translate.cpp user.cpp Log Message: Fix a problem of empty messages and unreadable messages do to encoding problems. We can now read the UTF-16 messages (encoded as UCS-2BE) and send them as well. I have tested this with a few different clients, including the official client and as long as you set the encoding properly for the user you won't have any problems. * This may require some more testing, so please try it out! Also, UTF8 encoded nicknames are retrieved properly from the server. *** Licq now requires iconv to compile *** Index: icqd-srv.cpp =================================================================== RCS file: /cvsroot/licq/licq/src/icqd-srv.cpp,v retrieving revision 1.159 retrieving revision 1.160 diff -u -d -r1.159 -r1.160 --- icqd-srv.cpp 25 Apr 2005 23:56:36 -0000 1.159 +++ icqd-srv.cpp 20 May 2005 02:35:23 -0000 1.160 @@ -1464,14 +1464,15 @@ //-----icqSendThroughServer----------------------------------------------------- ICQEvent* CICQDaemon::icqSendThroughServer(const char *szId, - unsigned char format, char *_sMessage, CUserEvent* ue, unsigned short nCharset) + unsigned char format, char *_sMessage, CUserEvent* ue, unsigned short nCharset, + size_t nMsgLen) { ICQEvent* result; ICQUser *u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_R); bool bOffline = u->StatusOffline(); gUserManager.DropUser(u); - CPU_ThroughServer *p = new CPU_ThroughServer(szId, format, _sMessage, nCharset, bOffline); + CPU_ThroughServer *p = new CPU_ThroughServer(szId, format, _sMessage, nCharset, bOffline, nMsgLen); switch (format) { @@ -2497,6 +2498,30 @@ u->SetSharedFilesStatus(ICQ_PLUGIN_STATUSxINACTIVE); } + if (packet.hasTLV(0x000D)) + { + CBuffer capBuf = packet.UnpackTLV(0x000D); + int nCapSize = packet.getTLVLen(0x000D); + char *caps = new char[nCapSize]; + for (unsigned short i = 0; i < nCapSize; i++) + capBuf >> caps[i]; + + // Check if they support UTF8 + bool bUTF8 = false; + for (int i = 0; i < (nCapSize / CAP_LENGTH); i++) + { + if (memcmp(caps+(i * CAP_LENGTH), ICQ_CAPABILITY_UTF8, CAP_LENGTH) == 0) + { + bUTF8 = true; + break; + } + } + delete [] caps; + + if (u) + u->SetSupportsUTF8(bUTF8); + } + if (packet.hasTLV(0x0011)) { CBuffer msg = packet.UnpackTLV(0x0011); @@ -2771,15 +2796,15 @@ nMsgLen -= 4; char* szMessage = new char[nMsgLen+1]; - for (int i = 0; i < nMsgLen; ++i) + for (int i = 0; i < nMsgLen; i++) szMessage[i] = msgTxt.UnpackChar(); - + szMessage[nMsgLen] = '\0'; char* szMsg = 0; if (nEncoding == 2) // utf-8 or utf-16? { char *szTmpMsg = 0; - szTmpMsg = gTranslator.FromUnicode(szMessage); + szTmpMsg = gTranslator.FromUTF16(szMessage, nMsgLen); szMsg = gTranslator.RNToN(szTmpMsg); delete [] szTmpMsg; } @@ -2826,7 +2851,7 @@ { //I must admit, any server that does anything like this is a pile of shit CBuffer msgTxt = packet.UnpackTLV(5); - if (msgTxt.getDataSize() == 0) break; + if (msgTxt.getDataSize() == 0) break; unsigned short nCancel = msgTxt.UnpackUnsignedShort(); @@ -2847,6 +2872,18 @@ CBuffer advMsg = msgTxt.UnpackTLV(0x2711); if (advMsg.getDataSize() == 0) break; + // Check if they support UTF8 + bool bUTF8 = false; + if (memcmp(cap, ICQ_CAPABILITY_UTF8, CAP_LENGTH) == 0) + bUTF8 = true; + + ICQUser *u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_W); + if (u) + { + u->SetSupportsUTF8(bUTF8); + gUserManager.DropUser(u); + } + if (memcmp(cap, ICQ_CAPABILITY_DIRECT, CAP_LENGTH) == 0) { // reverse connect request @@ -2956,7 +2993,7 @@ gTranslator.ServerToClient(szMsg); bool bNewUser = false; - ICQUser *u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_W); + u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_W); if (u == NULL) { u = new ICQUser(szId, LICQ_PPID); Index: icqd-tcp.cpp =================================================================== RCS file: /cvsroot/licq/licq/src/icqd-tcp.cpp,v retrieving revision 1.79 retrieving revision 1.80 diff -u -d -r1.79 -r1.80 --- icqd-tcp.cpp 6 Apr 2005 09:26:06 -0000 1.79 +++ icqd-tcp.cpp 20 May 2005 02:35:24 -0000 1.80 @@ -62,6 +62,8 @@ ICQEvent *result = NULL; char *mDos = NULL; + char *szMessage = NULL; + bool bUTF16 = false; if (m != NULL) { mDos = gTranslator.NToRN(m); @@ -83,23 +85,48 @@ if (nLevel == ICQ_TCPxMSG_URGENT) f |= E_URGENT; if (bMultipleRecipients) f |= E_MULTIxREC; - if (!online) // send offline + // What kinda encoding do we have here? + unsigned short nCharset = CHARSET_ASCII; + size_t nUTFLen = 0; + if (gTranslator.CheckEncoding(mDos, strlen(mDos)) == CHARSET_UNICODE) { - unsigned short nCharset = 0; - u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_R); - if (u && u->UserEncoding()) - nCharset = 3; - gUserManager.DropUser(u); - + u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_R); + if (u && isdigit(u->IdString()[0])) + { + // ICQ Users can send a flag that says UTF8/16 is ok + if (u->SupportsUTF8()) + nCharset = CHARSET_UNICODE; + else if (u->UserEncoding()) + nCharset = CHARSET_CUSTOM; + } + else if(u && !(isdigit(u->IdString()[0]))) + { + // AIM users support UTF8/16 + nCharset = CHARSET_UNICODE; + } + gUserManager.DropUser(u); + } + + szMessage = cipher ? cipher : mDos; + if (nCharset == CHARSET_UNICODE && cipher == 0) + { + bUTF16 = true; + szMessage = gTranslator.ToUTF16(mDos, nUTFLen); + } + + // We took care of the charset so lets finally start getting this message sent! + + if (!online) // send offline + { e = new CEventMsg(m, ICQ_CMDxSND_THRUxSERVER, TIME_NOW, f); - if (strlen(mDos) > MAX_MESSAGE_SIZE) + if (strlen(szMessage) > MAX_MESSAGE_SIZE) { gLog.Warn(tr("%sTruncating message to %d characters to send through server.\n"), L_WARNxSTR, MAX_MESSAGE_SIZE); - mDos[MAX_MESSAGE_SIZE] = '\0'; + szMessage[MAX_MESSAGE_SIZE] = '\0'; } result = icqSendThroughServer(szId, ICQ_CMDxSUB_MSG | (bMultipleRecipients ? ICQ_CMDxSUB_FxMULTIREC : 0), - cipher ? cipher : mDos, e, nCharset); + cipher ? cipher : szMessage, e, nCharset, nUTFLen); u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_W); } else // send direct @@ -109,7 +136,7 @@ if (u->Secure()) f |= E_ENCRYPTED; e = new CEventMsg(m, ICQ_CMDxTCP_START, TIME_NOW, f); if (pColor != NULL) e->SetColor(pColor); - CPT_Message *p = new CPT_Message(cipher ? cipher : mDos, nLevel, bMultipleRecipients, pColor, u); + CPT_Message *p = new CPT_Message(cipher ? cipher : szMessage, nLevel, bMultipleRecipients, pColor, u, nUTFLen); gLog.Info(tr("%sSending %smessage to %s (#%hu).\n"), L_TCPxSTR, nLevel == ICQ_TCPxMSG_URGENT ? tr("urgent ") : "", u->GetAlias(), -p->Sequence()); @@ -127,11 +154,13 @@ if (pColor != NULL) CICQColor::SetDefaultColors(pColor); + if (bUTF16 && szMessage) + delete [] szMessage; if (cipher) free(cipher); if (mDos) delete [] mDos; - + if (result != NULL) return result->EventId(); else Index: icqpacket.cpp =================================================================== RCS file: /cvsroot/licq/licq/src/icqpacket.cpp,v retrieving revision 1.112 retrieving revision 1.113 diff -u -d -r1.112 -r1.113 --- icqpacket.cpp 13 Apr 2005 00:25:49 -0000 1.112 +++ icqpacket.cpp 20 May 2005 02:35:25 -0000 1.113 @@ -1243,12 +1243,17 @@ //-----ThroughServer------------------------------------------------------- CPU_ThroughServer::CPU_ThroughServer(const char *szId, unsigned char msgType, char *szMessage, - unsigned short nCharset, bool bOffline) + unsigned short nCharset, bool bOffline, + size_t nLen) : CPU_CommonFamily(ICQ_SNACxFAM_MESSAGE, ICQ_SNACxMSG_SENDxSERVER) { m_nSubCommand = msgType; - int msgLen = szMessage ? strlen(szMessage) : 0; + int msgLen; + if (nLen) + msgLen = nLen; + else + msgLen = szMessage ? strlen(szMessage) : 0; int nUinLen = strlen(szId); unsigned short nFormat = 0; int nTypeLen = 0, nTLVType = 0; @@ -4113,7 +4118,8 @@ } CPacketTcp::CPacketTcp(unsigned long _nCommand, unsigned short _nSubCommand, - const char *szMessage, bool _bAccept, unsigned short nLevel, ICQUser *user) + const char *szMessage, bool _bAccept, unsigned short nLevel, ICQUser *user, + size_t nLen) { // Setup the message type and status fields using our online status ICQOwner *o = gUserManager.FetchOwner(LOCK_R); @@ -4204,14 +4210,24 @@ m_nSourceUin = gUserManager.OwnerUin(); m_nCommand = _nCommand; m_nSubCommand = _nSubCommand; - m_szMessage = (szMessage == NULL ? strdup("") : strdup(szMessage)); + if (nLen) + { + m_szMessage = (char *)malloc(nLen); + memcpy(m_szMessage, szMessage, nLen); + m_nMsgLen = nLen; + } + else + { + m_szMessage = (szMessage == NULL ? strdup("") : strdup(szMessage)); + m_nMsgLen = strlen(m_szMessage); + } m_nLocalPort = user->LocalPort(); // don't increment the sequence if this is an ack and cancel packet if (m_nCommand == ICQ_CMDxTCP_START) m_nSequence = user->Sequence(true); // v4,6 packets are smaller then v2 so we just set the size based on a v2 packet - m_nSize = 18 + strlen(m_szMessage) + 25; + m_nSize = 18 + m_nMsgLen + 25; buffer = NULL; } @@ -4275,7 +4291,7 @@ buffer->PackUnsignedLong(m_nCommand); buffer->PackUnsignedLong(m_nSourceUin); buffer->PackUnsignedShort(m_nSubCommand); - buffer->PackString(m_szMessage); + buffer->Pack(m_szMessage, m_nMsgLen); buffer->PackUnsignedLong(s_nLocalIp); buffer->PackUnsignedLong(s_nRealIp); m_szLocalPortOffset = buffer->getDataPosWrite(); @@ -4307,7 +4323,7 @@ buffer->PackUnsignedLong(m_nCommand); buffer->PackUnsignedLong(m_nSourceUin); buffer->PackUnsignedShort(m_nSubCommand); - buffer->PackString(m_szMessage); + buffer->Pack(m_szMessage, m_nMsgLen); buffer->PackUnsignedLong(s_nLocalIp); buffer->PackUnsignedLong(s_nRealIp); m_szLocalPortOffset = buffer->getDataPosWrite(); @@ -4339,7 +4355,7 @@ buffer->PackUnsignedShort(m_nSubCommand); buffer->PackUnsignedShort(m_nStatus); buffer->PackUnsignedShort(m_nMsgType); - buffer->PackString(m_szMessage); + buffer->Pack(m_szMessage, m_nMsgLen); m_szLocalPortOffset = NULL; } @@ -4369,7 +4385,7 @@ // buffer->PackUnsignedShort(0x0021); if (Channel() == ICQ_CHNxNONE) { - buffer->PackString(m_szMessage); + buffer->Pack(m_szMessage, m_nMsgLen); } else { @@ -4387,10 +4403,10 @@ //-----Message------------------------------------------------------------------ CPT_Message::CPT_Message(char *_sMessage, unsigned short nLevel, bool bMR, - CICQColor *pColor, ICQUser *pUser) + CICQColor *pColor, ICQUser *pUser, size_t nLen) : CPacketTcp(ICQ_CMDxTCP_START, ICQ_CMDxSUB_MSG | (bMR ? ICQ_CMDxSUB_FxMULTIREC : 0), - _sMessage, true, nLevel, pUser) + _sMessage, true, nLevel, pUser, nLen) { InitBuffer(); if (m_nVersion >= 6) Index: translate.cpp =================================================================== RCS file: /cvsroot/licq/licq/src/translate.cpp,v retrieving revision 1.11 retrieving revision 1.12 diff -u -d -r1.11 -r1.12 --- translate.cpp 24 Sep 2004 15:30:40 -0000 1.11 +++ translate.cpp 20 May 2005 02:35:33 -0000 1.12 @@ -25,6 +25,7 @@ #include "licq_translate.h" #include "licq_log.h" +#include "licq_icq.h" CTranslator gTranslator; @@ -145,6 +146,27 @@ return true; } +//============ CheckEncoding ================// + +unsigned short CTranslator::CheckEncoding(const char *szCheck, int nSize) +{ + // We can't really differentiate between CUSTOM and UNICODE so the + // daemon will take care of. We just report ASCII or other (in this case + // UNICODE). + unsigned short nEncoding = CHARSET_ASCII; + + for (int i = 0; i < nSize; i++) + { + if ((unsigned char)szCheck[i] >= 0x80) + { + nEncoding = CHARSET_UNICODE; + break; + } + } + + return nEncoding; +} + //============ translateToClient ============// void CTranslator::ServerToClient(char *szString) @@ -218,38 +240,84 @@ //-----FromUTF8-------------------------------------------------------------- char *CTranslator::FromUnicode(char *_sz) { - if (_sz == NULL) return NULL; + if (_sz == NULL) return NULL; unsigned short nLen = strlen(_sz); char *szNewStr = new char[nLen + 1]; - unsigned long j = 0; - for (unsigned long i = 0; i < nLen; i++) + unsigned int nInSize, nOutSize; + + char *_bi = _sz, *_bo = szNewStr; + iconv_t _tr; + + _i = nLen; + _o = nLen; + + _tr = iconv_open("", "UTF-8"); + size_t ret = iconv(_tr, &_bi, &_i, &_bo, &_o); + iconv_close(_tr); + + if (ret == (size_t)(-1)) { - if ((unsigned char)_sz[i] <= 0x7F) // good ole ascii - szNewStr[j++] = _sz[i]; - else - { - unsigned short nChar; + _tr = iconv_open("", "UCS-2BE"); + iconv(_tr, &_bi, &_i, &_bo, &_o); + iconv_close(_tr); + } - if ((unsigned char)_sz[i] >= 0xE0) - { - nChar = ((_sz[i] & 0x0F) << 12) | ((_sz[i+1] & 0x3F) << 6) | - (_sz[i+2] & 0x3F); - i += 2; - } - else - { - nChar = ((_sz[i] & 0x1F) << 6) | (_sz[i+1] & 0x3F); - i++; - } + *_bo = '\0'; - if (nChar > 0xFF) - szNewStr[j++] = '?'; - else - szNewStr[j++] = (char)nChar; - } + return szNewStr; +} + +//-----FromUTF8-------------------------------------------------------------- +char *CTranslator::FromUTF16(char *_sz, int nMsgLen) +{ + if (_sz == NULL) return NULL; + unsigned short nLen = nMsgLen > 0 ? nMsgLen : strlen(_sz); + char *szNewStr = new char[nLen * 2]; + unsigned int nInSize, nOutSize; + + char *szIn = _sz, *szOut = szNewStr; + iconv_t _tr; + + nInSize = nLen; + nOutSize = nLen * 2; + + tr = iconv_open("", "UCS-2BE"); + size_t ret = iconv(tr, &szIn, &nInSize, &szOut, &nOutSize); + iconv_close(tr); + + if (ret == (size_t)(-1)) + { + gLog.Error("Error decoding a UTF-16 message.\n"); } + + *szOut = '\0'; + + return szNewStr; +} - szNewStr[j] = '\0'; +//-----ToUTF16----------------------------------------------------------------- +char *CTranslator::ToUTF16(char *_sz, size_t &nSize) +{ + if (_sz == NULL) return NULL; + unsigned short nLen = strlen(_sz) * 3; + char *szNewStr = new char[nLen + 1]; + unsigned int _i, _o; + char *_bi = _sz, *_bo = szNewStr; + iconv_t _tr; + + _i = strlen(_sz); + _o = nLen; + + _tr = iconv_open("UCS-2BE", "UTF-8"); + size_t ret = iconv(_tr, &_bi, &_i, &_bo, &_o); + iconv_close(_tr); + + if (ret == (size_t)-1) + gLog.Error("Error encoding to UTF-16.\n"); + + *_bo = '\0'; + nSize = nLen - _o; + return szNewStr; } Index: user.cpp =================================================================== RCS file: /cvsroot/licq/licq/src/user.cpp,v retrieving revision 1.103 retrieving revision 1.104 diff -u -d -r1.103 -r1.104 --- user.cpp 16 May 2005 01:51:22 -0000 1.103 +++ user.cpp 20 May 2005 02:35:33 -0000 1.104 @@ -2013,7 +2013,8 @@ m_fConf.ReadBool("UseGPG", m_bUseGPG, false ); m_fConf.ReadStr("GPGKey", szTemp, "" ); SetString( &m_szGPGKey, szTemp ); - + m_bSupportsUTF8 = false; + if (nNewMessages > 0) { HistoryList hist; |