/*
 *    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
 */

/*
 *    Author : Boris Mikhailenko <stg34@stargazer.dp.ua>
 */

 /*
 $Revision: 1.7 $
 $Date: 2010/03/15 12:58:17 $
 */

#include <libintl.h>

#include <csignal>
#include <cstdio>
#include <cstring>
#include <ctime>

#include "stg/common.h"
#include "stg/ia.h"
#include "web.h"

extern WEB * web;
extern IA_CLIENT_PROT * clnp;

#define LISTEN_PORT (5580)

#include "css.h"

//---------------------------------------------------------------------------
#ifndef WIN32
void * RunWeb(void *)
{
sigset_t signalSet;
sigfillset(&signalSet);
pthread_sigmask(SIG_BLOCK, &signalSet, NULL);

#else
unsigned long WINAPI RunWeb(void *)
{
#endif
while (1)
    web->Run();
return NULL;
}
//---------------------------------------------------------------------------
WEB::WEB()
    : res(0),
      listenSocket(0),
      outerSocket(0),
      refreshPeriod(0),
      listenWebAddr(0)
{
#ifdef WIN32
res = WSAStartup(MAKEWORD(2,0), &wsaData);
#endif

for (int i = 0; i < DIR_NUM; i++)
    dirName[i] = "-";

refreshPeriod = 5;

memset(&ls, 0, sizeof(ls));
}
//---------------------------------------------------------------------------
void WEB::Start()
{
#ifdef WIN32
unsigned long pt;
CreateThread(
    NULL,   // pointer to thread security attributes
    16384,  // initial thread stack size, in bytes
    RunWeb, // pointer to thread function
    NULL,   // argument for new thread
    0,      // CREATE_SUSPENDED, // creation flags
    &pt     // pointer to returned thread identifier
   );
#else
pthread_create(&thread, NULL, RunWeb, NULL);
#endif
}
//---------------------------------------------------------------------------
void WEB::PrepareNet()
{
listenSocket = socket(PF_INET, SOCK_STREAM, 0);

struct sockaddr_in listenAddr;
listenAddr.sin_family = AF_INET;
listenAddr.sin_port = htons(LISTEN_PORT);
listenAddr.sin_addr.s_addr = listenWebAddr;

#ifndef WIN32
int lng = 1;
if (0 != setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &lng, 4))
    {
    printf("Setsockopt Fail\n");
    printf(">>> Error %s\n", strerror(errno));
    }
#else
//??? TODO
#endif


res = bind(listenSocket, (struct sockaddr*)&listenAddr, sizeof(listenAddr));

if (res == -1)
    {
    printf("Bind failed.\n");
    exit(0);
    }

res = listen(listenSocket, 0);
if (res == -1)
    {
    printf("Listen failed.\n");
    exit(0);
    }
}
//---------------------------------------------------------------------------
void WEB::SetRefreshPagePeriod(int p)
{
refreshPeriod = p;
if (refreshPeriod <= 0 || refreshPeriod > 24*3600)
    refreshPeriod = 5;
}
//---------------------------------------------------------------------------
void WEB::SetListenAddr(uint32_t ip)
{
listenWebAddr = ip;
}
//---------------------------------------------------------------------------
void WEB::Run()
{
PrepareNet();
char recvBuffer[4096];
while (1)
    {
    struct sockaddr_in outerAddr;

    #ifndef WIN32
    socklen_t outerAddrLen = sizeof(outerAddr);
    #else
    int outerAddrLen = sizeof(outerAddr);
    #endif

    outerSocket = accept(listenSocket, (struct sockaddr*)&outerAddr, &outerAddrLen);
    if (outerSocket == -1)
        {
        printf(">>> Error %s\n", strerror(errno));
        continue;
        }
    recv(outerSocket, recvBuffer, sizeof(recvBuffer), 0);

    if (strncmp(recvBuffer, "GET /sgauth.css", strlen("GET /sgauth.css")) == 0)
        {
        SendCSS();
        //printf("(1) recvBuffer=%s\n", recvBuffer);
        }
    else if (strncmp(recvBuffer, "GET /disconnect", strlen("GET /disconnect")) == 0)
        {
        clnp->Disconnect();
        Redirect("/");
        //printf("(2) recvBuffer=%s\n", recvBuffer);
        }
    else if (strncmp(recvBuffer, "GET /connect", strlen("GET /connect")) == 0)
        {
        clnp->Connect();
        Redirect("/");
        //printf("(3) recvBuffer=%s\n", recvBuffer);
        }
    else if (strncmp(recvBuffer, "GET /exit", strlen("GET /exit")) == 0)
        {
        Redirect("/");
        clnp->Disconnect();
        #ifdef WIN32
        Sleep(1000);
        #else
        struct timespec ts = {1, 0};
        nanosleep(&ts, NULL);
        #endif
        exit(0);
        }
    else
       {
       SendReply();
       //printf("(4) recvBuffer=%s\n", recvBuffer);
       }

    #ifdef WIN32
    closesocket(outerSocket);
    #else
    close(outerSocket);
    #endif
    }
}
//---------------------------------------------------------------------------
int WEB::Redirect(const char * url)
{
const char * redirect =
    "HTTP/1.0 200 OK\n"
    "Content-Type: text/html\n"
    "Connection: close"
    "\n\n"
    "<html>\n"
    "<head>\n"
    "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0;%s\">\n"
    "</head>\n"
    "<body>\n"
    "</body></html>\n\n";

char buff[2000];
sprintf(buff, redirect, url);
send(outerSocket, buff, strlen(buff), 0);

return 0;
}
//---------------------------------------------------------------------------
int WEB::SendReply()
{
int j, rowNum;

const char * replyHeader =
    "HTTP/1.0 200 OK\n"
    "Content-Type: text/html\n"
    "Connection: close"
    "\n\n"
    "<html>\n"
    "<head>\n"
    "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"%d\">\n"
    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1251\">\n"
    "<title>sgauth</title>\n"
    "<link rel=\"Stylesheet\" href=\"sgauth.css\">"
    "</head>\n"
    "<body>\n"
    "<H3>Stargazer</H3><p>\n";

const char * replyFooter = "</body></html>\n\n";

char replyHeaderBuffer[2000];
sprintf(replyHeaderBuffer, replyHeader, refreshPeriod);

send(outerSocket, replyHeaderBuffer, strlen(replyHeaderBuffer), 0);

char str[512];

int st = clnp->GetAuthorized();

sprintf(str, "<a href=\"connect\">%s</a><p>\n", gettext("Connect"));
res = send(outerSocket, str, strlen(str), 0);

sprintf(str, "<a href=\"disconnect\">%s</a><p>\n", gettext("Disconnect"));
res = send(outerSocket, str, strlen(str), 0);

sprintf(str, "<a href=\"/\">%s</a><p>\n", gettext("Refresh"));
res = send(outerSocket, str, strlen(str), 0);

sprintf(str, "<a href=\"exit\">%s</a><p>\n", gettext("Exit"));
res = send(outerSocket, str, strlen(str), 0);

sprintf(str, "<div id=\"%s\">%s</div><p>\n" , st ? "ConnectionStateOnline":"ConnectionStateOffline", st ? "Online":"Offline");
res = send(outerSocket, str, strlen(str), 0);

sprintf(str, "<div id=\"Cash\">%s: %.3f</div><p>\n" , gettext("Cash"), ls.cash / 1000.0);
res = send(outerSocket, str, strlen(str), 0);

sprintf(str, "<div id=\"Prepaid Traffic\">%s: %s</div><p>\n" ,
        gettext("PrepaidTraffic"),
        ls.freeMb[0] == 'C' ? ls.freeMb + 1 : ls.freeMb);
res = send(outerSocket, str, strlen(str), 0);

sprintf(str, "<TABLE id=\"TraffTable\">\n");
res = send(outerSocket, str, strlen(str), 0);
sprintf(str, "    <TR id=\"TraffTableCaptionRow\">\n");
res = send(outerSocket, str, strlen(str), 0);
sprintf(str, "       <TD id=\"TraffTableCaptionCellC\">&nbsp;</TD>\n");
res = send(outerSocket, str, strlen(str), 0);

rowNum = 0;
for (j = 0; j < DIR_NUM; j++)
    {
    if (dirName[j][0] == 0)
        continue;
    std::string s;
    KOIToWin(dirName[j], &s);// +++++++++ sigsegv ==========   TODO too long dir name crashes sgauth
    sprintf(str, "       <TD id=\"TraffTableCaptionCell%d\">%s</TD>\n", rowNum++, s.c_str());
    send(outerSocket, str, strlen(str), 0);
    }

sprintf(str,"    </TR>\n");
send(outerSocket, str, strlen(str), 0);

sprintf(str,"    <TR id=\"TraffTableUMRow\">\n");
send(outerSocket, str, strlen(str), 0);

sprintf(str,"        <TD id=\"TraffTableUMCellC\">%s</TD>\n", gettext("Month Upload"));
send(outerSocket, str, strlen(str), 0);

rowNum = 0;
for (j = 0; j < DIR_NUM; j++)
    {
    if (dirName[j][0] == 0)
        continue;
    sprintf(str,"        <TD id=\"TraffTableUMCell%d\">%s</TD>\n", rowNum++, IntToKMG(ls.mu[j], ST_F));
    res = send(outerSocket, str, strlen(str), 0);
    }

sprintf(str,"    </TR>\n");
res = send(outerSocket, str, strlen(str), 0);
sprintf(str,"    <TR id=\"TraffTableDMRow\">\n");
res = send(outerSocket, str, strlen(str), 0);
sprintf(str,"        <TD id=\"TraffTableDMCellC\">%s</TD>\n", gettext("Month Download"));
res = send(outerSocket, str, strlen(str), 0);

rowNum = 0;
for (j = 0; j < DIR_NUM; j++)
    {
    if (dirName[j][0] == 0)
        continue;
    sprintf(str,"        <TD id=\"TraffTableDMCell%d\">%s</TD>\n", rowNum++, IntToKMG(ls.md[j], ST_F));
    res = send(outerSocket, str, strlen(str), 0);
    }
sprintf(str,"    </TR>\n");
res = send(outerSocket, str, strlen(str), 0);


sprintf(str,"    <TR id=\"TraffTableUSRow\">\n");
res = send(outerSocket, str, strlen(str), 0);
sprintf(str,"        <TD id=\"TraffTableUSCellC\">%s</TD>\n", gettext("Session Upload"));
res = send(outerSocket, str, strlen(str), 0);

rowNum = 0;
for (j = 0; j < DIR_NUM; j++)
    {
    if (dirName[j][0] == 0)
        continue;
    sprintf(str,"        <TD id=\"TraffTableUSCell%d\">%s</TD>\n", rowNum++, IntToKMG(ls.su[j], ST_F));
    res = send(outerSocket, str, strlen(str), 0);
    }

sprintf(str,"    </TR>\n");
res = send(outerSocket, str, strlen(str), 0);
sprintf(str,"    <TR id=\"TraffTableDSRow\">\n");
res = send(outerSocket, str, strlen(str), 0);
sprintf(str,"        <TD id=\"TraffTableDSCellC\">%s</TD>\n", gettext("Session Download"));
res = send(outerSocket, str, strlen(str), 0);

for (j = 0; j < DIR_NUM; j++)
    {
    if (dirName[j][0] == 0)
        continue;
    sprintf(str,"        <TD id=\"TraffTableDSCell%d\">%s</TD>\n", j, IntToKMG(ls.sd[j], ST_F));
    res = send(outerSocket, str, strlen(str), 0);
    }

sprintf(str,"    </TR>\n");
res = send(outerSocket, str, strlen(str), 0);

sprintf(str,"</TABLE>\n");
res = send(outerSocket, str, strlen(str), 0);

rowNum = 0;
if (!messages.empty())
    {
    sprintf(str,"    <TABLE id=\"MessagesTable\">\n");
    res = send(outerSocket, str, strlen(str), 0);

    sprintf(str,"        <TR id=\"MessagesTableRowC\">\n");
    send(outerSocket, str, strlen(str), 0);
    sprintf(str,"            <TD>Date</TD>\n");
    send(outerSocket, str, strlen(str), 0);
    sprintf(str,"            <TD>Text</TD>\n");
    send(outerSocket, str, strlen(str), 0);
    sprintf(str,"        </TR>\n");
    send(outerSocket, str, strlen(str), 0);

    std::list<STG_MESSAGE>::reverse_iterator it;
    it = messages.rbegin();
    while (it != messages.rend())
        {
        sprintf(str,"        <TR id=\"MessagesTableRow%d\">\n", rowNum);
        send(outerSocket, str, strlen(str), 0);
        sprintf(str,"            <TD>%s</TD>\n", it->recvTime.c_str());
        send(outerSocket, str, strlen(str), 0);
        sprintf(str,"            <TD>%s</TD>\n", it->msg.c_str());
        send(outerSocket, str, strlen(str), 0);
        sprintf(str,"        </TR>\n");
        send(outerSocket, str, strlen(str), 0);
        ++it;
        ++rowNum;
        }

    sprintf(str,"   </TABLE>\n");
    res = send(outerSocket, str, strlen(str), 0);
    }

time_t t = time(NULL);
sprintf(str,"Îáíîâëåíî: %s</b>" , ctime(&t));
res = send(outerSocket, str, strlen(str), 0);

send(outerSocket, replyFooter, strlen(replyFooter), 0);

return 0;
}
//---------------------------------------------------------------------------
int WEB::SendCSS()
{
const char * replyHeader =
    "HTTP/1.0 200 OK\n"
    "Content-Type: text/css\n"
    "Connection: close\n\n";

const char * replyFooter= "\n\n";

send(outerSocket, replyHeader, strlen(replyHeader), 0);
send(outerSocket, SGAuth::css, strlen(SGAuth::css), 0);
send(outerSocket, replyFooter, strlen(replyFooter), 0);

return 0;
}
//---------------------------------------------------------------------------
void WEB::SetDirName(const std::string & dn, int n)
{
web->dirName[n] =  dn;
}
//---------------------------------------------------------------------------
void WEB::AddMessage(const std::string & message, int type)
{
time_t t = time(NULL);
STG_MESSAGE m;

m.msg = message;
m.type = type;
m.recvTime = ctime(&t);

messages.push_back(m);

if (messages.size() > MAX_MESSAGES)
    messages.pop_front();

}
//---------------------------------------------------------------------------
void WEB::UpdateStat(const LOADSTAT & ls)
{
memcpy((void*)&(WEB::ls), &ls, sizeof(LOADSTAT));
}
//---------------------------------------------------------------------------