/*
* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include <sys/socket.h>
#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <wchar.h>
#include <string.h>
#include "logger.h"
#include "connectionlistener.h"
#include "javacommonutils.h"
#include "datagramserverconnection.h"
#include "datagramserverconnectionfactory.h"
#include "pushexception.h"
#include "pusherrorcodes.h"
#define INVALID_SOCKET (-1)
#define MAX_SIZE 256
#define BUFFER_SIZE 512
using namespace java::util;
DatagramServerConnection::DatagramServerConnection()
{
LOG(ESOCKET, EInfo, "+DatagramServerConnection - default constructor");
mListener = NULL,
mThreadId = 0;
mKeepRunning = false;
mListenDatagram = INVALID_SOCKET;
mAcceptDatagram = INVALID_SOCKET;
mPort = 0;
mIsNormalServerConnection = true;
mError = 0;
mOpenMonitor = 0;
}
OS_EXPORT DatagramServerConnection::DatagramServerConnection(
const std::wstring& aUri, const std::wstring& aFilter) :
mListener(0), mThreadId(0), mKeepRunning(false), mListenDatagram(
INVALID_SOCKET), mAcceptDatagram(INVALID_SOCKET), mUri(aUri),
mFilter(aFilter), mPort(0)
{
JELOG2(ESOCKET);
// sanity check for url
ILOG1(ESOCKET,
"++DatagramServerConnection::DatagramServerConnection() aUri = %s",
aUri.c_str());
mIsNormalServerConnection = false;
mIsAppRunning = false;
mError = 0;
mOpenMonitor = 0;
if (0 == mOpenMonitor)
{
mOpenMonitor = Monitor::createMonitor();
}
if (aUri.length() == 12)
mPort = 0;
else
{
std::wstring port = aUri.substr(12); // "datagram://:"
mPort = JavaCommonUtils::wstringToInt(port);
}
LOG1(ESOCKET, EInfo, "created DatagramServerConnection on port %d", mPort);
}
OS_EXPORT DatagramServerConnection::~DatagramServerConnection()
{
if (mOpenMonitor)
{
delete mOpenMonitor;
}
JELOG2(ESOCKET);
}
OS_EXPORT void DatagramServerConnection::open(ConnectionListener* aListener)
{
JELOG2(ESOCKET);
mListener = aListener;
mKeepRunning = true;
int rc = pthread_create(&mThreadId, NULL,
DatagramServerConnection::listenThread, this);
mOpenMonitor->wait();
ILOG2(ESOCKET,"after release in datagram rc = %dm mPort = %d",rc,mPort);
if (rc < 0 || mError < 0)
{
ELOG1(ESOCKET,"ERROR!!! Datagram server conn already exists on Port %d : Open Failed",mPort);
std::string errTxt("ERROR!!! Opening of Datagram Server failed ");
throw PushException(COMMON_SRV_CONN_PLUGIN_ERROR,errTxt,__FILE__, __FUNCTION__,__LINE__);
}
}
void* DatagramServerConnection::listenThread(void* aParams)
{
JELOG2(ESOCKET);
struct sockaddr_in sender_addr;
sender_addr.sin_family = AF_INET;
unsigned int size = sizeof(sender_addr);
bool match = false;
DatagramServerConnection* pThis =
reinterpret_cast<DatagramServerConnection*>(aParams);
pThis->mListenDatagram = pThis->open(pThis->mPort);
if (pThis->mListenDatagram < 0)
{
pThis->mError = -errno;
(pThis->mOpenMonitor)->notify();
pthread_exit(0);
}
(pThis->mOpenMonitor)->notify();
int rt = -1;
char * addr = new char[MAX_SIZE];
if (rt < 0 && pThis->mKeepRunning)
{
char *buf = new char[BUFFER_SIZE];
// wchar_t * sender = new wchar_t[256];
char * sender = new char[256];
while (match == false)
{
rt = recvfrom(pThis->mListenDatagram, buf, BUFFER_SIZE, 0,
(sockaddr*) &sender_addr, &size);
strcpy(sender,inet_ntoa(sender_addr.sin_addr));
ILOG2(ESOCKET, "pThis->mListenDatagram = %d, port = %d",pThis-> mListenDatagram,pThis->mPort);
// validate the filter
/*if(strcmp((const char *)pThis->mFilter.c_str(),(const char *) NULL)== 0)
{
LOG(ESOCKET,EInfo,"filter is NULL");
match = true;
}
else
{
if( strcmp((const char *)"*",(const char *)pThis->mFilter.c_str()) == 0)
{
LOG(ESOCKET,EInfo,"filter = '*'");
match = true;
}
if( strcmp((const char *)"?",(const char *)pThis->mFilter.c_str()) == 0)
{
LOG(ESOCKET,EInfo,"filter = '?'");
match = true;
}
}*/
match = true;
}
}
if (!(rt < 0)) // we have incoming connection
{
pThis->mAcceptDatagram = pThis->mListenDatagram;
if (pThis->mIsAppRunning == true)
{
pThis->setActivityFlag(false);
pthread_exit(0);
}
else
{
if (pThis->mListener)
{
pThis->mListener->msgArrived();
}
pThis->setActivityFlag(true);
}
}
return 0;
}
OS_EXPORT int DatagramServerConnection::open()
{
JELOG2(ESOCKET);
mIsAppRunning = true;
if (mKeepRunning) // we are listening for push connection
{
mKeepRunning = false;
pthread_join(mThreadId, NULL);
}
else
{
mListenDatagram = open(mPort);
}
LOG2(ESOCKET,EInfo, "mListenDatagram = %d, mPort = %d", mListenDatagram, mPort);
setActivityFlag(false);
return mListenDatagram;
}
int DatagramServerConnection::open(int aPort)
{
JELOG2(ESOCKET);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons((unsigned short) aPort);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd == INVALID_SOCKET)
{
return -1;
}
addr.sin_addr.s_addr = htonl(INADDR_ANY);
int res = bind(fd, (struct sockaddr*) &addr, sizeof(addr));
// when server is opened on dynamic port, append the port to the url
/*if(mPort == 0)
{
mPort = addr.sin_port;
LOG1(ESOCKET, EInfo, "dynamic port : %d", mPort);
std::wstring portString = JavaCommonUtils::intToWstring(mPort);
mUri.append(portString);
LOG1(ESOCKET, EInfo, "after appending mUri with dynamic port : %s", mUri);
}*/
LOG3(ESOCKET,EInfo, "server datgram sd = %dm bind returns %d, mPort = %d ", fd, res, mPort);
if (res != -1)
{
return fd;
}
LOG1(ESOCKET,EInfo, "-errno : %d" , -errno);
close(fd);
return -1;
}
OS_EXPORT void DatagramServerConnection::close()
{
JELOG2(ESOCKET);
::close(mListenDatagram);
::close(mAcceptDatagram);
if (mKeepRunning) // we are listening for push connection
{
mKeepRunning = false;
pthread_join(mThreadId, NULL);
}
// reset members
mListenDatagram = INVALID_SOCKET;
mAcceptDatagram = INVALID_SOCKET;
mListener = 0;
mThreadId = 0;
//mKeepRunning = false;
}
OS_EXPORT std::wstring DatagramServerConnection::getUri() const
{
LOG(ESOCKET,EInfo, "DatagramServerConnection::getUri() ");
return mUri;
}
OS_EXPORT std::wstring DatagramServerConnection::getFilter() const
{
return mFilter;
}
OS_EXPORT void DatagramServerConnection::setFilter(const std::wstring& aFilter)
{
JELOG2(ESOCKET);
mFilter = aFilter;
}
OS_EXPORT bool DatagramServerConnection::isNormalServerConnection()
{
return mIsNormalServerConnection;
}
OS_EXPORT void DatagramServerConnection::setNormalServerConnection()
{
LOG(ESOCKET,EInfo,"Push is enabled");
mIsNormalServerConnection = true;
}
int DatagramServerConnection::close(int fd)
{
return ::close(fd);
}
/**
* SetActivityFlag method updates status whether
* ServerConnection object has pending message(= message arrived but not yet read).
* @param aFlag Updating logic of pending message flag:
* - Set it 'true' when message arrives to the connection.
* - Set it 'false' when ServerConnection::open() operation has been called.
*/
void DatagramServerConnection::setActivityFlag(bool aFlag)
{
DatagramServerConnectionFactory& scf =
DatagramServerConnectionFactory::getFactory();
scf.setPendingMsgFlag(mUri, aFlag);
}