]> jfr.im git - irc/rizon/znc.git/blob - Template.cpp
Write forceserver and webircpassword to conf
[irc/rizon/znc.git] / Template.cpp
1 /*
2 * Copyright (C) 2004-2011 See the AUTHORS file for details.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
7 */
8
9 #include "Template.h"
10 #include "FileUtils.h"
11 #include "ZNCDebug.h"
12 #include <sstream>
13 #include <algorithm>
14
15 using std::stringstream;
16
17 void CTemplateOptions::Parse(const CString& sLine) {
18 CString sName = sLine.Token(0, false, "=").Trim_n().AsUpper();
19 CString sValue = sLine.Token(1, true, "=").Trim_n();
20
21 if (sName == "ESC") {
22 m_eEscapeTo = CString::ToEscape(sValue);
23 } else if (sName == "ESCFROM") {
24 m_eEscapeFrom = CString::ToEscape(sValue);
25 }
26 }
27
28 CTemplate* CTemplateLoopContext::GetRow(unsigned int uIndex) {
29 unsigned int uSize = m_pvRows->size();
30
31 if (uIndex < uSize) {
32 if (m_bReverse) {
33 return (*m_pvRows)[uSize - uIndex -1];
34 } else {
35 return (*m_pvRows)[uIndex];
36 }
37 }
38
39 return NULL;
40 }
41
42 CString CTemplateLoopContext::GetValue(const CString& sName, bool bFromIf) {
43 CTemplate* pTemplate = GetCurRow();
44
45 if (!pTemplate) {
46 DEBUG("Loop [" + GetName() + "] has no row index [" + CString(GetRowIndex()) + "]");
47 return "";
48 }
49
50 if (sName.Equals("__ID__")) {
51 return CString(GetRowIndex() +1);
52 } else if (sName.Equals("__COUNT__")) {
53 return CString(GetRowCount());
54 } else if (sName.Equals("__ODD__")) {
55 return ((GetRowIndex() %2) ? "" : "1");
56 } else if (sName.Equals("__EVEN__")) {
57 return ((GetRowIndex() %2) ? "1" : "");
58 } else if (sName.Equals("__FIRST__")) {
59 return ((GetRowIndex() == 0) ? "1" : "");
60 } else if (sName.Equals("__LAST__")) {
61 return ((GetRowIndex() == m_pvRows->size() -1) ? "1" : "");
62 } else if (sName.Equals("__OUTER__")) {
63 return ((GetRowIndex() == 0 || GetRowIndex() == m_pvRows->size() -1) ? "1" : "");
64 } else if (sName.Equals("__INNER__")) {
65 return ((GetRowIndex() == 0 || GetRowIndex() == m_pvRows->size() -1) ? "" : "1");
66 }
67
68 return pTemplate->GetValue(sName, bFromIf);
69 }
70
71 CTemplate::~CTemplate() {
72 for (map<CString, vector<CTemplate*> >::iterator it = m_mvLoops.begin(); it != m_mvLoops.end(); ++it) {
73 vector<CTemplate*>& vLoop = it->second;
74 for (unsigned int a = 0; a < vLoop.size(); a++) {
75 delete vLoop[a];
76 }
77 }
78
79 for (unsigned int a = 0; a < m_vLoopContexts.size(); a++) {
80 delete m_vLoopContexts[a];
81 }
82 }
83
84 void CTemplate::Init() {
85 /* We have no CConfig in znc land
86 CString sPath(CConfig::GetValue("WebFilesPath"));
87
88 if (!sPath.empty()) {
89 SetPath(sPath);
90 }
91 */
92
93 ClearPaths();
94 m_pParent = NULL;
95 }
96
97 CString CTemplate::ExpandFile(const CString& sFilename, bool bFromInc) {
98 /*if (sFilename.Left(1) == "/" || sFilename.Left(2) == "./") {
99 return sFilename;
100 }*/
101
102 CString sFile(ResolveLiteral(sFilename).TrimLeft_n("/"));
103
104 for (list<pair<CString, bool> >::iterator it = m_lsbPaths.begin(); it != m_lsbPaths.end(); ++it) {
105 CString& sRoot = it->first;
106 CString sFilePath(CDir::ChangeDir(sRoot, sFile));
107
108 // Make sure path ends with a slash because "/foo/pub*" matches "/foo/public_keep_out/" but "/foo/pub/*" doesn't
109 if (!sRoot.empty() && sRoot.Right(1) != "/") {
110 sRoot += "/";
111 }
112
113 if (it->second && !bFromInc) {
114 DEBUG("\t\tSkipping path (not from INC) [" + sFilePath + "]");
115 continue;
116 }
117
118 if (CFile::Exists(sFilePath)) {
119 if (sRoot.empty() || sFilePath.Left(sRoot.length()) == sRoot) {
120 DEBUG(" Found [" + sFilePath + "]");
121 return sFilePath;
122 } else {
123 DEBUG("\t\tOutside of root [" + sFilePath + "] !~ [" + sRoot + "]");
124 }
125 }
126 }
127
128 switch (m_lsbPaths.size()) {
129 case 0:
130 DEBUG("Unable to find [" + sFile + "] using the current directory");
131 break;
132 case 1:
133 DEBUG("Unable to find [" + sFile + "] in the defined path [" + m_lsbPaths.begin()->first + "]");
134 break;
135 default:
136 DEBUG("Unable to find [" + sFile + "] in any of the " + CString(m_lsbPaths.size()) + " defined paths");
137 }
138
139 return "";
140 }
141
142 void CTemplate::SetPath(const CString& sPaths) {
143 VCString vsDirs;
144 sPaths.Split(":", vsDirs, false);
145
146 for (size_t a = 0; a < vsDirs.size(); a++) {
147 AppendPath(vsDirs[a], false);
148 }
149 }
150
151 CString CTemplate::MakePath(const CString& sPath) const {
152 CString sRet(CDir::ChangeDir("./", sPath + "/"));
153
154 if (!sRet.empty() && sRet.Right(1) != "/") {
155 sRet += "/";
156 }
157
158 return sRet;
159 }
160
161 void CTemplate::PrependPath(const CString& sPath, bool bIncludesOnly) {
162 DEBUG("CTemplate::PrependPath(" + sPath + ") == [" + MakePath(sPath) + "]");
163 m_lsbPaths.push_front(make_pair(MakePath(sPath), bIncludesOnly));
164 }
165
166 void CTemplate::AppendPath(const CString& sPath, bool bIncludesOnly) {
167 DEBUG("CTemplate::AppendPath(" + sPath + ") == [" + MakePath(sPath) + "]");
168 m_lsbPaths.push_back(make_pair(MakePath(sPath), bIncludesOnly));
169 }
170
171 void CTemplate::RemovePath(const CString& sPath) {
172 DEBUG("CTemplate::RemovePath(" + sPath + ") == [" + CDir::ChangeDir("./", sPath + "/") + "]");
173
174 for (list<pair<CString, bool> >::iterator it = m_lsbPaths.begin(); it != m_lsbPaths.end(); ++it) {
175 if (it->first == sPath) {
176 m_lsbPaths.remove(*it);
177 RemovePath(sPath); // @todo probably shouldn't use recursion, being lazy
178 return;
179 }
180 }
181 }
182
183 void CTemplate::ClearPaths() {
184 m_lsbPaths.clear();
185 }
186
187 bool CTemplate::SetFile(const CString& sFileName) {
188 m_sFileName = ExpandFile(sFileName, false);
189 PrependPath(sFileName + "/..");
190
191 if (sFileName.empty()) {
192 DEBUG("CTemplate::SetFile() - Filename is empty");
193 return false;
194 }
195
196 if (m_sFileName.empty()) {
197 DEBUG("CTemplate::SetFile() - [" + sFileName + "] does not exist");
198 return false;
199 }
200
201 DEBUG("Set template file to [" + m_sFileName + "]");
202
203 return true;
204 }
205
206 class CLoopSorter {
207 CString m_sType;
208 public:
209 CLoopSorter(const CString& sType) : m_sType(sType) {}
210 bool operator()(CTemplate* pTemplate1, CTemplate* pTemplate2) {
211 return (pTemplate1->GetValue(m_sType, false) < pTemplate2->GetValue(m_sType, false));
212 }
213 };
214
215 CTemplate& CTemplate::AddRow(const CString& sName) {
216 CTemplate* pTmpl = new CTemplate(m_spOptions, this);
217 m_mvLoops[sName].push_back(pTmpl);
218
219 return *pTmpl;
220 }
221
222 CTemplate* CTemplate::GetRow(const CString& sName, unsigned int uIndex) {
223 vector<CTemplate*>* pvLoop = GetLoop(sName);
224
225 if (pvLoop) {
226 if (pvLoop->size() > uIndex) {
227 return (*pvLoop)[uIndex];
228 }
229 }
230
231 return NULL;
232 }
233
234 vector<CTemplate*>* CTemplate::GetLoop(const CString& sName) {
235 CTemplateLoopContext* pContext = GetCurLoopContext();
236
237 if (pContext) {
238 CTemplate* pTemplate = pContext->GetCurRow();
239
240 if (pTemplate) {
241 return pTemplate->GetLoop(sName);
242 }
243 }
244
245 map<CString, vector<CTemplate*> >::iterator it = m_mvLoops.find(sName);
246
247 if (it != m_mvLoops.end()) {
248 return &(it->second);
249 }
250
251 return NULL;
252 }
253
254 bool CTemplate::PrintString(CString& sRet) {
255 sRet.clear();
256 stringstream sStream;
257 bool bRet = Print(sStream);
258
259 sRet = sStream.str();
260
261 return bRet;
262 }
263
264 bool CTemplate::Print(ostream& oOut) {
265 return Print(m_sFileName, oOut);
266 }
267
268 bool CTemplate::Print(const CString& sFileName, ostream& oOut) {
269 if (sFileName.empty()) {
270 DEBUG("Empty filename in CTemplate::Print()");
271 return false;
272 }
273
274 CFile File(sFileName);
275
276 if (!File.Open()) {
277 DEBUG("Unable to open file [" + sFileName + "] in CTemplate::Print()");
278 return false;
279 }
280
281 CString sLine;
282 CString sSetBlockVar;
283 bool bValidLastIf = false;
284 bool bInSetBlock = false;
285 unsigned long uFilePos = 0;
286 unsigned long uCurPos = 0;
287 unsigned int uLineNum = 0;
288 unsigned int uNestedIfs = 0;
289 unsigned int uSkip = 0;
290 bool bLoopCont = false;
291 bool bLoopBreak = false;
292 bool bExit = false;
293
294 while (File.ReadLine(sLine)) {
295 CString sOutput;
296 bool bFoundATag = false;
297 bool bTmplLoopHasData = false;
298 uLineNum++;
299 CString::size_type iPos = 0;
300 uCurPos = uFilePos;
301 unsigned int uLineSize = sLine.size();
302 bool bBroke = false;
303
304 while (1) {
305 iPos = sLine.find("<?");
306
307 if (iPos == CString::npos) {
308 break;
309 }
310
311 uCurPos += iPos;
312 bFoundATag = true;
313
314 if (!uSkip) {
315 sOutput += sLine.substr(0, iPos);
316 }
317
318 sLine = sLine.substr(iPos +2);
319
320 CString::size_type iPos2 = sLine.find("?>");
321
322 // Make sure our tmpl tag is ended properly
323 if (iPos2 == CString::npos) {
324 DEBUG("Template tag not ended properly in file [" + sFileName + "] [<?" + sLine + "]");
325 return false;
326 }
327
328 uCurPos += iPos2 +4;
329
330 CString sMid = CString(sLine.substr(0, iPos2)).Trim_n();
331
332 // Make sure we don't have a nested tag
333 if (sMid.find("<?") == CString::npos) {
334 sLine = sLine.substr(iPos2 +2);
335 CString sAction = sMid.Token(0);
336 CString sArgs = sMid.Token(1, true);
337 bool bNotFound = false;
338
339 // If we're breaking or continuing from within a loop, skip all tags that aren't ENDLOOP
340 if ((bLoopCont || bLoopBreak) && !sAction.Equals("ENDLOOP")) {
341 continue;
342 }
343
344 if (!uSkip) {
345 if (sAction.Equals("INC")) {
346 if (!Print(ExpandFile(sArgs, true), oOut)) {
347 DEBUG("Unable to print INC'd file [" + sArgs + "]");
348 return false;
349 }
350 } else if (sAction.Equals("SETOPTION")) {
351 m_spOptions->Parse(sArgs);
352 } else if (sAction.Equals("ADDROW")) {
353 CString sLoopName = sArgs.Token(0);
354 MCString msRow;
355
356 if (sArgs.Token(1, true, " ").OptionSplit(msRow)) {
357 CTemplate& NewRow = AddRow(sLoopName);
358
359 for (MCString::iterator it = msRow.begin(); it != msRow.end(); ++it) {
360 NewRow[it->first] = it->second;
361 }
362 }
363 } else if (sAction.Equals("SET")) {
364 CString sName = sArgs.Token(0);
365 CString sValue = sArgs.Token(1, true);
366
367 (*this)[sName] = sValue;
368 } else if (sAction.Equals("JOIN")) {
369 VCString vsArgs;
370 //sArgs.Split(" ", vsArgs, false, "\"", "\"");
371 sArgs.QuoteSplit(vsArgs);
372
373 if (vsArgs.size() > 1) {
374 CString sDelim = vsArgs[0];
375 bool bFoundOne = false;
376 CString::EEscape eEscape = CString::EASCII;
377
378 for (unsigned int a = 1; a < vsArgs.size(); a++) {
379 const CString& sArg = vsArgs[a];
380
381 if (sArg.Equals("ESC=", false, 4)) {
382 eEscape = CString::ToEscape(sArg.LeftChomp_n(4));
383 } else {
384 CString sValue = GetValue(sArg);
385
386 if (!sValue.empty()) {
387 if (bFoundOne) {
388 sOutput += sDelim;
389 }
390
391 sOutput += sValue.Escape_n(eEscape);
392 bFoundOne = true;
393 }
394 }
395 }
396 }
397 } else if (sAction.Equals("SETBLOCK")) {
398 sSetBlockVar = sArgs;
399 bInSetBlock = true;
400 } else if (sAction.Equals("EXPAND")) {
401 sOutput += ExpandFile(sArgs, true);
402 } else if (sAction.Equals("VAR")) {
403 sOutput += GetValue(sArgs);
404 } else if (sAction.Equals("LT")) {
405 sOutput += "<?";
406 } else if (sAction.Equals("GT")) {
407 sOutput += "?>";
408 } else if (sAction.Equals("CONTINUE")) {
409 CTemplateLoopContext* pContext = GetCurLoopContext();
410
411 if (pContext) {
412 uSkip++;
413 bLoopCont = true;
414
415 break;
416 } else {
417 DEBUG("[" + sFileName + ":" + CString(uCurPos - iPos2 -4) + "] <? CONTINUE ?> must be used inside of a loop!");
418 }
419 } else if (sAction.Equals("BREAK")) {
420 // break from loop
421 CTemplateLoopContext* pContext = GetCurLoopContext();
422
423 if (pContext) {
424 uSkip++;
425 bLoopBreak = true;
426
427 break;
428 } else {
429 DEBUG("[" + sFileName + ":" + CString(uCurPos - iPos2 -4) + "] <? BREAK ?> must be used inside of a loop!");
430 }
431 } else if (sAction.Equals("EXIT")) {
432 bExit = true;
433 } else if (sAction.Equals("DEBUG")) {
434 DEBUG("CTemplate DEBUG [" + sFileName + "@" + CString(uCurPos - iPos2 -4) + "b] -> [" + sArgs + "]");
435 } else if (sAction.Equals("LOOP")) {
436 CTemplateLoopContext* pContext = GetCurLoopContext();
437
438 if (!pContext || pContext->GetFilePosition() != uCurPos) {
439 // we are at a brand new loop (be it new or a first pass at an inner loop)
440
441 CString sLoopName = sArgs.Token(0);
442 bool bReverse = (sArgs.Token(1).Equals("REVERSE"));
443 bool bSort = (sArgs.Token(1).Left(4).Equals("SORT"));
444 vector<CTemplate*>* pvLoop = GetLoop(sLoopName);
445
446 if (bSort && pvLoop != NULL && pvLoop->size() > 1) {
447 CString sKey;
448
449 if(sArgs.Token(1).TrimPrefix_n("SORT").Left(4).Equals("ASC=")) {
450 sKey = sArgs.Token(1).TrimPrefix_n("SORTASC=");
451 } else if(sArgs.Token(1).TrimPrefix_n("SORT").Left(5).Equals("DESC=")) {
452 sKey = sArgs.Token(1).TrimPrefix_n("SORTDESC=");
453 bReverse = true;
454 }
455
456 if (!sKey.empty()) {
457 std::sort(pvLoop->begin(), pvLoop->end(), CLoopSorter(sKey));
458 }
459 }
460
461 if (pvLoop) {
462 // If we found data for this loop, add it to our context vector
463 //unsigned long uBeforeLoopTag = uCurPos - iPos2 - 4;
464 unsigned long uAfterLoopTag = uCurPos;
465
466 for (CString::size_type t = 0; t < sLine.size(); t++) {
467 char c = sLine[t];
468 if (c == '\r' || c == '\n') {
469 uAfterLoopTag++;
470 } else {
471 break;
472 }
473 }
474
475 m_vLoopContexts.push_back(new CTemplateLoopContext(uAfterLoopTag, sLoopName, bReverse, pvLoop));
476 } else { // If we don't have data, just skip this loop and everything inside
477 uSkip++;
478 }
479 }
480 } else if (sAction.Equals("IF")) {
481 if (ValidIf(sArgs)) {
482 uNestedIfs++;
483 bValidLastIf = true;
484 } else {
485 uSkip++;
486 bValidLastIf = false;
487 }
488 } else if (sAction.Equals("REM")) {
489 uSkip++;
490 } else {
491 bNotFound = true;
492 }
493 } else if (sAction.Equals("REM")) {
494 uSkip++;
495 } else if (sAction.Equals("IF")) {
496 uSkip++;
497 } else if (sAction.Equals("LOOP")) {
498 uSkip++;
499 }
500
501 if (sAction.Equals("ENDIF")) {
502 if (uSkip) {
503 uSkip--;
504 } else {
505 uNestedIfs--;
506 }
507 } else if (sAction.Equals("ENDREM")) {
508 if (uSkip) {
509 uSkip--;
510 }
511 } else if (sAction.Equals("ENDSETBLOCK")) {
512 bInSetBlock = false;
513 sSetBlockVar = "";
514 } else if (sAction.Equals("ENDLOOP")) {
515 if (bLoopCont && uSkip == 1) {
516 uSkip--;
517 bLoopCont = false;
518 }
519
520 if (bLoopBreak && uSkip == 1) {
521 uSkip--;
522 }
523
524 if (uSkip) {
525 uSkip--;
526 } else {
527 // We are at the end of the loop so we need to inc the index
528 CTemplateLoopContext* pContext = GetCurLoopContext();
529
530 if (pContext) {
531 pContext->IncRowIndex();
532
533 // If we didn't go out of bounds we need to seek back to the top of our loop
534 if (!bLoopBreak && pContext->GetCurRow()) {
535 uCurPos = pContext->GetFilePosition();
536 uFilePos = uCurPos;
537 uLineSize = 0;
538
539 File.Seek(uCurPos);
540 bBroke = true;
541
542 if (!sOutput.Trim_n().empty()) {
543 pContext->SetHasData();
544 }
545
546 break;
547 } else {
548 if (sOutput.Trim_n().empty()) {
549 sOutput.clear();
550 }
551
552 bTmplLoopHasData = pContext->HasData();
553 DelCurLoopContext();
554 bLoopBreak = false;
555 }
556 }
557 }
558 } else if (sAction.Equals("ELSE")) {
559 if (!bValidLastIf && uSkip == 1) {
560 CString sArg = sArgs.Token(0);
561
562 if (sArg.empty() || (sArg.Equals("IF") && ValidIf(sArgs.Token(1, true)))) {
563 uSkip = 0;
564 bValidLastIf = true;
565 }
566 } else if (!uSkip) {
567 uSkip = 1;
568 }
569 } else if (bNotFound) {
570 // Unknown tag that isn't being skipped...
571 vector<CSmartPtr<CTemplateTagHandler> >& vspTagHandlers = GetTagHandlers();
572
573 if (!vspTagHandlers.empty()) { // @todo this should go up to the top to grab handlers
574 CTemplate* pTmpl = GetCurTemplate();
575 CString sCustomOutput;
576
577 for (unsigned int j = 0; j < vspTagHandlers.size(); j++) {
578 CSmartPtr<CTemplateTagHandler> spTagHandler = vspTagHandlers[j];
579
580 if (spTagHandler->HandleTag(*pTmpl, sAction, sArgs, sCustomOutput)) {
581 sOutput += sCustomOutput;
582 bNotFound = false;
583 break;
584 }
585 }
586
587 if (bNotFound) {
588 DEBUG("Unknown/Unhandled tag [" + sAction + "]");
589 }
590 }
591 }
592
593 continue;
594 }
595
596 DEBUG("Malformed tag on line " + CString(uLineNum) + " of [" << File.GetLongName() + "]");
597 DEBUG("--------------- [" + sLine + "]");
598 }
599
600 if (!bBroke) {
601 uFilePos += uLineSize;
602
603 if (!uSkip) {
604 sOutput += sLine;
605 }
606 }
607
608 if (!bFoundATag || bTmplLoopHasData || sOutput.find_first_not_of(" \t\r\n") != CString::npos) {
609 if (bInSetBlock) {
610 CString sName = sSetBlockVar.Token(0);
611 //CString sValue = sSetBlockVar.Token(1, true);
612 (*this)[sName] += sOutput;
613 } else {
614 oOut << sOutput;
615 }
616 }
617
618 if (bExit) {
619 break;
620 }
621 }
622
623 oOut.flush();
624
625 return true;
626 }
627
628 void CTemplate::DelCurLoopContext() {
629 if (m_vLoopContexts.empty()) {
630 return;
631 }
632
633 delete m_vLoopContexts.back();
634 m_vLoopContexts.pop_back();
635 }
636
637 CTemplateLoopContext* CTemplate::GetCurLoopContext() {
638 if (!m_vLoopContexts.empty()) {
639 return m_vLoopContexts.back();
640 }
641
642 return NULL;
643 }
644
645 bool CTemplate::ValidIf(const CString& sArgs) {
646 CString sArgStr = sArgs;
647 //sArgStr.Replace(" ", "", "\"", "\"", true);
648 sArgStr.Replace(" &&", "&&", "\"", "\"", false);
649 sArgStr.Replace("&& ", "&&", "\"", "\"", false);
650 sArgStr.Replace(" ||", "||", "\"", "\"", false);
651 sArgStr.Replace("|| ", "||", "\"", "\"", false);
652
653 CString::size_type uOrPos = sArgStr.find("||");
654 CString::size_type uAndPos = sArgStr.find("&&");
655
656 while (uOrPos != CString::npos || uAndPos != CString::npos || !sArgStr.empty()) {
657 bool bAnd = false;
658
659 if (uAndPos < uOrPos) {
660 bAnd = true;
661 }
662
663 CString sExpr = sArgStr.Token(0, false, ((bAnd) ? "&&" : "||"));
664 sArgStr = sArgStr.Token(1, true, ((bAnd) ? "&&" : "||"));
665
666 if (ValidExpr(sExpr)) {
667 if (!bAnd) {
668 return true;
669 }
670 } else {
671 if (bAnd) {
672 return false;
673 }
674 }
675
676 uOrPos = sArgStr.find("||");
677 uAndPos = sArgStr.find("&&");
678 }
679
680 return false;
681 }
682
683 bool CTemplate::ValidExpr(const CString& sExpression) {
684 bool bNegate = false;
685 CString sExpr(sExpression);
686 CString sName;
687 CString sValue;
688
689 if (sExpr.Left(1) == "!") {
690 bNegate = true;
691 sExpr.LeftChomp();
692 }
693
694 if (sExpr.find("!=") != CString::npos) {
695 sName = sExpr.Token(0, false, "!=").Trim_n();
696 sValue = sExpr.Token(1, true, "!=", false, "\"", "\"", true).Trim_n();
697 bNegate = !bNegate;
698 } else if (sExpr.find("==") != CString::npos) {
699 sName = sExpr.Token(0, false, "==").Trim_n();
700 sValue = sExpr.Token(1, true, "==", false, "\"", "\"", true).Trim_n();
701 } else if (sExpr.find(">=") != CString::npos) {
702 sName = sExpr.Token(0, false, ">=").Trim_n();
703 sValue = sExpr.Token(1, true, ">=", false, "\"", "\"", true).Trim_n();
704 return (GetValue(sName, true).ToLong() >= sValue.ToLong());
705 } else if (sExpr.find("<=") != CString::npos) {
706 sName = sExpr.Token(0, false, "<=").Trim_n();
707 sValue = sExpr.Token(1, true, "<=", false, "\"", "\"", true).Trim_n();
708 return (GetValue(sName, true).ToLong() <= sValue.ToLong());
709 } else if (sExpr.find(">") != CString::npos) {
710 sName = sExpr.Token(0, false, ">").Trim_n();
711 sValue = sExpr.Token(1, true, ">", false, "\"", "\"", true).Trim_n();
712 return (GetValue(sName, true).ToLong() > sValue.ToLong());
713 } else if (sExpr.find("<") != CString::npos) {
714 sName = sExpr.Token(0, false, "<").Trim_n();
715 sValue = sExpr.Token(1, true, "<", false, "\"", "\"", true).Trim_n();
716 return (GetValue(sName, true).ToLong() < sValue.ToLong());
717 } else {
718 sName = sExpr.Trim_n();
719 }
720
721 if (sValue.empty()) {
722 return (bNegate != IsTrue(sName));
723 }
724
725 sValue = ResolveLiteral(sValue);
726
727 return (bNegate != GetValue(sName, true).Equals(sValue));
728 }
729
730 bool CTemplate::IsTrue(const CString& sName) {
731 if (HasLoop(sName)) {
732 return true;
733 }
734
735 return GetValue(sName, true).ToBool();
736 }
737
738 bool CTemplate::HasLoop(const CString& sName) {
739 return (GetLoop(sName) != NULL);
740 }
741
742 CTemplate* CTemplate::GetParent(bool bRoot) {
743 if (!bRoot) {
744 return m_pParent;
745 }
746
747 return (m_pParent) ? m_pParent->GetParent(bRoot) : this;
748 }
749
750 CTemplate* CTemplate::GetCurTemplate() {
751 CTemplateLoopContext* pContext = GetCurLoopContext();
752
753 if (!pContext) {
754 return this;
755 }
756
757 return pContext->GetCurRow();
758 }
759
760 CString CTemplate::ResolveLiteral(const CString& sString) {
761 if (sString.Left(2) == "**") {
762 // Allow string to start with a literal * by using two in a row
763 return sString.substr(1);
764 } else if (sString.Left(1) == "*") {
765 // If it starts with only one * then treat it as a var and do a lookup
766 return GetValue(sString.substr(1));
767 }
768
769 return sString;
770 }
771
772 CString CTemplate::GetValue(const CString& sArgs, bool bFromIf) {
773 CTemplateLoopContext* pContext = GetCurLoopContext();
774 CString sName = sArgs.Token(0);
775 CString sRest = sArgs.Token(1, true);
776 CString sRet;
777
778 while (sRest.Replace(" =", "=", "\"", "\"")) {}
779 while (sRest.Replace("= ", "=", "\"", "\"")) {}
780
781 VCString vArgs;
782 MCString msArgs;
783 //sRest.Split(" ", vArgs, false, "\"", "\"");
784 sRest.QuoteSplit(vArgs);
785
786 for (unsigned int a = 0; a < vArgs.size(); a++) {
787 const CString& sArg = vArgs[a];
788
789 msArgs[sArg.Token(0, false, "=").AsUpper()] = sArg.Token(1, true, "=");
790 }
791
792 /* We have no CConfig in znc land
793 if (msArgs.find("CONFIG") != msArgs.end()) {
794 sRet = CConfig::GetValue(sName);
795 } else*/ if (msArgs.find("ROWS") != msArgs.end()) {
796 vector<CTemplate*>* pLoop = GetLoop(sName);
797 sRet = CString((pLoop) ? pLoop->size() : 0);
798 } else if (msArgs.find("TOP") == msArgs.end() && pContext) {
799 sRet = pContext->GetValue(sArgs, bFromIf);
800
801 if (!sRet.empty()) {
802 return sRet;
803 }
804 } else {
805 if (sName.Left(1) == "*") {
806 sName.LeftChomp(1);
807 MCString::iterator it = find(sName);
808 sName = (it != end()) ? it->second : "";
809 }
810
811 MCString::iterator it = find(sName);
812 sRet = (it != end()) ? it->second : "";
813 }
814
815 vector<CSmartPtr<CTemplateTagHandler> >& vspTagHandlers = GetTagHandlers();
816
817 if (!vspTagHandlers.empty()) { // @todo this should go up to the top to grab handlers
818 CTemplate* pTmpl = GetCurTemplate();
819
820 if (sRet.empty()) {
821 for (unsigned int j = 0; j < vspTagHandlers.size(); j++) {
822 CSmartPtr<CTemplateTagHandler> spTagHandler = vspTagHandlers[j];
823 CString sCustomOutput;
824
825 if (!bFromIf && spTagHandler->HandleVar(*pTmpl, sArgs.Token(0), sArgs.Token(1, true), sCustomOutput)) {
826 sRet = sCustomOutput;
827 break;
828 } else if (bFromIf && spTagHandler->HandleIf(*pTmpl, sArgs.Token(0), sArgs.Token(1, true), sCustomOutput)) {
829 sRet = sCustomOutput;
830 break;
831 }
832 }
833 }
834
835 for (unsigned int j = 0; j < vspTagHandlers.size(); j++) {
836 CSmartPtr<CTemplateTagHandler> spTagHandler = vspTagHandlers[j];
837
838 if (spTagHandler->HandleValue(*pTmpl, sRet, msArgs)) {
839 break;
840 }
841 }
842 }
843
844 if (!bFromIf) {
845 if (sRet.empty()) {
846 sRet = ResolveLiteral(msArgs["DEFAULT"]);
847 }
848
849 MCString::iterator it = msArgs.find("ESC");
850
851 if (it != msArgs.end()) {
852 VCString vsEscs;
853 it->second.Split(",", vsEscs, false);
854
855 for (unsigned int a = 0; a < vsEscs.size(); a++) {
856 sRet.Escape(CString::ToEscape(vsEscs[a]));
857 }
858 } else {
859 sRet.Escape(m_spOptions->GetEscapeFrom(), m_spOptions->GetEscapeTo());
860 }
861 }
862
863 return sRet;
864 }