// $Id$
// Author: Sergey Linev   28/12/2013

#include "TFastCgi.h"

#include "TThread.h"
#include "TUrl.h"
#include "THttpServer.h"

#include <string.h>

#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif


#ifndef HTTP_WITHOUT_FASTCGI

#include "fcgiapp.h"
#include <fstream>
#include <stdlib.h>

void FCGX_ROOT_send_file(FCGX_Request *request, const char *fname)
{
   std::ifstream is(fname);

   char *buf = 0;
   int length = 0;

   if (is) {
      is.seekg(0, is.end);
      length = is.tellg();
      is.seekg(0, is.beg);

      buf = (char *) malloc(length);
      is.read(buf, length);
      if (!is) {
         free(buf);
         buf = 0;
         length = 0;
      }
   }

   if (buf == 0) {
      FCGX_FPrintF(request->out,
                   "Status: 404 Not Found\r\n"
                   "Content-Length: 0\r\n" // Always set Content-Length
                   "Connection: close\r\n\r\n");
   } else {

      /*      char sbuf[100], etag[100];
            time_t curtime = time(NULL);
            strftime(sbuf, sizeof(sbuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&curtime));
            snprintf(etag, sizeof(etag), "\"%lx.%ld\"",
                     (unsigned long) curtime, (long) length);

            // Send HTTP reply to the client
            FCGX_FPrintF(request->out,
                   "HTTP/1.1 200 OK\r\n"
                   "Date: %s\r\n"
                   "Last-Modified: %s\r\n"
                   "Etag: %s\r\n"
                   "Content-Type: %s\r\n"
                   "Content-Length: %d\r\n"     // Always set Content-Length
                   "\r\n", sbuf, sbuf, etag, THttpServer::GetMimeType(fname), length);

      */

      FCGX_FPrintF(request->out,
                   "Status: 200 OK\r\n"
                   "Content-Type: %s\r\n"
                   "Content-Length: %d\r\n"     // Always set Content-Length
                   "\r\n", THttpServer::GetMimeType(fname), length);


      FCGX_PutStr(buf, length, request->out);

      free(buf);
   }
}


#endif


//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TFastCgi                                                             //
//                                                                      //
// Http engine implementation, based on fastcgi package                 //
// Allows to redirect http requests from normal web server like         //
// Apache or lighttpd                                                   //
//                                                                      //
// Configuration example for lighttpd                                   //
//                                                                      //
// server.modules += ( "mod_fastcgi" )                                  //
// fastcgi.server = (                                                   //
//   "/remote_scripts/" =>                                              //
//     (( "host" => "192.168.1.11",                                     //
//        "port" => 9000,                                               //
//        "check-local" => "disable",                                   //
//        "docroot" => "/"                                              //
//     ))                                                               //
// )                                                                    //
//                                                                      //
// When creating THttpServer, one should specify:                       //
//                                                                      //
//  THttpServer* serv = new THttpServer("fastcgi:9000");                //
//                                                                      //
// In this case, requests to lighttpd server will be                    //
// redirected to ROOT session. Like:                                    //
//    http://lighttpdhost/remote_scripts/root.cgi/                      //
//                                                                      //
// Following additional options can be specified                        //
//    top=foldername - name of top folder, seen in the browser          //
//    debug=1 - run fastcgi server in debug mode                        //
// Example:                                                             //
//    serv->CreateEngine("fastcgi:9000/none?top=fastcgiserver"          //
//                                                                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


//______________________________________________________________________________
TFastCgi::TFastCgi() :
   THttpEngine("fastcgi", "fastcgi interface to webserver"),
   fSocket(0),
   fDebugMode(kFALSE),
   fTopName(),
   fThrd(0)
{
   // normal constructor
}

//______________________________________________________________________________
TFastCgi::~TFastCgi()
{
   // destructor

   if (fThrd) {
      // running thread will be killed
      fThrd->Kill();
      delete fThrd;
      fThrd = 0;
   }

   if (fSocket > 0) {
      // close opened socket
      close(fSocket);
      fSocket = 0;
   }
}

