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.47 2003/03/24 04:31:03 robertj
28 * Added function to set and get strings from PConfig in correct format for
29 * use with HTTP form array contsructs.
31 * Revision 1.46 2002/11/22 06:20:26 robertj
32 * Added extra space around data entry fields.
33 * Added borders around arrays and composite fields.
34 * Added multi-line data entry for HTTPStringField > 128 characters.
36 * Revision 1.45 2002/11/06 22:47:25 robertj
37 * Fixed header comment (copyright etc)
39 * Revision 1.44 2002/10/10 04:43:44 robertj
40 * VxWorks port, thanks Martijn Roest
42 * Revision 1.43 2002/07/17 08:44:58 robertj
43 * Added links back to page and home page on accepted data html.
44 * Fixed display of validation error text if page not accepted.
46 * Revision 1.42 2001/10/10 08:07:48 robertj
47 * Fixed large memory leak of strings when doing POST to a form.
49 * Revision 1.41 2001/05/16 06:03:14 craigs
50 * Changed to allow access to absolute registry paths from within subforms
52 * Revision 1.40 2001/02/07 04:44:47 robertj
53 * Added ability to use check box to add/delete fields from arrays.
55 * Revision 1.39 2001/01/08 04:13:23 robertj
56 * Fixed bug with skipping every second option in determining the selected
57 * option in a SELECT field. No longer requires a </option> to work.
59 * Revision 1.38 2000/12/20 02:23:39 robertj
60 * Fixed variable array size value (caused extra blank entry ever commit).
62 * Revision 1.37 2000/12/18 12:13:08 robertj
63 * Fixed bug in auto-generated HTML in fixed size arrays, should not have add/delete box.
65 * Revision 1.36 2000/12/18 11:41:01 robertj
66 * Fixed bug in auto-generated HTML in non-array composite fields
68 * Revision 1.35 2000/12/18 07:14:30 robertj
69 * Added ability to have fixed length array fields.
70 * Fixed regular expressions so can have single '-' in field name.
71 * Fixed use of non-array subforprefix based compsite fields.
73 * Revision 1.34 2000/12/12 07:21:35 robertj
74 * Added ability to expand fields based on regex into repeated chunks of HTML.
76 * Revision 1.33 2000/11/02 21:55:28 craigs
77 * Added extra constructor
79 * Revision 1.32 2000/09/05 09:52:24 robertj
80 * Fixed bug in HTTP form updating SELECT fields from registry.
82 * Revision 1.31 2000/06/19 11:35:01 robertj
83 * Fixed bug in setting current value of options in select form fields.
85 * Revision 1.30 1999/02/10 13:19:45 robertj
86 * Fixed PConfig update problem when POSTing to the form. Especiall with arrays.
88 * Revision 1.29 1998/11/30 04:51:57 robertj
89 * New directory structure
91 * Revision 1.28 1998/11/14 11:11:06 robertj
92 * PPC GNU compiler compatibility.
94 * Revision 1.27 1998/10/01 09:05:11 robertj
95 * Fixed bug in nested composite field names, array indexes not being set correctly.
97 * Revision 1.26 1998/09/23 06:22:11 robertj
98 * Added open source copyright license.
100 * Revision 1.25 1998/08/20 05:51:06 robertj
101 * Fixed bug where substitutions did not always occur if near end of macro block.
102 * Improved internationalisation. Allow HTML override of strings in macros.
104 * Revision 1.24 1998/08/09 11:25:51 robertj
105 * GNU C++ warning removal.
107 * Revision 1.23 1998/08/09 10:35:11 robertj
108 * Changed array control so can have language override.
110 * Revision 1.22 1998/07/24 06:56:05 robertj
111 * Fixed case significance problem in HTTP forms.
112 * Improved detection of VALUE= fields with and without quotes.
114 * Revision 1.21 1998/03/20 03:16:43 robertj
115 * Fixed bug in beaing able to reset a check box field.
117 * Revision 1.20 1998/02/03 06:26:09 robertj
118 * Fixed propagation of inital values in arrays subfields.
119 * Fixed problem where hidden fields were being relaced with default values from PHTTPForm.
121 * Revision 1.19 1998/01/26 02:49:17 robertj
124 * Revision 1.18 1998/01/26 01:51:37 robertj
125 * Fixed uninitialised variable.
127 * Revision 1.17 1998/01/26 00:25:25 robertj
128 * Major rewrite of HTTP forms management.
130 * Revision 1.16 1997/12/18 05:06:51 robertj
131 * Added missing braces to kill GNU compiler warning.
133 * Revision 1.15 1997/10/10 10:43:43 robertj
134 * Fixed bug in password encryption, missing string terminator.
136 * Revision 1.14 1997/08/28 12:48:29 robertj
137 * Changed array fields to allow for reordering.
139 * Revision 1.13 1997/08/21 12:44:10 robertj
140 * Fixed bug in HTTP form array size field.
141 * Fixed bug where section list was only replacing first instance of macro.
143 * Revision 1.12 1997/08/09 07:46:52 robertj
144 * Fixed problems with value of SELECT fields in form
146 * Revision 1.11 1997/08/04 10:41:13 robertj
147 * Fixed bug in new section list page for names with special characters in them.
149 * Revision 1.10 1997/07/26 11:38:20 robertj
150 * Support for overridable pages in HTTP service applications.
152 * Revision 1.9 1997/07/14 11:49:51 robertj
153 * Put "Add" and "Keep" on check boxes in array fields.
155 * Revision 1.8 1997/07/08 13:12:29 robertj
156 * Major HTTP form enhancements for lists and arrays of fields.
158 * Revision 1.7 1997/06/08 04:47:27 robertj
159 * Adding new llist based form field.
161 * Revision 1.6 1997/04/12 02:07:26 robertj
162 * Fixed boolean check boxes being more flexible on string values.
164 * Revision 1.5 1997/04/01 06:00:53 robertj
165 * Changed PHTTPConfig so if section empty string, does not write PConfig parameters.
167 * Revision 1.4 1996/10/08 13:10:34 robertj
168 * Fixed bug in boolean (checkbox) html forms, cannot be reset.
170 * Revision 1.3 1996/09/14 13:09:31 robertj
172 * rearranged sockets to help support IPX.
173 * added indirect channel class and moved all protocols to descend from it,
174 * separating the protocol from the low level byte transport.
176 * Revision 1.2 1996/08/08 13:34:10 robertj
177 * Removed redundent call.
179 * Revision 1.1 1996/06/28 12:56:20 robertj
185 #pragma implementation "httpform.h"
189 #include <ptclib/httpform.h>
190 #include <ptclib/cypher.h>
193 //////////////////////////////////////////////////////////////////////////////
196 PHTTPField::PHTTPField(const char * nam
, const char * titl
, const char * hlp
)
197 : baseName(nam
), fullName(nam
),
198 title(titl
!= NULL
? titl
: nam
),
199 help(hlp
!= NULL
? hlp
: "")
205 PObject::Comparison
PHTTPField::Compare(const PObject
& obj
) const
207 PAssert(obj
.IsDescendant(PHTTPField::Class()), PInvalidCast
);
208 return fullName
.Compare(((const PHTTPField
&)obj
).fullName
);
212 void PHTTPField::SetName(const PString
& newName
)
218 const PHTTPField
* PHTTPField::LocateName(const PString
& name
) const
220 if (fullName
== name
)
227 void PHTTPField::SetHelp(const PString
& hotLinkURL
,
228 const PString
& linkText
)
230 help
= "<A HREF=\"" + hotLinkURL
+ "\">" + linkText
+ "</A>\r\n";
234 void PHTTPField::SetHelp(const PString
& hotLinkURL
,
235 const PString
& imageURL
,
236 const PString
& imageText
)
238 help
= "<A HREF=\"" + hotLinkURL
+ "\"><IMG SRC=\"" +
239 imageURL
+ "\" ALT=\"" + imageText
+ "\" ALIGN=absmiddle></A>\r\n";
243 static BOOL
FindSpliceBlock(const PRegularExpression
& startExpr
,
244 const PRegularExpression
& endExpr
,
245 const PString
& text
,
252 start
= finish
= P_MAX_INDEX
;
254 if (!text
.FindRegEx(startExpr
, pos
, len
, offset
))
257 PINDEX endpos
, endlen
;
258 if (!text
.FindRegEx(endExpr
, endpos
, endlen
, pos
+len
))
263 len
= endpos
- pos
+ endlen
;
268 static BOOL
FindSpliceBlock(const PRegularExpression
& startExpr
,
269 const PString
& text
,
276 static PRegularExpression
EndBlock("<?!--#form[ \t\r\n]+end[ \t\r\n]*-->?",
277 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
278 return FindSpliceBlock(startExpr
, EndBlock
, text
, offset
, pos
, len
, start
, finish
);
282 static BOOL
FindSpliceName(const PCaselessString
& text
,
288 if (text
[start
+1] != '!') {
289 static PRegularExpression
NameExpr("name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"",
290 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
291 if ((pos
= text
.FindRegEx(NameExpr
, start
)) == P_MAX_INDEX
)
297 pos
= text
.Find('"', pos
) + 1;
298 end
= text
.Find('"', pos
) - 1;
301 pos
= start
+ 9; // Skip over the <!--#form
302 while (isspace(text
[pos
])) // Skip over blanks
304 while (pos
< finish
&& !isspace(text
[pos
])) // Skip over keyword
306 while (isspace(text
[pos
])) // Skip over more blanks
309 end
= text
.Find("--", pos
) - 1;
316 static BOOL
FindSpliceFieldName(const PString
& text
,
322 static PRegularExpression
FieldName("<?!--#form[ \t\r\n]+[a-z0-9]+[ \t\r\n]+(-?[^-])+-->?"
324 "<[a-z]+[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
325 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
326 if (!text
.FindRegEx(FieldName
, pos
, len
, offset
))
329 PINDEX nameStart
, nameEnd
;
330 if (!FindSpliceName(text
, pos
, pos
+len
-1, nameStart
, nameEnd
))
333 name
= text(nameStart
, nameEnd
);
335 len
= nameEnd
- nameStart
+ 1;
340 static void SpliceAdjust(const PString
& str
,
346 text
.Splice(str
, pos
, len
);
347 PINDEX newLen
= str
.GetLength();
348 if (finish
!= P_MAX_INDEX
)
349 finish
+= newLen
- len
;
354 void PHTTPField::ExpandFieldNames(PString
& text
, PINDEX start
, PINDEX
& finish
) const
358 while (start
< finish
&& FindSpliceFieldName(text
, start
, pos
, len
, name
)) {
361 if (baseName
== name
)
362 SpliceAdjust(fullName
, text
, pos
, len
, finish
);
368 static BOOL
FindInputValue(const PString
& text
, PINDEX
& before
, PINDEX
& after
)
370 static PRegularExpression
Value("value[ \t\r\n]*=[ \t\r\n]*(\"[^\"]*\"|[^> \t\r\n]+)",
371 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
372 PINDEX pos
= text
.FindRegEx(Value
);
373 if (pos
== P_MAX_INDEX
)
376 before
= text
.Find('"', pos
);
377 if (before
!= P_MAX_INDEX
)
378 after
= text
.Find('"', before
+1);
380 before
= text
.Find('=', pos
);
381 while (isspace(text
[before
+1]))
384 while (text
[after
] != '\0' && text
[after
] != '>' && !isspace(text
[after
]))
391 PString
PHTTPField::GetHTMLInput(const PString
& input
) const
393 PINDEX before
, after
;
394 if (FindInputValue(input
, before
, after
))
395 return input(0, before
) + GetValue(FALSE
) + input
.Mid(after
);
397 return "<input value=\"" + GetValue(FALSE
) + "\"" + input
.Mid(6);
401 static void AdjustSelectOptions(PString
& text
, PINDEX begin
, PINDEX end
,
402 const PString
& myValue
, PStringList
& validValues
,
403 PINDEX
& finishAdjust
)
405 PINDEX start
, finish
;
408 static PRegularExpression
StartOption("<[ \t\r\n]*option[^>]*>",
409 PRegularExpression::IgnoreCase
);
410 static PRegularExpression
EndOption("<[ \t\r\n]*/?option[^>]*>",
411 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
412 while (FindSpliceBlock(StartOption
, EndOption
, text
, pos
+len
, pos
, len
, start
, finish
) && pos
< end
) {
413 if (start
== P_MAX_INDEX
)
414 start
= text
.Find('>', pos
)+1;
416 // Check for if option was not closed by </option> but another <option>
417 PINDEX optpos
= text
.FindRegEx(StartOption
, start
);
418 if (optpos
< pos
+len
) // Adjust back to before <option> if so.
421 PCaselessString option
= text(pos
, start
-1);
422 PINDEX before
, after
;
423 if (FindInputValue(option
, before
, after
)) {
424 start
= pos
+ before
+ 1;
425 finish
= pos
+ after
- 1;
427 PINDEX selpos
= option
.Find("selected");
428 PString thisValue
= text(start
, finish
).Trim();
429 if (thisValue
== myValue
) {
430 if (selpos
== P_MAX_INDEX
) {
431 text
.Splice(" selected", pos
+7, 0);
432 if (finishAdjust
!= P_MAX_INDEX
)
434 if (end
!= P_MAX_INDEX
)
440 if (validValues
.GetSize() > 0) {
442 for (valid
= 0; valid
< validValues
.GetSize(); valid
++) {
443 if (thisValue
== validValues
[valid
])
446 if (valid
>= validValues
.GetSize()) {
447 text
.Delete(pos
, len
);
448 selpos
= P_MAX_INDEX
;
449 if (finishAdjust
!= P_MAX_INDEX
)
451 if (end
!= P_MAX_INDEX
)
456 if (selpos
!= P_MAX_INDEX
) {
459 if (text
[selpos
-1] == ' ') {
463 text
.Delete(selpos
, sellen
);
464 if (finishAdjust
!= P_MAX_INDEX
)
465 finishAdjust
-= sellen
;
466 if (end
!= P_MAX_INDEX
)
474 PString
PHTTPField::GetHTMLSelect(const PString
& selection
) const
476 PString text
= selection
;
478 PINDEX dummy2
= P_MAX_INDEX
;
479 AdjustSelectOptions(text
, 0, P_MAX_INDEX
, GetValue(FALSE
), dummy1
, dummy2
);
484 void PHTTPField::GetHTMLHeading(PHTML
&) const
489 static int SplitConfigKey(const PString
& fullName
,
490 PString
& section
, PString
& key
)
492 if (fullName
.IsEmpty())
495 PINDEX slash
= fullName
.FindLast('\\');
496 if (slash
== 0 || slash
>= fullName
.GetLength()-1) {
501 section
= fullName
.Left(slash
);
502 key
= fullName
.Mid(slash
+1);
503 if (section
.IsEmpty() || key
.IsEmpty())
510 void PHTTPField::LoadFromConfig(PConfig
& cfg
)
512 PString section
, key
;
513 switch (SplitConfigKey(fullName
, section
, key
)) {
515 SetValue(cfg
.GetString(key
, GetValue(TRUE
)));
518 SetValue(cfg
.GetString(section
, key
, GetValue(TRUE
)));
523 void PHTTPField::SaveToConfig(PConfig
& cfg
) const
525 PString section
, key
;
526 switch (SplitConfigKey(fullName
, section
, key
)) {
528 cfg
.SetString(key
, GetValue());
531 cfg
.SetString(section
, key
, GetValue());
536 BOOL
PHTTPField::Validated(const PString
&, PStringStream
&) const
542 void PHTTPField::GetAllNames(PStringList
& list
) const
544 list
.AppendString(fullName
);
548 void PHTTPField::SetAllValues(const PStringToString
& data
)
550 if (!baseName
&& data
.Contains(fullName
))
551 SetValue(data
[fullName
]);
555 BOOL
PHTTPField::ValidateAll(const PStringToString
& data
, PStringStream
& msg
) const
557 if (data
.Contains(fullName
))
558 return Validated(data
[fullName
], msg
);
563 //////////////////////////////////////////////////////////////////////////////
564 // PHTTPCompositeField
566 PHTTPCompositeField::PHTTPCompositeField(const char * nam
,
569 : PHTTPField(nam
, titl
, hlp
)
574 void PHTTPCompositeField::SetName(const PString
& newName
)
576 if (fullName
.IsEmpty() || newName
.IsEmpty())
579 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++) {
580 PHTTPField
& field
= fields
[i
];
582 PString firstPartOfName
= psprintf(fullName
, i
+1);
583 PString subFieldName
;
584 if (field
.GetName().Find(firstPartOfName
) == 0)
585 subFieldName
= field
.GetName().Mid(firstPartOfName
.GetLength());
587 subFieldName
= field
.GetName();
589 firstPartOfName
= psprintf(newName
, i
+1);
590 if (subFieldName
[0] == '\\' || firstPartOfName
[firstPartOfName
.GetLength()-1] == '\\')
591 field
.SetName(firstPartOfName
+ subFieldName
);
593 field
.SetName(firstPartOfName
& subFieldName
);
596 PHTTPField::SetName(newName
);
600 const PHTTPField
* PHTTPCompositeField::LocateName(const PString
& name
) const
602 if (fullName
== name
)
605 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++) {
606 const PHTTPField
* field
= fields
[i
].LocateName(name
);
615 PHTTPField
* PHTTPCompositeField::NewField() const
617 PHTTPCompositeField
* fld
= new PHTTPCompositeField(baseName
, title
, help
);
618 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
619 fld
->Append(fields
[i
].NewField());
624 void PHTTPCompositeField::GetHTMLTag(PHTML
& html
) const
626 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++) {
627 if (i
!= 0 && html
.Is(PHTML::InTable
))
628 html
<< PHTML::TableData("NOWRAP ALIGN=CENTER");
629 fields
[i
].GetHTMLTag(html
);
634 PString
PHTTPCompositeField::GetHTMLInput(const PString
& input
) const
640 void PHTTPCompositeField::ExpandFieldNames(PString
& text
, PINDEX start
, PINDEX
& finish
) const
642 static PRegularExpression
FieldName( "!--#form[ \t\r\n]+(-?[^-])+[ \t\r\n]+(-?[^-])+--"
644 "<[a-z]*[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
645 PRegularExpression::IgnoreCase
);
649 while (start
< finish
&& FindSpliceFieldName(text
, start
, pos
, len
, name
)) {
652 for (PINDEX fld
= 0; fld
< fields
.GetSize(); fld
++) {
653 if (fields
[fld
].GetBaseName() *= name
) {
654 SpliceAdjust(fields
[fld
].GetName(), text
, pos
, len
, finish
);
663 void PHTTPCompositeField::GetHTMLHeading(PHTML
& html
) const
665 html
<< PHTML::TableRow();
666 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
667 html
<< PHTML::TableHeader() << fields
[i
].GetTitle();
671 PString
PHTTPCompositeField::GetValue(BOOL dflt
) const
674 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
675 value
<< fields
[i
].GetValue(dflt
) << '\n';
680 void PHTTPCompositeField::SetValue(const PString
&)
682 PAssertAlways(PLogicError
);
686 void PHTTPCompositeField::LoadFromConfig(PConfig
& cfg
)
689 for (PINDEX i
= 0; i
< GetSize(); i
++)
690 fields
[i
].LoadFromConfig(cfg
);
694 void PHTTPCompositeField::SaveToConfig(PConfig
& cfg
) const
696 for (PINDEX i
= 0; i
< GetSize(); i
++)
697 fields
[i
].SaveToConfig(cfg
);
701 void PHTTPCompositeField::GetAllNames(PStringList
& list
) const
703 for (PINDEX i
= 0; i
< GetSize(); i
++)
704 fields
[i
].GetAllNames(list
);
708 void PHTTPCompositeField::SetAllValues(const PStringToString
& data
)
710 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
711 fields
[i
].SetAllValues(data
);
715 BOOL
PHTTPCompositeField::ValidateAll(const PStringToString
& data
,
716 PStringStream
& msg
) const
718 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
719 if (!fields
[i
].ValidateAll(data
, msg
))
726 PINDEX
PHTTPCompositeField::GetSize() const
728 return fields
.GetSize();
732 void PHTTPCompositeField::Append(PHTTPField
* fld
)
738 //////////////////////////////////////////////////////////////////////////////
741 PHTTPSubForm::PHTTPSubForm(const PString
& subForm
,
746 : PHTTPCompositeField(name
, title
, NULL
),
754 PHTTPField
* PHTTPSubForm::NewField() const
756 PHTTPCompositeField
* fld
= new PHTTPSubForm(subFormName
, baseName
, title
, primary
, secondary
);
757 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
758 fld
->Append(fields
[i
].NewField());
763 void PHTTPSubForm::GetHTMLTag(PHTML
& html
) const
765 PString value
= fields
[primary
].GetValue();
768 html
<< PHTML::HotLink(subFormName
+
769 "?subformprefix=" + PURL::TranslateString(fullName
, PURL::QueryTranslation
))
770 << value
<< PHTML::HotLink();
772 if (secondary
!= P_MAX_INDEX
)
773 html
<< PHTML::TableData("NOWRAP") << fields
[secondary
].GetValue();
777 void PHTTPSubForm::GetHTMLHeading(PHTML
&) const
782 //////////////////////////////////////////////////////////////////////////////
785 PHTTPFieldArray::PHTTPFieldArray(PHTTPField
* fld
, BOOL ordered
, PINDEX fixedSize
)
786 : PHTTPCompositeField(fld
->GetName(), fld
->GetTitle(), fld
->GetHelp()),
789 orderedArray
= ordered
;
790 canAddElements
= fixedSize
== 0;
795 PHTTPFieldArray::~PHTTPFieldArray()
801 void PHTTPFieldArray::SetSize(PINDEX newSize
)
803 while (fields
.GetSize() > newSize
)
804 fields
.RemoveAt(fields
.GetSize()-1);
805 while (fields
.GetSize() < newSize
)
812 PHTTPField
* PHTTPFieldArray::NewField() const
814 return new PHTTPFieldArray(baseField
->NewField(), orderedArray
);
818 static const char ArrayControlBox
[] = " Array Control";
819 static const char ArrayControlKeep
[] = "Keep";
820 static const char ArrayControlRemove
[] = "Remove";
821 static const char ArrayControlMoveUp
[] = "Move Up";
822 static const char ArrayControlMoveDown
[] = "Move Down";
823 static const char ArrayControlToTop
[] = "To Top";
824 static const char ArrayControlToBottom
[] = "To Bottom";
825 static const char ArrayControlIgnore
[] = "Ignore";
826 static const char ArrayControlAddTop
[] = "Add Top";
827 static const char ArrayControlAddBottom
[] = "Add Bottom";
828 static const char ArrayControlAdd
[] = "Add";
830 static PStringList
GetArrayControlOptions(PINDEX fld
, PINDEX size
, BOOL orderedArray
)
835 options
.AppendString(ArrayControlIgnore
);
836 if (size
== 0 || !orderedArray
)
837 options
.AppendString(ArrayControlAdd
);
839 options
.AppendString(ArrayControlAddTop
);
840 options
.AppendString(ArrayControlAddBottom
);
844 options
.AppendString(ArrayControlKeep
);
845 options
.AppendString(ArrayControlRemove
);
848 options
.AppendString(ArrayControlMoveUp
);
850 options
.AppendString(ArrayControlMoveDown
);
852 options
.AppendString(ArrayControlToTop
);
854 options
.AppendString(ArrayControlToBottom
);
861 void PHTTPFieldArray::AddArrayControlBox(PHTML
& html
, PINDEX fld
) const
863 PStringList options
= GetArrayControlOptions(fld
, fields
.GetSize()-1, orderedArray
);
864 html
<< PHTML::Select(fields
[fld
].GetName() + ArrayControlBox
);
865 for (PINDEX i
= 0; i
< options
.GetSize(); i
++)
866 html
<< PHTML::Option(i
== 0 ? PHTML::Selected
: PHTML::NotSelected
) << options
[i
];
867 html
<< PHTML::Select();
871 void PHTTPFieldArray::GetHTMLTag(PHTML
& html
) const
873 html
<< PHTML::TableStart("border=1 cellspacing=0 cellpadding=8");
874 baseField
->GetHTMLHeading(html
);
875 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++) {
876 html
<< PHTML::TableRow() << PHTML::TableData("NOWRAP");
877 fields
[i
].GetHTMLTag(html
);
878 html
<< PHTML::TableData("NOWRAP");
880 AddArrayControlBox(html
, i
);
882 html
<< PHTML::TableEnd();
886 void PHTTPFieldArray::ExpandFieldNames(PString
& text
, PINDEX start
, PINDEX
& finish
) const
888 PString original
= text(start
, finish
);
889 PINDEX origFinish
= finish
;
890 PINDEX finalFinish
= finish
;
892 PINDEX fld
= fields
.GetSize();
894 fields
[--fld
].ExpandFieldNames(text
, start
, finish
);
897 static PRegularExpression
RowNum("<?!--#form[ \t\r\n]+rownum[ \t\r\n]*-->?",
898 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
899 while (text
.FindRegEx(RowNum
, pos
, len
, start
, finish
))
900 SpliceAdjust(psprintf("%u", fld
+1), text
, pos
, len
, finish
);
902 static PRegularExpression
SubForm("<?!--#form[ \t\r\n]+subform[ \t\r\n]*-->?",
903 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
904 while (text
.FindRegEx(SubForm
, pos
, len
, start
, finish
)) {
905 PString fmt
= fullName
;
906 if (fmt
.Find("%u") == P_MAX_INDEX
)
908 SpliceAdjust("subformprefix=" + PURL::TranslateString(psprintf(fmt
, fld
+1), PURL::QueryTranslation
),
909 text
, pos
, len
, finish
);
912 static PRegularExpression
RowControl("<?!--#form[ \t\r\n]+rowcontrol[ \t\r\n]*-->?",
913 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
914 while (text
.FindRegEx(RowControl
, pos
, len
, start
, finish
)) {
915 PHTML
html(PHTML::InForm
);
917 AddArrayControlBox(html
, fld
);
918 SpliceAdjust(html
, text
, pos
, len
, finish
);
921 static PRegularExpression
RowCheck("<?!--#form[ \t\r\n]+row(add|delete)[ \t\r\n]*(-?[^-])*-->?",
922 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
923 while (text
.FindRegEx(RowCheck
, pos
, len
, start
, finish
)) {
924 PStringStream checkbox
;
925 if (canAddElements
) {
926 PINDEX titlepos
= text
.Find("row", start
)+3;
927 BOOL adding
= text
[titlepos
] == 'a';
928 if (( adding
&& fld
>= fields
.GetSize()-1) ||
929 (!adding
&& fld
< fields
.GetSize()-1)) {
930 titlepos
+= adding
? 3 : 6;
931 PINDEX dashes
= text
.Find("--", titlepos
);
932 PString title
= text(titlepos
, dashes
-1).Trim();
933 if (title
.IsEmpty() && adding
)
936 << "<INPUT TYPE=checkbox NAME=\""
937 << fields
[fld
].GetName()
940 << (adding
? ArrayControlAdd
: ArrayControlRemove
)
944 SpliceAdjust(checkbox
, text
, pos
, len
, finish
);
947 static PRegularExpression
SelectRow("<select[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"!--#form[ \t\r\n]+rowselect[ \t\r\n]*--\"[^>]*>",
948 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
949 static PRegularExpression
SelEndRegEx("</select[^>]*>",
950 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
952 while (FindSpliceBlock(SelectRow
, SelEndRegEx
, text
, 0, pos
, len
, begin
, end
)) {
953 PStringList options
= GetArrayControlOptions(fld
, fields
.GetSize()-1, orderedArray
);
954 AdjustSelectOptions(text
, begin
, end
, options
[0], options
, finish
);
955 static PRegularExpression
RowSelect("!--#form[ \t\r\n]+rowselect[ \t\r\n]*--",
956 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
957 if (text
.FindRegEx(RowSelect
, pos
, len
, pos
, begin
))
958 SpliceAdjust(fields
[fld
].GetName() + ArrayControlBox
, text
, pos
, len
, finish
);
961 finalFinish
+= finish
- origFinish
;
964 text
.Splice(original
, start
, 0);
966 finalFinish
+= finish
- start
;
970 finish
= finalFinish
;
974 static int SplitArraySizeKey(const PString
& fullName
,
975 PString
& section
, PString
& key
)
977 static const char ArraySize
[] = "Array Size";
978 PINDEX pos
= fullName
.Find("%u");
979 if (pos
== P_MAX_INDEX
)
980 return SplitConfigKey(fullName
& ArraySize
, section
, key
);
982 PINDEX endPos
= fullName
.GetLength() - 1;
983 if (fullName
[endPos
] == '\\')
985 return SplitConfigKey(fullName
.Left(pos
) & ArraySize
& fullName(pos
+2, endPos
), section
, key
);
989 void PHTTPFieldArray::LoadFromConfig(PConfig
& cfg
)
991 if (canAddElements
) {
992 PString section
, key
;
993 switch (SplitArraySizeKey(fullName
, section
, key
)) {
995 SetSize(cfg
.GetInteger(key
, GetSize()));
998 SetSize(cfg
.GetInteger(section
, key
, GetSize()));
1001 PHTTPCompositeField::LoadFromConfig(cfg
);
1005 void PHTTPFieldArray::SaveToConfig(PConfig
& cfg
) const
1007 if (canAddElements
) {
1008 PString section
, key
;
1009 switch (SplitArraySizeKey(fullName
, section
, key
)) {
1011 cfg
.SetInteger(key
, GetSize());
1014 cfg
.SetInteger(section
, key
, GetSize());
1017 PHTTPCompositeField::SaveToConfig(cfg
);
1021 void PHTTPFieldArray::SetArrayFieldName(PINDEX idx
) const
1023 PString fmt
= fullName
;
1024 if (fmt
.Find("%u") == P_MAX_INDEX
)
1026 fields
[idx
].SetName(psprintf(fmt
, idx
+1));
1030 void PHTTPFieldArray::SetAllValues(const PStringToString
& data
)
1032 PHTTPFieldList newFields
;
1033 newFields
.DisallowDeleteObjects();
1035 for (i
= 0; i
< fields
.GetSize(); i
++)
1036 newFields
.Append(fields
.GetAt(i
));
1038 BOOL lastFieldIsSet
= FALSE
;
1039 PINDEX size
= fields
.GetSize();
1040 for (i
= 0; i
< size
; i
++) {
1041 PHTTPField
* fieldPtr
= &fields
[i
];
1042 PINDEX pos
= newFields
.GetObjectsIndex(fieldPtr
);
1043 fieldPtr
->SetAllValues(data
);
1045 PString control
= data(fieldPtr
->GetName() + ArrayControlBox
);
1046 if (control
== ArrayControlMoveUp
) {
1048 newFields
.SetAt(pos
, newFields
.GetAt(pos
-1));
1049 newFields
.SetAt(pos
-1, fieldPtr
);
1052 else if (control
== ArrayControlMoveDown
) {
1053 if (size
> 2 && pos
< size
-2) {
1054 newFields
.SetAt(pos
, newFields
.GetAt(pos
+1));
1055 newFields
.SetAt(pos
+1, fieldPtr
);
1058 else if (control
== ArrayControlToTop
) {
1059 newFields
.RemoveAt(pos
);
1060 newFields
.InsertAt(0, fieldPtr
);
1062 else if (control
== ArrayControlToBottom
) {
1063 newFields
.RemoveAt(pos
);
1064 newFields
.Append(fieldPtr
);
1066 else if (control
== ArrayControlAddTop
) {
1068 newFields
.RemoveAt(pos
);
1069 newFields
.InsertAt(0, fieldPtr
);
1070 lastFieldIsSet
= TRUE
;
1073 else if (control
== ArrayControlAddBottom
|| control
== ArrayControlAdd
) {
1075 newFields
.RemoveAt(pos
);
1076 newFields
.Append(fieldPtr
);
1077 lastFieldIsSet
= TRUE
;
1080 else if (control
== ArrayControlIgnore
) {
1081 newFields
.RemoveAt(pos
);
1082 newFields
.Append(fieldPtr
);
1084 else if (control
== ArrayControlRemove
)
1085 newFields
.RemoveAt(pos
);
1088 fields
.DisallowDeleteObjects();
1089 for (i
= 0; i
< newFields
.GetSize(); i
++)
1090 fields
.Remove(newFields
.GetAt(i
));
1091 fields
.AllowDeleteObjects();
1094 for (i
= 0; i
< newFields
.GetSize(); i
++) {
1095 fields
.Append(newFields
.GetAt(i
));
1096 SetArrayFieldName(i
);
1099 if (lastFieldIsSet
&& canAddElements
)
1104 PINDEX
PHTTPFieldArray::GetSize() const
1106 PINDEX size
= fields
.GetSize();
1107 PAssert(size
> 0, PLogicError
);
1114 void PHTTPFieldArray::AddBlankField()
1116 fields
.Append(baseField
->NewField());
1117 SetArrayFieldName(fields
.GetSize()-1);
1121 PStringArray
PHTTPFieldArray::GetStrings(PConfig
& cfg
)
1123 LoadFromConfig(cfg
);
1125 PStringArray
values(GetSize());
1127 for (PINDEX i
= 0; i
< GetSize(); i
++)
1128 values
[i
] = fields
[i
].GetValue(FALSE
);
1134 void PHTTPFieldArray::SetStrings(PConfig
& cfg
, const PStringArray
& values
)
1136 SetSize(values
.GetSize());
1137 for (PINDEX i
= 0; i
< values
.GetSize(); i
++)
1138 fields
[i
].SetValue(values
[i
]);
1144 //////////////////////////////////////////////////////////////////////////////
1147 PHTTPStringField::PHTTPStringField(const char * name
,
1149 const char * initVal
,
1151 : PHTTPField(name
, NULL
, help
),
1152 value(initVal
!= NULL
? initVal
: ""),
1159 PHTTPStringField::PHTTPStringField(const char * name
,
1162 const char * initVal
,
1164 : PHTTPField(name
, title
, help
),
1165 value(initVal
!= NULL
? initVal
: ""),
1172 PHTTPField
* PHTTPStringField::NewField() const
1174 return new PHTTPStringField(baseName
, title
, size
, initialValue
, help
);
1178 void PHTTPStringField::GetHTMLTag(PHTML
& html
) const
1181 html
<< PHTML::InputText(fullName
, size
, value
);
1183 html
<< PHTML::TextArea(fullName
, (size
+79)/80, 80) << value
<< PHTML::TextArea(fullName
);
1187 void PHTTPStringField::SetValue(const PString
& newVal
)
1193 PString
PHTTPStringField::GetValue(BOOL dflt
) const
1196 return initialValue
;
1202 //////////////////////////////////////////////////////////////////////////////
1203 // PHTTPPasswordField
1205 PHTTPPasswordField::PHTTPPasswordField(const char * name
,
1207 const char * initVal
,
1209 : PHTTPStringField(name
, siz
, initVal
, help
)
1214 PHTTPPasswordField::PHTTPPasswordField(const char * name
,
1217 const char * initVal
,
1219 : PHTTPStringField(name
, title
, siz
, initVal
, help
)
1224 PHTTPField
* PHTTPPasswordField::NewField() const
1226 return new PHTTPPasswordField(baseName
, title
, size
, initialValue
, help
);
1230 void PHTTPPasswordField::GetHTMLTag(PHTML
& html
) const
1232 html
<< PHTML::InputPassword(fullName
, size
, value
);
1236 static const PTEACypher::Key PasswordKey
= {
1238 103, 60, 222, 17, 128, 157, 31, 137,
1239 133, 64, 82, 148, 94, 136, 4, 209
1243 void PHTTPPasswordField::SetValue(const PString
& newVal
)
1245 value
= Decrypt(newVal
);
1249 PString
PHTTPPasswordField::GetValue(BOOL dflt
) const
1252 return initialValue
;
1254 PTEACypher
crypt(PasswordKey
);
1255 return crypt
.Encode(value
);
1259 PString
PHTTPPasswordField::Decrypt(const PString
& pword
)
1262 PTEACypher
crypt(PasswordKey
);
1263 return crypt
.Decode(pword
, clear
) ? clear
: pword
;
1267 //////////////////////////////////////////////////////////////////////////////
1268 // PHTTPIntegerField
1270 PHTTPIntegerField::PHTTPIntegerField(const char * nam
,
1275 : PHTTPField(nam
, NULL
, help
), units(unit
!= NULL
? unit
: "")
1279 value
= initialValue
= initVal
;
1282 PHTTPIntegerField::PHTTPIntegerField(const char * nam
,
1288 : PHTTPField(nam
, titl
, help
), units(unit
!= NULL
? unit
: "")
1292 value
= initialValue
= initVal
;
1296 PHTTPField
* PHTTPIntegerField::NewField() const
1298 return new PHTTPIntegerField(baseName
, title
, low
, high
, initialValue
, units
, help
);
1302 void PHTTPIntegerField::GetHTMLTag(PHTML
& html
) const
1304 html
<< PHTML::InputRange(fullName
, low
, high
, value
) << " " << units
;
1308 void PHTTPIntegerField::SetValue(const PString
& newVal
)
1310 value
= newVal
.AsInteger();
1314 PString
PHTTPIntegerField::GetValue(BOOL dflt
) const
1316 return PString(PString::Signed
, dflt
? initialValue
: value
);
1320 void PHTTPIntegerField::LoadFromConfig(PConfig
& cfg
)
1322 PString section
, key
;
1323 switch (SplitConfigKey(fullName
, section
, key
)) {
1325 value
= cfg
.GetInteger(key
, initialValue
);
1328 value
= cfg
.GetInteger(section
, key
, initialValue
);
1333 void PHTTPIntegerField::SaveToConfig(PConfig
& cfg
) const
1335 PString section
, key
;
1336 switch (SplitConfigKey(fullName
, section
, key
)) {
1338 cfg
.SetInteger(key
, value
);
1341 cfg
.SetInteger(section
, key
, value
);
1346 BOOL
PHTTPIntegerField::Validated(const PString
& newVal
, PStringStream
& msg
) const
1348 int val
= newVal
.AsInteger();
1349 if (val
>= low
&& val
<= high
)
1352 msg
<< "The field \"" << GetName() << "\" should be between "
1353 << low
<< " and " << high
<< ".<BR>";
1358 //////////////////////////////////////////////////////////////////////////////
1359 // PHTTPBooleanField
1361 PHTTPBooleanField::PHTTPBooleanField(const char * name
,
1364 : PHTTPField(name
, NULL
, help
)
1366 value
= initialValue
= initVal
;
1370 PHTTPBooleanField::PHTTPBooleanField(const char * name
,
1374 : PHTTPField(name
, title
, help
)
1376 value
= initialValue
= initVal
;
1380 PHTTPField
* PHTTPBooleanField::NewField() const
1382 return new PHTTPBooleanField(baseName
, title
, initialValue
, help
);
1386 void PHTTPBooleanField::GetHTMLTag(PHTML
& html
) const
1388 html
<< PHTML::HiddenField(fullName
, "FALSE")
1389 << PHTML::CheckBox(fullName
, value
? PHTML::Checked
: PHTML::UnChecked
);
1393 static void SpliceChecked(PString
& text
, BOOL value
)
1395 PINDEX pos
= text
.Find("checked");
1397 if (pos
== P_MAX_INDEX
)
1398 text
.Splice(" checked", 6, 0);
1401 if (pos
!= P_MAX_INDEX
) {
1403 if (text
[pos
-1] == ' ') {
1407 text
.Delete(pos
, len
);
1413 PString
PHTTPBooleanField::GetHTMLInput(const PString
& input
) const
1415 static PRegularExpression
checkboxRegEx("type[ \t\r\n]*=[ \t\r\n]*\"?checkbox\"?",
1416 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1417 if (input
.FindRegEx(checkboxRegEx
) != P_MAX_INDEX
) {
1418 PCaselessString text
;
1419 PINDEX before
, after
;
1420 if (FindInputValue(input
, before
, after
))
1421 text
= input(0, before
) + "TRUE" + input
.Mid(after
);
1423 text
= "<input value=\"TRUE\"" + input
.Mid(6);
1424 SpliceChecked(text
, value
);
1425 return "<input type=hidden name=\"" + fullName
+ "\">" + text
;
1428 static PRegularExpression
radioRegEx("type[ \t\r\n]*=[ \t\r\n]*\"?radio\"?",
1429 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1430 if (input
.FindRegEx(radioRegEx
) != P_MAX_INDEX
) {
1431 PINDEX before
, after
;
1432 if (FindInputValue(input
, before
, after
)) {
1433 PCaselessString text
= input
;
1434 PString val
= input(before
+1, after
-1);
1435 SpliceChecked(text
, (value
&& (val
*= "TRUE")) || (!value
&& (val
*= "FALSE")));
1441 return PHTTPField::GetHTMLInput(input
);
1445 void PHTTPBooleanField::SetValue(const PString
& val
)
1447 value
= toupper(val
[0]) == 'T' || toupper(val
[0]) == 'y' ||
1448 val
.AsInteger() != 0 || val
.Find("TRUE") != P_MAX_INDEX
;
1452 PString
PHTTPBooleanField::GetValue(BOOL dflt
) const
1454 return ((dflt
? initialValue
: value
) ? "True" : "False");
1458 void PHTTPBooleanField::LoadFromConfig(PConfig
& cfg
)
1460 PString section
, key
;
1461 switch (SplitConfigKey(fullName
, section
, key
)) {
1463 value
= cfg
.GetBoolean(key
, initialValue
);
1466 value
= cfg
.GetBoolean(section
, key
, initialValue
);
1471 void PHTTPBooleanField::SaveToConfig(PConfig
& cfg
) const
1473 PString section
, key
;
1474 switch (SplitConfigKey(fullName
, section
, key
)) {
1476 cfg
.SetBoolean(key
, value
);
1479 cfg
.SetBoolean(section
, key
, value
);
1484 //////////////////////////////////////////////////////////////////////////////
1487 PHTTPRadioField::PHTTPRadioField(const char * name
,
1488 const PStringArray
& valueArray
,
1491 : PHTTPField(name
, NULL
, help
),
1494 value(valueArray
[initVal
]),
1500 PHTTPRadioField::PHTTPRadioField(const char * name
,
1501 const PStringArray
& valueArray
,
1502 const PStringArray
& titleArray
,
1505 : PHTTPField(name
, NULL
, help
),
1508 value(valueArray
[initVal
]),
1514 PHTTPRadioField::PHTTPRadioField(const char * name
,
1516 const char * const * valueStrings
,
1519 : PHTTPField(name
, NULL
, help
),
1520 values(count
, valueStrings
),
1521 titles(count
, valueStrings
),
1522 value(valueStrings
[initVal
]),
1528 PHTTPRadioField::PHTTPRadioField(const char * name
,
1530 const char * const * valueStrings
,
1531 const char * const * titleStrings
,
1534 : PHTTPField(name
, NULL
, help
),
1535 values(count
, valueStrings
),
1536 titles(count
, titleStrings
),
1537 value(valueStrings
[initVal
]),
1543 PHTTPRadioField::PHTTPRadioField(const char * name
,
1544 const char * groupTitle
,
1545 const PStringArray
& valueArray
,
1548 : PHTTPField(name
, groupTitle
, help
),
1551 value(valueArray
[initVal
]),
1557 PHTTPRadioField::PHTTPRadioField(const char * name
,
1558 const char * groupTitle
,
1559 const PStringArray
& valueArray
,
1560 const PStringArray
& titleArray
,
1563 : PHTTPField(name
, groupTitle
, help
),
1566 value(valueArray
[initVal
]),
1572 PHTTPRadioField::PHTTPRadioField(const char * name
,
1573 const char * groupTitle
,
1575 const char * const * valueStrings
,
1578 : PHTTPField(name
, groupTitle
, help
),
1579 values(count
, valueStrings
),
1580 titles(count
, valueStrings
),
1581 value(valueStrings
[initVal
]),
1587 PHTTPRadioField::PHTTPRadioField(const char * name
,
1588 const char * groupTitle
,
1590 const char * const * valueStrings
,
1591 const char * const * titleStrings
,
1594 : PHTTPField(name
, groupTitle
, help
),
1595 values(count
, valueStrings
),
1596 titles(count
, titleStrings
),
1597 value(valueStrings
[initVal
]),
1603 PHTTPField
* PHTTPRadioField::NewField() const
1605 return new PHTTPRadioField(*this);
1609 void PHTTPRadioField::GetHTMLTag(PHTML
& html
) const
1611 for (PINDEX i
= 0; i
< values
.GetSize(); i
++)
1612 html
<< PHTML::RadioButton(fullName
, values
[i
],
1613 values
[i
] == value
? PHTML::Checked
: PHTML::UnChecked
)
1615 << PHTML::BreakLine();
1619 PString
PHTTPRadioField::GetHTMLInput(const PString
& input
) const
1622 PINDEX before
, after
;
1623 if (FindInputValue(input
, before
, after
))
1624 inval
= input(before
+1, after
-1);
1631 return "<input checked" + input
.Mid(6);
1635 PString
PHTTPRadioField::GetValue(BOOL dflt
) const
1638 return initialValue
;
1644 void PHTTPRadioField::SetValue(const PString
& newVal
)
1650 //////////////////////////////////////////////////////////////////////////////
1653 PHTTPSelectField::PHTTPSelectField(const char * name
,
1654 const PStringArray
& valueArray
,
1657 : PHTTPField(name
, NULL
, help
),
1660 initialValue
= initVal
;
1661 if (initVal
< values
.GetSize())
1662 value
= values
[initVal
];
1666 PHTTPSelectField::PHTTPSelectField(const char * name
,
1668 const char * const * valueStrings
,
1671 : PHTTPField(name
, NULL
, help
),
1672 values(count
, valueStrings
)
1674 initialValue
= initVal
;
1675 if (initVal
< count
)
1676 value
= values
[initVal
];
1680 PHTTPSelectField::PHTTPSelectField(const char * name
,
1682 const PStringArray
& valueArray
,
1685 : PHTTPField(name
, title
, help
),
1688 initialValue
= initVal
;
1689 if (initVal
< values
.GetSize())
1690 value
= values
[initVal
];
1694 PHTTPSelectField::PHTTPSelectField(const char * name
,
1697 const char * const * valueStrings
,
1700 : PHTTPField(name
, title
, help
),
1701 values(count
, valueStrings
)
1703 initialValue
= initVal
;
1704 if (initVal
< values
.GetSize())
1705 value
= values
[initVal
];
1709 PHTTPField
* PHTTPSelectField::NewField() const
1711 return new PHTTPSelectField(baseName
, title
, values
, initialValue
, help
);
1715 void PHTTPSelectField::GetHTMLTag(PHTML
& html
) const
1717 html
<< PHTML::Select(fullName
);
1718 for (PINDEX i
= 0; i
< values
.GetSize(); i
++)
1719 html
<< PHTML::Option(values
[i
] == value
? PHTML::Selected
: PHTML::NotSelected
)
1721 html
<< PHTML::Select();
1725 PString
PHTTPSelectField::GetValue(BOOL dflt
) const
1728 if (initialValue
< values
.GetSize())
1729 return values
[initialValue
];
1737 void PHTTPSelectField::SetValue(const PString
& newVal
)
1744 //////////////////////////////////////////////////////////////////////////////
1747 PHTTPForm::PHTTPForm(const PURL
& url
)
1753 PHTTPForm::PHTTPForm(const PURL
& url
, const PHTTPAuthority
& auth
)
1754 : PHTTPString(url
, auth
),
1759 PHTTPForm::PHTTPForm(const PURL
& url
, const PString
& html
)
1760 : PHTTPString(url
, html
),
1765 PHTTPForm::PHTTPForm(const PURL
& url
,
1766 const PString
& html
,
1767 const PHTTPAuthority
& auth
)
1768 : PHTTPString(url
, html
, auth
),
1774 static BOOL
FindSpliceAccepted(const PString
& text
,
1781 static PRegularExpression
Accepted("<?!--#form[ \t\r\n]+accepted[ \t\r\n]*-->?",
1782 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1783 return FindSpliceBlock(Accepted
, text
, offset
, pos
, len
, start
, finish
);
1787 static BOOL
FindSpliceErrors(const PString
& text
,
1794 static PRegularExpression
Errors("<?!--#form[ \t\r\n]+errors[ \t\r\n]*-->?",
1795 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1796 return FindSpliceBlock(Errors
, text
, offset
, pos
, len
, start
, finish
);
1800 static BOOL
FindSpliceField(const PRegularExpression
& startExpr
,
1801 const PRegularExpression
& endExpr
,
1802 const PString
& text
,
1804 const PHTTPField
& rootField
,
1809 const PHTTPField
* & field
)
1813 if (!FindSpliceBlock(startExpr
, endExpr
, text
, offset
, pos
, len
, start
, finish
))
1816 PINDEX endBlock
= start
!= finish
? (start
-1) : (pos
+len
-1);
1817 PINDEX namePos
, nameEnd
;
1818 if (FindSpliceName(text
, pos
, endBlock
, namePos
, nameEnd
))
1819 field
= rootField
.LocateName(text(namePos
, nameEnd
));
1824 static const char ListFieldDeleteBox
[] = "List Row Delete ";
1826 void PHTTPForm::OnLoadedText(PHTTPRequest
& request
, PString
& text
)
1828 PINDEX pos
, len
, start
, finish
;
1829 const PHTTPField
* field
;
1831 // Remove the subsections for POST command
1833 while (FindSpliceAccepted(text
, pos
, pos
, len
, start
, finish
))
1834 text
.Delete(pos
, len
);
1837 while (FindSpliceErrors(text
, pos
, pos
, len
, start
, finish
))
1838 text
.Delete(pos
, len
);
1840 // See if are a subform, set root composite field accordingly
1841 PString prefix
= request
.url
.GetQueryVars()("subformprefix");
1843 static PRegularExpression
SubFormPrefix("<?!--#form[ \t\r\n]+subformprefix[ \t\r\n]*-->?",
1844 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1845 while (text
.FindRegEx(SubFormPrefix
, pos
, len
))
1846 text
.Splice("subformprefix=" +
1847 PURL::TranslateString(prefix
, PURL::QueryTranslation
),
1849 field
= fields
.LocateName(prefix
);
1850 if (field
!= NULL
) {
1851 finish
= P_MAX_INDEX
;
1852 field
->ExpandFieldNames(text
, 0, finish
);
1856 // Locate <!--#form list name--> macros and expand them
1857 static PRegularExpression
ListRegEx("<!--#form[ \t\r\n]+listfields[ \t\r\n]+(-?[^-])+-->",
1858 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1859 static PRegularExpression
EndBlock("<?!--#form[ \t\r\n]+end[ \t\r\n]+(-?[^-])+-->?",
1860 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1862 while (FindSpliceBlock(ListRegEx
, EndBlock
, text
, pos
+len
, pos
, len
, start
, finish
)) {
1863 if (start
!= finish
) {
1864 PString repeat
= text(start
, finish
);
1866 PINDEX namePos
, nameEnd
;
1867 PRegularExpression fieldsRegEx
;
1868 if (FindSpliceName(text
, pos
, start
-1, namePos
, nameEnd
))
1869 fieldsRegEx
.Compile(text(namePos
, nameEnd
), PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1871 fieldsRegEx
.Compile(".*");
1874 for (PINDEX f
= 0; f
< fields
.GetSize(); f
++) {
1875 if (fields
[f
].GetName().FindRegEx(fieldsRegEx
) != P_MAX_INDEX
) {
1876 PString iteration
= repeat
;
1879 static PRegularExpression
FieldNameRegEx("<?!--#form[ \t\r\n]+fieldname[ \t\r\n]*-->?",
1880 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1881 while (iteration
.FindRegEx(FieldNameRegEx
, npos
, nlen
))
1882 iteration
.Splice(fields
[f
].GetName(), npos
, nlen
);
1884 static PRegularExpression
RowDeleteRegEx("<?!--#form[ \t\r\n]+rowdelete[ \t\r\n]*-->?",
1885 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1886 while (iteration
.FindRegEx(RowDeleteRegEx
, npos
, nlen
)) {
1887 PHTML
html(PHTML::InForm
);
1888 html
<< PHTML::CheckBox(ListFieldDeleteBox
+ fields
[f
].GetName());
1889 iteration
.Splice(html
, npos
, nlen
);
1892 insert
+= iteration
;
1895 text
.Splice(insert
, pos
, len
);
1899 // Locate <!--#form array name--> macros and expand them
1900 static PRegularExpression
ArrayRegEx("<!--#form[ \t\r\n]+array[ \t\r\n]+(-?[^-])+-->",
1901 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1903 while (FindSpliceField(ArrayRegEx
, EndBlock
, text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1904 if (start
!= finish
&& field
!= NULL
)
1905 field
->ExpandFieldNames(text
, start
, finish
);
1908 // Have now expanded all field names to be fully qualified
1910 static PRegularExpression
HTMLRegEx("<!--#form[ \t\r\n]+html[ \t\r\n]+(-?[^-])+-->",
1911 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1912 while (FindSpliceField(HTMLRegEx
, "", text
, 0, fields
, pos
, len
, start
, finish
, field
)) {
1913 if (field
!= NULL
) {
1914 PHTML
html(PHTML::InForm
);
1915 field
->GetHTMLTag(html
);
1916 text
.Splice(html
, pos
, len
);
1921 static PRegularExpression
ValueRegEx("<!--#form[ \t\r\n]+value[ \t\r\n]+(-?[^-])+-->",
1922 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1923 while (FindSpliceField(ValueRegEx
, "", text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1925 text
.Splice(field
->GetValue(), pos
, len
);
1929 static PRegularExpression
InputRegEx("<input[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
1930 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1931 while (FindSpliceField(InputRegEx
, "", text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1932 if (field
!= NULL
) {
1933 static PRegularExpression
HiddenRegEx("type[ \t\r\n]*=[ \t\r\n]*\"?hidden\"?",
1934 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1935 PString substr
= text
.Mid(pos
, len
);
1936 if (substr
.FindRegEx(HiddenRegEx
) == P_MAX_INDEX
)
1937 text
.Splice(field
->GetHTMLInput(substr
), pos
, len
);
1942 static PRegularExpression
SelectRegEx("<select[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
1943 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1944 static PRegularExpression
SelEndRegEx("</select[^>]*>",
1945 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1946 while (FindSpliceField(SelectRegEx
, SelEndRegEx
, text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1948 text
.Splice(field
->GetHTMLSelect(text(start
, finish
)), start
, finish
-start
+1);
1952 static PRegularExpression
TextRegEx("<textarea[ \t\r\n][^>]*name[ \t\r\n]*=[ \t\r\n]*\"[^\"]*\"[^>]*>",
1953 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
1954 static PRegularExpression
TextEndRegEx("</textarea[^>]*>", PRegularExpression::IgnoreCase
);
1955 while (FindSpliceField(TextRegEx
, TextEndRegEx
, text
, pos
+len
, fields
, pos
, len
, start
, finish
, field
)) {
1957 text
.Splice(field
->GetValue(), start
, finish
-start
+1);
1962 PHTTPField
* PHTTPForm::Add(PHTTPField
* fld
)
1965 PAssert(!fieldNames
[fld
->GetName()], "Field already on form!");
1966 fieldNames
+= fld
->GetName();
1972 void PHTTPForm::BuildHTML(const char * heading
)
1974 PHTML
html(heading
);
1979 void PHTTPForm::BuildHTML(const PString
& heading
)
1981 PHTML
html(heading
);
1986 void PHTTPForm::BuildHTML(PHTML
& html
, BuildOptions option
)
1988 if (!html
.Is(PHTML::InForm
))
1989 html
<< PHTML::Form("POST");
1991 html
<< PHTML::TableStart("cellspacing=8");
1992 for (PINDEX fld
= 0; fld
< fields
.GetSize(); fld
++) {
1993 PHTTPField
& field
= fields
[fld
];
1994 if (field
.NotYetInHTML()) {
1995 html
<< PHTML::TableRow()
1996 << PHTML::TableData("align=right")
1998 << PHTML::TableData("align=left")
1999 << "<!--#form html " << field
.GetName() << "-->"
2000 << PHTML::TableData()
2005 html
<< PHTML::TableEnd();
2006 if (option
!= InsertIntoForm
)
2007 html
<< PHTML::Paragraph()
2008 << ' ' << PHTML::SubmitButton("Accept")
2009 << ' ' << PHTML::ResetButton("Reset")
2012 if (option
== CompleteHTML
) {
2013 html
<< PHTML::Body();
2019 BOOL
PHTTPForm::Post(PHTTPRequest
& request
,
2020 const PStringToString
& data
,
2023 //PString prefix = request.url.GetQueryVars()("subformprefix");
2024 const PHTTPField
* field
= NULL
;
2026 // field = fields.LocateName(prefix);
2027 //if (field == NULL)
2030 PStringStream errors
;
2031 if (field
->ValidateAll(data
, errors
)) {
2032 ((PHTTPField
*)field
)->SetAllValues(data
);
2034 if (msg
.IsEmpty()) {
2035 msg
<< PHTML::Title() << "Accepted New Configuration" << PHTML::Body()
2036 << PHTML::Heading(1) << "Accepted New Configuration" << PHTML::Heading(1)
2037 << PHTML::HotLink(request
.url
.AsString()) << "Reload page" << PHTML::HotLink()
2038 << " "
2039 << PHTML::HotLink("/") << "Home page" << PHTML::HotLink();
2044 PINDEX len
, start
, finish
;
2045 while (FindSpliceAccepted(msg
, pos
, pos
, len
, start
, finish
))
2046 msg
.Splice(msg(start
, finish
), pos
, len
);
2048 while (FindSpliceErrors(msg
, pos
, pos
, len
, start
, finish
))
2049 msg
.Delete(pos
, len
);
2053 if (msg
.IsEmpty()) {
2054 msg
<< PHTML::Title() << "Validation Error in Request" << PHTML::Body()
2055 << PHTML::Heading(1) << "Validation Error in Request" << PHTML::Heading(1)
2057 << PHTML::Paragraph()
2058 << PHTML::HotLink(request
.url
.AsString()) << "Reload page" << PHTML::HotLink()
2059 << " "
2060 << PHTML::HotLink("/") << "Home page" << PHTML::HotLink();
2064 PINDEX len
, start
, finish
;
2065 while (FindSpliceAccepted(msg
, pos
, pos
, len
, start
, finish
))
2066 msg
.Delete(pos
, len
);
2068 BOOL appendErrors
= TRUE
;
2070 while (FindSpliceErrors(msg
, pos
, pos
, len
, start
, finish
)) {
2071 PString block
= msg(start
, finish
);
2073 static PRegularExpression
Validation("<?!--#form[ \t\r\n]+validation[ \t\r\n]*-->?",
2074 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
2075 if (block
.FindRegEx(Validation
, vPos
, vLen
))
2076 block
.Splice(errors
, vPos
, vLen
);
2079 msg
.Splice(block
, pos
, len
);
2080 appendErrors
= FALSE
;
2092 //////////////////////////////////////////////////////////////////////////////
2095 PHTTPConfig::PHTTPConfig(const PURL
& url
,
2096 const PString
& sect
)
2097 : PHTTPForm(url
), section(sect
)
2103 PHTTPConfig::PHTTPConfig(const PURL
& url
,
2104 const PString
& sect
,
2105 const PHTTPAuthority
& auth
)
2106 : PHTTPForm(url
, auth
), section(sect
)
2112 PHTTPConfig::PHTTPConfig(const PURL
& url
,
2113 const PString
& html
,
2114 const PString
& sect
)
2115 : PHTTPForm(url
, html
), section(sect
)
2121 PHTTPConfig::PHTTPConfig(const PURL
& url
,
2122 const PString
& html
,
2123 const PString
& sect
,
2124 const PHTTPAuthority
& auth
)
2125 : PHTTPForm(url
, html
, auth
), section(sect
)
2131 void PHTTPConfig::Construct()
2133 sectionField
= NULL
;
2139 void PHTTPConfig::LoadFromConfig()
2141 PConfig
cfg(section
);
2142 fields
.LoadFromConfig(cfg
);
2146 void PHTTPConfig::OnLoadedText(PHTTPRequest
& request
, PString
& text
)
2148 if (sectionField
== NULL
) {
2149 PString sectionName
= request
.url
.GetQueryVars()("section", section
);
2151 section
= sectionName
;
2156 PHTTPForm::OnLoadedText(request
, text
);
2160 BOOL
PHTTPConfig::Post(PHTTPRequest
& request
,
2161 const PStringToString
& data
,
2164 // Make sure the internal structure is up to date before accepting new data
2169 PSortedStringList oldValues
;
2171 // Remember fields that are here now, so can delete removed array fields
2173 for (fld
= 0; fld
< fields
.GetSize(); fld
++) {
2174 PHTTPField
& field
= fields
[fld
];
2175 if (&field
!= keyField
&& &field
!= valField
&& &field
!= sectionField
) {
2177 field
.GetAllNames(names
);
2182 PHTTPForm::Post(request
, data
, msg
);
2183 if (request
.code
!= PHTTP::RequestOK
)
2186 if (sectionField
!= NULL
)
2187 section
= sectionPrefix
+ sectionField
->GetValue() + sectionSuffix
;
2189 PString sectionName
= request
.url
.GetQueryVars()("section", section
);
2190 if (sectionName
.IsEmpty())
2193 PConfig
cfg(sectionName
);
2195 for (fld
= 0; fld
< fields
.GetSize(); fld
++) {
2196 PHTTPField
& field
= fields
[fld
];
2197 if (&field
== keyField
) {
2198 PString key
= field
.GetValue();
2200 cfg
.SetString(key
, valField
->GetValue());
2202 else if (&field
!= valField
&& &field
!= sectionField
)
2203 field
.SaveToConfig(cfg
);
2206 // Find out which fields have been removed (arrays elements deleted)
2207 for (fld
= 0; fld
< fields
.GetSize(); fld
++) {
2208 PHTTPField
& field
= fields
[fld
];
2209 if (&field
!= keyField
&& &field
!= valField
&& &field
!= sectionField
) {
2211 field
.GetAllNames(names
);
2212 for (PINDEX i
= 0; i
< names
.GetSize(); i
++) {
2213 PINDEX idx
= oldValues
.GetStringsIndex(names
[i
]);
2214 if (idx
!= P_MAX_INDEX
)
2215 oldValues
.RemoveAt(idx
);
2220 for (fld
= 0; fld
< oldValues
.GetSize(); fld
++) {
2221 PString section
, key
;
2222 switch (SplitConfigKey(oldValues
[fld
], section
, key
)) {
2227 cfg
.DeleteKey(section
, key
);
2228 if (cfg
.GetKeys(section
).IsEmpty())
2229 cfg
.DeleteSection(section
);
2233 section
= sectionName
;
2238 PHTTPField
* PHTTPConfig::AddSectionField(PHTTPField
* sectionFld
,
2239 const char * prefix
,
2240 const char * suffix
)
2242 sectionField
= PAssertNULL(sectionFld
);
2243 PAssert(!sectionField
->IsDescendant(PHTTPCompositeField::Class()), "Section field is composite");
2247 sectionPrefix
= prefix
;
2249 sectionSuffix
= suffix
;
2251 return sectionField
;
2255 void PHTTPConfig::AddNewKeyFields(PHTTPField
* keyFld
,
2256 PHTTPField
* valFld
)
2258 keyField
= PAssertNULL(keyFld
);
2260 valField
= PAssertNULL(valFld
);
2265 //////////////////////////////////////////////////////////////////////////////
2266 // PHTTPConfigSectionList
2268 static const char FormListInclude
[] = "<!--#form pagelist-->";
2270 PHTTPConfigSectionList::PHTTPConfigSectionList(const PURL
& url
,
2271 const PHTTPAuthority
& auth
,
2272 const PString
& prefix
,
2273 const PString
& valueName
,
2274 const PURL
& editSection
,
2275 const PURL
& newSection
,
2276 const PString
& newTitle
,
2278 : PHTTPString(url
, auth
),
2279 sectionPrefix(prefix
),
2280 additionalValueName(valueName
),
2281 newSectionLink(newSection
.AsString(PURL::URIOnly
)),
2282 newSectionTitle(newTitle
),
2283 editSectionLink(editSection
.AsString(PURL::URIOnly
) +
2284 "?section=" + PURL::TranslateString(prefix
, PURL::QueryTranslation
))
2286 if (heading
.Is(PHTML::InBody
))
2287 heading
<< FormListInclude
<< PHTML::Body();
2292 void PHTTPConfigSectionList::OnLoadedText(PHTTPRequest
&, PString
& text
)
2295 PStringList nameList
= cfg
.GetSections();
2297 PINDEX pos
= text
.Find(FormListInclude
);
2298 if (pos
!= P_MAX_INDEX
) {
2299 PINDEX endpos
= text
.Find(FormListInclude
, pos
+ sizeof(FormListInclude
)-1);
2300 if (endpos
== P_MAX_INDEX
) {
2301 PHTML
html(PHTML::InBody
);
2302 html
<< PHTML::Form("POST") << PHTML::TableStart();
2305 for (i
= 0; i
< nameList
.GetSize(); i
++) {
2306 if (nameList
[i
].Find(sectionPrefix
) == 0) {
2307 PString name
= nameList
[i
].Mid(sectionPrefix
.GetLength());
2308 html
<< PHTML::TableRow()
2309 << PHTML::TableData()
2310 << PHTML::HotLink(editSectionLink
+ PURL::TranslateString(name
, PURL::QueryTranslation
))
2312 << PHTML::HotLink();
2313 if (!additionalValueName
)
2314 html
<< PHTML::TableData()
2315 << PHTML::HotLink(editSectionLink
+ PURL::TranslateString(name
, PURL::QueryTranslation
))
2316 << cfg
.GetString(nameList
[i
], additionalValueName
, "")
2317 << PHTML::HotLink();
2318 html
<< PHTML::TableData() << PHTML::SubmitButton("Remove", name
);
2322 html
<< PHTML::TableRow()
2323 << PHTML::TableData()
2324 << PHTML::HotLink(newSectionLink
)
2327 << PHTML::TableEnd()
2330 text
.Splice(html
, pos
, sizeof(FormListInclude
)-1);
2333 PString repeat
= text(pos
+ sizeof(FormListInclude
)-1, endpos
-1);
2334 text
.Delete(pos
, endpos
- pos
);
2337 for (i
= 0; i
< nameList
.GetSize(); i
++) {
2338 if (nameList
[i
].Find(sectionPrefix
) == 0) {
2339 PString name
= nameList
[i
].Mid(sectionPrefix
.GetLength());
2340 text
.Splice(repeat
, pos
, 0);
2341 text
.Replace("<!--#form hotlink-->",
2342 editSectionLink
+ PURL::TranslateString(name
, PURL::QueryTranslation
),
2344 if (!additionalValueName
)
2345 text
.Replace("<!--#form additional-->",
2346 cfg
.GetString(nameList
[i
], additionalValueName
, ""),
2348 text
.Replace("<!--#form section-->", name
, TRUE
, pos
);
2349 pos
= text
.Find(FormListInclude
, pos
);
2352 text
.Delete(text
.Find(FormListInclude
, pos
), sizeof(FormListInclude
)-1);
2358 BOOL
PHTTPConfigSectionList::Post(PHTTPRequest
&,
2359 const PStringToString
& data
,
2360 PHTML
& replyMessage
)
2363 PStringList nameList
= cfg
.GetSections();
2365 for (i
= 0; i
< nameList
.GetSize(); i
++) {
2366 if (nameList
[i
].Find(sectionPrefix
) == 0) {
2367 PString name
= nameList
[i
].Mid(sectionPrefix
.GetLength());
2368 if (data
.Contains(name
)) {
2369 cfg
.DeleteSection(nameList
[i
]);
2370 replyMessage
<< name
<< " removed.";
2380 // End Of File ///////////////////////////////////////////////////////////////