/* Plugin of Miranda IM for communicating with users of the MSN Messenger protocol. Copyright (c) 2006-2011 Boris Krasnovskiy. Copyright (c) 2003-2005 George Hazan. Copyright (c) 2002-2003 Richard Hughes (original version). 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, see . */ // this plugin is for Miranda 0.9 or later #define MIRANDA_VER 0x0A00 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "m_proto_listeningto.h" #include "m_folders.h" #include "m_metacontacts.h" #include "ezxml.h" #include "resource.h" ///////////////////////////////////////////////////////////////////////////////////////// // MSN error codes #define ERR_SYNTAX_ERROR 200 #define ERR_INVALID_PARAMETER 201 #define ERR_INVALID_USER 205 #define ERR_FQDN_MISSING 206 #define ERR_ALREADY_LOGIN 207 #define ERR_INVALID_USERNAME 208 #define ERR_INVALID_FRIENDLY_NAME 209 #define ERR_LIST_FULL 210 #define ERR_ALREADY_THERE 215 #define ERR_NOT_ON_LIST 216 #define ERR_NOT_ONLINE 217 #define ERR_ALREADY_IN_THE_MODE 218 #define ERR_ALREADY_IN_OPPOSITE_LIST 219 #define ERR_CONTACT_LIST_FAILED 241 #define ERR_SWITCHBOARD_FAILED 280 #define ERR_NOTIFY_XFR_FAILED 281 #define ERR_REQUIRED_FIELDS_MISSING 300 #define ERR_NOT_LOGGED_IN 302 #define ERR_INTERNAL_SERVER 500 #define ERR_DB_SERVER 501 #define ERR_LIST_UNAVAILABLE 508 #define ERR_FILE_OPERATION 510 #define ERR_MEMORY_ALLOC 520 #define ERR_SERVER_BUSY 600 #define ERR_SERVER_UNAVAILABLE 601 #define ERR_PEER_NS_DOWN 602 #define ERR_DB_CONNECT 603 #define ERR_SERVER_GOING_DOWN 604 #define ERR_CREATE_CONNECTION 707 #define ERR_INVALID_LOCALE 710 #define ERR_BLOCKING_WRITE 711 #define ERR_SESSION_OVERLOAD 712 #define ERR_USER_TOO_ACTIVE 713 #define ERR_TOO_MANY_SESSIONS 714 #define ERR_NOT_EXPECTED 715 #define ERR_BAD_FRIEND_FILE 717 #define ERR_AUTHENTICATION_FAILED 911 #define ERR_NOT_ALLOWED_WHEN_OFFLINE 913 #define ERR_NOT_ACCEPTING_NEW_USERS 920 #define ERR_EMAIL_ADDRESS_NOT_VERIFIED 924 ///////////////////////////////////////////////////////////////////////////////////////// // Global definitions #define MSN_MAX_EMAIL_LEN 128 #define MSN_GUID_LEN 40 #define MSN_PACKETS_COMBINE 7 #define MSN_DEFAULT_PORT 1863 #define MSN_DEFAULT_GATEWAY_PORT 80 const char MSN_DEFAULT_LOGIN_SERVER[] = "messenger.hotmail.com"; const char MSN_DEFAULT_GATEWAY[] = "gateway.messenger.hotmail.com"; const char MSN_USER_AGENT[] = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1)"; #define MSN_BLOCK "/BlockCommand" #define MSN_INVITE "/InviteCommand" #define MSN_NETMEETING "/NetMeeting" #define MSN_VIEW_PROFILE "/ViewProfile" #define MSN_SEND_NUDGE "/SendNudge" #define MS_GOTO_INBOX "/GotoInbox" #define MS_EDIT_PROFILE "/EditProfile" #define MS_EDIT_ALERTS "/EditAlerts" #define MS_SET_NICKNAME_UI "/SetNicknameUI" #define MSN_GETUNREAD_EMAILCOUNT "/GetUnreadEmailCount" extern const char sttVoidUid[]; ///////////////////////////////////////////////////////////////////////////////////////// // MSN plugin functions struct CMsnProto; #define NEWSTR_ALLOCA(A) (A==NULL)?NULL:strcpy((char*)alloca(strlen(A)+1),A) #define NEWTSTR_ALLOCA(A) (A==NULL)?NULL:_tcscpy((TCHAR*)alloca(sizeof(TCHAR)*(_tcslen(A)+1)),A) #define MSN_ALLOW_MSGBOX 1 #define MSN_ALLOW_ENTER 2 #define MSN_HOTMAIL_POPUP 4 #define MSN_SHOW_ERROR 8 #define MSN_ALERT_POPUP 16 void HtmlDecode(char* str); char* HtmlEncode(const char* str); bool txtParseParam (const char* szData, const char* presearch, const char* start, const char* finish, char* param, const int size); void stripBBCode(char* src); void stripColorCode(char* src); void parseWLID(char* wlid, char** net, char** email, char** inst); char* MSN_Base64Decode(const char* str); char* GetGlobalIp(void); template void UrlDecode(chartype* str); void UrlEncode(const char* src, char* dest, size_t cbDest); void __cdecl MSN_ConnectionProc(HANDLE hNewConnection, DWORD dwRemoteIP, void*); char* MSN_GetAvatarHash(char* szContext); int MSN_GetImageFormat(void* buf, const TCHAR** ext); int MSN_GetImageFormat(const TCHAR* file); bool MSN_MsgWndExist(HANDLE hContact); #define MSN_SendNickname(a) MSN_SendNicknameUtf(UTF8(a)) #if defined(_DEBUG) #define MSN_CallService CallService #else INT_PTR MSN_CallService(const char* szSvcName, WPARAM wParam, LPARAM lParam); #endif void MSN_FreeVariant(DBVARIANT* dbv); char* MSN_Translate(const char* str); unsigned MSN_GenRandom(void); void MSN_InitContactMenu(void); void MSN_RemoveContactMenus(void); HANDLE GetIconHandle(int iconId); HICON LoadIconEx(const char* name, bool big = false); void ReleaseIconEx(const char* name, bool big = false); void MsnInitIcons(void); int sttDivideWords(char* parBuffer, int parMinItems, char** parDest); void MSN_MakeDigest(const char* chl, char* dgst); char* getNewUuid(void); TCHAR* EscapeChatTags(const TCHAR* pszText); TCHAR* UnEscapeChatTags(TCHAR* str_in); void overrideStr(TCHAR*& dest, const TCHAR* src, bool unicode, const TCHAR* def = NULL); void replaceStr(char*& dest, const char* src); void replaceStr(wchar_t*& dest, const wchar_t* src); char* __fastcall ltrimp(char* str); char* __fastcall rtrim(char* string); wchar_t* __fastcall rtrim(wchar_t* string); __inline char* lrtrimp(char* str) { return ltrimp(rtrim(str)); }; char* arrayToHex(BYTE* data, size_t datasz); inline unsigned short _htons(unsigned short s) { return s>>8|s<<8; } inline unsigned long _htonl(unsigned long s) { return s<<24|(s&0xff00)<<8|((s>>8)&0xff00)|s>>24; } inline unsigned __int64 _htonl64(unsigned __int64 s) { return (unsigned __int64)_htonl(s & 0xffffffff) << 32 | _htonl(s >> 32); } ///////////////////////////////////////////////////////////////////////////////////////// // Popup interface typedef struct _tag_PopupData { unsigned flags; char* url; TCHAR* title; TCHAR* text; CMsnProto* proto; } PopupData; struct STRLIST : public LIST { static int compare(const char* p1, const char* p2) { return _stricmp(p1, p2); } STRLIST() : LIST(2, compare) {} ~STRLIST() { destroy(); } void destroy( void ) { for (int i=0; i < count; i++) mir_free(items[i]); li.List_Destroy((SortedList*)this); } int insertn(const char* p) { return insert(mir_strdup(p)); } int remove(int idx) { mir_free(items[idx]); return li.List_Remove((SortedList*)this, idx); } int remove(const char* p) { int idx; return li.List_GetIndex((SortedList*)this, (char*)p, &idx) == 1 ? remove(idx) : -1; } }; ///////////////////////////////////////////////////////////////////////////////////////// // MIME headers processing class MimeHeaders { public: MimeHeaders(); MimeHeaders(unsigned); ~MimeHeaders(); void clear(void); char* decodeMailBody(char* msgBody); const char* find(const char* fieldName); char* flipStr(const char* src, size_t len, char* dest); size_t getLength(void); char* readFromBuffer(char* src); char* writeToBuffer(char* dest); void addString(const char* name, const char* szValue, unsigned flags = 0); void addLong(const char* name, long lValue, unsigned flags = 0); void addULong(const char* name, unsigned lValue); void addBool(const char* name, bool lValue); const char* operator[](const char* fieldName) { return find(fieldName); } static wchar_t* decode(const char* val); private: typedef struct tag_MimeHeader { const char* name; const char* value; unsigned flags; } MimeHeader; unsigned mCount; unsigned mAllocCount; MimeHeader* mVals; unsigned allocSlot(void); }; ///////////////////////////////////////////////////////////////////////////////////////// // File transfer helper struct ThreadData; struct HReadBuffer { HReadBuffer(ThreadData* info, int iStart = 0); ~HReadBuffer(); BYTE* surelyRead(size_t parBytes); ThreadData* owner; BYTE* buffer; size_t totalDataSize; size_t startOffset; }; enum TInfoType { SERVER_NOTIFICATION, SERVER_SWITCHBOARD, SERVER_FILETRANS, SERVER_P2P_DIRECT }; struct filetransfer { filetransfer(CMsnProto* prt); ~filetransfer(void); void close(void); void complete(void); int create(void); int openNext(void); CMsnProto* proto; PROTOFILETRANSFERSTATUS std; bool bCanceled; // flag to interrupt a transfer bool bCompleted; // was a FT ever completed? bool bAccepted; // was a FT ever completed? int fileId; // handle of file being transferring (r/w) HANDLE hLockHandle; ThreadData *info; TInfoType tType; TInfoType tTypeReq; time_t ts; clock_t nNotify; unsigned cf; bool p2p_waitack; // wait for ack bool p2p_isV2; // P2P V2 unsigned p2p_sessionid; // session id unsigned p2p_acksessid; // acknowledged session id unsigned p2p_sendmsgid; // send message id unsigned p2p_byemsgid; // bye message id unsigned p2p_ackID; // number of ack's state unsigned p2p_appID; // application id: 1 = avatar, 2 = file transfer unsigned p2p_type; // application id: 1 = avatar, 2 = file transfer, 3 = custom emoticon char* p2p_branch; // header Branch: field char* p2p_callID; // header Call-ID: field char* p2p_dest; // destination e-mail address char* p2p_object; // MSN object for a transfer //---- receiving a file char* szInvcookie; // cookie for receiving unsigned __int64 lstFilePtr; }; struct directconnection { directconnection(const char* CallID, const char* Wlid); ~directconnection(); char* calcHashedNonce(UUID* nonce); char* mNonceToText(void); char* mNonceToHash(void) { return calcHashedNonce(mNonce); } void xNonceToBin(UUID* nonce); UUID* mNonce; char* xNonce; char* callId; char* wlid; time_t ts; bool useHashedNonce; bool bAccepted; CMsnProto* proto; }; #pragma pack(1) typedef struct _tag_HFileContext { unsigned len; unsigned ver; unsigned __int64 dwSize; unsigned type; wchar_t wszFileName[MAX_PATH]; char unknown[30]; unsigned id; char unknown2[64]; } HFileContext; struct P2PB_Header { virtual char* parseMsg(char *buf) = 0; virtual char* createMsg(char *buf, const char* wlid, CMsnProto *ppro) = 0; virtual bool isV2Hdr(void) = 0; virtual void logHeader(CMsnProto *ppro) = 0; }; struct P2P_Header : P2PB_Header { unsigned mSessionID; unsigned mID; unsigned __int64 mOffset; unsigned __int64 mTotalSize; unsigned mPacketLen; unsigned mFlags; unsigned mAckSessionID; unsigned mAckUniqueID; unsigned __int64 mAckDataSize; P2P_Header() { memset(&mSessionID, 0, 48); } P2P_Header(char *buf) { parseMsg(buf); } char* parseMsg(char *buf) { memcpy(&mSessionID, buf, 48); return buf + 48; } char* createMsg(char *buf, const char* wlid, CMsnProto *ppro); bool isV2Hdr(void) { return false; } void logHeader(CMsnProto *ppro); } ; struct P2PV2_Header : P2PB_Header { unsigned mSessionID; unsigned mID; const char* mCap; unsigned __int64 mRemSize; unsigned mPacketLen; unsigned mPacketNum; unsigned mAckUniqueID; unsigned char mOpCode; unsigned char mTFCode; P2PV2_Header() { memset(&mSessionID, 0, ((char*)&mTFCode - (char*)&mSessionID) + sizeof(mTFCode)); } P2PV2_Header(char *buf) { parseMsg(buf); } char* parseMsg(char *buf); char* createMsg(char *buf, const char* wlid, CMsnProto *ppro); bool isV2Hdr(void) { return true; } void logHeader(CMsnProto *ppro); }; #pragma pack() bool p2p_IsDlFileOk(filetransfer* ft); ///////////////////////////////////////////////////////////////////////////////////////// // Thread handling functions and datatypes #define MSG_DISABLE_HDR 1 #define MSG_REQUIRE_ACK 2 #define MSG_RTL 4 #define MSG_OFFLINE 8 struct CMsnProto; typedef void (__cdecl CMsnProto::*MsnThreadFunc)(void*); struct ThreadData { ThreadData(); ~ThreadData(); STRLIST mJoinedContactsWLID; STRLIST mJoinedIdentContactsWLID; char* mInitialContactWLID; TInfoType mType; // thread type MsnThreadFunc mFunc; // thread entry point char mServer[80]; // server name HANDLE s; // NetLib connection for the thread HANDLE mIncomingBoundPort; // Netlib listen for the thread HANDLE hWaitEvent; WORD mIncomingPort; TCHAR mChatID[10]; bool mIsMainThread; clock_t mWaitPeriod; CMsnProto* proto; //----| for gateways |---------------------------------------------------------------- char mSessionID[50]; // Gateway session ID char mGatewayIP[80]; // Gateway IP address int mGatewayTimeout; bool sessionClosed; bool termPending; bool gatewayType; //----| for switchboard servers only |------------------------------------------------ bool firstMsgRecv; int mCaller; char mCookie[130]; // for switchboard servers only LONG mTrid; // current message ID UINT mTimerId; // typing notifications timer id //----| for file transfers only |----------------------------------------------------- filetransfer* mMsnFtp; // file transfer block bool mBridgeInit; //----| internal data buffer |-------------------------------------------------------- int mBytesInData; // bytes available in data buffer char mData[8192]; // data buffer for connection //----| methods |--------------------------------------------------------------------- void applyGatewayData(HANDLE hConn, bool isPoll); void getGatewayUrl(char* dest, int destlen, bool isPoll); void processSessionData(const char* xMsgr, const char* xHost); void startThread(MsnThreadFunc , CMsnProto *prt); int send(const char data[], size_t datalen); int recv(char* data, size_t datalen); void resetTimeout(bool term = false); bool isTimeout(void); void sendTerminate(void); void sendCaps(void); int sendMessage(int msgType, const char* email, int netId, const char* msg, int parFlags); int sendRawMessage(int msgType, const char* data, int datLen); int sendPacket(const char* cmd, const char* fmt, ...); int contactJoined(const char* email); int contactLeft(const char* email); HANDLE getContactHandle(void); }; ///////////////////////////////////////////////////////////////////////////////////////// // MSN P2P session support #define MSN_APPID_AVATAR 1 #define MSN_APPID_AVATAR2 12 #define MSN_APPID_FILE 2 #define MSN_APPID_WEBCAM 4 #define MSN_APPID_MEDIA_SHARING 35 #define MSN_APPID_IMAGE 33 #define MSN_APPID_CUSTOMSMILEY 3 #define MSN_APPID_CUSTOMANIMATEDSMILEY 4 #define MSN_TYPEID_FTPREVIEW 0 #define MSN_TYPEID_FTNOPREVIEW 1 #define MSN_TYPEID_CUSTOMSMILEY 2 #define MSN_TYPEID_DISPLAYPICT 3 #define MSN_TYPEID_BKGNDSHARING 4 #define MSN_TYPEID_BKGNDIMG 5 #define MSN_TYPEID_WINK 8 inline bool IsChatHandle(HANDLE hContact) { return (INT_PTR)hContact < 0; } ///////////////////////////////////////////////////////////////////////////////////////// // Message queue #define MSGQUE_RAW 1 struct MsgQueueEntry { char* wlid; char* message; filetransfer* ft; STRLIST* cont; int msgType; int msgSize; int seq; int allocatedToThread; time_t ts; int flags; }; ///////////////////////////////////////////////////////////////////////////////////////// // User lists template< class T > int CompareId(const T* p1, const T* p2) { return _stricmp(p1->id, p2->id); } struct ServerGroupItem { char* id; char* name; // in UTF8 }; struct MsnPlace { char *id; unsigned cap1; unsigned cap2; unsigned p2pMsgId; unsigned short p2pPktNum; ~MsnPlace() { mir_free(id); } }; struct MsnContact { char *email; char *invite; char *nick; HANDLE hContact; int list; int netId; int p2pMsgId; unsigned cap1; unsigned cap2; OBJLIST places; MsnContact() : places(1, CompareId) {} ~MsnContact() { mir_free(email); mir_free(nick); mir_free(invite); } }; #define cap_OnlineViaMobile 0x00000001 #define cap_OnlineMSN8User 0x00000002 #define cap_SupportsGifInk 0x00000004 #define cap_SupportsIsfInk 0x00000008 #define cap_WebCamDetected 0x00000010 #define cap_SupportsChunking 0x00000020 #define cap_MobileEnabled 0x00000040 #define cap_WebWatchEnabled 0x00000080 #define cap_SupportsActivities 0x00000100 #define cap_OnlineViaWebIM 0x00000200 #define cap_MobileDevice 0x00000400 #define cap_OnlineViaTGW 0x00000800 #define cap_HasSpace 0x00001000 #define cap_IsMceUser 0x00002000 #define cap_SupportsDirectIM 0x00004000 #define cap_SupportsWinks 0x00008000 #define cap_SupportsSharedSearch 0x00010000 #define cap_IsBot 0x00020000 #define cap_SupportsVoiceIM 0x00040000 #define cap_SupportsSChannel 0x00080000 #define cap_SupportsSipInvite 0x00100000 #define cap_SupportsMultipartyMedia 0x00200000 #define cap_SupportsSDrive 0x00400000 #define cap_SupportsPageModeMessaging 0x00800000 #define cap_HasOneCare 0x01000000 #define cap_SupportsTurn 0x02000000 #define cap_SupportsP2PBootstrap 0x04000000 #define cap_UsingAlias 0x08000000 #define capex_IsSmsOnly 0x00000001 #define capex_SupportsVoiceOverMsnp 0x00000002 #define capex_SupportsUucpSipStack 0x00000004 #define capex_SupportsApplicationMsg 0x00000008 #define capex_RTCVideoEnabled 0x00000010 #define capex_SupportsPeerToPeerV2 0x00000020 #define capex_IsAuthWebIMUser 0x00000040 #define capex_Supports1On1ViaGroup 0x00000080 #define capex_SupportsOfflineIM 0x00000100 #define capex_SupportsSharingVideo 0x00000200 #define capex_SupportsNudges 0x00000400 #define capex_CircleVoiceIMEnabled 0x00000800 #define capex_SharingEnabled 0x00001000 #define capex_MobileSuspendIMFanoutDisable 0x00002000 #define capex_SupportsP2PMixerRelay 0x00008000 #define capex_ConvWindowFileTransfer 0x00020000 #define capex_VideoCallSupports16x9 0x00040000 #define capex_SupportsP2PEnveloping 0x00080000 #define capex_YahooIMDisabled 0x00400000 #define capex_SIPTunnelVersion2 0x00800000 #define capex_VoiceClipSupportsWMAFormat 0x01000000 #define capex_VoiceClipSupportsCircleIM 0x02000000 #define capex_SupportsSocialNewsObjectTypes 0x04000000 #define capex_CustomEmoticonsCapable 0x08000000 #define capex_SupportsUTF8MoodMessages 0x10000000 #define capex_FTURNCapable 0x20000000 #define capex_SupportsP4Activity 0x40000000 #define capex_SupportsChats 0x80000000 #define NETID_UNKNOWN 0x0000 #define NETID_MSN 0x0001 #define NETID_LCS 0x0002 #define NETID_MOB 0x0004 #define NETID_MOBNET 0x0008 #define NETID_CIRCLE 0x0009 #define NETID_TMPCIRCLE 0x000A #define NETID_CID 0x000B #define NETID_CONNECT 0x000D #define NETID_REMOTE 0x000E #define NETID_SMTP 0x0010 #define NETID_YAHOO 0x0020 #define LIST_FL 0x0001 #define LIST_AL 0x0002 #define LIST_BL 0x0004 #define LIST_RL 0x0008 #define LIST_PL 0x0010 #define LIST_LL 0x0080 #define LIST_REMOVE 0x0100 #define LIST_REMOVENH 0x0300 ///////////////////////////////////////////////////////////////////////////////////////// // MSN plugin options typedef struct _tag_MYOPTIONS { bool EnableSounds; bool ShowErrorsAsPopups; bool SlowSend; bool ManageServer; char szEmail[MSN_MAX_EMAIL_LEN]; char szMachineGuid[MSN_GUID_LEN]; char szMachineGuidP2P[MSN_GUID_LEN]; } MYOPTIONS; ///////////////////////////////////////////////////////////////////////////////////////// // Windows error class struct TWinErrorCode { WINAPI TWinErrorCode(); WINAPI ~TWinErrorCode(); char* WINAPI getText(); long mErrorCode; char* mErrorText; }; ///////////////////////////////////////////////////////////////////////////////////////// // External variables #define MSN_NUM_MODES 9 const char msnProtChallenge[] = "C1BX{V4W}Q3*10SM"; const char msnProductID[] = "PROD0120PW!CCV9@"; const char msnAppID[] = "AAD9B99B-58E6-4F23-B975-D9EC1F9EC24A"; const char msnStoreAppId[] = "Messenger Client 9.0"; const char msnProductVer[] = "14.0.8117.0416"; const char msnProtID[] = "MSNP18"; extern HINSTANCE hInst; extern bool msnHaveChatDll; /////////////////////////////////////////////////////////////////////////////// // UTF8 encode helper class UTFEncoder { private: char* m_body; public: UTFEncoder(const char* pSrc) : m_body(mir_utf8encode(pSrc)) {} UTFEncoder(const wchar_t* pSrc) : m_body(mir_utf8encodeW(pSrc)) {} ~UTFEncoder() { mir_free(m_body); } const char* str() const { return m_body; } }; #define UTF8(A) UTFEncoder(A).str() typedef enum _tag_ConEnum { conUnknown, conDirect, conUnknownNAT, conIPRestrictNAT, conPortRestrictNAT, conSymmetricNAT, conFirewall, conISALike } ConEnum; #pragma pack(1) typedef struct _tag_UDPProbePkt { unsigned char version; unsigned char serviceCode; unsigned short clientPort; unsigned clientIP; unsigned short discardPort; unsigned short testPort; unsigned testIP; unsigned trId; } UDPProbePkt; #pragma pack() extern const char* conStr[]; typedef struct _tag_MyConnectionType { unsigned intIP; unsigned extIP; ConEnum udpConType; ConEnum tcpConType; unsigned weight; bool upnpNAT; bool icf; const IN_ADDR GetMyExtIP(void) { return *((PIN_ADDR)&extIP); } const char* GetMyExtIPStr(void) { return inet_ntoa(GetMyExtIP()); } const char* GetMyUdpConStr(void) { return conStr[udpConType]; } void SetUdpCon(const char* str); void CalculateWeight(void); } MyConnectionType; struct chunkedmsg { char* id; char* msg; size_t size; size_t recvsz; bool bychunk; chunkedmsg(const char* tid, const size_t totsz, const bool bychunk); ~chunkedmsg(); void add(const char* msg, size_t offset, size_t portion); bool get(char*& tmsg, size_t& tsize); }; struct DeleteParam { CMsnProto *proto; HANDLE hContact; }; INT_PTR CALLBACK DlgDeleteContactUI(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); struct InviteChatParam { TCHAR* id; HANDLE hContact; CMsnProto* ppro; InviteChatParam(const TCHAR* id, HANDLE hContact, CMsnProto* ppro) : id(mir_tstrdup(id)), hContact(hContact), ppro(ppro) {} ~InviteChatParam() { mir_free(id); } }; INT_PTR CALLBACK DlgInviteToChat(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);