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

#include "TCivetweb.h"

#include "../civetweb/civetweb.h"

#include <stdlib.h>
#include <string.h>

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

static int log_message_handler(const struct mg_connection *conn, const char *message)
{
   TCivetweb* engine = (TCivetweb*) mg_get_request_info((struct mg_connection *)conn)->user_data;

   if (engine) return engine->ProcessLog(message);

   // provide debug output
   if ((gDebug>0) || (strstr(message,"cannot bind to")!=0))
      fprintf(stderr, "Error in <TCivetweb::Log> %s\n",message);

   return 0;
}


static int begin_request_handler(struct mg_connection *conn)
{
   TCivetweb *engine = (TCivetweb *) mg_get_request_info(conn)->user_data;
   if (engine == 0) return 0;
   THttpServer *serv = engine->GetServer();
   if (serv == 0) return 0;

   const struct mg_request_info *request_info = mg_get_request_info(conn);

   THttpCallArg arg;

   TString filename;

   Bool_t execres = kTRUE, debug = engine->IsDebugMode();

   if (!debug && serv->IsFileRequested(request_info->uri, filename)) {
      if ((filename.Index(".js") != kNPOS) || (filename.Index(".css") != kNPOS)) {
         Int_t length = 0;
         char *buf = THttpServer::ReadFileContent(filename.Data(), length);
         if (buf == 0) {
            arg.Set404();
         } else {
            arg.SetContentType(THttpServer::GetMimeType(filename.Data()));
            arg.SetBinData(buf, length);
            arg.AddHeader("Cache-Control", "max-age=3600");
            arg.SetZipping(2);
         }
      } else {
         arg.SetFile(filename.Data());
      }
   } else {
      arg.SetPathAndFileName(request_info->uri); // path and file name
      arg.SetQuery(request_info->query_string);  // query arguments
      arg.SetTopName(engine->GetTopName());
      arg.SetMethod(request_info->request_method); // method like GET or POST
      if (request_info->remote_user!=0)
         arg.SetUserName(request_info->remote_user);

      TString header;
      for (int n = 0; n < request_info->num_headers; n++)
         header.Append(TString::Format("%s: %s\r\n", request_info->http_headers[n].name, request_info->http_headers[n].value));
      arg.SetRequestHeader(header);

      const char* len = mg_get_header(conn, "Content-Length");
      Int_t ilen = len!=0 ? TString(len).Atoi() : 0;

      if (ilen>0) {
         void* buf = malloc(ilen+1); // one byte more for null-termination
         Int_t iread = mg_read(conn, buf, ilen);
         if (iread==ilen) arg.SetPostData(buf, ilen);
                     else free(buf);
      }

      if (debug) {
         TString cont;
         cont.Append("<title>Civetweb echo</title>");
         cont.Append("<h1>Civetweb echo</h1>\n");

         static int count = 0;

         cont.Append(TString::Format("Request %d:<br/>\n<pre>\n", ++count));
         cont.Append(TString::Format("  Method   : %s\n", arg.GetMethod()));
         cont.Append(TString::Format("  PathName : %s\n", arg.GetPathName()));
         cont.Append(TString::Format("  FileName : %s\n", arg.GetFileName()));
         cont.Append(TString::Format("  Query    : %s\n", arg.GetQuery()));
         cont.Append(TString::Format("  PostData : %ld\n", arg.GetPostDataLength()));
         if (arg.GetUserName())
         cont.Append(TString::Format("  User     : %s\n", arg.GetUserName()));

         cont.Append("</pre><p>\n");

         cont.Append("Environment:<br/>\n<pre>\n");
         for (int n = 0; n < request_info->num_headers; n++)
            cont.Append(TString::Format("  %s = %s\n", request_info->http_headers[n].name, request_info->http_headers[n].value));
         cont.Append("</pre><p>\n");

         arg.SetContentType("text/html");

         arg.SetContent(cont);

      } else {
         execres = serv->ExecuteHttp(&arg);
      }
   }

   if (!execres || arg.Is404()) {
      TString hdr;
      arg.FillHttpHeader(hdr, "HTTP/1.1");
      mg_printf(conn, "%s", hdr.Data());
   } else if (arg.IsFile()) {
      mg_send_file(conn, (const char *) arg.GetContent());
   } else {

      Bool_t dozip = arg.GetZipping() > 0;
      switch (arg.GetZipping()) {
         case 2:
            if (arg.GetContentLength() < 10000) {
               dozip = kFALSE;
               break;
            }
         case 1:
            // check if request header has Accept-Encoding
            dozip = kFALSE;
            for (int n = 0; n < request_info->num_headers; n++) {
               TString name = request_info->http_headers[n].name;
               if (name.Index("Accept-Encoding", 0, TString::kIgnoreCase) != 0) continue;
               TString value = request_info->http_headers[n].value;
               dozip = (value.Index("gzip", 0, TString::kIgnoreCase) != kNPOS);
               break;
            }

            break;
         case 3:
            dozip = kTRUE;
            break;
      }

      if (dozip) arg.CompressWithGzip();

      TString hdr;
      arg.FillHttpHeader(hdr, "HTTP/1.1");
      mg_printf(conn, "%s", hdr.Data());

      if (arg.GetContentLength() > 0)
         mg_write(conn, arg.GetContent(), (size_t) arg.GetContentLength());
   }

   // Returning non-zero tells civetweb that our function has replied to
   // the client, and civetweb should not send client any more data.
   return 1;
}