//______________________________________________________________________________
Bool_t TFastCgi::Create(const char *args)
{
   // initializes fastcgi variables and start thread,
   // which will process incoming http requests

#ifndef HTTP_WITHOUT_FASTCGI
   FCGX_Init();

//   Info("Create", "Analyze url %s", s.Data());

   TString sport = ":9000";

   if ((args != 0) && (strlen(args) > 0)) {
      TUrl url(TString::Format("http://localhost:%s", args));

      if (url.IsValid()) {
         url.ParseOptions();
         if (url.GetPort() > 0) sport.Form(":%d", url.GetPort());

         if (url.GetValueFromOptions("debug") != 0) fDebugMode = kTRUE;

         const char *top = url.GetValueFromOptions("top");

         if (top != 0) fTopName = top;
      }

//      Info("Create", "valid url opt %s debug = %d", url.GetOptions(), fDebugMode);
   }

   Info("Create", "Starting FastCGI server on port %s", sport.Data());

   fSocket = FCGX_OpenSocket(sport.Data(), 10);
   fThrd = new TThread("FastCgiThrd", TFastCgi::run_func, this);
   fThrd->Run();

   return kTRUE;
#else
   (void)args;
   Error("Create", "ROOT compiled without fastcgi support");
   return kFALSE;
#endif
}


