blob: b006eab82b43be46b505e4c23e1414eb648af888 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 2001-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39#include <cc++/config.h>
40#ifdef CCXX_WITHOUT_EXTRAS
41#include <cc++/export.h>
42#endif
43#include <cc++/file.h>
44#include <cc++/thread.h>
45#include <cc++/exception.h>
46#ifndef CCXX_WITHOUT_EXTRAS
47#include <cc++/export.h>
48#endif
49#include <cc++/xml.h>
50#ifndef WIN32
51#include <syslog.h>
52#endif
53
54#include <cstdlib>
55
56// very ugly, but saves a lot of #ifdefs. To understand this, look at
57// the private members of XMLRPC.
58#ifndef HAVE_SSTREAM
59#define strBuf (*oldStrBuf)
60#endif
61
62#ifdef CCXX_NAMESPACES
63namespace ost {
64#ifdef HAVE_SSTREAM
65using std::stringstream;
66#else
67using std::strstream;
68#endif
69using std::streambuf;
70using std::ofstream;
71using std::ostream;
72using std::clog;
73using std::endl;
74using std::ends;
75using std::ios;
76#endif
77
78static bool isElement(char c)
79{
80 return isalnum(c) || c == ':' || c == '-' || c == '.' || c == '_';
81}
82
83void XMLStream::putData(char c)
84{
85 dbuf[dp++] = c;
86 if(dp >= sizeof(dbuf)) {
87 if(ecount)
88 characters((unsigned char *)dbuf, dp);
89 dp = 0;
90 }
91}
92
93void XMLStream::clrData(void)
94{
95 if(dp && ecount)
96 characters((unsigned char *)dbuf, dp);
97 dp = 0;
98}
99
100void XMLStream::parseInit(void)
101{
102 state = NONE;
103 dp = 0;
104 ecount = dcount = 0;
105}
106
107bool XMLStream::parseTag(void)
108{
109 size_t len = dp;
110 const char *data = dbuf;
111 bool end = false;
112 const unsigned char *attrib[128];
113 unsigned attr = 0;
114 char *ep;
115
116 if(*data == '/') {
117 while(--len) {
118 if(!isElement(*(++data)))
119 break;
120 }
121 if(len)
122 return false;
123
124 dbuf[dp] = 0;
125 endElement((const unsigned char *)(dbuf + 1));
126 dp = 0;
127 --ecount;
128 if(ecount < 0)
129 return false;
130 if(!ecount)
131 endDocument();
132 }
133 else if(*data == '!') {
134 dp = 0;
135 return true; // dtd
136 }
137 else if(*data == '?') {
138 if(!strnicmp(data, "?xml version=\"", 14)) {
139 // version info
140 }
141 dp = 0;
142 }
143 else if(!isElement(*data))
144 return false;
145 else {
146 end = false;
147 if(dbuf[dp - 1] == '/') {
148 --dp;
149 end = true;
150 }
151 len = 0;
152 data = dbuf;
153 while(len < dp) {
154 if(!isElement(*data))
155 break;
156 ++len;
157 ++data;
158 }
159 if(len == dp) {
160 if(!ecount)
161 startDocument();
162 ++ecount;
163 attrib[0] = attrib[1] = NULL;
164 dbuf[dp] = 0;
165 startElement((const unsigned char *)dbuf, attrib);
166 if(end) {
167ending:
168 --ecount;
169 endElement((const unsigned char *)dbuf);
170 if(!ecount)
171 endDocument();
172 }
173 dp = 0;
174 return true;
175 }
176 if(!ecount)
177 startDocument();
178 ++ecount;
179
180 // attributes, name is between data and len
181
182 for(;;) {
183 while(!isElement(dbuf[len]) && len < dp) {
184 if(!isspace(dbuf[len]))
185 return false;
186 dbuf[len++] = 0;
187 }
188
189 if(len == dp)
190 break;
191
192 attrib[attr++] = (const unsigned char *)(dbuf + len);
193 while(len < dp && isElement(dbuf[len]))
194 ++len;
195
196 if(len == dp)
197 return false;
198
199 if(dbuf[len] != '=')
200 return false;
201
202 dbuf[len++] = 0;
203 if(len == dp) {
204 attrib[attr++] = (const unsigned char *)"";
205 break;
206 }
207
208 if(isspace(dbuf[len])) {
209 attrib[attr++] = (const unsigned char *)"";
210 continue;
211 }
212 if(dbuf[len] == '\'' || dbuf[len] == '\"') {
213 ep = strchr(dbuf + len + 1, dbuf[len]);
214 if(!ep)
215 return false;
216 attrib[attr++] = (const unsigned char *)dbuf + len + 1;
217 *(ep++) = 0;
218 len = ep - dbuf;
219 continue;
220 }
221 if(!isElement(dbuf[len]))
222 return false;
223 attrib[attr++] = (const unsigned char *)dbuf;
224 while(isElement(dbuf[len]) && len < dp)
225 ++len;
226 if(len == dp) {
227 dbuf[len] = 0;
228 break;
229 }
230 }
231
232 attrib[attr++] = NULL;
233 attrib[attr++] = NULL;
234 startElement((const unsigned char *)dbuf, attrib);
235 if(end)
236 goto ending;
237 dp = 0;
238 return true;
239 }
240 return true;
241}
242
243bool XMLStream::parseChunk(const char *data, size_t len)
244{
245 unsigned char cp;
246 while(len--) {
247 switch(state) {
248 case AMP:
249 if((!dp && *data == '#') || isElement(*data)) {
250 dbuf[dp++] = *data;
251 break;
252 }
253 if(*data != ';')
254 return false;
255 dbuf[dp] = 0;
256 if(dbuf[0] == '#')
257 cp = atoi(dbuf + 1);
258 else if(!stricmp(dbuf, "amp"))
259 cp = '&';
260 else if(!stricmp(dbuf, "lt"))
261 cp = '<';
262 else if(!stricmp(dbuf, "gt"))
263 cp = '>';
264 else if(!stricmp(dbuf, "apos"))
265 cp = '`';
266 else if(!stricmp(dbuf, "quot"))
267 cp = '\"';
268 else
269 return false;
270 characters(&cp, 1);
271 dp = 0;
272 state = NONE;
273 break;
274 case TAG:
275 if(*data == '>') {
276 state = NONE;
277 if(!parseTag())
278 return false;
279 }
280 else if(*data == '[' && dp == 7 && !strncmp(dbuf, "![CDATA", 7)) {
281 state = CDATA;
282 }
283 else if(*data == '-' && dp == 2 && !strncmp(dbuf, "!-", 2)) {
284 state = COMMENT;
285 dp = 0;
286 }
287 else if(*data == '[' && !strncmp(dbuf, "!DOCTYPE ", 9)) {
288 state = DTD;
289 dp = 0;
290 }
291 else
292 putData(*data);
293 break;
294 case COMMENT:
295 if(*data == '>' && dp >= 2 && !strncmp(&dbuf[dp - 2], "--", 2)) {
296 dp -= 2;
297 if(dp)
298 comment((unsigned char *)dbuf, dp);
299 dp = 0;
300 state = NONE;
301 }
302 else {
303 dbuf[dp++] = *data;
304 if(dp == sizeof(dbuf)) {
305 comment((unsigned char *)dbuf, dp);
306 dp = 0;
307 }
308 }
309 break;
310 case CDATA:
311 putData(*data);
312 if(dp > 2)
313 if(!strcmp(&dbuf[dp - 3], "]]>")) {
314 dp -= 3;
315 state = NONE;
316 clrData();
317 }
318 break;
319 case DTD:
320 if(*data == '<')
321 ++dcount;
322 else if(*data == '>' && dcount)
323 --dcount;
324 else if(*data == '>')
325 state = NONE;
326 break;
327 case NONE:
328 if(*data == '<') {
329 clrData();
330 state = TAG;
331 }
332 else if(ecount && *data == '&') {
333 clrData();
334 state = AMP;
335 }
336 else if(ecount)
337 putData(*data);
338 }
339 ++data;
340 }
341 return true;
342}
343
344bool XMLStream::parse(const char *resource)
345{
346 bool ret = false;
347 char buffer[1024];
348 int res;
349
350 if(resource)
351 if(!open(resource))
352 return false;
353
354 parseInit();
355
356 while((res = read((unsigned char *)buffer, 1024)))
357 ret = parseChunk(buffer, res);
358 return ret;
359}
360
361XMLRPC::XMLRPC(size_t bufferSize) :
362XMLStream()
363{
364#ifdef HAVE_SSTREAM
365 // nothing
366#else
367 buffer = new char[bufferSize];
368 oldStrBuf = NULL;
369 bufSize = bufferSize;
370#endif
371}
372
373XMLRPC::~XMLRPC()
374{
375#ifdef HAVE_SSTREAM
376 // nothing
377#else
378 if(buffer)
379 delete[] buffer;
380 if(oldStrBuf)
381 delete oldStrBuf;
382#endif
383 close();
384}
385
386void XMLRPC::invoke(const char *member)
387{
388#ifdef HAVE_SSTREAM
389 strBuf.str() = "";
390#else
391 buffer[0] = 0;
392 oldStrBuf = new strstream(buffer,bufSize);
393#endif
394
395 structFlag = reply = fault = false;
396 array = 0;
397
398 strBuf << "<?xml version=\"1.0\"?>" << endl;
399 strBuf << "<methodCall>" << endl;
400 strBuf << "<methodName>" << member << "</methodName>" << endl;
401 strBuf << "<params>" << endl;
402}
403
404void XMLRPC::response(bool f)
405{
406 reply = true;
407 structFlag = false;
408 fault = f;
409 array = 0;
410
411#ifdef HAVE_SSTREAM
412 // nothing
413#else
414 buffer[0] = 0;
415 oldStrBuf = new strstream(buffer,bufSize);
416#endif
417
418 strBuf << "<?xml version=\"1.0\"?>" << endl;
419 strBuf << "<methodResponse>" << endl;
420 if(fault)
421 strBuf << "<fault>" << endl;
422 else
423 strBuf << "<params>" << endl;
424}
425
426void XMLRPC::addMember(const char *name, long value)
427{
428#ifdef HAVE_SSTREAM
429 // nothing
430#else
431 if(!oldStrBuf)
432 return;
433#endif
434
435 begStruct();
436
437 strBuf << "<member><name>" << name << "</name>" << endl;
438 strBuf << "<value><i4>" << value << "</i4></value></member>" << endl;
439}
440
441void XMLRPC::addMember(const char *name, const char *value)
442{
443#ifdef HAVE_SSTREAM
444 // nothing
445#else
446 if(!oldStrBuf)
447 return;
448#endif
449
450 begStruct();
451
452 strBuf << "<member><name>" << name << "</name>" << endl;
453 strBuf << "<value><string>" << value << "</string></value></member>" << endl;
454}
455
456
457void XMLRPC::addMember(const char *name, bool value)
458{
459#ifdef HAVE_SSTREAM
460 // nothing
461#else
462 if(!oldStrBuf)
463 return;
464#endif
465
466 begStruct();
467
468 strBuf << "<member><name>" << name << "</name>" << endl;
469 strBuf << "<value><boolean>";
470 if(value)
471 strBuf << "1";
472 else
473 strBuf << "0";
474
475 strBuf << "</boolean></value></member>" << endl;
476}
477
478void XMLRPC::addParam(bool value)
479{
480#ifdef HAVE_SSTREAM
481 // nothing
482#else
483 if(!oldStrBuf)
484 return;
485#endif
486
487 endStruct();
488
489 if(!fault && !array)
490 strBuf << "<param>";
491
492 strBuf << "<value><boolean>";
493 if(value)
494 strBuf << "1";
495 else
496 strBuf << "0";
497 strBuf << "</boolean></value>";
498
499 if(!fault && !array)
500 strBuf << "</param>";
501
502 strBuf << endl;
503}
504
505void XMLRPC::addParam(long value)
506{
507#ifdef HAVE_SSTREAM
508 // nothing
509#else
510 if(!oldStrBuf)
511 return;
512#endif
513
514 endStruct();
515
516 if(!fault && !array)
517 strBuf << "<param>";
518
519 strBuf << "<value><i4>" << value << "</i4></value>";
520
521 if(!fault && !array)
522 strBuf << "</param>";
523
524 strBuf << endl;
525}
526
527void XMLRPC::addParam(const char *value)
528{
529#ifdef HAVE_SSTREAM
530 // nothing
531#else
532 if(!oldStrBuf)
533 return;
534#endif
535
536 endStruct();
537
538 if(!fault && !array)
539 strBuf << "<param>" << endl;
540
541 strBuf << "<value><string>" << value << "</string></value>";
542
543 if(!fault && !array)
544 strBuf << "</param>";
545
546 strBuf << endl;
547}
548
549void XMLRPC::begArray(void)
550{
551#ifdef HAVE_SSTREAM
552 // nothing
553#else
554 if(!oldStrBuf)
555 return;
556#endif
557
558 if(fault) //do not include arrays in fault responses.
559 return;
560
561 if(!array)
562 strBuf << "<param>";
563 array++;
564 strBuf << "<array><data>" << endl;
565}
566
567void XMLRPC::endArray(void)
568{
569#ifdef HAVE_SSTREAM
570 // nothing
571#else
572 if(!oldStrBuf)
573 return;
574#endif
575
576 if(!array)
577 return;
578
579 strBuf << "</data></array>";
580
581 if(!--array)
582 strBuf << "</param>";
583
584 strBuf << endl;
585}
586
587void XMLRPC::begStruct(void)
588{
589 if(structFlag)
590 return;
591
592 structFlag = true;
593
594 if(!fault && !array)
595 strBuf << "<param>";
596
597 strBuf << "<value><struct>" << endl;
598}
599
600void XMLRPC::endStruct(void)
601{
602 if(!structFlag)
603 return;
604
605 strBuf << "</struct></value>";
606
607 if(!fault && !array)
608 strBuf << "</param>";
609 strBuf << endl;
610 structFlag = false;
611}
612
613bool XMLRPC::send(const char *resource)
614{
615#ifdef HAVE_SSTREAM
616 // nothing
617#else
618 if(!oldStrBuf)
619 return false;
620#endif
621
622 endStruct();
623 while(array) {
624 strBuf << "</data></array>" << endl;
625 --array;
626 }
627 if(!fault)
628 strBuf << "</params>" << endl;
629 else
630 strBuf << "</fault>" << endl;
631
632 if(reply)
633 strBuf << "</methodResponse>" << endl << ends;
634 else
635 strBuf << "</methodCall>" << endl << ends;
636
637 bool result;
638
639#ifdef HAVE_SSTREAM
640 result = post(resource, strBuf.str().c_str());
641 strBuf.str("");
642#else
643 delete oldStrBuf;
644 oldStrBuf = NULL;
645 result = post(resource, (const char *)buffer);
646#endif
647
648 return result;
649}
650
651bool XMLStream::open(const char *resource)
652{
653 return true;
654}
655
656void XMLStream::close(void)
657{}
658
659Slog::Level XMLStream::getLogging(void)
660{
661 return Slog::levelCritical;
662}
663
664void XMLStream::comment(const unsigned char *text, size_t len)
665{}
666
667void XMLStream::startDocument(void)
668{}
669
670void XMLStream::endDocument(void)
671{}
672
673XMLStream::~XMLStream()
674{}
675
676#ifdef CCXX_NAMESPACES
677}
678#endif
679
680/** EMACS **
681 * Local variables:
682 * mode: c++
683 * c-basic-offset: 4
684 * End:
685 */