//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TCivetweb                                                            //
//                                                                      //
// http server implementation, based on civetweb embedded server        //
// It is default kind of engine, created for THttpServer                //
//                                                                      //
// Following additional options can be specified                        //
//    top=foldername - name of top folder, seen in the browser          //
//    thrds=N - use N threads to run civetweb server (default 5)        //
//    auth_file - global authentication file                            //
//    auth_domain - domain name, used for authentication                //
//                                                                      //
// Example:                                                             //
//    new THttpServer("http:8080?top=MyApp&thrds=3");                   //
//                                                                      //
// Authentication:                                                      //
//    When auth_file and auth_domain parameters are specified, access   //
//    to running http server will be possible only after user           //
//    authentication, using so-call digest method. To generate          //
//    authentication file, htdigest routine should be used:             //
//                                                                      //
//        [shell] htdigest -c .htdigest domain_name user                //
//                                                                      //
//    When creating server, parameters should be:                       //
//                                                                      //
//       new THttpServer("http:8080?auth_file=.htdigets&auth_domain=domain_name");  //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


ClassImp(TCivetweb)

//______________________________________________________________________________
TCivetweb::TCivetweb() :
   THttpEngine("civetweb", "compact embedded http server"),
   fCtx(0),
   fCallbacks(0),
   fTopName(),
   fDebug(kFALSE)
{
   // constructor
}

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

   if (fCtx != 0) mg_stop((struct mg_context *) fCtx);
   if (fCallbacks != 0) free(fCallbacks);
   fCtx = 0;
   fCallbacks = 0;
}

//______________________________________________________________________________
Int_t TCivetweb::ProcessLog(const char* message)
{
   // process civetweb log message, can be used to detect critical errors

   if (gDebug>0) Error("Log", "%s", message);

   return 0;
}

//______________________________________________________________________________
Bool_t TCivetweb::Create(const char *args)
{
   // Creates embedded civetweb server
   // As main argument, http port should be specified like "8090".
   // Or one can provide combination of ipaddress and portnumber like 127.0.0.1:8090
   // Extra parameters like in URL string could be specified after '?' mark:
   //    thrds=N   - there N is number of threads used by the civetweb (default is 5)
   //    top=name  - configure top name, visible in the web browser
   //    auth_file=filename  - authentication file name, created with htdigets utility
   //    auth_domain=domain   - authentication domain
   //    loopback  - bind specified port to loopback 127.0.0.1 address
   //    debug  - enable debug mode, server always returns html page with request info

   fCallbacks = malloc(sizeof(struct mg_callbacks));
   memset(fCallbacks, 0, sizeof(struct mg_callbacks));
   ((struct mg_callbacks *) fCallbacks)->begin_request = begin_request_handler;
   ((struct mg_callbacks *) fCallbacks)->log_message = log_message_handler;
   TString sport = "8080";
   TString num_threads = "5";
   TString auth_file, auth_domain, log_file;

   // extract arguments
   if ((args != 0) && (strlen(args) > 0)) {

      // first extract port number
      sport = "";
      while ((*args != 0) && (*args != '?') && (*args != '/'))
         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();

            const char *top = url.GetValueFromOptions("top");
            if (top != 0) fTopName = top;

            const char *log = url.GetValueFromOptions("log");
            if (log != 0) log_file = log;

            Int_t thrds = url.GetIntValueFromOptions("thrds");
            if (thrds > 0) num_threads.Form("%d", thrds);

            const char *afile = url.GetValueFromOptions("auth_file");
            if (afile != 0) auth_file = afile;

            const char *adomain = url.GetValueFromOptions("auth_domain");
            if (adomain != 0) auth_domain = adomain;

            if (url.HasOption("debug")) fDebug = kTRUE;

            if (url.HasOption("loopback") && (sport.Index(":")==kNPOS))
               sport = TString("127.0.0.1:") + sport;
         }
      }
   }

   const char *options[20];
   int op(0);

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

   options[op++] = "listening_ports";
   options[op++] = sport.Data();
   options[op++] = "num_threads";
   options[op++] = num_threads.Data();

   if ((auth_file.Length() > 0) && (auth_domain.Length() > 0)) {
      options[op++] = "global_auth_file";
      options[op++] = auth_file.Data();
      options[op++] = "authentication_domain";
      options[op++] = auth_domain.Data();
   }

   if (log_file.Length() > 0) {
      options[op++] = "error_log_file";
      options[op++] = log_file.Data();
   }

   options[op++] = 0;

   // Start the web server.
   fCtx = mg_start((struct mg_callbacks *) fCallbacks, this, options);

   return fCtx != 0;
}

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