1 | /*
|
---|
2 | * xmpp_xdata.cpp - a class for jabber:x:data forms
|
---|
3 | * Copyright (C) 2003-2004 Michail Pishchagin
|
---|
4 | *
|
---|
5 | * This program is free software; you can redistribute it and/or
|
---|
6 | * modify it under the terms of the GNU General Public License
|
---|
7 | * as published by the Free Software Foundation; either version 2
|
---|
8 | * of the License, or (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This program is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | * GNU General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU General Public License
|
---|
16 | * along with this library; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
18 | *
|
---|
19 | */
|
---|
20 |
|
---|
21 | #include "xmpp_xdata.h"
|
---|
22 |
|
---|
23 | #include <qshared.h>
|
---|
24 |
|
---|
25 | #include "im.h" // for JID validation
|
---|
26 | #include "xmpp_xmlcommon.h"
|
---|
27 |
|
---|
28 | using namespace XMPP;
|
---|
29 | using namespace XMLHelper;
|
---|
30 |
|
---|
31 | // TODO: report, item
|
---|
32 |
|
---|
33 | //----------------------------------------------------------------------------
|
---|
34 | // XData::Field
|
---|
35 | //----------------------------------------------------------------------------
|
---|
36 | XData::Field::Field()
|
---|
37 | {
|
---|
38 | }
|
---|
39 |
|
---|
40 | XData::Field::~Field()
|
---|
41 | {
|
---|
42 | }
|
---|
43 |
|
---|
44 | QString XData::Field::desc() const
|
---|
45 | {
|
---|
46 | return _desc;
|
---|
47 | }
|
---|
48 |
|
---|
49 | void XData::Field::setDesc(const QString &d)
|
---|
50 | {
|
---|
51 | _desc = d;
|
---|
52 | }
|
---|
53 |
|
---|
54 | XData::Field::OptionList XData::Field::options() const
|
---|
55 | {
|
---|
56 | return _options;
|
---|
57 | }
|
---|
58 |
|
---|
59 | void XData::Field::setOptions(XData::Field::OptionList o)
|
---|
60 | {
|
---|
61 | _options = o;
|
---|
62 | }
|
---|
63 |
|
---|
64 | bool XData::Field::required() const
|
---|
65 | {
|
---|
66 | return _required;
|
---|
67 | }
|
---|
68 |
|
---|
69 | void XData::Field::setRequired(bool r)
|
---|
70 | {
|
---|
71 | _required = r;
|
---|
72 | }
|
---|
73 |
|
---|
74 | QString XData::Field::label() const
|
---|
75 | {
|
---|
76 | return _label;
|
---|
77 | }
|
---|
78 |
|
---|
79 | void XData::Field::setLabel(const QString &l)
|
---|
80 | {
|
---|
81 | _label = l;
|
---|
82 | }
|
---|
83 |
|
---|
84 | QString XData::Field::var() const
|
---|
85 | {
|
---|
86 | return _var;
|
---|
87 | }
|
---|
88 |
|
---|
89 | void XData::Field::setVar(const QString &v)
|
---|
90 | {
|
---|
91 | _var = v;
|
---|
92 | }
|
---|
93 |
|
---|
94 | QStringList XData::Field::value() const
|
---|
95 | {
|
---|
96 | return _value;
|
---|
97 | }
|
---|
98 |
|
---|
99 | void XData::Field::setValue(const QStringList &v)
|
---|
100 | {
|
---|
101 | _value = v;
|
---|
102 | }
|
---|
103 |
|
---|
104 | XData::Field::Type XData::Field::type() const
|
---|
105 | {
|
---|
106 | return _type;
|
---|
107 | }
|
---|
108 |
|
---|
109 | void XData::Field::setType(XData::Field::Type t)
|
---|
110 | {
|
---|
111 | _type = t;
|
---|
112 | }
|
---|
113 |
|
---|
114 | bool XData::Field::isValid() const
|
---|
115 | {
|
---|
116 | if ( _required && _value.isEmpty() )
|
---|
117 | return false;
|
---|
118 |
|
---|
119 | if ( _type == Field_Boolean ) {
|
---|
120 | if ( _value.count() != 1 )
|
---|
121 | return false;
|
---|
122 |
|
---|
123 | QString str = _value.first();
|
---|
124 | if ( str == "0" || str == "1" || str == "true" || str == "false" || str == "yes" || str == "no" )
|
---|
125 | return true;
|
---|
126 | }
|
---|
127 | if ( _type == Field_TextSingle || _type == Field_TextPrivate ) {
|
---|
128 | if ( _value.count() == 1 )
|
---|
129 | return true;
|
---|
130 | }
|
---|
131 | if ( _type == Field_JidSingle ) {
|
---|
132 | if ( _value.count() != 1 )
|
---|
133 | return false;
|
---|
134 |
|
---|
135 | Jid j( _value.first() );
|
---|
136 | return j.isValid();
|
---|
137 | }
|
---|
138 | if ( _type == Field_JidMulti ) {
|
---|
139 | QStringList::ConstIterator it = _value.begin();
|
---|
140 | bool allValid = true;
|
---|
141 | for ( ; it != _value.end(); ++it) {
|
---|
142 | Jid j(*it);
|
---|
143 | if ( !j.isValid() ) {
|
---|
144 | allValid = false;
|
---|
145 | break;
|
---|
146 | }
|
---|
147 | }
|
---|
148 | return allValid;
|
---|
149 | }
|
---|
150 |
|
---|
151 | return false;
|
---|
152 | }
|
---|
153 |
|
---|
154 | void XData::Field::fromXml(const QDomElement &e)
|
---|
155 | {
|
---|
156 | if ( e.tagName() != "field" )
|
---|
157 | return;
|
---|
158 |
|
---|
159 | _var = e.attribute("var");
|
---|
160 | _label = e.attribute("label");
|
---|
161 |
|
---|
162 | QString type = e.attribute("type");
|
---|
163 | if ( type == "boolean" )
|
---|
164 | _type = Field_Boolean;
|
---|
165 | else if ( type == "fixed" )
|
---|
166 | _type = Field_Fixed;
|
---|
167 | else if ( type == "hidden" )
|
---|
168 | _type = Field_Hidden;
|
---|
169 | else if ( type == "jid-multi" )
|
---|
170 | _type = Field_JidMulti;
|
---|
171 | else if ( type == "jid-single" )
|
---|
172 | _type = Field_JidSingle;
|
---|
173 | else if ( type == "list-multi" )
|
---|
174 | _type = Field_ListMulti;
|
---|
175 | else if ( type == "list-single" )
|
---|
176 | _type = Field_ListSingle;
|
---|
177 | else if ( type == "text-multi" )
|
---|
178 | _type = Field_TextMulti;
|
---|
179 | else if ( type == "text-private" )
|
---|
180 | _type = Field_TextPrivate;
|
---|
181 | else
|
---|
182 | _type = Field_TextSingle;
|
---|
183 |
|
---|
184 | _required = false;
|
---|
185 | _desc = QString::null;
|
---|
186 | _options.clear();
|
---|
187 | _value.clear();
|
---|
188 |
|
---|
189 | QDomNode n = e.firstChild();
|
---|
190 | for ( ; !n.isNull(); n = n.nextSibling() ) {
|
---|
191 | QDomElement i = n.toElement();
|
---|
192 | if ( i.isNull() )
|
---|
193 | continue;
|
---|
194 |
|
---|
195 | QString tag = i.tagName();
|
---|
196 | if ( tag == "required" )
|
---|
197 | _required = true;
|
---|
198 | else if ( tag == "desc" )
|
---|
199 | _desc = i.text().simplifyWhiteSpace();
|
---|
200 | else if ( tag == "option" ) {
|
---|
201 | Option o;
|
---|
202 | bool found;
|
---|
203 | o.label = i.attribute("label");
|
---|
204 |
|
---|
205 | QDomElement e = findSubTag( i, "value", &found );
|
---|
206 | o.value = ( found ? e.text() : QString("") );
|
---|
207 | _options.append(o);
|
---|
208 | }
|
---|
209 | else if ( tag == "value" ) {
|
---|
210 | _value.append(i.text());
|
---|
211 | }
|
---|
212 | }
|
---|
213 | }
|
---|
214 |
|
---|
215 | QDomElement XData::Field::toXml(QDomDocument *doc, bool submitForm) const
|
---|
216 | {
|
---|
217 | QDomElement f = doc->createElement("field");
|
---|
218 |
|
---|
219 | // setting attributes...
|
---|
220 | if ( !_var.isEmpty() )
|
---|
221 | f.setAttribute("var", _var);
|
---|
222 | if ( !submitForm && !_label.isEmpty() )
|
---|
223 | f.setAttribute("label", _label);
|
---|
224 |
|
---|
225 | // now we're gonna get the 'type'
|
---|
226 | QString type = "text-single";
|
---|
227 | if ( _type == Field_Boolean )
|
---|
228 | type = "boolean";
|
---|
229 | else if ( _type == Field_Fixed )
|
---|
230 | type = "fixed";
|
---|
231 | else if ( _type == Field_Hidden )
|
---|
232 | type = "hidden";
|
---|
233 | else if ( _type == Field_JidMulti )
|
---|
234 | type = "jid-multi";
|
---|
235 | else if ( _type == Field_JidSingle )
|
---|
236 | type = "jid-single";
|
---|
237 | else if ( _type == Field_ListMulti )
|
---|
238 | type = "list-multi";
|
---|
239 | else if ( _type == Field_ListSingle )
|
---|
240 | type = "list-single";
|
---|
241 | else if ( _type == Field_TextMulti )
|
---|
242 | type = "text-multi";
|
---|
243 | else if ( _type == Field_TextPrivate )
|
---|
244 | type = "text-private";
|
---|
245 |
|
---|
246 | f.setAttribute("type", type);
|
---|
247 |
|
---|
248 | // now, setting nested tags...
|
---|
249 | if ( !submitForm && _required )
|
---|
250 | f.appendChild( emptyTag(doc, "required") );
|
---|
251 |
|
---|
252 | if ( !submitForm && !_desc.isEmpty() )
|
---|
253 | f.appendChild( textTag(doc, "desc", _desc) );
|
---|
254 |
|
---|
255 | if ( !submitForm && !_options.isEmpty() ) {
|
---|
256 | OptionList::ConstIterator it = _options.begin();
|
---|
257 | for ( ; it != _options.end(); ++it) {
|
---|
258 | QDomElement o = doc->createElement("option");
|
---|
259 | o.appendChild(textTag(doc, "value", (*it).value));
|
---|
260 | if ( !(*it).label.isEmpty() )
|
---|
261 | o.setAttribute("label", (*it).label);
|
---|
262 | f.appendChild(o);
|
---|
263 | }
|
---|
264 | }
|
---|
265 |
|
---|
266 | if ( !_value.isEmpty() ) {
|
---|
267 | QStringList::ConstIterator it = _value.begin();
|
---|
268 | for ( ; it != _value.end(); ++it)
|
---|
269 | f.appendChild( textTag(doc, "value", *it) );
|
---|
270 | }
|
---|
271 |
|
---|
272 | return f;
|
---|
273 | }
|
---|
274 |
|
---|
275 | //----------------------------------------------------------------------------
|
---|
276 | // XData
|
---|
277 | //----------------------------------------------------------------------------
|
---|
278 | class XData::Private : public QShared {
|
---|
279 | public:
|
---|
280 | QString title, instructions;
|
---|
281 | XData::Type type;
|
---|
282 | FieldList fields;
|
---|
283 | QValueList<ReportField> report;
|
---|
284 | QValueList<ReportItem> reportItems;
|
---|
285 | };
|
---|
286 |
|
---|
287 | XData::XData()
|
---|
288 | {
|
---|
289 | d = new Private;
|
---|
290 | }
|
---|
291 |
|
---|
292 | XData::~XData()
|
---|
293 | {
|
---|
294 | if ( d->deref() )
|
---|
295 | delete d;
|
---|
296 | }
|
---|
297 |
|
---|
298 | XData::XData(const XData &x)
|
---|
299 | {
|
---|
300 | d = x.d;
|
---|
301 | d->ref();
|
---|
302 | }
|
---|
303 |
|
---|
304 | QString XData::title() const
|
---|
305 | {
|
---|
306 | return d->title;
|
---|
307 | }
|
---|
308 |
|
---|
309 | void XData::setTitle(const QString &t)
|
---|
310 | {
|
---|
311 | detach();
|
---|
312 | d->title = t;
|
---|
313 | }
|
---|
314 |
|
---|
315 | QString XData::instructions() const
|
---|
316 | {
|
---|
317 | return d->instructions;
|
---|
318 | }
|
---|
319 |
|
---|
320 | void XData::setInstructions(const QString &i)
|
---|
321 | {
|
---|
322 | detach();
|
---|
323 | d->instructions = i;
|
---|
324 | }
|
---|
325 |
|
---|
326 | XData::Type XData::type() const
|
---|
327 | {
|
---|
328 | return d->type;
|
---|
329 | }
|
---|
330 |
|
---|
331 | void XData::setType(Type t)
|
---|
332 | {
|
---|
333 | detach();
|
---|
334 | d->type = t;
|
---|
335 | }
|
---|
336 |
|
---|
337 | XData::FieldList XData::fields() const
|
---|
338 | {
|
---|
339 | return d->fields;
|
---|
340 | }
|
---|
341 |
|
---|
342 | void XData::setFields(const FieldList &f)
|
---|
343 | {
|
---|
344 | detach();
|
---|
345 | d->fields = f;
|
---|
346 | }
|
---|
347 |
|
---|
348 | void XData::fromXml(const QDomElement &e)
|
---|
349 | {
|
---|
350 | if ( e.attribute("xmlns") != "jabber:x:data" )
|
---|
351 | return;
|
---|
352 |
|
---|
353 | detach();
|
---|
354 |
|
---|
355 | QString type = e.attribute("type");
|
---|
356 | if ( type == "result" )
|
---|
357 | d->type = Data_Result;
|
---|
358 | else if ( type == "submit" )
|
---|
359 | d->type = Data_Submit;
|
---|
360 | else if ( type == "cancel" )
|
---|
361 | d->type = Data_Cancel;
|
---|
362 | else
|
---|
363 | d->type = Data_Form;
|
---|
364 |
|
---|
365 | d->title = subTagText(e, "title");
|
---|
366 | d->instructions = subTagText(e, "instructions");
|
---|
367 |
|
---|
368 | d->fields.clear();
|
---|
369 |
|
---|
370 | QDomNode n = e.firstChild();
|
---|
371 | for ( ; !n.isNull(); n = n.nextSibling() ) {
|
---|
372 | QDomElement i = n.toElement();
|
---|
373 | if ( i.isNull() )
|
---|
374 | continue;
|
---|
375 |
|
---|
376 | if ( i.tagName() == "field" ) {
|
---|
377 | Field f;
|
---|
378 | f.fromXml(i);
|
---|
379 | d->fields.append(f);
|
---|
380 | }
|
---|
381 | else if ( i.tagName() == "reported" ) {
|
---|
382 | d->report.clear();
|
---|
383 | d->reportItems.clear();
|
---|
384 |
|
---|
385 | QDomNode nn = i.firstChild();
|
---|
386 | for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
|
---|
387 | QDomElement ii = nn.toElement();
|
---|
388 | if ( ii.isNull() )
|
---|
389 | continue;
|
---|
390 |
|
---|
391 | if ( ii.tagName() == "field" ) {
|
---|
392 | d->report.append( ReportField( ii.attribute("label"), ii.attribute("var") ) );
|
---|
393 | }
|
---|
394 | }
|
---|
395 | }
|
---|
396 | else if ( i.tagName() == "item" ) {
|
---|
397 | ReportItem item;
|
---|
398 |
|
---|
399 | QDomNode nn = i.firstChild();
|
---|
400 | for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
|
---|
401 | QDomElement ii = nn.toElement();
|
---|
402 | if ( ii.isNull() )
|
---|
403 | continue;
|
---|
404 |
|
---|
405 | if ( ii.tagName() == "field" ) {
|
---|
406 | QString name = ii.attribute("var");
|
---|
407 | QString value;
|
---|
408 |
|
---|
409 | bool found;
|
---|
410 | QDomElement e = findSubTag( ii, "value", &found );
|
---|
411 | if ( found )
|
---|
412 | value = e.text();
|
---|
413 |
|
---|
414 | item[name] = value;
|
---|
415 | }
|
---|
416 | }
|
---|
417 |
|
---|
418 | d->reportItems.append( item );
|
---|
419 | }
|
---|
420 | }
|
---|
421 | }
|
---|
422 |
|
---|
423 | QDomElement XData::toXml(QDomDocument *doc, bool submitForm) const
|
---|
424 | {
|
---|
425 | QDomElement x = doc->createElement("x");
|
---|
426 | x.setAttribute("xmlns", "jabber:x:data");
|
---|
427 |
|
---|
428 | QString type = "form";
|
---|
429 | if ( d->type == Data_Result )
|
---|
430 | type = "result";
|
---|
431 | else if ( d->type == Data_Submit )
|
---|
432 | type = "submit";
|
---|
433 | else if ( d->type == Data_Cancel )
|
---|
434 | type = "cancel";
|
---|
435 |
|
---|
436 | x.setAttribute("type", type);
|
---|
437 |
|
---|
438 | if ( !submitForm && !d->title.isEmpty() )
|
---|
439 | x.appendChild( textTag(doc, "title", d->title) );
|
---|
440 | if ( !submitForm && !d->instructions.isEmpty() )
|
---|
441 | x.appendChild( textTag(doc, "instructions", d->instructions) );
|
---|
442 |
|
---|
443 | if ( !d->fields.isEmpty() ) {
|
---|
444 | FieldList::ConstIterator it = d->fields.begin();
|
---|
445 | for ( ; it != d->fields.end(); ++it) {
|
---|
446 | Field f = *it;
|
---|
447 | if ( !(submitForm && f.var().isEmpty()) )
|
---|
448 | x.appendChild( f.toXml(doc, submitForm) );
|
---|
449 | }
|
---|
450 | }
|
---|
451 |
|
---|
452 | return x;
|
---|
453 | }
|
---|
454 |
|
---|
455 | XData &XData::operator= (const XData &from)
|
---|
456 | {
|
---|
457 | if ( d->deref() )
|
---|
458 | delete d;
|
---|
459 |
|
---|
460 | d = from.d;
|
---|
461 | d->ref();
|
---|
462 |
|
---|
463 | return *this;
|
---|
464 | }
|
---|
465 |
|
---|
466 | XData XData::copy() const
|
---|
467 | {
|
---|
468 | XData foo;
|
---|
469 | delete foo.d;
|
---|
470 | foo.d = new Private( *this->d );
|
---|
471 |
|
---|
472 | return foo;
|
---|
473 | }
|
---|
474 |
|
---|
475 | void XData::detach()
|
---|
476 | {
|
---|
477 | if ( d->count != 1 ) // only if >1 reference
|
---|
478 | *this = copy();
|
---|
479 | }
|
---|
480 |
|
---|
481 | const QValueList<XData::ReportField> &XData::report() const
|
---|
482 | {
|
---|
483 | return d->report;
|
---|
484 | }
|
---|
485 |
|
---|
486 | const QValueList<XData::ReportItem> &XData::reportItems() const
|
---|
487 | {
|
---|
488 | return d->reportItems;
|
---|
489 | }
|
---|