00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "DcConnectionThread.h"
00026
00027 #define SSI_ERR(inst) " --- Cannot Execute: " + inst + " --- "
00028
00029
00030 int volatile CdcConnectionThread::nThreadCounter = 0;
00031
00032
00033 CdcConnectionThread::CdcConnectionThread (CdcSocket* psoc) throw (CdcException)
00034 : oRequest (psoc), oResponse (psoc, oBuffList), pUserStatus (NULL)
00035 {
00036 if (psoc == NULL)
00037 throw (CdcException ("Socket pointer is null"));
00038 pSocket = psoc;
00039
00040 sLog.reserve (1000);
00041 nThreadNumber = ++nThreadCounter;
00042 oThread.MakeThread (DcRun, (void*)this);
00043 oThread.SetPriority (DC_TP_NORNAL);
00044 }
00045
00046
00047 CdcConnectionThread::~CdcConnectionThread ()
00048 {
00049
00050 if (sLog.IsEmpty () == false)
00051 PrintString (sLog, PS_LOG);
00052 if (pSocket != NULL)
00053 delete pSocket;
00054 }
00055
00056 bool CdcConnectionThread::ReadRequest ()
00057 {
00058 try
00059 {
00060 oRequest.ReadNextRequest ();
00061 }
00062 catch (CdcExceptionParse& e)
00063 {
00064 if (e.GetExpCode () == DC_NOT_SUPPORT)
00065 oResponse.SetStatus (501);
00066 else
00067 oResponse.SetStatus (400);
00068 sLastError = e.ToStr ();
00069 oBuffList.Clear ();
00070
00071 oBuffList.Add (CdcBuffer (sLastError.GetLength ()).Append (sLastError));
00072 return false;
00073 }
00074 return true;
00075 }
00076
00077
00078 bool CdcConnectionThread::CheckAccept (CdcString type)
00079 {
00080 CdcString accept = oRequest.GetHeaderField ("Accept");
00081 if (accept.IsEmpty ()) return true;
00082 if (accept.Find (type) != -1 ) return true;
00083 type = type.Left (type.Find ('/')) + "/*";
00084 if (accept.Find (type) != -1) return true;
00085 type = "*/*";
00086 if (accept.Find (type) != -1) return true;
00087
00088 return false;
00089 }
00090
00091 bool CdcConnectionThread::DoIfModified (const CdcRetrieveFile& file)
00092 {
00093 if (oRequest.GetVersion () == PROTOCOL10_VRS)
00094 return true;
00095
00096 CdcString ifs (oRequest.GetHeaderField ("If-Modified-Since"));
00097 if (ifs.IsEmpty ()) return true;
00098
00099
00100 if (CdcParameters::GetParam ("Server->File-Types->" +
00101 file.GetFileAttrib ().GetFileExtention () +
00102 "->Client-Type") == "text/html")
00103 return true;
00104
00105
00106 try
00107 {
00108 CdcHttpTime mod (ifs);
00109 if (mod < CdcHttpTime::GetCurrentTimeGMT() &&
00110 mod > file.GetFileAttrib ().GetLastModified ())
00111 {
00112 oResponse.SetStatus (304);
00113 return false;
00114 }
00115 else
00116 return true;
00117 }
00118
00119 catch (CdcException& e) { PrintString (e.ToStr (), PS_DBG); }
00120 return true;
00121 }
00122
00123
00124
00125 bool CdcConnectionThread::DoIfUnmodified (const CdcRetrieveFile& file)
00126 {
00127 if (oRequest.GetVersion () == PROTOCOL10_VRS)
00128 return true;
00129
00130 CdcString ifs (oRequest.GetHeaderField ("If-Unmodified-Since"));
00131 if (ifs.IsEmpty ()) return true;
00132
00133
00134 if (CdcParameters::GetParam ("Server->File-Types->" +
00135 file.GetFileAttrib ().GetFileExtention () +
00136 "->Client-Type") == "text/html")
00137 return true;
00138
00139
00140 try
00141 {
00142 CdcHttpTime mod (ifs);
00143 if (mod < CdcHttpTime::GetCurrentTime() &&
00144 mod < file.GetFileAttrib ().GetLastModified ())
00145 {
00146 oResponse.SetStatus (412);
00147 return false;
00148 }
00149 else
00150 return true;
00151 }
00152
00153 catch (CdcException& e) { PrintString (e.ToStr (), PS_DBG); }
00154 return true;
00155 }
00156
00157
00158 bool CdcConnectionThread::BuildNormalResponse (CdcRetrieveFile& file)
00159 {
00160
00161 oResponse.SetStatus (200);
00162 oResponse.SetHeaderField ("Content-Type",
00163 file.GetFileAttrib ().GetClientFileType ());
00164 oResponse.SetHeaderField ("Last-Modified",
00165 file.GetFileAttrib ().GetLastModified ().FormatGmt (TIME_FORMAT));
00166 if (oRequest.GetMethod () == "HEAD")
00167 return true;
00168
00169 nResSizeLimit = CdcParameters::ToInt (CdcParameters::GetParam
00170 ("Server->Response-Size-Limit"));
00171 nResTimeLimit = CdcParameters::ToInt (CdcParameters::GetParam
00172 ("Server->Response-Time-Limit"));
00173
00174 CdcBuffersList buff_list;
00175 bool check_allow = true;
00176
00177 isExecScript = file.GetFileAttrib ().IsExecScript ();
00178 oIncSet.clear ();
00179 BuildBuffersList (file, buff_list, check_allow);
00180
00181 if (isExecScript)
00182 ExecuteScript (buff_list);
00183 else
00184 oBuffList = buff_list;
00185
00186 if (CdcLoginManager::CheckLogout (oRequest, oResponse))
00187
00188 oResponse.SetHeaderField ("Cache-Control", "no-cache");
00189 pUserStatus = NULL;
00190 return true;
00191 }
00192
00193
00194 bool CdcConnectionThread::BuildBuffersList (const CdcRetrieveFile& file,
00195 CdcBuffersList& list,
00196 bool& check_allow)
00197 {
00198
00199 if (oIncSet.find (file.GetFileAttrib ().GetURL ()) != oIncSet.end ())
00200 return true;
00201 oIncSet.insert (file.GetFileAttrib ().GetURL ());
00202
00203 if (check_allow)
00204 {
00205 int ret = CheckAllow (file.GetFileAttrib ().GetURL (), list);
00206 if (ret == DC_LM_SEND)
00207 return false;
00208 if (ret == DC_LM_OK)
00209 check_allow = false;
00210 }
00211
00212 CdcBuffer& buff = file.GetData ();
00213 int new_place, place = 0;
00214
00215 if (file.GetFileAttrib ().GetClientFileType () == "text/html")
00216 { while ((new_place = buff.Find ("<!--#", place)) > -1)
00217 {
00218 if (new_place > place && buff.IsWhiteSpaces (place, new_place) == false)
00219 list.Add (buff, file.GetFileAttrib(), place, new_place - place);
00220 place = new_place + 5;
00221 int temp = buff.Find ("-->", place);
00222 if (temp == -1) continue;
00223 CdcString inner (buff.GetPointer () + place, temp - place);
00224 bool isInclude = false;
00225 CdcString inc = ExecuteSSI (inner, file, isInclude);
00226 if (isInclude)
00227 {
00228 CdcRetrieveFile inc_file (inc, oRequest.GetHost (), true);
00229 if (inc_file.GetFileAttrib ().IsFileFound ())
00230
00231 if (BuildBuffersList (inc_file, list, check_allow) == false)
00232 return false;
00233 }
00234 else if (inc.IsEmpty () == false)
00235 {
00236 CdcBuffer tmp_buf (inc.GetLength ());
00237 tmp_buf.Append (inc);
00238 list.Add (tmp_buf, 0, -1);
00239 }
00240 place = temp + 3;
00241 }
00242 }
00243
00244 if (place < buff.GetDataSize () && buff.IsWhiteSpaces (place) == false)
00245 list.Add (buff, file.GetFileAttrib(), place, buff.GetDataSize () - place);
00246 oIncSet.erase (file.GetFileAttrib ().GetURL ());
00247 return true;
00248 }
00249
00250
00251
00252 CdcString ClenSsiItem (const CdcString& str)
00253 {
00254 int start = 0, end = str.GetLength () - 1;
00255 char chr, qt = 0;
00256 while (start < str.GetLength ())
00257 {
00258 chr = str[start];
00259 if (! (isspace (chr) || chr == '=' || chr == '"' || chr == '\'')) break;
00260 ++start;
00261 if (chr == '"' || chr == '\'') break;
00262 }
00263 if (start == str.GetLength ()) return EMPTY_STR;
00264 if (chr == '"' || chr == '\'') qt = chr;
00265 while (end > 0)
00266 {
00267 chr = str[end];
00268 if (! (isspace (chr) || chr == qt)) break;
00269 --end;
00270 if (chr == qt) break;
00271 }
00272 if (end == 0) return EMPTY_STR;
00273 if (qt && chr != qt) --start;
00274 return str.Mid (start, end - start + 1);
00275 }
00276
00277
00278
00279
00280
00281 CdcString CdcConnectionThread::ExecuteSSI (const CdcString& str,
00282 const CdcRetrieveFile& file,
00283 bool& isInclude)
00284 {
00285
00286 CdcString inner = str.Trim ();
00287 int place;
00288 if ((place = inner.FindOneOf (" \t")) == -1) return SSI_ERR (str);
00289 CdcString inst = inner.Left (place);
00290 inner = inner.Mid (place).Trim ();
00291
00292 if (inst == "include")
00293 {
00294 CdcString tmp = ExtractURL (inner, file);
00295 if (tmp.IsEmpty ()) return SSI_ERR (str);
00296 isInclude = true;
00297 return tmp;
00298 }
00299 if (inst == "echo")
00300 {
00301 if ((place = inner.Find ("var")) == -1) return SSI_ERR (str);
00302 CdcString tmp = oRequest.GetVariable (ClenSsiItem (inner.Mid (place + 3)));
00303 if (tmp.Find (SCRIPT_STR) > -1)
00304 return SSI_ERR (CdcString ("Might have script in variable"));
00305 return tmp;
00306 }
00307 if (inst == "set")
00308 {
00309 if ((place = inner.Find ("value")) == -1) return SSI_ERR (str);
00310 CdcString val = ClenSsiItem (inner.Mid (place + 5));
00311 inner = inner.Left (place);
00312 if ((place = inner.Find ("var")) == -1) return SSI_ERR (str);
00313 CdcString name = ClenSsiItem (inner.Mid (place + 3));
00314 if (name == "BNI_TimeLimit")
00315
00316 nResTimeLimit = CdcParameters::ToInt (val);
00317 else if (name == "BNI_SizeLimit")
00318 nResSizeLimit = CdcParameters::ToInt (val);
00319 else
00320 oRequest.SetVariable (name, val);
00321 return EMPTY_STR;
00322 }
00323 return SSI_ERR (str);
00324 }
00325
00326 CdcString CdcConnectionThread::ExtractURL (const CdcString& inner,
00327 const CdcRetrieveFile& file)
00328 {
00329
00330 int place;
00331 if ((place = inner.Find ("file")) > -1)
00332 {
00333 CdcString url = ClenSsiItem (inner.Mid (place + 4));
00334 CdcString base = "/" + file.GetFileAttrib ().GetURL ();
00335 bool cont;
00336 do
00337 {
00338 base = base.Left (base.ReverseFind ('/'));
00339 cont = (url.Left (3) == "../");
00340 if (cont)
00341 url = url.Mid (3);
00342 } while (cont);
00343 return base + url;
00344 }
00345 else if ((place = inner.Find ("virtual")) > -1)
00346 return ClenSsiItem (inner.Mid (place + 7));
00347 return EMPTY_STR;
00348 }
00349
00350 int CdcConnectionThread::CheckAllow (const CdcString& url, CdcBuffersList& list)
00351 {
00352 CdcString url_temp = url;
00353 url_temp.MakeLower ();
00354 CdcString param_path ("Login-Manager->Access->" + oRequest.GetHost ()
00355 + "->." + url_temp);
00356 CdcString group;
00357 int place;
00358 do
00359 {
00360 if (!(group = CdcParameters::GetParam (param_path)).IsEmpty ())
00361 break;
00362 place = param_path.ReverseFind ('/');
00363 if (place == -1) break;
00364 param_path = param_path.Left (place);
00365 } while (true);
00366 if (group.IsEmpty ()) return DC_LM_NONE;
00367
00368
00369 int code;
00370 CdcString new_page;
00371 pUserStatus = CdcLoginManager::CheckAccess
00372 (group, oRequest, oResponse, code, new_page);
00373 if (code == DC_LM_SEND)
00374 {
00375 list.Clear ();
00376 if (new_page.IsEmpty () == false)
00377 {
00378 CdcRetrieveFile file (new_page, oRequest.GetHost ());
00379
00380
00381 oResponse.SetHeaderField ("Content-Type",
00382 file.GetFileAttrib ().GetClientFileType ());
00383 oResponse.SetHeaderField ("Last-Modified",
00384 file.GetFileAttrib ().GetLastModified ().FormatGmt (TIME_FORMAT));
00385 isExecScript = file.GetFileAttrib ().IsExecScript ();
00386 bool ca = false;
00387 BuildBuffersList (file, list, ca);
00388 }
00389 }
00390
00391 oResponse.SetHeaderField ("Cache-Control", "no-cache");
00392 return code;
00393 }
00394
00395
00396
00397 void CdcConnectionThread::ExecuteScript (CdcBuffersList& bufflist)
00398 {
00399 int offset, len;
00400 oBuffList.Clear ();
00401
00402 int start_time = time (NULL);
00403 CdcEnginesBank eb (&oRequest, &oResponse, pUserStatus);
00404 bool ret = bufflist.SeekFirst ();
00405 while (ret)
00406 {
00407 const CdcBuffer* pbuff = bufflist.GetBufferPointer ();
00408 offset = bufflist.GetBufferOffset ();
00409 len = bufflist.GetLength ();
00410 if (bufflist.GetFileAttrib ().IsExecScript ())
00411 {
00412 CdcString eng = bufflist.GetFileAttrib ().GetScriptEngine ();
00413 if (eng.IsEmpty () == false)
00414 {
00415 if (ExecuteSection (eb, eng, pbuff, start_time, offset, len)
00416 == false) break;
00417 }
00418 else
00419 {
00420 if (ExecuteBufferSections (eb, pbuff, start_time, offset, len)
00421 == false) break;
00422 }
00423 }
00424 else
00425 oBuffList.Add (*pbuff, offset, len);
00426 if (nResSizeLimit > 0 && oBuffList.GetDataSize () >= nResSizeLimit)
00427 break;
00428 ret = bufflist.SeekNext ();
00429 }
00430 }
00431
00432
00433 bool CdcConnectionThread::ExecuteBufferSections (CdcEnginesBank& eb,
00434 const CdcBuffer* pbuff, int start_time, int offset, int len)
00435 {
00436 int place, end_place, tmp_place, tmp2_place;
00437 if (pbuff == NULL) return false;
00438 while ((place = pbuff->Find (SCRIPT_STR, offset, offset + len)) > -1)
00439 {
00440 tmp_place = place + SCRIPT_STR_LEN;
00441 if ((end_place = pbuff->Find (SCRIPT_TRM, tmp_place, offset + len)) == -1)
00442 return false;
00443
00444 CdcString eng_name;
00445 if (tmp_place != end_place)
00446 {
00447 eng_name = CdcString (pbuff->GetPointer () + tmp_place,
00448 end_place - tmp_place);
00449 tmp_place = end_place + 1;
00450 eng_name.Trim ();
00451 }
00452
00453 if ((end_place = pbuff->Find (SCRIPT_END, tmp_place, offset + len)) == -1)
00454 return false;
00455 if ((tmp2_place = pbuff->Find (SCRIPT_TRM, end_place, offset + len)) == -1)
00456 return false;
00457
00458 oBuffList.Add (*pbuff, offset, place - offset);
00459 place = tmp_place;
00460 tmp_place = end_place + SCRIPT_END_LEN;
00461
00462 bool eng_del = tmp_place != tmp2_place &&
00463 (CdcString (pbuff->GetPointer () + tmp_place,
00464 tmp2_place - tmp_place)).Trim () == DEL_CMD;
00465 bool isok = ExecuteSection (eb, eng_name, pbuff, start_time,
00466 place, end_place - place);
00467 if (eng_del || isok == false)
00468 eb.DeleteEngine (eng_name);
00469 if (isok == false) return false;
00470 end_place = tmp2_place + 1;
00471 len -= (end_place - offset);
00472 offset = end_place;
00473 }
00474
00475 oBuffList.Add (*pbuff, offset, len);
00476 return true;
00477 }
00478
00479
00480 bool CdcConnectionThread::ExecuteSection (CdcEnginesBank& eb,
00481 const CdcString eng_name, const CdcBuffer* pbuff, int start_time,
00482 int offset, int len)
00483 {
00484 bool isok = false;
00485
00486 IdcEngine* peng = eb.GetEngine (eng_name);
00487 if (peng == NULL || pbuff == NULL) return false;
00488
00489 BufferHolder code (pbuff->GetPointer () + offset, len);
00490 BufferHolder result;
00491 if ((nResSizeLimit == 0 || oBuffList.GetDataSize () < nResSizeLimit) &&
00492 (nResTimeLimit == 0 || time (NULL) - start_time < nResTimeLimit))
00493 isok = peng->Process (code, result, nResSizeLimit - oBuffList.GetDataSize ());
00494 if (result.empty () == false)
00495 {
00496 CdcBuffer new_buff (result.size);
00497 new_buff.Append (result.pointer, result.size);
00498 oBuffList.Add (new_buff);
00499 }
00500 if (isok == false ||
00501 nResSizeLimit > 0 && oBuffList.GetDataSize () >= nResSizeLimit ||
00502 nResTimeLimit > 0 && time (NULL) - start_time >= nResTimeLimit)
00503 return false;
00504 return true;
00505 }
00506
00507 void CdcConnectionThread::FillInLog ()
00508 {
00509
00510 CdcString temp_log = CdcParameters::GetParam ("Server->Log-File->Format");
00511 temp_log.reserve (500);
00512
00513 temp_log.Replace ("THREADNO", CdcString::IntToString (nThreadNumber));
00514 temp_log.Replace ("TIME", CdcHttpTime::GetCurrentTime ().Format ("%c"));
00515 temp_log.Replace ("IP", pSocket->GetClientAddress ());
00516 temp_log.Replace ("CLIENT", oRequest.GetHeaderField ("user-agent"));
00517 temp_log.Replace ("METHOD", oRequest.GetMethod ());
00518 temp_log.Replace ("URL", oRequest.GetURL ());
00519 temp_log.Replace ("FULL", oRequest.GetFullURL ());
00520 temp_log.Replace ("HOST", oRequest.GetHost ());
00521 temp_log.Replace ("STATUS", oResponse.GetStatus ());
00522 temp_log.Replace ("ERROR", sLastError);
00523 temp_log.Replace ("BR", "\n");
00524 temp_log.Replace ("TAB", "\t");
00525
00526 sLog += temp_log;
00527 }
00528
00529
00530 int DcRun (void* pParam)
00531 {
00532 CdcConnectionThread* pThis = (CdcConnectionThread*)pParam;
00533 if (pThis == NULL) return 1;
00534
00535 PrintString (CdcString::IntToString (pThis->nThreadNumber) +
00536 " - Connection opened", PS_TRD);
00537
00538 bool keep_alive;
00539 try
00540 {
00541 do
00542 {
00543 if ((keep_alive = pThis->ReadRequest ()) == true)
00544 {
00545 CdcRetrieveFile file (pThis->oRequest.GetURL (),
00546 pThis->oRequest.GetHost ());
00547
00548 if (file.GetFileAttrib ().IsRedirect () == true)
00549 {
00550 pThis->oResponse.SetStatus (301);
00551 pThis->oResponse.SetHeaderField ("Location",
00552 file.GetFileAttrib ().GetURL ());
00553 }
00554
00555 else if (file.GetFileAttrib ().IsFileFound () == false)
00556 pThis->oResponse.SetStatus (404);
00557
00558 else if (pThis->CheckAccept (
00559 file.GetFileAttrib ().GetClientFileType ()) == false)
00560 pThis->oResponse.SetStatus (406);
00561
00562 else if (pThis->DoIfModified (file) &&
00563 pThis->DoIfUnmodified (file))
00564 pThis->BuildNormalResponse (file);
00565
00566 keep_alive = pThis->oRequest.GetHeaderField ("Connection") !=
00567 "close" && pThis->oResponse.GetHeaderField ("Connection") != "close";
00568 }
00569 if (keep_alive == false)
00570 pThis->oResponse.SetHeaderField ("Connection", "close");
00571
00572 pThis->oResponse.SendResponse ();
00573 pThis->FillInLog ();
00574 if (keep_alive) pThis->oResponse.Restart ();
00575 }
00576 while (keep_alive);
00577 PrintString (CdcString::IntToString (pThis->nThreadNumber) +
00578 " - Connection closed", PS_TRD);
00579 }
00580 catch (CdcException& e)
00581 {
00582 PrintString (CdcString::IntToString (pThis->nThreadNumber) + " - "
00583 + e.ToStr (), PS_TRD);
00584 }
00585 catch (int e)
00586 {
00587 PrintString (CdcString::IntToString (pThis->nThreadNumber) + " - " +
00588 "Exception from a script engine: " + CdcString::IntToString (e), PS_TRD);
00589 }
00590 catch (BufferHolder e)
00591 {
00592
00593 PrintString ("Unhandled exception from an adapter");
00594 }
00595 CdcLoadController::ThreadDone (pThis->pSocket);
00596 delete pThis;
00597 return 0;
00598 }