4 * Forms using HTTP user interface.
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
27 * Revision 1.49 2004/04/24 06:27:56 rjongbloed
28 * Fixed GCC 3.4.0 warnings about PAssertNULL and improved recoverability on
29 * NULL pointer usage in various bits of code.
31 * Revision 1.48 2004/04/03 08:22:20 csoutheren
32 * Remove pseudo-RTTI and replaced with real RTTI
34 * Revision 1.47 2003/03/24 04:31:03 robertj
35 * Added function to set and get strings from PConfig in correct format for
36 * use with HTTP form array contsructs.
38 * Revision 1.46 2002/11/22 06:20:26 robertj
39 * Added extra space around data entry fields.
40 * Added borders around arrays and composite fields.
41 * Added multi-line data entry for HTTPStringField > 128 characters.
43 * Revision 1.45 2002/11/06 22:47:25 robertj
44 * Fixed header comment (copyright etc)
46 * Revision 1.44 2002/10/10 04:43:44 robertj
47 * VxWorks port, thanks Martijn Roest
49 * Revision 1.43 2002/07/17 08:44:58 robertj
50 * Added links back to page and home page on accepted data html.
51 * Fixed display of validation error text if page not accepted.
53 * Revision 1.42 2001/10/10 08:07:48 robertj
54 * Fixed large memory leak of strings when doing POST to a form.
56 * Revision 1.41 2001/05/16 06:03:14 craigs
57 * Changed to allow access to absolute registry paths from within subforms
59 * Revision 1.40 2001/02/07 04:44:47 robertj
60 * Added ability to use check box to add/delete fields from arrays.
62 * Revision 1.39 2001/01/08 04:13:23 robertj
63 * Fixed bug with skipping every second option in determining the selected
64 * option in a SELECT field. No longer requires a </option> to work.
66 * Revision 1.38 2000/12/20 02:23:39 robertj
67 * Fixed variable array size value (caused extra blank entry ever commit).
69 * Revision 1.37 2000/12/18 12:13:08 robertj
70 * Fixed bug in auto-generated HTML in fixed size arrays, should not have add/delete box.
72 * Revision 1.36 2000/12/18 11:41:01 robertj
73 * Fixed bug in auto-generated HTML in non-array composite fields
75 * Revision 1.35 2000/12/18 07:14:30 robertj
76 * Added ability to have fixed length array fields.
77 * Fixed regular expressions so can have single '-' in field name.
78 * Fixed use of non-array subforprefix based compsite fields.
80 * Revision 1.34 2000/12/12 07:21:35 robertj
81 * Added ability to expand fields based on regex into repeated chunks of HTML.
83 * Revision 1.33 2000/11/02 21:55:28 craigs
84 * Added extra constructor
86 * Revision 1.32 2000/09/05 09:52:24 robertj
87 * Fixed bug in HTTP form updating SELECT fields from registry.
89 * Revision 1.31 2000/06/19 11:35:01 robertj
90 * Fixed bug in setting current value of options in select form fields.
92 * Revision 1.30 1999/02/10 13:19:45 robertj
93 * Fixed PConfig update problem when POSTing to the form. Especiall with arrays.
95 * Revision 1.29 1998/11/30 04:51:57 robertj
96 * New directory structure
98 * Revision 1.28 1998/11/14 11:11:06 robertj
99 * PPC GNU compiler compatibility.
101 * Revision 1.27 1998/10/01 09:05:11 robertj
102 * Fixed bug in nested composite field names, array indexes not being set correctly.
104 * Revision 1.26 1998/09/23 06:22:11 robertj
105 * Added open source copyright license.
107 * Revision 1.25 1998/08/20 05:51:06 robertj
108 * Fixed bug where substitutions did not always occur if near end of macro block.
109 * Improved internationalisation. Allow HTML override of strings in macros.
111 * Revision 1.24 1998/08/09 11:25:51 robertj
112 * GNU C++ warning removal.
114 * Revision 1.23 1998/08/09 10:35:11 robertj
115 * Changed array control so can have language override.
117 * Revision 1.22 1998/07/24 06:56:05 robertj
118 * Fixed case significance problem in HTTP forms.
119 * Improved detection of VALUE= fields with and without quotes.
121 * Revision 1.21 1998/03/20 03:16:43 robertj
122 * Fixed bug in beaing able to reset a check box field.
124 * Revision 1.20 1998/02/03 06:26:09 robertj
125 * Fixed propagation of inital values in arrays subfields.
126 * Fixed problem where hidden fields were being relaced with default values from PHTTPForm.
128 * Revision 1.19 1998/01/26 02:49:17 robertj
131 * Revision 1.18 1998/01/26 01:51:37 robertj
132 * Fixed uninitialised variable.
134 * Revision 1.17 1998/01/26 00:25:25 robertj
135 * Major rewrite of HTTP forms management.
137 * Revision 1.16 1997/12/18 05:06:51 robertj
138 * Added missing braces to kill GNU compiler warning.
140 * Revision 1.15 1997/10/10 10:43:43 robertj
141 * Fixed bug in password encryption, missing string terminator.
143 * Revision 1.14 1997/08/28 12:48:29 robertj
144 * Changed array fields to allow for reordering.
146 * Revision 1.13 1997/08/21 12:44:10 robertj
147 * Fixed bug in HTTP form array size field.
148 * Fixed bug where section list was only replacing first instance of macro.
150 * Revision 1.12 1997/08/09 07:46:52 robertj
151 * Fixed problems with value of SELECT fields in form
153 * Revision 1.11 1997/08/04 10:41:13 robertj
154 * Fixed bug in new section list page for names with special characters in them.
156 * Revision 1.10 1997/07/26 11:38:20 robertj
157 * Support for overridable pages in HTTP service applications.
159 * Revision 1.9 1997/07/14 11:49:51 robertj
160 * Put "Add" and "Keep" on check boxes in array fields.
162 * Revision 1.8 1997/07/08 13:12:29 robertj
163 * Major HTTP form enhancements for lists and arrays of fields.
165 * Revision 1.7 1997/06/08 04:47:27 robertj
166 * Adding new llist based form field.
168 * Revision 1.6 1997/04/12 02:07:26 robertj
169 * Fixed boolean check boxes being more flexible on string values.
171 * Revision 1.5 1997/04/01 06:00:53 robertj
172 * Changed PHTTPConfig so if section empty string, does not write PConfig parameters.
174 * Revision 1.4 1996/10/08 13:10:34 robertj
175 * Fixed bug in boolean (checkbox) html forms, cannot be reset.
177 * Revision 1.3 1996/09/14 13:09:31 robertj
179 * rearranged sockets to help support IPX.
180 * added indirect channel class and moved all protocols to descend from it,
181 * separating the protocol from the low level byte transport.
183 * Revision 1.2 1996/08/08 13:34:10 robertj
184 * Removed redundent call.
186 * Revision 1.1 1996/06/28 12:56:20 robertj
192 #pragma implementation "httpform.h"
196 #include <ptclib/httpform.h>
197 #include <ptclib/cypher.h>
200 //////////////////////////////////////////////////////////////////////////////
203 PHTTPField::PHTTPField(const char * nam
, const char * titl
, const char * hlp
)
204 : baseName(nam
), fullName(nam
),
205 title(titl
!= NULL
? titl
: nam
),
206 help(hlp
!= NULL
? hlp
: "")
212 PObject::Comparison
PHTTPField::Compare(const PObject
& obj
) const
214 PAssert(PIsDescendant(&obj
, PHTTPField
), PInvalidCast
);
215 return fullName
.Compare(((const PHTTPField
&)obj
).fullName
);
219 void PHTTPField::SetName(const PString
& newName
)
225 const PHTTPField
* PHTTPField::LocateName(const PString
& name
) const
227 if (fullName
== name
)
234 void PHTTPField::SetHelp(const PString
& hotLinkURL
,
235 const PString
& linkText
)
237 help
= "<A HREF=\"" + hotLinkURL
+ "\">" + linkText
+ "</A>\r\n";
241 void PHTTPField::SetHelp(const PString
& hotLinkURL
,
242 const PString
& imageURL
,
243 const PString
& imageText
)
245 help
= "<A HREF=\"" + hotLinkURL
+ "\"><IMG SRC=\"" +
246 imageURL
+ "\" ALT=\"" + imageText
+ "\" ALIGN=absmiddle></A>\r\n";
250 static BOOL
FindSpliceBlock(const PRegularExpression
& startExpr
,
251 const PRegularExpression
& endExpr
,
252 const PString
& text
,
259 start
= finish
= P_MAX_INDEX
;
261 if (!text
.FindRegEx(startExpr
, pos
, len
, offset
))
264 PINDEX endpos
, endlen
;
265 if (!text
.FindRegEx(endExpr
, endpos
, endlen
, pos
+len
))
270 len
= endpos
- pos
+ endlen
;
275 static BOOL
FindSpliceBlock(const PRegularExpression
& startExpr
,
276 const PString
& text
,
283 static PRegularExpression
EndBlock("<?!--#form[ \t\r\n]+end[ \t\r\n]*-->?",
284 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
285 return FindSpliceBlock(startExpr
, EndBlock
, text
, offset
, pos
, len
, start
, finish
);
289 static BOOL
FindSpliceName(const PCaselessString
& text
,
295 if (text
[start
+1] != '!') {
296 static PRegularExpression
NameExpr("name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"",
297 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
298 if ((pos
= text
.FindRegEx(NameExpr
, start
)) == P_MAX_INDEX
)
304 pos
= text
.Find('"', pos
) + 1;
305 end
= text
.Find('"', pos
) - 1;
308 pos
= start
+ 9; // Skip over the <!--#form
309 while (isspace(text
[pos
])) // Skip over blanks
311 while (pos
< finish
&& !isspace(text
[pos
])) // Skip over keyword
313 while (isspace(text
[pos
])) // Skip over more blanks
316 end
= text
.Find("--", pos
) - 1;
323 static BOOL
FindSpliceFieldName(const PString
& text
,
329 static PRegularExpression
FieldName("<?!--#form[ \t\r\n]+[a-z0-9]+[ \t\r\n]+(-?[^-])+-->?"
331 "<[a-z]+[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
332 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
333 if (!text
.FindRegEx(FieldName
, pos
, len
, offset
))
336 PINDEX nameStart
, nameEnd
;
337 if (!FindSpliceName(text
, pos
, pos
+len
-1, nameStart
, nameEnd
))
340 name
= text(nameStart
, nameEnd
);
342 len
= nameEnd
- nameStart
+ 1;
347 static void SpliceAdjust(const PString
& str
,
353 text
.Splice(str
, pos
, len
);
354 PINDEX newLen
= str
.GetLength();
355 if (finish
!= P_MAX_INDEX
)
356 finish
+= newLen
- len
;
361 void PHTTPField::ExpandFieldNames(PString
& text
, PINDEX start
, PINDEX
& finish
) const
365 while (start
< finish
&& FindSpliceFieldName(text
, start
, pos
, len
, name
)) {
368 if (baseName
== name
)
369 SpliceAdjust(fullName
, text
, pos
, len
, finish
);
375 static BOOL
FindInputValue(const PString
& text
, PINDEX
& before
, PINDEX
& after
)
377 static PRegularExpression
Value("value[ \t\r\n]*=[ \t\r\n]*(\"[^\"]*\"|[^> \t\r\n]+)",
378 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
379 PINDEX pos
= text
.FindRegEx(Value
);
380 if (pos
== P_MAX_INDEX
)
383 before
= text
.Find('"', pos
);
384 if (before
!= P_MAX_INDEX
)
385 after
= text
.Find('"', before
+1);
387 before
= text
.Find('=', pos
);
388 while (isspace(text
[before
+1]))
391 while (text
[after
] != '\0' && text
[after
] != '>' && !isspace(text
[after
]))
398 PString
PHTTPField::GetHTMLInput(const PString
& input
) const
400 PINDEX before
, after
;
401 if (FindInputValue(input
, before
, after
))
402 return input(0, before
) + GetValue(FALSE
) + input
.Mid(after
);
404 return "<input value=\"" + GetValue(FALSE
) + "\"" + input
.Mid(6);
408 static void AdjustSelectOptions(PString
& text
, PINDEX begin
, PINDEX end
,
409 const PString
& myValue
, PStringList
& validValues
,
410 PINDEX
& finishAdjust
)
412 PINDEX start
, finish
;
415 static PRegularExpression
StartOption("<[ \t\r\n]*option[^>]*>",
416 PRegularExpression::IgnoreCase
);
417 static PRegularExpression
EndOption("<[ \t\r\n]*/?option[^>]*>",
418 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
419 while (FindSpliceBlock(StartOption
, EndOption
, text
, pos
+len
, pos
, len
, start
, finish
) && pos
< end
) {
420 if (start
== P_MAX_INDEX
)
421 start
= text
.Find('>', pos
)+1;
423 // Check for if option was not closed by </option> but another <option>
424 PINDEX optpos
= text
.FindRegEx(StartOption
, start
);
425 if (optpos
< pos
+len
) // Adjust back to before <option> if so.
428 PCaselessString option
= text(pos
, start
-1);
429 PINDEX before
, after
;
430 if (FindInputValue(option
, before
, after
)) {
431 start
= pos
+ before
+ 1;
432 finish
= pos
+ after
- 1;
434 PINDEX selpos
= option
.Find("selected");
435 PString thisValue
= text(start
, finish
).Trim();
436 if (thisValue
== myValue
) {
437 if (selpos
== P_MAX_INDEX
) {
438 text
.Splice(" selected", pos
+7, 0);
439 if (finishAdjust
!= P_MAX_INDEX
)
441 if (end
!= P_MAX_INDEX
)
447 if (validValues
.GetSize() > 0) {
449 for (valid
= 0; valid
< validValues
.GetSize(); valid
++) {
450 if (thisValue
== validValues
[valid
])
453 if (valid
>= validValues
.GetSize()) {
454 text
.Delete(pos
, len
);
455 selpos
= P_MAX_INDEX
;
456 if (finishAdjust
!= P_MAX_INDEX
)
458 if (end
!= P_MAX_INDEX
)
463 if (selpos
!= P_MAX_INDEX
) {
466 if (text
[selpos
-1] == ' ') {
470 text
.Delete(selpos
, sellen
);
471 if (finishAdjust
!= P_MAX_INDEX
)
472 finishAdjust
-= sellen
;
473 if (end
!= P_MAX_INDEX
)
481 PString
PHTTPField::GetHTMLSelect(const PString
& selection
) const
483 PString text
= selection
;
485 PINDEX dummy2
= P_MAX_INDEX
;
486 AdjustSelectOptions(text
, 0, P_MAX_INDEX
, GetValue(FALSE
), dummy1
, dummy2
);
491 void PHTTPField::GetHTMLHeading(PHTML
&) const
496 static int SplitConfigKey(const PString
& fullName
,
497 PString
& section
, PString
& key
)
499 if (fullName
.IsEmpty())
502 PINDEX slash
= fullName
.FindLast('\\');
503 if (slash
== 0 || slash
>= fullName
.GetLength()-1) {
508 section
= fullName
.Left(slash
);
509 key
= fullName
.Mid(slash
+1);
510 if (section
.IsEmpty() || key
.IsEmpty())
517 void PHTTPField::LoadFromConfig(PConfig
& cfg
)
519 PString section
, key
;
520 switch (SplitConfigKey(fullName
, section
, key
)) {
522 SetValue(cfg
.GetString(key
, GetValue(TRUE
)));
525 SetValue(cfg
.GetString(section
, key
, GetValue(TRUE
)));
530 void PHTTPField::SaveToConfig(PConfig
& cfg
) const
532 PString section
, key
;
533 switch (SplitConfigKey(fullName
, section
, key
)) {
535 cfg
.SetString(key
, GetValue());
538 cfg
.SetString(section
, key
, GetValue());
543 BOOL
PHTTPField::Validated(const PString
&, PStringStream
&) const
549 void PHTTPField::GetAllNames(PStringList
& list
) const
551 list
.AppendString(fullName
);
555 void PHTTPField::SetAllValues(const PStringToString
& data
)
557 if (!baseName
&& data
.Contains(fullName
))
558 SetValue(data
[fullName
]);
562 BOOL
PHTTPField::ValidateAll(const PStringToString
& data
, PStringStream
& msg
) const
564 if (data
.Contains(fullName
))
565 return Validated(data
[fullName
], msg
);
570 //////////////////////////////////////////////////////////////////////////////
571 // PHTTPCompositeField
573 PHTTPCompositeField::PHTTPCompositeField(const char * nam
,
576 : PHTTPField(nam
, titl
, hlp
)
581 void PHTTPCompositeField::SetName(const PString
& newName
)
583 if (fullName
.IsEmpty() || newName
.IsEmpty())
586 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++) {
587 PHTTPField
& field
= fields
[i
];
589 PString firstPartOfName
= psprintf(fullName
, i
+1);
590 PString subFieldName
;
591 if (field
.GetName().Find(firstPartOfName
) == 0)
592 subFieldName
= field
.GetName().Mid(firstPartOfName
.GetLength());
594 subFieldName
= field
.GetName();
596 firstPartOfName
= psprintf(newName
, i
+1);
597 if (subFieldName
[0] == '\\' || firstPartOfName
[firstPartOfName
.GetLength()-1] == '\\')
598 field
.SetName(firstPartOfName
+ subFieldName
);
600 field
.SetName(firstPartOfName
& subFieldName
);
603 PHTTPField::SetName(newName
);
607 const PHTTPField
* PHTTPCompositeField::LocateName(const PString
& name
) const
609 if (fullName
== name
)
612 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++) {
613 const PHTTPField
* field
= fields
[i
].LocateName(name
);
622 PHTTPField
* PHTTPCompositeField::NewField() const
624 PHTTPCompositeField
* fld
= new PHTTPCompositeField(baseName
, title
, help
);
625 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
626 fld
->Append(fields
[i
].NewField());
631 void PHTTPCompositeField::GetHTMLTag(PHTML
& html
) const
633 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++) {
634 if (i
!= 0 && html
.Is(PHTML::InTable
))
635 html
<< PHTML::TableData("NOWRAP ALIGN=CENTER");
636 fields
[i
].GetHTMLTag(html
);
641 PString
PHTTPCompositeField::GetHTMLInput(const PString
& input
) const
647 void PHTTPCompositeField::ExpandFieldNames(PString
& text
, PINDEX start
, PINDEX
& finish
) const
649 static PRegularExpression
FieldName( "!--#form[ \t\r\n]+(-?[^-])+[ \t\r\n]+(-?[^-])+--"
651 "<[a-z]*[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
652 PRegularExpression::IgnoreCase
);
656 while (start
< finish
&& FindSpliceFieldName(text
, start
, pos
, len
, name
)) {
659 for (PINDEX fld
= 0; fld
< fields
.GetSize(); fld
++) {
660 if (fields
[fld
].GetBaseName() *= name
) {
661 SpliceAdjust(fields
[fld
].GetName(), text
, pos
, len
, finish
);
670 void PHTTPCompositeField::GetHTMLHeading(PHTML
& html
) const
672 html
<< PHTML::TableRow();
673 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
674 html
<< PHTML::TableHeader() << fields
[i
].GetTitle();
678 PString
PHTTPCompositeField::GetValue(BOOL dflt
) const
681 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
682 value
<< fields
[i
].GetValue(dflt
) << '\n';
687 void PHTTPCompositeField::SetValue(const PString
&)
689 PAssertAlways(PLogicError
);
693 void PHTTPCompositeField::LoadFromConfig(PConfig
& cfg
)
696 for (PINDEX i
= 0; i
< GetSize(); i
++)
697 fields
[i
].LoadFromConfig(cfg
);
701 void PHTTPCompositeField::SaveToConfig(PConfig
& cfg
) const
703 for (PINDEX i
= 0; i
< GetSize(); i
++)
704 fields
[i
].SaveToConfig(cfg
);
708 void PHTTPCompositeField::GetAllNames(PStringList
& list
) const
710 for (PINDEX i
= 0; i
< GetSize(); i
++)
711 fields
[i
].GetAllNames(list
);
715 void PHTTPCompositeField::SetAllValues(const PStringToString
& data
)
717 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
718 fields
[i
].SetAllValues(data
);
722 BOOL
PHTTPCompositeField::ValidateAll(const PStringToString
& data
,
723 PStringStream
& msg
) const
725 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
726 if (!fields
[i
].ValidateAll(data
, msg
))
733 PINDEX
PHTTPCompositeField::GetSize() const
735 return fields
.GetSize();
739 void PHTTPCompositeField::Append(PHTTPField
* fld
)
745 //////////////////////////////////////////////////////////////////////////////
748 PHTTPSubForm::PHTTPSubForm(const PString
& subForm
,
753 : PHTTPCompositeField(name
, title
, NULL
),
761 PHTTPField
* PHTTPSubForm::NewField() const
763 PHTTPCompositeField
* fld
= new PHTTPSubForm(subFormName
, baseName
, title
, primary
, secondary
);
764 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
765 fld
->Append(fields
[i
].NewField());
770 void PHTTPSubForm::GetHTMLTag(PHTML
& html
) const
772 PString value
= fields
[primary
].GetValue();
775 html
<< PHTML::HotLink(subFormName
+
776 "?subformprefix=" + PURL::TranslateString(fullName
, PURL::QueryTranslation
))
777 << value
<< PHTML::HotLink();
779 if (secondary
!= P_MAX_INDEX
)
780 html
<< PHTML::TableData("NOWRAP") << fields
[secondary
].GetValue();
784 void PHTTPSubForm::GetHTMLHeading(PHTML
&) const
789 //////////////////////////////////////////////////////////////////////////////
792 PHTTPFieldArray::PHTTPFieldArray(PHTTPField
* fld
, BOOL ordered
, PINDEX fixedSize
)
793 : PHTTPCompositeField(fld
->GetName(), fld
->GetTitle(), fld
->GetHelp()),
796 orderedArray
= ordered
;
797 canAddElements
= fixedSize
== 0;
802 PHTTPFieldArray::~PHTTPFieldArray()
808 void PHTTPFieldArray::SetSize(PINDEX newSize
)
810 while (fields
.GetSize() > newSize
)
811 fields
.RemoveAt(fields
.GetSize()-1);
812 while (fields
.GetSize() < newSize
)
819 PHTTPField
* PHTTPFieldArray::NewField() const
821 return new PHTTPFieldArray(baseField
->NewField(), orderedArray
);
825 static const char ArrayControlBox
[] = " Array Control";
826 static const char ArrayControlKeep
[] = "Keep";
827 static const char ArrayControlRemove
[] = "Remove";
828 static const char ArrayControlMoveUp
[] = "Move Up";
829 static const char ArrayControlMoveDown
[] = "Move Down";
830 static const char ArrayControlToTop
[] = "To Top";
831 static const char ArrayControlToBottom
[] = "To Bottom";
832 static const char ArrayControlIgnore
[] = "Ignore";
833 static const char ArrayControlAddTop
[] = "Add Top";
834 static const char ArrayControlAddBottom
[] = "Add Bottom";
835 static const char ArrayControlAdd
[] = "Add";
837 static PStringList
GetArrayControlOptions(PINDEX fld
, PINDEX size
, BOOL orderedArray
)
842 options
.AppendString(ArrayControlIgnore
);
843 if (size
== 0 || !orderedArray
)
844 options
.AppendString(ArrayControlAdd
);
846 options
.AppendString(ArrayControlAddTop
);
847 options
.AppendString(ArrayControlAddBottom
);
851 options
.AppendString(ArrayControlKeep
);
852 options
.AppendString(ArrayControlRemove
);
855 options
.AppendString(ArrayControlMoveUp
);
857 options
.AppendString(ArrayControlMoveDown
);
859 options
.AppendString(ArrayControlToTop
);
861 options
.AppendString(ArrayControlToBottom
);
868 void PHTTPFieldArray::AddArrayControlBox(PHTML
& html
, PINDEX fld
) const
870 PStringList options
= GetArrayControlOptions(fld
, fields
.GetSize()-1, orderedArray
);
871 html
<< PHTML::Select(fields
[fld
].GetName() + ArrayControlBox
);
872 for (PINDEX i
= 0; i
< options
.GetSize(); i
++)
873 html
<< PHTML::Option(i
== 0 ? PHTML::Selected
: PHTML::NotSelected
) << options
[i
];
874 html
<< PHTML::Select();
878 void PHTTPFieldArray::GetHTMLTag(PHTML
& html
) const
880 html
<< PHTML::TableStart("border=1 cellspacing=0 cellpadding=8");
881 baseField
->GetHTMLHeading(html
);
882 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++) {
883 html
<< PHTML::TableRow() << PHTML::TableData("NOWRAP");
884 fields
[i
].GetHTMLTag(html
);
885 html
<< PHTML::TableData("NOWRAP");
887 AddArrayControlBox(html
, i
);
889 html
<< PHTML::TableEnd();
893 void PHTTPFieldArray::ExpandFieldNames(PString
& text
, PINDEX start
, PINDEX
& finish
) const
895 PString original
= text(start
, finish
);
896 PINDEX origFinish
= finish
;
897 PINDEX finalFinish
= finish
;
899 PINDEX fld
= fields
.GetSize();
901 fields
[--fld
].ExpandFieldNames(text
, start
, finish
);
904 static PRegularExpression
RowNum("<?!--#form[ \t\r\n]+rownum[ \t\r\n]*-->?",
905 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
906 while (text
.FindRegEx(RowNum
, pos
, len
, start
, finish
))
907 SpliceAdjust(psprintf("%u", fld
+1), text
, pos
, len
, finish
);
909 static PRegularExpression
SubForm("<?!--#form[ \t\r\n]+subform[ \t\r\n]*-->?",
910 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
911 while (text
.FindRegEx(SubForm
, pos
, len
, start
, finish
)) {
912 PString fmt
= fullName
;
913 if (fmt
.Find("%u") == P_MAX_INDEX
)
915 SpliceAdjust("subformprefix=" + PURL::TranslateString(psprintf(fmt
, fld
+1), PURL::QueryTranslation
),
916 text
, pos
, len
, finish
);
919 static PRegularExpression
RowControl("<?!--#form[ \t\r\n]+rowcontrol[ \t\r\n]*-->?",
920 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
921 while (text
.FindRegEx(RowControl
, pos
, len
, start
, finish
)) {
922 PHTML
html(PHTML::InForm
);
924 AddArrayControlBox(html
, fld
);
925 SpliceAdjust(html
, text
, pos
, len
, finish
);
928 static PRegularExpression
RowCheck("<?!--#form[ \t\r\n]+row(add|delete)[ \t\r\n]*(-?[^-])*-->?",
929 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
930 while (text
.FindRegEx(RowCheck
, pos
, len
, start
, finish
)) {
931 PStringStream checkbox
;
932 if (canAddElements
) {
933 PINDEX titlepos
= text
.Find("row", start
)+3;
934 BOOL adding
= text
[titlepos
] == 'a';
935 if (( adding
&& fld
>= fields
.GetSize()-1) ||
936 (!adding
&& fld
< fields
.GetSize()-1)) {
937 titlepos
+= adding
? 3 : 6;
938 PINDEX dashes
= text
.Find("--", titlepos
);
939 PString title
= text(titlepos
, dashes
-1).Trim();
940 if (title
.IsEmpty() && adding
)
943 << "<INPUT TYPE=checkbox NAME=\""
944 << fields
[fld
].GetName()
947 << (adding
? ArrayControlAdd
: ArrayControlRemove
)
951 SpliceAdjust(checkbox
, text
, pos
, len
, finish
);
954 static PRegularExpression
SelectRow("<select[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"!--#form[ \t\r\n]+rowselect[ \t\r\n]*--\"[^>]*>",
955 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
956 static PRegularExpression
SelEndRegEx("</select[^>]*>",
957 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
959 while (FindSpliceBlock(SelectRow
, SelEndRegEx
, text
, 0, pos
, len
, begin
, end
)) {
960 PStringList options
= GetArrayControlOptions(fld
, fields
.GetSize()-1, orderedArray
);
961 AdjustSelectOptions(text
, begin
, end
, options
[0], options
, finish
);
962 static PRegularExpression
RowSelect("!--#form[ \t\r\n]+rowselect[ \t\r\n]*--",
963 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
964 if (text
.FindRegEx(RowSelect
, pos
, len
, pos
, begin
))
965 SpliceAdjust(fields
[fld
].GetName() + ArrayControlBox
, text
, pos
, len
, finish
);
968 finalFinish
+= finish
- origFinish
;
971 text
.Splice(original
, start
, 0);
973 finalFinish
+= finish
- start
;
977 finish
= finalFinish
;
981 static int SplitArraySizeKey(const PString
& fullName
,
982 PString
& section
, PString
& key
)
984 static const char ArraySize
[] = "Array Size";
985 PINDEX pos
= fullName
.Find("%u");
986 if (pos
== P_MAX_INDEX
)
987 return SplitConfigKey(fullName
& ArraySize
, section
, key
);
989 PINDEX endPos
= fullName
.GetLength() - 1;
990 if (fullName
[endPos
] == '\\')
992 return SplitConfigKey(fullName
.Left(pos
) & ArraySize
& fullName(pos
+2, endPos
), section
, key
);
996 void PHTTPFieldArray::LoadFromConfig(PConfig
& cfg
)
998 if (canAddElements
) {
999 PString section
, key
;
1000 switch (SplitArraySizeKey(fullName
, section
, key
)) {
1002 SetSize(cfg
.GetInteger(key
, GetSize()));
1005 SetSize(cfg
.GetInteger(section
, key
, GetSize()));
1008 PHTTPCompositeField::LoadFromConfig(cfg
);
1012 void PHTTPFieldArray::SaveToConfig(PConfig
& cfg
) const
1014 if (canAddElements
) {
1015 PString section
, key
;
1016 switch (SplitArraySizeKey(fullName
, section
, key
)) {
1018 cfg
.SetInteger(key
, GetSize());
1021 cfg
.SetInteger(section
, key
, GetSize());
1024 PHTTPCompositeField::SaveToConfig(cfg
);
1028 void PHTTPFieldArray::SetArrayFieldName(PINDEX idx
) const
1030 PString fmt
= fullName
;
1031 if (fmt
.Find("%u") == P_MAX_INDEX
)
1033 fields
[idx
].SetName(psprintf(fmt
, idx
+1));
1037 void PHTTPFieldArray::SetAllValues(const PStringToString
& data
)
1039 PHTTPFieldList newFields
;
1040 newFields
.DisallowDeleteObjects();
1042 for (i
= 0; i
< fields
.GetSize(); i
++)
1043 newFields
.Append(fields
.GetAt(i
));
1045 BOOL lastFieldIsSet
= FALSE
;
1046 PINDEX size
= fields
.GetSize();
1047 for (i
= 0; i
< size
; i
++) {
1048 PHTTPField
* fieldPtr
= &fields
[i
];
1049 PINDEX pos
= newFields
.GetObjectsIndex(fieldPtr
);
1050 fieldPtr
->SetAllValues(data
);
1052 PString control
= data(fieldPtr
->GetName() + ArrayControlBox
);
1053 if (control
== ArrayControlMoveUp
) {
1055 newFields
.SetAt(pos
, newFields
.GetAt(pos
-1));
1056 newFields
.SetAt(pos
-1, fieldPtr
);
1059 else if (control
== ArrayControlMoveDown
) {
1060 if (size
> 2 && pos
< size
-2) {
1061 newFields
.SetAt(pos
, newFields
.GetAt(pos
+1));
1062 newFields
.SetAt(pos
+1, fieldPtr
);
1065 else if (control
== ArrayControlToTop
) {
1066 newFields
.RemoveAt(pos
);
1067 newFields
.InsertAt(0, fieldPtr
);
1069 else if (control
== ArrayControlToBottom
) {
1070 newFields
.RemoveAt(pos
);
1071 newFields
.Append(fieldPtr
);
1073 else if (control
== ArrayControlAddTop
) {
1075 newFields
.RemoveAt(pos
);
1076 newFields
.InsertAt(0, fieldPtr
);
1077 lastFieldIsSet
= TRUE
;
1080 else if (control
== ArrayControlAddBottom
|| control
== ArrayControlAdd
) {
1082 newFields
.RemoveAt(pos
);
1083 newFields
.Append(fieldPtr
);
1084 lastFieldIsSet
= TRUE
;
1087 else if (control
== ArrayControlIgnore
) {
1088 newFields
.RemoveAt(pos
);
1089 newFields
.Append(fieldPtr
);
1091 else if (control
== ArrayControlRemove
)
1092 newFields
.RemoveAt(pos
);
1095 fields
.DisallowDeleteObjects();
1096 for (i
= 0; i
< newFields
.GetSize(); i
++)
1097 fields
.Remove(newFields
.GetAt(i
));
1098 fields
.AllowDeleteObjects();
1101 for (i
= 0; i
< newFields
.GetSize(); i
++) {
1102 fields
.Append(newFields
.GetAt(i
));
1103 SetArrayFieldName(i
);
1106 if (lastFieldIsSet
&& canAddElements
)
1111 PINDEX
PHTTPFieldArray::GetSize() const
1113 PINDEX size
= fields
.GetSize();
1114 PAssert(size
> 0, PLogicError
);
1121 void PHTTPFieldArray::AddBlankField()
1123 fields
.Append(baseField
->NewField());
1124 SetArrayFieldName(fields
.GetSize()-1);
1128 PStringArray
PHTTPFieldArray::GetStrings(PConfig
& cfg
)
1130 LoadFromConfig(cfg
);
1132 PStringArray
values(GetSize());
1134 for (PINDEX i
= 0; i
< GetSize(); i
++)
1135 values
[i
] = fields
[i
].GetValue(FALSE
);
1141 void PHTTPFieldArray::SetStrings(PConfig
& cfg
, const PStringArray
& values
)
1143 SetSize(values
.GetSize());
1144 for (PINDEX i
= 0; i
< values
.GetSize(); i
++)
1145 fields
[i
].SetValue(values
[i
]);
1151 //////////////////////////////////////////////////////////////////////////////
1154 PHTTPStringField::PHTTPStringField(const char * name
,
1156 const char * initVal
,
1158 : PHTTPField(name
, NULL
, help
),
1159 value(initVal
!= NULL
? initVal
: ""),
1166 PHTTPStringField::PHTTPStringField(const char * name
,
1169 const char * initVal
,
1171 : PHTTPField(name
, title
, help
),
1172 value(initVal
!= NULL
? initVal
: ""),
1179 PHTTPField
* PHTTPStringField::NewField() const
1181 return new PHTTPStringField(baseName
, title
, size
, initialValue
, help
);
1185 void PHTTPStringField::GetHTMLTag(PHTML
& html
) const
1188 html
<< PHTML::InputText(fullName
, size
, value
);
1190 html
<< PHTML::TextArea(fullName
, (size
+79)/80, 80) << value
<< PHTML::TextArea(fullName
);
1194 void PHTTPStringField::SetValue(const PString
& newVal
)
1200 PString
PHTTPStringField::GetValue(BOOL dflt
) const
1203 return initialValue
;
1209 //////////////////////////////////////////////////////////////////////////////
1210 // PHTTPPasswordField
1212 PHTTPPasswordField::PHTTPPasswordField(const char * name
,
1214 const char * initVal
,
1216 : PHTTPStringField(name
, siz
, initVal
, help
)
1221 PHTTPPasswordField::PHTTPPasswordField(const char * name
,
1224 const char * initVal
,
1226 : PHTTPStringField(name
, title
, siz
, initVal
, help
)
1231 PHTTPField
* PHTTPPasswordField::NewField() const
1233 return new PHTTPPasswordField(baseName
, title
, size
, initialValue
, help
);
1237 void PHTTPPasswordField::GetHTMLTag(PHTML
& html
) const
1239 html
<< PHTML::InputPassword(fullName
, size
, value
);
1243 static const PTEACypher::Key PasswordKey
= {
1245 103, 60, 222, 17, 128, 157, 31, 137,
1246 133, 64, 82, 148, 94, 136, 4, 209
1250 void PHTTPPasswordField::SetValue(const PString
& newVal
)
1252 value
= Decrypt(newVal
);
1256 PString
PHTTPPasswordField::GetValue(BOOL dflt
) const
1259 return initialValue
;
1261 PTEACypher
crypt(PasswordKey
);
1262 return crypt
.Encode(value
);
1266 PString
PHTTPPasswordField::Decrypt(const PString
& pword
)
1269 PTEACypher
crypt(PasswordKey
);
1270 return crypt
.Decode(pword
, clear
) ? clear
: pword
;
1274 //////////////////////////////////////////////////////////////////////////////
1275 // PHTTPIntegerField
1277 PHTTPIntegerField::PHTTPIntegerField(const char * nam
,
1282 : PHTTPField(nam
, NULL
, help
), units(unit
!= NULL
? unit
: "")
1286 value
= initialValue
= initVal
;
1289 PHTTPIntegerField::PHTTPIntegerField(const char * nam
,
1295 : PHTTPField(nam
, titl
, help
), units(unit
!= NULL
? unit
: "")
1299 value
= initialValue
= initVal
;
1303 PHTTPField
* PHTTPIntegerField::NewField() const
1305 return new PHTTPIntegerField(baseName
, title
, low
, high
, initialValue
, units
, help
);
1309 void PHTTPIntegerField::GetHTMLTag(PHTML
& html
) const
1311 html
<< PHTML::InputRange(fullName
, low
, high
, value
) << " " << units
;
1315 void PHTTPIntegerField::SetValue(const PString
& newVal
)
1317 value
= newVal
.AsInteger();
1321 PString
PHTTPIntegerField::GetValue(BOOL dflt
) const
1323 return PString(PString::Signed
, dflt
? initialValue
: value
);
1327 void PHTTPIntegerField::LoadFromConfig(PConfig
& cfg
)
1329 PString section
, key
;
1330 switch (SplitConfigKey(fullName
, section
, key
)) {
1332 value
= cfg
.GetInteger(key
, initialValue
);
1335 value
= cfg
.GetInteger(section
, key
, initialValue
);
1340 void PHTTPIntegerField::SaveToConfig(PConfig
& cfg
) const
1342 PString section
, key
;
1343 switch (SplitConfigKey(fullName
, section
, key
)) {
1345 cfg
.SetInteger(key
, value
);
1348 cfg
.SetInteger(section
, key
, value
);
1353 BOOL
PHTTPIntegerField::Validated(const PString
& newVal
, PStringStream
& msg
) const
1355 int val
= newVal
.AsInteger();
1356 if (val
>= low
&& val
<= high
)
1359 msg
<< "The field \"" << GetName() << "\" should be between "
1360 << low
<< " and " << high
<< ".<BR>";
1365 //////////////////////////////////////////////////////////////////////////////
1366 // PHTTPBooleanField
1368 PHTTPBooleanField::PHTTPBooleanField(const char * name
,
1371 : PHTTPField(name
, NULL
, help
)
1373 value
= initialValue
= initVal
;
1377 PHTTPBooleanField::PHTTPBooleanField(const char * name
,
1381 : PHTTPField(name
, title
, help
)
1383 value
= initialValue
= initVal
;
1387 PHTTPField
* PHTTPBooleanField::NewField() const
1389 return new PHTTPBooleanField(baseName
, title
, initialValue
, help
);
1393 void PHTTPBooleanField::GetHTMLTag(PHTML
& html
) const
1395 html
<< PHTML::HiddenField(fullName
, "FALSE")
1396 << PHTML::CheckBox(fullName
, value
? PHTML::Checked
: PHTML::UnChecked
);
1400 static void SpliceChecked(PString
& text
, BOOL value
)
1402 PINDEX pos
= text
.Find("checked");
1404 if (pos
== P_MAX_INDEX
)
1405 text
.Splice(" checked", 6, 0);
1408 if (pos
!= P_MAX_INDEX
) {
1410 if (text
[pos
-1] == ' ') {
1414 text
.Delete(pos
, len
);
1420 PString
PHTTPBooleanField::GetHTMLInput(const PString
& input
) const
1422 static PRegularExpression
checkboxRegEx("type[ \t\r\n]*=[ \t\r\n]*\"?checkbox\"?",
1423 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1424 if (input
.FindRegEx(checkboxRegEx
) != P_MAX_INDEX
) {
1425 PCaselessString text
;
1426 PINDEX before
, after
;
1427 if (FindInputValue(input
, before
, after
))
1428 text
= input(0, before
) + "TRUE" + input
.Mid(after
);
1430 text
= "<input value=\"TRUE\"" + input
.Mid(6);
1431 SpliceChecked(text
, value
);
1432 return "<input type=hidden name=\"" + fullName
+ "\">" + text
;
1435 static PRegularExpression
radioRegEx("type[ \t\r\n]*=[ \t\r\n]*\"?radio\"?",
1436 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1437 if (input
.FindRegEx(radioRegEx
) != P_MAX_INDEX
) {
1438 PINDEX before
, after
;
1439 if (FindInputValue(input
, before
, after
)) {
1440 PCaselessString text
= input
;
1441 PString val
= input(before
+1, after
-1);
1442 SpliceChecked(text
, (value
&& (val
*= "TRUE")) || (!value
&& (val
*= "FALSE")));
1448 return PHTTPField::GetHTMLInput(input
);
1452 void PHTTPBooleanField::SetValue(const PString
& val
)
1454 value
= toupper(val
[0]) == 'T' || toupper(val
[0]) == 'y' ||
1455 val
.AsInteger() != 0 || val
.Find("TRUE") != P_MAX_INDEX
;
1459 PString
PHTTPBooleanField::GetValue(BOOL dflt
) const
1461 return ((dflt
? initialValue
: value
) ? "True" : "False");
1465 void PHTTPBooleanField::LoadFromConfig(PConfig
& cfg
)
1467 PString section
, key
;
1468 switch (SplitConfigKey(fullName
, section
, key
)) {
1470 value
= cfg
.GetBoolean(key
, initialValue
);
1473 value
= cfg
.GetBoolean(section
, key
, initialValue
);
1478 void PHTTPBooleanField::SaveToConfig(PConfig
& cfg
) const
1480 PString section
, key
;
1481 switch (SplitConfigKey(fullName
, section
, key
)) {
1483 cfg
.SetBoolean(key
, value
);
1486 cfg
.SetBoolean(section
, key
, value
);
1491 //////////////////////////////////////////////////////////////////////////////
1494 PHTTPRadioField::PHTTPRadioField(const char * name
,
1495 const PStringArray
& valueArray
,
1498 : PHTTPField(name
, NULL
, help
),
1501 value(valueArray
[initVal
]),
1507 PHTTPRadioField::PHTTPRadioField(const char * name
,
1508 const PStringArray
& valueArray
,
1509 const PStringArray
& titleArray
,
1512 : PHTTPField(name
, NULL
, help
),
1515 value(valueArray
[initVal
]),
1521 PHTTPRadioField::PHTTPRadioField(const char * name
,
1523 const char * const * valueStrings
,
1526 : PHTTPField(name
, NULL
, help
),
1527 values(count
, valueStrings
),
1528 titles(count
, valueStrings
),
1529 value(valueStrings
[initVal
]),
1535 PHTTPRadioField::PHTTPRadioField(const char * name
,
1537 const char * const * valueStrings
,
1538 const char * const * titleStrings
,
1541 : PHTTPField(name
, NULL
, help
),
1542 values(count
, valueStrings
),
1543 titles(count
, titleStrings
),
1544 value(valueStrings
[initVal
]),
1550 PHTTPRadioField::PHTTPRadioField(const char * name
,
1551 const char * groupTitle
,
1552 const PStringArray
& valueArray
,
1555 : PHTTPField(name
, groupTitle
, help
),
1558 value(valueArray
[initVal
]),
1564 PHTTPRadioField::PHTTPRadioField(const char * name
,
1565 const char * groupTitle
,
1566 const PStringArray
& valueArray
,
1567 const PStringArray
& titleArray
,
1570 : PHTTPField(name
, groupTitle
, help
),
1573 value(valueArray
[initVal
]),
1579 PHTTPRadioField::PHTTPRadioField(const char * name
,
1580 const char * groupTitle
,
1582 const char * const * valueStrings
,
1585 : PHTTPField(name
, groupTitle
, help
),
1586 values(count
, valueStrings
),
1587 titles(count
, valueStrings
),
1588 value(valueStrings
[initVal
]),
1594 PHTTPRadioField::PHTTPRadioField(const char * name
,
1595 const char * groupTitle
,
1597 const char * const * valueStrings
,
1598 const char * const * titleStrings
,
1601 : PHTTPField(name
, groupTitle
, help
),
1602 values(count
, valueStrings
),
1603 titles(count
, titleStrings
),
1604 value(valueStrings
[initVal
]),
1610 PHTTPField
* PHTTPRadioField::NewField() const
1612 return new PHTTPRadioField(*this);
1616 void PHTTPRadioField::GetHTMLTag(PHTML
& html
) const
1618 for (PINDEX i
= 0; i
< values
.GetSize(); i
++)
1619 html
<< PHTML::RadioButton(fullName
, values
[i
],
1620 values
[i
] == value
? PHTML::Checked
: PHTML::UnChecked
)
1622 << PHTML::BreakLine();
1626 PString
PHTTPRadioField::GetHTMLInput(const PString
& input
) const
1629 PINDEX before
, after
;
1630 if (FindInputValue(input
, before
, after
))
1631 inval
= input(before
+1, after
-1);
1638 return "<input checked" + input
.Mid(6);
1642 PString
PHTTPRadioField::GetValue(BOOL dflt
) const
1645 return initialValue
;
1651 void PHTTPRadioField::SetValue(const PString
& newVal
)
1657 //////////////////////////////////////////////////////////////////////////////
1660 PHTTPSelectField::PHTTPSelectField(const char * name
,
1661 const PStringArray
& valueArray
,
1664 : PHTTPField(name
, NULL
, help
),
1667 initialValue
= initVal
;
1668 if (initVal
< values
.GetSize())
1669 value
= values
[initVal
];
1673 PHTTPSelectField::PHTTPSelectField(const char * name
,
1675 const char * const * valueStrings
,
1678 : PHTTPField(name
, NULL
, help
),
1679 values(count
, valueStrings
)
1681 initialValue
= initVal
;
1682 if (initVal
< count
)
1683 value
= values
[initVal
];
1687 PHTTPSelectField::PHTTPSelectField(const char * name
,
1689 const PStringArray
& valueArray
,
1692 : PHTTPField(name
, title
, help
),
1695 initialValue
= initVal
;
1696 if (initVal
< values
.GetSize())
1697 value
= values
[initVal
];
1701 PHTTPSelectField::PHTTPSelectField(const char * name
,
1704 const char * const * valueStrings
,
1707 : PHTTPField(name
, title
, help
),
1708 values(count
, valueStrings
)
1710 initialValue
= initVal
;
1711 if (initVal
< values
.GetSize())
1712 value
= values
[initVal
];
1716 PHTTPField
* PHTTPSelectField::NewField() const
1718 return new PHTTPSelectField(baseName
, title
, values
, initialValue
, help
);
1722 void PHTTPSelectField::GetHTMLTag(PHTML
& html
) const
1724 html
<< PHTML::Select(fullName
);
1725 for (PINDEX i
= 0; i
< values
.GetSize(); i
++)
1726 html
<< PHTML::Option(values
[i
] == value
? PHTML::Selected
: PHTML::NotSelected
)
1728 html
<< PHTML::Select();
1732 PString
PHTTPSelectField::GetValue(BOOL dflt
) const
1735 if (initialValue
< values
.GetSize())
1736 return values
[initialValue
];
1744 void PHTTPSelectField::SetValue(const PString
& newVal
)
1751 //////////////////////////////////////////////////////////////////////////////
1754 PHTTPForm::PHTTPForm(const PURL
& url
)
1760 PHTTPForm::PHTTPForm(const PURL
& url
, const PHTTPAuthority
& auth
)
1761 : PHTTPString(url
, auth
),
1766 PHTTPForm::PHTTPForm(const PURL
& url
, const PString
& html
)
1767 : PHTTPString(url
, html
),
1772 PHTTPForm::PHTTPForm(const PURL
& url
,
1773 const PString
& html
,
1774 const PHTTPAuthority
& auth
)
1775 : PHTTPString(url
, html
, auth
),
1781 static BOOL
FindSpliceAccepted(const PString
& text
,
1788 static PRegularExpression
Accepted("<?!--#form[ \t\r\n]+accepted[ \t\r\n]*-->?",
1789 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1790 return FindSpliceBlock(Accepted
, text
, offset
, pos
, len
, start
, finish
);
1794 static BOOL
FindSpliceErrors(const PString
& text
,
1801 static PRegularExpression
Errors("<?!--#form[ \t\r\n]+errors[ \t\r\n]*-->?",
1802 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1803 return FindSpliceBlock(Errors
, text
, offset
, pos
, len
, start
, finish
);
1807 static BOOL
FindSpliceField(const PRegularExpression
& startExpr
,
1808 const PRegularExpression
& endExpr
,
1809 const PString
& text
,
1811 const PHTTPField
& rootField
,
1816 const PHTTPField
* & field
)
1820 if (!FindSpliceBlock(startExpr
, endExpr
, text
, offset
, pos
, len
, start
, finish
))
1823 PINDEX endBlock
= start
!= finish
? (start
-1) : (pos
+len
-1);
1824 PINDEX namePos
, nameEnd
;
1825 if (FindSpliceName(text
, pos
, endBlock
, namePos
, nameEnd
))
1826 field
= rootField
.LocateName(text(namePos
, nameEnd
));
1831 static const char ListFieldDeleteBox
[] = "List Row Delete ";
1833 void PHTTPForm::OnLoadedText(PHTTPRequest
& request
, PString
& text
)
1835 PINDEX pos
, len
, start
, finish
;
1836 const PHTTPField
* field
;
1838 // Remove the subsections for POST command
1840 while (FindSpliceAccepted(text
, pos
, pos
, len
, start
, finish
))
1841 text
.Delete(pos
, len
);
1844 while (FindSpliceErrors(text
, pos
, pos
, len
, start
, finish
))
1845 text
.Delete(pos
, len
);
1847 // See if are a subform, set root composite field accordingly
1848 PString prefix
= request
.url
.GetQueryVars()("subformprefix");
1850 static PRegularExpression
SubFormPrefix("<?!--#form[ \t\r\n]+subformprefix[ \t\r\n]*-->?",
1851 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1852 while (text
.FindRegEx(SubFormPrefix
, pos
, len
))
1853 text
.Splice("subformprefix=" +
1854 PURL::TranslateString(prefix
, PURL::QueryTranslation
),
1856 field
= fields
.LocateName(prefix
);
1857 if (field
!= NULL
) {
1858 finish
= P_MAX_INDEX
;
1859 field
->ExpandFieldNames(text
, 0, finish
);
1863 // Locate <!--#form list name--> macros and expand them
1864 static PRegularExpression
ListRegEx("<!--#form[ \t\r\n]+listfields[ \t\r\n]+(-?[^-])+-->",
1865 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1866 static PRegularExpression
EndBlock("<?!--#form[ \t\r\n]+end[ \t\r\n]+(-?[^-])+-->?",
1867 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1869 while (FindSpliceBlock(ListRegEx
, EndBlock
, text
, pos
+len
, pos
, len
, start
, finish
)) {
1870 if (start
!= finish
) {
1871 PString repeat
= text(start
, finish
);
1873 PINDEX namePos
, nameEnd
;
1874 PRegularExpression fieldsRegEx
;
1875 if (FindSpliceName(text
, pos
, start
-1, namePos
, nameEnd
))
1876 fieldsRegEx
.Compile(text(namePos
, nameEnd
), PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1878 fieldsRegEx
.Compile(".*");
1881 for (PINDEX f
= 0; f
< fields
.GetSize(); f
++) {
1882 if (fields
[f
].GetName().FindRegEx(fieldsRegEx
) != P_MAX_INDEX
) {
1883 PString iteration
= repeat
;
1886 static PRegularExpression
FieldNameRegEx("<?!--#form[ \t\r\n]+fieldname[ \t\r\n]*-->?",
1887 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1888 while (iteration
.FindRegEx(FieldNameRegEx
, npos
, nlen
))
1889 iteration
.Splice(fields
[f
].GetName(), npos
, nlen
);
1891 static PRegularExpression
RowDeleteRegEx("<?!--#form[ \t\r\n]+rowdelete[ \t\r\n]*-->?",
1892 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1893 while (iteration
.FindRegEx(RowDeleteRegEx
, npos
, nlen
)) {
1894 PHTML
html(PHTML::InForm
);
1895 html
<< PHTML::CheckBox(ListFieldDeleteBox
+ fields
[f
].GetName());
1896 iteration
.Splice(html
, npos
, nlen
);
1899 insert
+= iteration
;
1902 text
.Splice(insert
, pos
, len
);
1906 // Locate <!--#form array name--> macros and expand them
1907 static PRegularExpression
ArrayRegEx("<!--#form[ \t\r\n]+array[ \t\r\n]+(-?[^-])+-->",
1908 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1910 while (FindSpliceField(ArrayRegEx
, EndBlock
, text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1911 if (start
!= finish
&& field
!= NULL
)
1912 field
->ExpandFieldNames(text
, start
, finish
);
1915 // Have now expanded all field names to be fully qualified
1917 static PRegularExpression
HTMLRegEx("<!--#form[ \t\r\n]+html[ \t\r\n]+(-?[^-])+-->",
1918 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1919 while (FindSpliceField(HTMLRegEx
, "", text
, 0, fields
, pos
, len
, start
, finish
, field
)) {
1920 if (field
!= NULL
) {
1921 PHTML
html(PHTML::InForm
);
1922 field
->GetHTMLTag(html
);
1923 text
.Splice(html
, pos
, len
);
1928 static PRegularExpression
ValueRegEx("<!--#form[ \t\r\n]+value[ \t\r\n]+(-?[^-])+-->",
1929 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1930 while (FindSpliceField(ValueRegEx
, "", text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1932 text
.Splice(field
->GetValue(), pos
, len
);
1936 static PRegularExpression
InputRegEx("<input[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
1937 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1938 while (FindSpliceField(InputRegEx
, "", text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1939 if (field
!= NULL
) {
1940 static PRegularExpression
HiddenRegEx("type[ \t\r\n]*=[ \t\r\n]*\"?hidden\"?",
1941 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1942 PString substr
= text
.Mid(pos
, len
);
1943 if (substr
.FindRegEx(HiddenRegEx
) == P_MAX_INDEX
)
1944 text
.Splice(field
->GetHTMLInput(substr
), pos
, len
);
1949 static PRegularExpression
SelectRegEx("<select[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
1950 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1951 static PRegularExpression
SelEndRegEx("</select[^>]*>",
1952 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1953 while (FindSpliceField(SelectRegEx
, SelEndRegEx
, text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1955 text
.Splice(field
->GetHTMLSelect(text(start
, finish
)), start
, finish
-start
+1);
1959 static PRegularExpression
TextRegEx("<textarea[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
1960 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1961 static PRegularExpression
TextEndRegEx("</textarea[^>]*>", PRegularExpression::IgnoreCase
);
1962 while (FindSpliceField(TextRegEx
, TextEndRegEx
, text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1964 text
.Splice(field
->GetValue(), start
, finish
-start
+1);
1969 PHTTPField
* PHTTPForm::Add(PHTTPField
* fld
)
1971 if (PAssertNULL(fld
) == NULL
)
1974 PAssert(!fieldNames
[fld
->GetName()], "Field already on form!");
1975 fieldNames
+= fld
->GetName();
1981 void PHTTPForm::BuildHTML(const char * heading
)
1983 PHTML
html(heading
);
1988 void PHTTPForm::BuildHTML(const PString
& heading
)
1990 PHTML
html(heading
);
1995 void PHTTPForm::BuildHTML(PHTML
& html
, BuildOptions option
)
1997 if (!html
.Is(PHTML::InForm
))
1998 html
<< PHTML::Form("POST");
2000 html
<< PHTML::TableStart("cellspacing=8");
2001 for (PINDEX fld
= 0; fld
< fields
.GetSize(); fld
++) {
2002 PHTTPField
& field
= fields
[fld
];
2003 if (field
.NotYetInHTML()) {
2004 html
<< PHTML::TableRow()
2005 << PHTML::TableData("align=right")
2007 << PHTML::TableData("align=left")
2008 << "<!--#form html " << field
.GetName() << "-->"
2009 << PHTML::TableData()
2014 html
<< PHTML::TableEnd();
2015 if (option
!= InsertIntoForm
)
2016 html
<< PHTML::Paragraph()
2017 << ' ' << PHTML::SubmitButton("Accept")
2018 << ' ' << PHTML::ResetButton("Reset")
2021 if (option
== CompleteHTML
) {
2022 html
<< PHTML::Body();
2028 BOOL
PHTTPForm::Post(PHTTPRequest
& request
,
2029 const PStringToString
& data
,
2032 //PString prefix = request.url.GetQueryVars()("subformprefix");
2033 const PHTTPField
* field
= NULL
;
2035 // field = fields.LocateName(prefix);
2036 //if (field == NULL)
2039 PStringStream errors
;
2040 if (field
->ValidateAll(data
, errors
)) {
2041 ((PHTTPField
*)field
)->SetAllValues(data
);
2043 if (msg
.IsEmpty()) {
2044 msg
<< PHTML::Title() << "Accepted New Configuration" << PHTML::Body()
2045 << PHTML::Heading(1) << "Accepted New Configuration" << PHTML::Heading(1)
2046 << PHTML::HotLink(request
.url
.AsString()) << "Reload page" << PHTML::HotLink()
2047 << " "
2048 << PHTML::HotLink("/") << "Home page" << PHTML::HotLink();
2053 PINDEX len
, start
, finish
;
2054 while (FindSpliceAccepted(msg
, pos
, pos
, len
, start
, finish
))
2055 msg
.Splice(msg(start
, finish
), pos
, len
);
2057 while (FindSpliceErrors(msg
, pos
, pos
, len
, start
, finish
))
2058 msg
.Delete(pos
, len
);
2062 if (msg
.IsEmpty()) {
2063 msg
<< PHTML::Title() << "Validation Error in Request" << PHTML::Body()
2064 << PHTML::Heading(1) << "Validation Error in Request" << PHTML::Heading(1)
2066 << PHTML::Paragraph()
2067 << PHTML::HotLink(request
.url
.AsString()) << "Reload page" << PHTML::HotLink()
2068 << " "
2069 << PHTML::HotLink("/") << "Home page" << PHTML::HotLink();
2073 PINDEX len
, start
, finish
;
2074 while (FindSpliceAccepted(msg
, pos
, pos
, len
, start
, finish
))
2075 msg
.Delete(pos
, len
);
2077 BOOL appendErrors
= TRUE
;
2079 while (FindSpliceErrors(msg
, pos
, pos
, len
, start
, finish
)) {
2080 PString block
= msg(start
, finish
);
2082 static PRegularExpression
Validation("<?!--#form[ \t\r\n]+validation[ \t\r\n]*-->?",
2083 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
2084 if (block
.FindRegEx(Validation
, vPos
, vLen
))
2085 block
.Splice(errors
, vPos
, vLen
);
2088 msg
.Splice(block
, pos
, len
);
2089 appendErrors
= FALSE
;
2101 //////////////////////////////////////////////////////////////////////////////
2104 PHTTPConfig::PHTTPConfig(const PURL
& url
,
2105 const PString
& sect
)
2106 : PHTTPForm(url
), section(sect
)
2112 PHTTPConfig::PHTTPConfig(const PURL
& url
,
2113 const PString
& sect
,
2114 const PHTTPAuthority
& auth
)
2115 : PHTTPForm(url
, auth
), section(sect
)
2121 PHTTPConfig::PHTTPConfig(const PURL
& url
,
2122 const PString
& html
,
2123 const PString
& sect
)
2124 : PHTTPForm(url
, html
), section(sect
)
2130 PHTTPConfig::PHTTPConfig(const PURL
& url
,
2131 const PString
& html
,
2132 const PString
& sect
,
2133 const PHTTPAuthority
& auth
)
2134 : PHTTPForm(url
, html
, auth
), section(sect
)
2140 void PHTTPConfig::Construct()
2142 sectionField
= NULL
;
2148 void PHTTPConfig::LoadFromConfig()
2150 PConfig
cfg(section
);
2151 fields
.LoadFromConfig(cfg
);
2155 void PHTTPConfig::OnLoadedText(PHTTPRequest
& request
, PString
& text
)
2157 if (sectionField
== NULL
) {
2158 PString sectionName
= request
.url
.GetQueryVars()("section", section
);
2160 section
= sectionName
;
2165 PHTTPForm::OnLoadedText(request
, text
);
2169 BOOL
PHTTPConfig::Post(PHTTPRequest
& request
,
2170 const PStringToString
& data
,
2173 // Make sure the internal structure is up to date before accepting new data
2178 PSortedStringList oldValues
;
2180 // Remember fields that are here now, so can delete removed array fields
2182 for (fld
= 0; fld
< fields
.GetSize(); fld
++) {
2183 PHTTPField
& field
= fields
[fld
];
2184 if (&field
!= keyField
&& &field
!= valField
&& &field
!= sectionField
) {
2186 field
.GetAllNames(names
);
2191 PHTTPForm::Post(request
, data
, msg
);
2192 if (request
.code
!= PHTTP::RequestOK
)
2195 if (sectionField
!= NULL
)
2196 section
= sectionPrefix
+ sectionField
->GetValue() + sectionSuffix
;
2198 PString sectionName
= request
.url
.GetQueryVars()("section", section
);
2199 if (sectionName
.IsEmpty())
2202 PConfig
cfg(sectionName
);
2204 for (fld
= 0; fld
< fields
.GetSize(); fld
++) {
2205 PHTTPField
& field
= fields
[fld
];
2206 if (&field
== keyField
) {
2207 PString key
= field
.GetValue();
2209 cfg
.SetString(key
, valField
->GetValue());
2211 else if (&field
!= valField
&& &field
!= sectionField
)
2212 field
.SaveToConfig(cfg
);
2215 // Find out which fields have been removed (arrays elements deleted)
2216 for (fld
= 0; fld
< fields
.GetSize(); fld
++) {
2217 PHTTPField
& field
= fields
[fld
];
2218 if (&field
!= keyField
&& &field
!= valField
&& &field
!= sectionField
) {
2220 field
.GetAllNames(names
);
2221 for (PINDEX i
= 0; i
< names
.GetSize(); i
++) {
2222 PINDEX idx
= oldValues
.GetStringsIndex(names
[i
]);
2223 if (idx
!= P_MAX_INDEX
)
2224 oldValues
.RemoveAt(idx
);
2229 for (fld
= 0; fld
< oldValues
.GetSize(); fld
++) {
2230 PString section
, key
;
2231 switch (SplitConfigKey(oldValues
[fld
], section
, key
)) {
2236 cfg
.DeleteKey(section
, key
);
2237 if (cfg
.GetKeys(section
).IsEmpty())
2238 cfg
.DeleteSection(section
);
2242 section
= sectionName
;
2247 PHTTPField
* PHTTPConfig::AddSectionField(PHTTPField
* sectionFld
,
2248 const char * prefix
,
2249 const char * suffix
)
2251 sectionField
= PAssertNULL(sectionFld
);
2252 PAssert(!PIsDescendant(sectionField
, PHTTPCompositeField
), "Section field is composite");
2256 sectionPrefix
= prefix
;
2258 sectionSuffix
= suffix
;
2260 return sectionField
;
2264 void PHTTPConfig::AddNewKeyFields(PHTTPField
* keyFld
,
2265 PHTTPField
* valFld
)
2267 keyField
= PAssertNULL(keyFld
);
2269 valField
= PAssertNULL(valFld
);
2274 //////////////////////////////////////////////////////////////////////////////
2275 // PHTTPConfigSectionList
2277 static const char FormListInclude
[] = "<!--#form pagelist-->";
2279 PHTTPConfigSectionList::PHTTPConfigSectionList(const PURL
& url
,
2280 const PHTTPAuthority
& auth
,
2281 const PString
& prefix
,
2282 const PString
& valueName
,
2283 const PURL
& editSection
,
2284 const PURL
& newSection
,
2285 const PString
& newTitle
,
2287 : PHTTPString(url
, auth
),
2288 sectionPrefix(prefix
),
2289 additionalValueName(valueName
),
2290 newSectionLink(newSection
.AsString(PURL::URIOnly
)),
2291 newSectionTitle(newTitle
),
2292 editSectionLink(editSection
.AsString(PURL::URIOnly
) +
2293 "?section=" + PURL::TranslateString(prefix
, PURL::QueryTranslation
))
2295 if (heading
.Is(PHTML::InBody
))
2296 heading
<< FormListInclude
<< PHTML::Body();
2301 void PHTTPConfigSectionList::OnLoadedText(PHTTPRequest
&, PString
& text
)
2304 PStringList nameList
= cfg
.GetSections();
2306 PINDEX pos
= text
.Find(FormListInclude
);
2307 if (pos
!= P_MAX_INDEX
) {
2308 PINDEX endpos
= text
.Find(FormListInclude
, pos
+ sizeof(FormListInclude
)-1);
2309 if (endpos
== P_MAX_INDEX
) {
2310 PHTML
html(PHTML::InBody
);
2311 html
<< PHTML::Form("POST") << PHTML::TableStart();
2314 for (i
= 0; i
< nameList
.GetSize(); i
++) {
2315 if (nameList
[i
].Find(sectionPrefix
) == 0) {
2316 PString name
= nameList
[i
].Mid(sectionPrefix
.GetLength());
2317 html
<< PHTML::TableRow()
2318 << PHTML::TableData()
2319 << PHTML::HotLink(editSectionLink
+ PURL::TranslateString(name
, PURL::QueryTranslation
))
2321 << PHTML::HotLink();
2322 if (!additionalValueName
)
2323 html
<< PHTML::TableData()
2324 << PHTML::HotLink(editSectionLink
+ PURL::TranslateString(name
, PURL::QueryTranslation
))
2325 << cfg
.GetString(nameList
[i
], additionalValueName
, "")
2326 << PHTML::HotLink();
2327 html
<< PHTML::TableData() << PHTML::SubmitButton("Remove", name
);
2331 html
<< PHTML::TableRow()
2332 << PHTML::TableData()
2333 << PHTML::HotLink(newSectionLink
)
2336 << PHTML::TableEnd()
2339 text
.Splice(html
, pos
, sizeof(FormListInclude
)-1);
2342 PString repeat
= text(pos
+ sizeof(FormListInclude
)-1, endpos
-1);
2343 text
.Delete(pos
, endpos
- pos
);
2346 for (i
= 0; i
< nameList
.GetSize(); i
++) {
2347 if (nameList
[i
].Find(sectionPrefix
) == 0) {
2348 PString name
= nameList
[i
].Mid(sectionPrefix
.GetLength());
2349 text
.Splice(repeat
, pos
, 0);
2350 text
.Replace("<!--#form hotlink-->",
2351 editSectionLink
+ PURL::TranslateString(name
, PURL::QueryTranslation
),
2353 if (!additionalValueName
)
2354 text
.Replace("<!--#form additional-->",
2355 cfg
.GetString(nameList
[i
], additionalValueName
, ""),
2357 text
.Replace("<!--#form section-->", name
, TRUE
, pos
);
2358 pos
= text
.Find(FormListInclude
, pos
);
2361 text
.Delete(text
.Find(FormListInclude
, pos
), sizeof(FormListInclude
)-1);
2367 BOOL
PHTTPConfigSectionList::Post(PHTTPRequest
&,
2368 const PStringToString
& data
,
2369 PHTML
& replyMessage
)
2372 PStringList nameList
= cfg
.GetSections();
2374 for (i
= 0; i
< nameList
.GetSize(); i
++) {
2375 if (nameList
[i
].Find(sectionPrefix
) == 0) {
2376 PString name
= nameList
[i
].Mid(sectionPrefix
.GetLength());
2377 if (data
.Contains(name
)) {
2378 cfg
.DeleteSection(nameList
[i
]);
2379 replyMessage
<< name
<< " removed.";
2389 // End Of File ///////////////////////////////////////////////////////////////