// $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 <stdlib.h>

void FCGX_ROOT_send_file(FCGX_Request *request, const char *fname)
{
   Int_t length = 0;

   char *buf = THttpServer::ReadFileContent(fname, length);

   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 {

      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?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)) {

      // first extract port number
      sport = ":";
      while ((*args != 0) && (*args >= '0') && (*args <= '9'))
         sport.Append(*args++);

      // than search for extra parameters
      while ((*args != 0) && (*args != '?')) args++;

      if (*args == '?') {
         TUrl url(TString::Format("http://localhost/folder%s", args));

         if (url.IsValid()) {

            url.ParseOptions();

            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() + 1);

   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);
      const char *inp_method = FCGX_GetParam("REQUEST_METHOD", request.envp);
      const char *inp_length = FCGX_GetParam("CONTENT_LENGTH", request.envp);

      THttpCallArg arg;
      if (inp_path != 0) arg.SetPathAndFileName(inp_path);
      if (inp_query != 0) arg.SetQuery(inp_query);
      if (inp_method != 0) arg.SetMethod(inp_method);
      if (engine->fTopName.Length() > 0) arg.SetTopName(engine->fTopName.Data());
      int len = 0;
      if (inp_length!=0) len = strtol(inp_length, NULL, 10);
      if (len>0) {
         void* buf = malloc(len+1); // one myte more for null-termination
         int nread = FCGX_GetStr((char*) buf, len, request.in);
         if (nread>0) arg.SetPostData(buf, nread);
                 else free(buf);
      }

      TString header;
      for (char **envp = request.envp; *envp != NULL; envp++) {
         TString entry = *envp;
         for (Int_t n=0;n<entry.Length();n++)
            if (entry[n] == '=') { entry[n] = ':'; break; }
         header.Append(entry);
         header.Append("\r\n");
      }
      arg.SetRequestHeader(header);

      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");

         FCGX_FPrintF(request.out, "Request %d:<br/>\n<pre>\n", count);
         FCGX_FPrintF(request.out, "  Method   : %s\n", arg.GetMethod());
         FCGX_FPrintF(request.out, "  PathName : %s\n", arg.GetPathName());
         FCGX_FPrintF(request.out, "  FileName : %s\n", arg.GetFileName());
         FCGX_FPrintF(request.out, "  Query    : %s\n", arg.GetQuery());
         FCGX_FPrintF(request.out, "  PostData : %ld\n", arg.GetPostDataLength());
         FCGX_FPrintF(request.out, "</pre><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, "Status:");
         FCGX_FPrintF(request.out, hdr.Data());
      } else if (arg.IsFile()) {
         FCGX_ROOT_send_file(&request, (const char *) arg.GetContent());
      } else {

         // TODO: check in request header that gzip encoding is supported
         if (arg.GetZipping() > 0) arg.CompressWithGzip();

         arg.FillHttpHeader(hdr, "Status:");
         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