/**
* @file miranda-utils.c
*
* pidgin-sipe
*
* Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
*
* 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 <windows.h>
#include "miranda-version.h"
#include "newpluginapi.h"
#include "m_protosvc.h"
#include "m_protoint.h"
#include "m_database.h"
#include "m_netlib.h"
#include "m_langpack.h"
#include "m_protomod.h"
#include "glib.h"
#include "sipe-backend.h"
#include "sipe-core.h"
#include "miranda-private.h"
/*
* Table to hold HTML entities we want to convert
*/
static GHashTable *entities = NULL;
/**
* Various shortcut functions to get database values
*/
gchar*
sipe_miranda_getGlobalString(const gchar* name)
{
return DBGetString( NULL, SIPSIMPLE_PROTOCOL_NAME, name );
}
gchar*
sipe_miranda_getContactString(const SIPPROTO *pr, HANDLE hContact, const gchar* name)
{
return DBGetString( hContact, pr->proto.m_szModuleName, name );
}
gchar*
sipe_miranda_getString(const SIPPROTO *pr, const gchar* name)
{
return sipe_miranda_getContactString( pr, NULL, name );
}
DWORD
sipe_miranda_getDword(const SIPPROTO *pr, HANDLE hContact, const gchar* name, DWORD* rv)
{
DBVARIANT dbv;
DBCONTACTGETSETTING cgs;
cgs.szModule = pr->proto.m_szModuleName;
cgs.szSetting = name;
cgs.pValue=&dbv;
if(CallService(MS_DB_CONTACT_GETSETTING,(WPARAM)hContact,(LPARAM)&cgs))
return 0;
if (rv) {
*rv = dbv.dVal;
return 1;
} else {
return dbv.dVal;
}
}
WORD
sipe_miranda_getGlobalWord(const gchar* name, WORD* rv)
{
DBVARIANT dbv;
DBCONTACTGETSETTING cgs;
cgs.szModule = SIPSIMPLE_PROTOCOL_NAME;
cgs.szSetting = name;
cgs.pValue=&dbv;
if(CallService(MS_DB_CONTACT_GETSETTING, (WPARAM)NULL,(LPARAM)&cgs))
return 0;
if (rv) {
*rv = dbv.wVal;
return 1;
} else {
return dbv.wVal;
}
}
WORD
sipe_miranda_getWord(const SIPPROTO *pr, HANDLE hContact, const gchar* name, WORD* rv)
{
DBVARIANT dbv;
DBCONTACTGETSETTING cgs;
cgs.szModule = pr->proto.m_szModuleName;
cgs.szSetting = name;
cgs.pValue=&dbv;
if(CallService(MS_DB_CONTACT_GETSETTING,(WPARAM)hContact,(LPARAM)&cgs))
return 0;
if (rv) {
*rv = dbv.wVal;
return 1;
} else {
return dbv.wVal;
}
}
gboolean
sipe_miranda_getBool(const SIPPROTO *pr, const gchar *name, gboolean defval)
{
WORD ret;
if (sipe_miranda_getWord( pr, NULL, name, &ret ))
return ret?TRUE:FALSE;
return defval;
}
int
sipe_miranda_getStaticString(const SIPPROTO *pr, HANDLE hContact, const gchar* valueName, gchar* dest, unsigned dest_len)
{
DBVARIANT dbv;
DBCONTACTGETSETTING sVal;
dbv.pszVal = dest;
dbv.cchVal = (WORD)dest_len;
dbv.type = DBVT_ASCIIZ;
sVal.pValue = &dbv;
sVal.szModule = pr->proto.m_szModuleName;
sVal.szSetting = valueName;
if (CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM)hContact, (LPARAM)&sVal) != 0)
return 1;
return (dbv.type != DBVT_ASCIIZ);
}
/**
* Various shortcut functions to set database values
*/
void
sipe_miranda_setGlobalString(const gchar* name, const gchar* value)
{
DBWriteContactSettingString(NULL, SIPSIMPLE_PROTOCOL_NAME, name, value);
}
void
sipe_miranda_setGlobalStringUtf(const gchar* valueName, const gchar* parValue )
{
DBWriteContactSettingStringUtf( NULL, SIPSIMPLE_PROTOCOL_NAME, valueName, parValue );
}
void
sipe_miranda_setContactString(const SIPPROTO *pr, HANDLE hContact, const gchar* name, const gchar* value)
{
DBWriteContactSettingString(hContact, pr->proto.m_szModuleName, name, value);
}
void
sipe_miranda_setContactStringUtf(const SIPPROTO *pr, HANDLE hContact, const gchar* valueName, const gchar* parValue )
{
DBWriteContactSettingStringUtf( hContact, pr->proto.m_szModuleName, valueName, parValue );
}
void
sipe_miranda_setString(const SIPPROTO *pr, const gchar* name, const gchar* value)
{
sipe_miranda_setContactString( pr, NULL, name, value );
}
void
sipe_miranda_setStringUtf(const SIPPROTO *pr, const gchar* name, const gchar* value)
{
sipe_miranda_setContactStringUtf( pr, NULL, name, value );
}
int
sipe_miranda_setGlobalWord(const gchar* szSetting, WORD wValue)
{
return DBWriteContactSettingWord(NULL, SIPSIMPLE_PROTOCOL_NAME, szSetting, wValue);
}
int
sipe_miranda_setWord(const SIPPROTO *pr, HANDLE hContact, const gchar* szSetting, WORD wValue)
{
return DBWriteContactSettingWord(hContact, pr->proto.m_szModuleName, szSetting, wValue);
}
int
sipe_miranda_setBool(const SIPPROTO *pr, const gchar *name, gboolean value)
{
return DBWriteContactSettingWord(NULL, pr->proto.m_szModuleName, name, value?1:0);
}
/*
* Initialize our table of HTML entities
*/
#define ADDENT(a,b) g_hash_table_insert(entities, a, b)
static void
initEntities(void)
{
entities = g_hash_table_new(g_str_hash, g_str_equal);
ADDENT("nbsp"," ");
ADDENT("quot","\"");
ADDENT("lt","<");
ADDENT("gt",">");
ADDENT("apos","'");
}
/*
* WARNING: Returns miranda-allocated string, not glib one
*/
gchar*
sipe_miranda_eliminate_html(const gchar *string, int len)
{
gchar *tmp = (char*)mir_alloc(len + 1);
int i,j;
BOOL tag = FALSE;
gchar *res;
if (!entities)
initEntities();
for (i=0,j=0;i<len;i++)
{
if (!tag && string[i] == '<')
{
if ((i + 4 <= len) && (!_strnicmp(string + i, "<br>", 4) || !_strnicmp(string + i, "<br/>", 5)))
{ // insert newline
tmp[j] = '\r';
j++;
tmp[j] = '\n';
j++;
}
tag = TRUE;
}
else if (tag && string[i] == '>')
{
tag = FALSE;
}
else if (!tag)
{
char *tkend;
if ((string[i] == '&') && (tkend = strstr((char*)&string[i], ";")))
{
gchar *rep;
gchar c = *tkend;
*tkend = '\0';
rep = (char*)g_hash_table_lookup(entities, &string[i+1]);
if (rep)
{
strcpy(&tmp[j], rep);
j += strlen(rep);
i += strlen(&string[i]);
*tkend = c;
}
else
{
*tkend = c;
tmp[j] = string[i];
j++;
}
}
else
{
tmp[j] = string[i];
j++;
}
}
tmp[j] = '\0';
}
res = tmp;
return res;
}
unsigned short
sipe_miranda_network_get_port_from_fd( HANDLE fd )
{
SOCKET sock = CallService(MS_NETLIB_GETSOCKET, (WPARAM)fd, (LPARAM)0);
struct sockaddr_in sockbuf;
int namelen = sizeof(sockbuf);
getsockname(sock, (struct sockaddr *)&sockbuf, &namelen);
SIPE_DEBUG_INFO("<%x> <%x><%x><%s>", namelen, sockbuf.sin_family, sockbuf.sin_port, inet_ntoa(sockbuf.sin_addr) );
return sockbuf.sin_port;
}
/* Misc functions */
TCHAR _tcharbuf[32768];
TCHAR* CHAR2TCHAR( const char *chr ) {
#ifdef UNICODE
if (!chr) return NULL;
mbstowcs( _tcharbuf, chr, strlen(chr)+1 );
return _tcharbuf;
#else
return chr;
#endif
}
char _charbuf[32768];
char* TCHAR2CHAR( const TCHAR *tchr ) {
#ifdef UNICODE
if (!tchr) return NULL;
wcstombs( _charbuf, tchr, wcslen(tchr)+1 );
return _charbuf;
#else
return tchr;
#endif
}
HANDLE
sipe_miranda_AddEvent(const SIPPROTO *pr, HANDLE hContact, WORD wType, DWORD dwTime, DWORD flags, DWORD cbBlob, PBYTE pBlob)
{
DBEVENTINFO dbei = {0};
dbei.cbSize = sizeof(dbei);
dbei.szModule = pr->proto.m_szModuleName;
dbei.timestamp = dwTime;
dbei.flags = flags;
dbei.eventType = wType;
dbei.cbBlob = cbBlob;
dbei.pBlob = pBlob;
return (HANDLE)CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei);
}
struct msgboxinfo {
TCHAR *msg;
TCHAR *caption;
};
static unsigned __stdcall
msgboxThread(void* arg)
{
struct msgboxinfo *err = (struct msgboxinfo*)arg;
if (!err)
return 0;
MessageBox(NULL, err->msg, err->caption, MB_OK);
mir_free(err->msg);
mir_free(err->caption);
g_free(err);
return 0;
}
void
sipe_miranda_msgbox(const char *msg, const char *caption)
{
struct msgboxinfo *info = g_new(struct msgboxinfo,1);
info->msg = mir_a2t(msg);
info->caption = mir_a2t(caption);
CloseHandle((HANDLE) mir_forkthreadex( msgboxThread, info, 8192, NULL ));
}
char* sipe_miranda_acktype_strings[] = {
"ACKTYPE_MESSAGE", "ACKTYPE_URL", "ACKTYPE_FILE",
"ACKTYPE_CHAT", "ACKTYPE_AWAYMSG", "ACKTYPE_AUTHREQ",
"ACKTYPE_ADDED", "ACKTYPE_GETINFO", "ACKTYPE_SETINFO",
"ACKTYPE_LOGIN", "ACKTYPE_SEARCH", "ACKTYPE_NEWUSER",
"ACKTYPE_STATUS", "ACKTYPE_CONTACTS", "ACKTYPE_AVATAR",
"ACKTYPE_EMAIL" };
char* sipe_miranda_ackresult_strings[] = {
"ACKRESULT_SUCCESS", "ACKRESULT_FAILED", "ACKRESULT_CONNECTING",
"ACKRESULT_CONNECTED", "ACKRESULT_INITIALISING", "ACKRESULT_SENTREQUEST",
"ACKRESULT_DATA", "ACKRESULT_NEXTFILE", "ACKRESULT_FILERESUME",
"ACKRESULT_DENIED", "ACKRESULT_STATUS", "ACKRESULT_LISTENING",
"ACKRESULT_CONNECTPROXY", "ACKRESULT_SEARCHRESULT" };
int
sipe_miranda_SendBroadcast(SIPPROTO *pr, HANDLE hContact,int type,int result,HANDLE hProcess,LPARAM lParam)
{
ACKDATA ack = {0};
ack.cbSize = sizeof(ACKDATA);
ack.szModule = pr->proto.m_szModuleName;
ack.hContact = hContact;
ack.type = type;
ack.result = result;
ack.hProcess = hProcess;
ack.lParam = lParam;
SIPE_DEBUG_INFO("broadcasting contact <%08x> type <%d:%s> result <%d:%s> par1 <%08x> par2 <%08x>",
hContact,
type, sipe_miranda_acktype_strings[type],
result, sipe_miranda_ackresult_strings[result>99 ? result-98 : result],
hProcess, lParam);
return CallServiceSync(MS_PROTO_BROADCASTACK,0,(LPARAM)&ack);
}
struct sipe_miranda_connection_info {
SIPPROTO *pr;
gchar *server_name;
int server_port;
int timeout;
gboolean tls;
void (*callback)(HANDLE fd, void *data, const gchar *reason);
void *data;
/* Private. For locking only */
HANDLE hDoneEvent;
HANDLE fd;
const gchar *reason;
};
static void __stdcall
connection_cb_async(void *data)
{
struct sipe_miranda_connection_info *entry = (struct sipe_miranda_connection_info*)data;
SIPE_DEBUG_INFO("[C:%08x] Calling real connected function", entry);
entry->callback(entry->fd, entry->data, entry->reason);
SetEvent(entry->hDoneEvent);
}
static unsigned __stdcall
sipe_miranda_connected_callback(void* data)
{
struct sipe_miranda_connection_info *info = (struct sipe_miranda_connection_info*)data;
SIPPROTO *pr = info->pr;
NETLIBOPENCONNECTION ncon = {0};
ncon.cbSize = sizeof(ncon);
ncon.flags = NLOCF_V2;
ncon.szHost = info->server_name;
ncon.wPort = info->server_port;
ncon.timeout = info->timeout;
info->fd = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)pr->m_hServerNetlibUser, (LPARAM)&ncon);
if (info->fd == NULL) {
SIPE_DEBUG_INFO("[C:%08x] Connection to <%s:%d> failed", info, info->server_name, info->server_port);
info->reason = "Connection failed";
} else {
SIPE_DEBUG_INFO("[C:%08x] connected <%d>", info, (int)info->fd);
if (info->tls)
{
if (!CallService(MS_NETLIB_STARTSSL, (WPARAM)info->fd, 0))
{
Netlib_CloseHandle(info->fd);
info->fd = NULL;
info->reason = "Failed to enabled SSL";
} else {
SIPE_DEBUG_INFO("[C:%08x] SSL enabled", info);
}
} else {
info->reason = NULL;
}
}
info->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
CallFunctionAsync(connection_cb_async, info);
WaitForSingleObject(info->hDoneEvent, INFINITE);
CloseHandle(info->hDoneEvent);
g_free(info->server_name);
g_free(info);
return 0;
}
struct sipe_miranda_connection_info *
sipe_miranda_connect(SIPPROTO *pr,
const gchar *host,
int port,
gboolean tls,
int timeout,
void (*callback)(HANDLE fd, void *data, const gchar *reason),
void *data)
{
struct sipe_miranda_connection_info *info = g_new0(struct sipe_miranda_connection_info, 1);
SIPE_DEBUG_INFO("[C:%08x] Connecting to <%s:%d> tls <%d> timeout <%d>", info, host, port, tls, timeout);
info->pr = pr;
info->server_name = g_strdup(host);
info->server_port = port;
info->timeout = timeout;
info->tls = tls;
info->callback = callback;
info->data = data;
CloseHandle((HANDLE) mir_forkthreadex( sipe_miranda_connected_callback, info, 65536, NULL ));
return info;
}
struct sipe_miranda_ack_args
{
HANDLE hContact;
int nAckType;
int nAckResult;
HANDLE hSequence;
gchar *pszMessage;
const gchar *modname;
};
static unsigned __stdcall
ProtocolAckThread(struct sipe_miranda_ack_args* args)
{
ProtoBroadcastAck(args->modname, args->hContact, args->nAckType, args->nAckResult, args->hSequence, (LPARAM)args->pszMessage);
if (args->nAckResult == ACKRESULT_SUCCESS)
SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent ACK");
else if (args->nAckResult == ACKRESULT_FAILED)
SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent NACK");
g_free(args->pszMessage);
g_free(args);
return 0;
}
void
sipe_miranda_SendProtoAck( SIPPROTO *pr, HANDLE hContact, DWORD dwCookie, int nAckResult, int nAckType, const char* pszMessage)
{
struct sipe_miranda_ack_args* pArgs = g_new0(struct sipe_miranda_ack_args, 1);
pArgs->hContact = hContact;
pArgs->hSequence = (HANDLE)dwCookie;
pArgs->nAckResult = nAckResult;
pArgs->nAckType = nAckType;
pArgs->pszMessage = g_strdup(pszMessage);
pArgs->modname = pr->proto.m_szModuleName;
CloseHandle((HANDLE) mir_forkthreadex(ProtocolAckThread, pArgs, 65536, NULL));
}
gboolean
sipe_miranda_cmd(gchar *cmd, gchar *buf, DWORD *maxlen)
{
STARTUPINFOA si = {0};
PROCESS_INFORMATION pi = {0};
SECURITY_ATTRIBUTES sa = {0};
HANDLE rd,wr;
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
if (!CreatePipe(&rd, &wr, &sa, 0))
{
SIPE_DEBUG_INFO_NOFORMAT("Could not create pipe");
return FALSE;
}
SetHandleInformation(rd, HANDLE_FLAG_INHERIT, 0);
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = wr;
si.hStdError = wr;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
if (!CreateProcessA(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
SIPE_DEBUG_INFO("Could not run child program <%s> (%d)", cmd, GetLastError());
return FALSE;
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
if (!ReadFile(rd, buf, *maxlen, maxlen, NULL))
{
SIPE_DEBUG_INFO("Could not read from child program <%s>", cmd);
return FALSE;
}
return TRUE;
}
gchar*
sipe_miranda_html2rtf(const gchar *text)
{
const gchar *intro = "{\\rtf1\\ansi";
const gchar *link1 = "{\\field{\\*\\fldinst{HYPERLINK \"";
const gchar *link2 = "}}{\\fldrslt {\\ul\\cf2 ";
gchar *tmp = g_malloc(strlen(text)+1);
int maxlen = strlen(text);
const gchar *i = text;
int j = 0;
gboolean skiptag = FALSE;
gboolean escape = FALSE;
gboolean copystring = FALSE;
gboolean link_stage2 = FALSE;
strncpy(tmp+j, intro, maxlen-j);
j += strlen(intro);
while (*i)
{
if (j+100>=maxlen) /* 100 is max substitution size */
{
maxlen += 128;
tmp = g_realloc(tmp, maxlen);
}
if (skiptag && !escape && *i != '>') {
i++;
} else if (skiptag && !escape) {
i++;
skiptag = FALSE;
} else if (copystring) {
if (!escape && *i == '"') copystring = FALSE;
if (escape) escape = FALSE;
else if (*i == '\\') escape = TRUE;
*(tmp+j) = *i;
j++;
i++;
} else if (link_stage2) {
strcpy(tmp+j, link2);
j += strlen(link2);
link_stage2 = FALSE;
skiptag = TRUE;
} else if (g_str_has_prefix(i,"<br/>")) {
strcpy(tmp+j, "\\par\n");
j += 5;
i += 5;
} else if (g_str_has_prefix(i,"<b>")) {
strcpy(tmp+j, "\\b");
j += 2;
i += 3;
} else if (g_str_has_prefix(i,"</b>")) {
strcpy(tmp+j, "\\b0");
j += 3;
i += 4;
} else if (g_str_has_prefix(i,"<font size=\"")) {
strcpy(tmp+j, "\\fs36");
j += 5;
i += 12;
skiptag = TRUE;
} else if (g_str_has_prefix(i,"</font>")) {
strcpy(tmp+j, "\\fs20");
j += 5;
i += 7;
} else if (g_str_has_prefix(i,"<a href=\"")) {
strcpy(tmp+j, link1);
j += strlen(link1);
link_stage2 = TRUE;
copystring = TRUE;
i += 9;
} else if (g_str_has_prefix(i,"</a>")) {
strcpy(tmp+j, "}}}\\cf0 ");
j += 7;
i += 4;
} else if (*i == '<') {
skiptag = TRUE;
} else {
if (escape) {
escape = FALSE;
} else if (*i == '\\') {
escape = TRUE;
}
if (!skiptag)
{
*(tmp+j) = *i;
j++;
}
i++;
}
}
*(tmp+j++) = '}';
*(tmp+j++) = '\0';
tmp = g_realloc(tmp, j);
return tmp;
}
int SipeStatusToMiranda(guint activity) {
switch (activity)
{
case SIPE_ACTIVITY_OFFLINE:
return ID_STATUS_OFFLINE;
case SIPE_ACTIVITY_AVAILABLE:
return ID_STATUS_ONLINE;
case SIPE_ACTIVITY_ON_PHONE:
return ID_STATUS_ONTHEPHONE;
case SIPE_ACTIVITY_DND:
case SIPE_ACTIVITY_URGENT_ONLY:
return ID_STATUS_DND;
case SIPE_ACTIVITY_AWAY:
case SIPE_ACTIVITY_OOF:
return ID_STATUS_NA;
case SIPE_ACTIVITY_LUNCH:
return ID_STATUS_OUTTOLUNCH;
case SIPE_ACTIVITY_BUSY:
case SIPE_ACTIVITY_IN_MEETING:
case SIPE_ACTIVITY_IN_CONF:
return ID_STATUS_OCCUPIED;
case SIPE_ACTIVITY_INVISIBLE:
return ID_STATUS_INVISIBLE;
case SIPE_ACTIVITY_BRB:
return ID_STATUS_AWAY;
case SIPE_ACTIVITY_UNSET:
return ID_STATUS_OFFLINE;
case SIPE_ACTIVITY_INACTIVE:
case SIPE_ACTIVITY_ONLINE:
case SIPE_ACTIVITY_BUSYIDLE:
return ID_STATUS_ONLINE;
default:
/* None of those? We'll have to guess. Online seems ok. */
return ID_STATUS_ONLINE;
}
/* Don't have SIPE equivalent of these:
- ID_STATUS_FREECHAT
*/
}
guint MirandaStatusToSipe(int status) {
switch (status)
{
case ID_STATUS_OFFLINE:
return SIPE_ACTIVITY_OFFLINE;
case ID_STATUS_ONLINE:
case ID_STATUS_FREECHAT:
return SIPE_ACTIVITY_AVAILABLE;
case ID_STATUS_ONTHEPHONE:
return SIPE_ACTIVITY_ON_PHONE;
case ID_STATUS_DND:
return SIPE_ACTIVITY_DND;
case ID_STATUS_NA:
return SIPE_ACTIVITY_AWAY;
case ID_STATUS_AWAY:
return SIPE_ACTIVITY_BRB;
case ID_STATUS_OUTTOLUNCH:
return SIPE_ACTIVITY_LUNCH;
case ID_STATUS_OCCUPIED:
return SIPE_ACTIVITY_BUSY;
case ID_STATUS_INVISIBLE:
return SIPE_ACTIVITY_INVISIBLE;
default:
return SIPE_ACTIVITY_UNSET;
}
}
gchar *sipe_miranda_uri_self(SIPPROTO *pr) {
gchar *username = sipe_miranda_getString(pr, "username");
gchar *uri = g_strdup_printf("sip:%s", username);
mir_free(username);
return uri;
}
/*
Local Variables:
mode: c
c-file-style: "bsd"
indent-tabs-mode: t
tab-width: 8
End:
*/