//______________________________________________________________________________
void *TFastCgi::run_func(void *args)
{
#ifndef HTTP_WITHOUT_FASTCGI

   TFastCgi *engine = (TFastCgi *) args;

   FCGX_Request request;

   FCGX_InitRequest(&request, engine->GetSocket(), 0);

   int count = 0;

   while (1) {

      int rc = FCGX_Accept_r(&request);

      if (rc != 0) continue;

      count++;

      const char *inp_path = FCGX_GetParam("PATH_INFO", request.envp);
      const char *inp_query = FCGX_GetParam("QUERY_STRING", request.envp);

      THttpCallArg arg;
      if (inp_path != 0) arg.SetPathAndFileName(inp_path);
      if (inp_query != 0) arg.SetQuery(inp_query);
      if (engine->fTopName.Length() > 0) arg.SetTopName(engine->fTopName.Data());

      if (engine->fDebugMode) {
         FCGX_FPrintF(request.out,
                      "Status: 200 OK\r\n"
                      "Content-type: text/html\r\n"
                      "\r\n"
                      "<title>FastCGI echo</title>"
                      "<h1>FastCGI echo</h1>\n"
                      "Request number %d<p>\n", count);

         char *contentLength = FCGX_GetParam("CONTENT_LENGTH", request.envp);
         int len = 0;

         if (contentLength != NULL)
            len = strtol(contentLength, NULL, 10);

         if (len <= 0) {
            FCGX_FPrintF(request.out, "No data from standard input.<p>\n");
         } else {
            int i, ch;

            FCGX_FPrintF(request.out, "Standard input:<br>\n<pre>\n");
            for (i = 0; i < len; i++) {
               if ((ch = FCGX_GetChar(request.in)) < 0) {
                  FCGX_FPrintF(request.out,
                               "Error: Not enough bytes received on standard input<p>\n");
                  break;
               }
               FCGX_PutChar(ch, request.out);
            }
            FCGX_FPrintF(request.out, "\n</pre><p>\n");
         }

         FCGX_FPrintF(request.out, "PATHNAME: %s<p>\n", arg.GetPathName());
         FCGX_FPrintF(request.out, "FILENAME: %s<p>\n", arg.GetFileName());
         FCGX_FPrintF(request.out, "QUERY:    %s<p>\n", arg.GetQuery());
         FCGX_FPrintF(request.out, "<p>\n");

         FCGX_FPrintF(request.out, "Environment:<br>\n<pre>\n");
         for (char **envp = request.envp; *envp != NULL; envp++) {
            FCGX_FPrintF(request.out, "%s\n", *envp);
         }
         FCGX_FPrintF(request.out, "</pre><p>\n");

         FCGX_Finish_r(&request);
         continue;
      }

      TString fname;

      if (engine->GetServer()->IsFileRequested(inp_path, fname)) {
         FCGX_ROOT_send_file(&request, fname.Data());
         FCGX_Finish_r(&request);
         continue;
      }

//      printf("PATHNAME %s FILENAME %s QUERY %s \n",
//             arg.GetPathName(), arg.GetFileName(), arg.GetQuery());

      TString hdr;

      if (!engine->GetServer()->ExecuteHttp(&arg) || arg.Is404()) {
         arg.FillHttpHeader(hdr, kFALSE);
         FCGX_FPrintF(request.out, hdr.Data());
      } else if (arg.IsFile()) {
         FCGX_ROOT_send_file(&request, (const char *) arg.GetContent());
      } else {

         arg.FillHttpHeader(hdr, kFALSE);
         FCGX_FPrintF(request.out, hdr.Data());

         FCGX_PutStr((const char *) arg.GetContent(),
                     (int) arg.GetContentLength(), request.out);
      }

      FCGX_Finish_r(&request);

   } /* while */

   return 0;

#else
   return args;
#endif
}

 TFastCgi.cxx:1
 TFastCgi.cxx:2
 TFastCgi.cxx:3
 TFastCgi.cxx:4
 TFastCgi.cxx:5
 TFastCgi.cxx:6
 TFastCgi.cxx:7
 TFastCgi.cxx:8
 TFastCgi.cxx:9
 TFastCgi.cxx:10
 TFastCgi.cxx:11
 TFastCgi.cxx:12
 TFastCgi.cxx:13
 TFastCgi.cxx:14
 TFastCgi.cxx:15
 TFastCgi.cxx:16
 TFastCgi.cxx:17
 TFastCgi.cxx:18
 TFastCgi.cxx:19
 TFastCgi.cxx:20
 TFastCgi.cxx:21
 TFastCgi.cxx:22
 TFastCgi.cxx:23
 TFastCgi.cxx:24
 TFastCgi.cxx:25
 TFastCgi.cxx:26
 TFastCgi.cxx:27
 TFastCgi.cxx:28
 TFastCgi.cxx:29
 TFastCgi.cxx:30
 TFastCgi.cxx:31
 TFastCgi.cxx:32
 TFastCgi.cxx:33
 TFastCgi.cxx:34
 TFastCgi.cxx:35
 TFastCgi.cxx:36
 TFastCgi.cxx:37
 TFastCgi.cxx:38
 TFastCgi.cxx:39
 TFastCgi.cxx:40
 TFastCgi.cxx:41
 TFastCgi.cxx:42
 TFastCgi.cxx:43
 TFastCgi.cxx:44
 TFastCgi.cxx:45
 TFastCgi.cxx:46
 TFastCgi.cxx:47
 TFastCgi.cxx:48
 TFastCgi.cxx:49
 TFastCgi.cxx:50
 TFastCgi.cxx:51
 TFastCgi.cxx:52
 TFastCgi.cxx:53
 TFastCgi.cxx:54
 TFastCgi.cxx:55
 TFastCgi.cxx:56
 TFastCgi.cxx:57
 TFastCgi.cxx:58
 TFastCgi.cxx:59
 TFastCgi.cxx:60
 TFastCgi.cxx:61
 TFastCgi.cxx:62
 TFastCgi.cxx:63
 TFastCgi.cxx:64
 TFastCgi.cxx:65
 TFastCgi.cxx:66
 TFastCgi.cxx:67
 TFastCgi.cxx:68
 TFastCgi.cxx:69
 TFastCgi.cxx:70
 TFastCgi.cxx:71
 TFastCgi.cxx:72
 TFastCgi.cxx:73
 TFastCgi.cxx:74
 TFastCgi.cxx:75
 TFastCgi.cxx:76
 TFastCgi.cxx:77
 TFastCgi.cxx:78
 TFastCgi.cxx:79
 TFastCgi.cxx:80
 TFastCgi.cxx:81
 TFastCgi.cxx:82
 TFastCgi.cxx:83
 TFastCgi.cxx:84
 TFastCgi.cxx:85
 TFastCgi.cxx:86
 TFastCgi.cxx:87
 TFastCgi.cxx:88
 TFastCgi.cxx:89
 TFastCgi.cxx:90
 TFastCgi.cxx:91
 TFastCgi.cxx:92
 TFastCgi.cxx:93
 TFastCgi.cxx:94
 TFastCgi.cxx:95
 TFastCgi.cxx:96
 TFastCgi.cxx:97
 TFastCgi.cxx:98
 TFastCgi.cxx:99
 TFastCgi.cxx:100
 TFastCgi.cxx:101
 TFastCgi.cxx:102
 TFastCgi.cxx:103
 TFastCgi.cxx:104
 TFastCgi.cxx:105
 TFastCgi.cxx:106
 TFastCgi.cxx:107
 TFastCgi.cxx:108
 TFastCgi.cxx:109
 TFastCgi.cxx:110
 TFastCgi.cxx:111
 TFastCgi.cxx:112
 TFastCgi.cxx:113
 TFastCgi.cxx:114
 TFastCgi.cxx:115
 TFastCgi.cxx:116
 TFastCgi.cxx:117
 TFastCgi.cxx:118
 TFastCgi.cxx:119
 TFastCgi.cxx:120
 TFastCgi.cxx:121
 TFastCgi.cxx:122
 TFastCgi.cxx:123
 TFastCgi.cxx:124
 TFastCgi.cxx:125
 TFastCgi.cxx:126
 TFastCgi.cxx:127
 TFastCgi.cxx:128
 TFastCgi.cxx:129
 TFastCgi.cxx:130
 TFastCgi.cxx:131
 TFastCgi.cxx:132
 TFastCgi.cxx:133
 TFastCgi.cxx:134
 TFastCgi.cxx:135
 TFastCgi.cxx:136
 TFastCgi.cxx:137
 TFastCgi.cxx:138
 TFastCgi.cxx:139
 TFastCgi.cxx:140
 TFastCgi.cxx:141
 TFastCgi.cxx:142
 TFastCgi.cxx:143
 TFastCgi.cxx:144
 TFastCgi.cxx:145
 TFastCgi.cxx:146
 TFastCgi.cxx:147
 TFastCgi.cxx:148
 TFastCgi.cxx:149
 TFastCgi.cxx:150
 TFastCgi.cxx:151
 TFastCgi.cxx:152
 TFastCgi.cxx:153
 TFastCgi.cxx:154
 TFastCgi.cxx:155
 TFastCgi.cxx:156
 TFastCgi.cxx:157
 TFastCgi.cxx:158
 TFastCgi.cxx:159
 TFastCgi.cxx:160
 TFastCgi.cxx:161
 TFastCgi.cxx:162
 TFastCgi.cxx:163
 TFastCgi.cxx:164
 TFastCgi.cxx:165
 TFastCgi.cxx:166
 TFastCgi.cxx:167
 TFastCgi.cxx:168
 TFastCgi.cxx:169
 TFastCgi.cxx:170
 TFastCgi.cxx:171
 TFastCgi.cxx:172
 TFastCgi.cxx:173
 TFastCgi.cxx:174
 TFastCgi.cxx:175
 TFastCgi.cxx:176
 TFastCgi.cxx:177
 TFastCgi.cxx:178
 TFastCgi.cxx:179
 TFastCgi.cxx:180
 TFastCgi.cxx:181
 TFastCgi.cxx:182
 TFastCgi.cxx:183
 TFastCgi.cxx:184
 TFastCgi.cxx:185
 TFastCgi.cxx:186
 TFastCgi.cxx:187
 TFastCgi.cxx:188
 TFastCgi.cxx:189
 TFastCgi.cxx:190
 TFastCgi.cxx:191
 TFastCgi.cxx:192
 TFastCgi.cxx:193
 TFastCgi.cxx:194
 TFastCgi.cxx:195
 TFastCgi.cxx:196
 TFastCgi.cxx:197
 TFastCgi.cxx:198
 TFastCgi.cxx:199
 TFastCgi.cxx:200
 TFastCgi.cxx:201
 TFastCgi.cxx:202
 TFastCgi.cxx:203
 TFastCgi.cxx:204
 TFastCgi.cxx:205
 TFastCgi.cxx:206
 TFastCgi.cxx:207
 TFastCgi.cxx:208
 TFastCgi.cxx:209
 TFastCgi.cxx:210
 TFastCgi.cxx:211
 TFastCgi.cxx:212
 TFastCgi.cxx:213
 TFastCgi.cxx:214
 TFastCgi.cxx:215
 TFastCgi.cxx:216
 TFastCgi.cxx:217
 TFastCgi.cxx:218
 TFastCgi.cxx:219
 TFastCgi.cxx:220
 TFastCgi.cxx:221
 TFastCgi.cxx:222
 TFastCgi.cxx:223
 TFastCgi.cxx:224
 TFastCgi.cxx:225
 TFastCgi.cxx:226
 TFastCgi.cxx:227
 TFastCgi.cxx:228
 TFastCgi.cxx:229
 TFastCgi.cxx:230
 TFastCgi.cxx:231
 TFastCgi.cxx:232
 TFastCgi.cxx:233
 TFastCgi.cxx:234
 TFastCgi.cxx:235
 TFastCgi.cxx:236
 TFastCgi.cxx:237
 TFastCgi.cxx:238
 TFastCgi.cxx:239
 TFastCgi.cxx:240
 TFastCgi.cxx:241
 TFastCgi.cxx:242
 TFastCgi.cxx:243
 TFastCgi.cxx:244
 TFastCgi.cxx:245
 TFastCgi.cxx:246
 TFastCgi.cxx:247
 TFastCgi.cxx:248
 TFastCgi.cxx:249
 TFastCgi.cxx:250
 TFastCgi.cxx:251
 TFastCgi.cxx:252
 TFastCgi.cxx:253
 TFastCgi.cxx:254
 TFastCgi.cxx:255
 TFastCgi.cxx:256
 TFastCgi.cxx:257
 TFastCgi.cxx:258
 TFastCgi.cxx:259
 TFastCgi.cxx:260
 TFastCgi.cxx:261
 TFastCgi.cxx:262
 TFastCgi.cxx:263
 TFastCgi.cxx:264
 TFastCgi.cxx:265
 TFastCgi.cxx:266
 TFastCgi.cxx:267
 TFastCgi.cxx:268
 TFastCgi.cxx:269
 TFastCgi.cxx:270
 TFastCgi.cxx:271
 TFastCgi.cxx:272
 TFastCgi.cxx:273
 TFastCgi.cxx:274
 TFastCgi.cxx:275
 TFastCgi.cxx:276
 TFastCgi.cxx:277
 TFastCgi.cxx:278
 TFastCgi.cxx:279
 TFastCgi.cxx:280
 TFastCgi.cxx:281
 TFastCgi.cxx:282
 TFastCgi.cxx:283
 TFastCgi.cxx:284
 TFastCgi.cxx:285
 TFastCgi.cxx:286
 TFastCgi.cxx:287
 TFastCgi.cxx:288
 TFastCgi.cxx:289
 TFastCgi.cxx:290
 TFastCgi.cxx:291
 TFastCgi.cxx:292
 TFastCgi.cxx:293
 TFastCgi.cxx:294
 TFastCgi.cxx:295
 TFastCgi.cxx:296
 TFastCgi.cxx:297
 TFastCgi.cxx:298
 TFastCgi.cxx:299
 TFastCgi.cxx:300
 TFastCgi.cxx:301
 TFastCgi.cxx:302
 TFastCgi.cxx:303
 TFastCgi.cxx:304
 TFastCgi.cxx:305
 TFastCgi.cxx:306
 TFastCgi.cxx:307
 TFastCgi.cxx:308
 TFastCgi.cxx:309
 TFastCgi.cxx:310
 TFastCgi.cxx:311
 TFastCgi.cxx:312
 TFastCgi.cxx:313
 TFastCgi.cxx:314