1 /*---------------------------------------------------------------------------*\
5 * Copyright (C) 2000-2002 by the OpenSG Forum *
9 * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
15 * This library is free software; you can redistribute it and/or modify it *
16 * under the terms of the GNU Library General Public License as published *
17 * by the Free Software Foundation, version 2. *
19 * This library is distributed in the hope that it will be useful, but *
20 * WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
22 * Library General Public License for more details. *
24 * You should have received a copy of the GNU Library General Public *
25 * License along with this library; if not, write to the Free Software *
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
37 \*---------------------------------------------------------------------------*/
39 #include "OSGConfig.h"
40 #include "OSGFieldContainerFields.h"
41 #include "OSGNodeCore.h"
43 #include "OSGNameAttachment.h"
44 #include "OSGRootGroup.h"
46 #include "OSGWebInterface.h"
50 /*! \class osg::WebInterface
52 The WebInterface class provides a simple access to all
53 FieldContainers in a running OpenSG application. If
54 handleRequests() is called in each render loop, it is
55 possible to connect the interface with a standard web
56 browser http://hostname:8888. In glut applications the
57 idle functions could be used. It is possible to add
58 new features to the interface by subclassing and adding
63 WebInterface::CreateFunc
WebInterface::_createFunc
= NULL
;
65 WebInterface::ObjTransitPtr
WebInterface::create(UInt32 port
)
69 return _createFunc(port
);
73 return WebInterfaceTransitPtr(new WebInterface(port
));
77 void WebInterface::setCreatorFunc(CreateFunc createFunc
)
79 _createFunc
= createFunc
;
82 /*-------------------------------------------------------------------------*/
85 WebInterface::WebInterface(void) :
92 _systemContainer(NULL
),
93 _header (getDefaultHeader()),
99 /*! Cunstruct a WebInterface for the given port. If the port
100 is used, try to bind to the following port number.
102 WebInterface::WebInterface(UInt32 port
) :
109 _systemContainer(NULL
),
110 _header (getDefaultHeader()),
117 _socket
.setReusePort(true);
123 _socket
.bind(SocketAddress(SocketAddress::ANY
,port
));
128 catch(SocketException
&)
135 FLOG(("WebInterface bound to port:%d\n",port
));
137 // register bas handlers
138 addHandler("/" , &WebInterface::rootHandler
);
139 addHandler("/changelist", &WebInterface::changelistHandler
);
140 addHandler("/fcview" , &WebInterface::fcViewHandler
);
141 addHandler("/fcedit" , &WebInterface::fcEditHandler
);
142 addHandler("/treeview" , &WebInterface::treeViewHandler
);
145 /*-------------------------------------------------------------------------*/
151 WebInterface::~WebInterface(void)
156 /*-------------------------------------------------------------------------*/
157 /* request handling */
159 /*! Handle all incoming http requests. Return, if no request is
163 void WebInterface::handleRequests(void)
165 std::string path
,url
;
167 HandlerT::iterator hI
;
169 while(checkRequest(url
))
171 decodeUrl(url
,path
,param
);
176 "HTTP/1.1 200 OK\r\n"
177 "Connection: close\r\n"
178 "Server: OpenSGMicroWebInterface\r\n"
182 hI
= _handler
.find(path
);
184 if(hI
!= _handler
.end())
186 (this->*hI
->second
)(_body
,path
.c_str(),param
);
190 _body
<< "Content-Type: text/html\r\n"
192 "<html>Invalid path</html>";
196 _accepted
.setDelay(false);
199 _accepted
.send(_body
.str().c_str(), _body
.str().length());
206 /*! Flush the data that's alrady in the stream to allow incremental creation
207 and updates on slow operaitons
210 void WebInterface::flush(void)
213 _accepted
.setDelay(false);
216 _accepted
.send(_body
.str().c_str(), _body
.str().length());
220 /*! Suspend processing until a http request is pending for the
221 given duration in seconds. If duration is < 0. waitRequest
225 bool WebInterface::waitRequest(double duration
)
229 if(!_socket
.waitReadable(duration
))
232 catch(SocketException
&)
240 /*-------------------------------------------------------------------------*/
243 /*! Set the ChangeList. By default the ChangeList of the current thread is
244 used, but it can be overriden by this function.
247 void WebInterface::setChangeList(ChangeList
*clist
)
252 /*! Set the scenegraph root node.
255 void WebInterface::setSystemContainer(FieldContainer
*system
)
257 _systemContainer
= system
;
260 /*! Set the scenegraph root node.
262 void WebInterface::setRoot(Node
*root
)
267 /*! Set the html header.
269 void WebInterface::setHeader(const std::string
&header
)
274 /*! Set the html footer.
276 void WebInterface::setFooter(const std::string
&footer
)
281 /*-------------------------------------------------------------------------*/
285 /*! Get the scenegraph root node.
288 FieldContainer
*WebInterface::getSystemContainer(void)
290 return _systemContainer
;
293 /*! Get the scenegraph root node.
296 Node
*WebInterface::getRoot(void)
301 /*! Get the html header.
304 const std::string
&WebInterface::getHeader(void)
309 /*! Get the html footer.
312 const std::string
&WebInterface::getFooter(void)
317 /*-------------------------------------------------------------------------*/
318 /* handler management */
320 /*! Add a new page handler
322 void WebInterface::addHandler(const Char8
*path
, MethodT method
)
324 // This is a workaround for gcc 3.1 on OS X
326 HandlerT::value_type
newEntry(path
, method
);
328 std::pair
<HandlerT::iterator
, bool> retVal
;
330 retVal
= _handler
.insert(newEntry
);
332 if(retVal
.second
== false)
333 retVal
.first
->second
= method
;
335 // _handler[IDString(path)] = method;
338 /*-------------------------------------------------------------------------*/
339 /* url encoding/decoding */
341 /*! Encode the given path and params into a valid http url.
344 std::string
WebInterface::encodeUrl(const std::string
&path
,
345 const ParameterT
¶m
)
347 std::string result
=path
;
348 ParameterT::const_iterator pI
;
352 const char *hex
="0123456789abcdef";
357 for(pI
= param
.begin() ; pI
!= param
.end() ; ++pI
)
359 if(pI
!= param
.begin())
362 result
+= pI
->first
.c_str();
364 if(!pI
->second
.empty())
367 for(c
= 0 ; c
< UInt32(pI
->second
.length()) ; ++c
)
369 ch
= pI
->second
.c_str()[c
];
385 result
+= hex
[ch
/ 16];
386 result
+= hex
[ch
& 15];
397 /*! strip and decode parameter values from a given URL. Parameter,
398 value paires are stored in a string map.
401 void WebInterface::decodeUrl(const std::string
&url
,
405 std::string::const_iterator sI
=url
.begin();
406 std::string name
,value
;
413 while(sI
!=url
.end() && *sI
!= ' ' && *sI
!= '?')
416 if(sI
!=url
.end() && *sI
== '?')
423 while(++sI
!= url
.end() &&
431 if(sI
!= url
.end() && *sI
== '=')
434 while(++sI
!= url
.end() && *sI
!= ' ' && *sI
!= '&')
438 case '+': value
+= ' ';
442 for(c
= 0 ; c
< 2 ; ++c
)
448 bu
[c
] = bu
[c
] - 'A' + 10;
453 bu
[c
] = bu
[c
] - 'a' + 10;
459 value
+= char(bu
[0]*16+bu
[1]);
468 setParam(param
,name
.c_str(),value
.c_str());
470 } while(sI
!= url
.end() && *sI
== '&');
474 /*-------------------------------------------------------------------------*/
477 /*! Helper function. Create a link to a node view html page.
480 std::string
WebInterface::createFCViewReference(FieldContainer
*fcPtr
,
483 std::stringstream result
;
488 result
<< "Unknown(" << fcId
<< ")";
494 result
<< "<a href =\"fcview?id="
497 << fcPtr
->getType().getName()
507 /*! get parameter. If the parameter is not set, NULL
511 const char *WebInterface::getParam(ParameterT
¶m
, const char *name
)
513 ParameterT::iterator pI
= param
.find(std::string(name
));
515 if(pI
== param
.end())
518 return pI
->second
.c_str();
521 /*! set parameter to the given value. If value is NULL, the
522 parameter is removed.
524 void WebInterface::setParam( ParameterT
¶m
,
529 param
.erase(std::string(name
));
531 param
[std::string(name
)] = std::string(value
);
534 /*! Handle pending http requests. The requested url is return in
535 url. If no request is pending, false is returned.
537 bool WebInterface::checkRequest(std::string
&url
)
539 std::string name
,value
;
547 if(!_socket
.waitReadable(0))
550 catch(SocketException
&)
555 _accepted
= _socket
.accept();
557 if(_accepted
.recv(bu
,4) && strncmp(bu
,"GET ",4) == 0)
559 while(_accepted
.recv(&ch
, 1) && ch
!= ' ')
564 while(_accepted
.recv(&ch
, 1) && ch
!= '\n');
566 while(_accepted
.recv(&ch
, 1) && ch
!= '\r');
568 _accepted
.recv(&ch
, 1);
576 /*! Traversal function for the treeViewHandler
578 void WebInterface::treeViewNode(std::ostream
&os
,
582 ParameterT::const_iterator pI
;
588 os
<< "<li>NullFC</li>\n";
592 sprintf(idstr
,"%u",node
->getId());
594 if(param
.count(std::string(idstr
)))
596 setParam(param
,"close",idstr
);
597 folder
= encodeUrl("treeview",param
);
598 setParam(param
,"close",NULL
);
599 os
<< "<li><a href=\"" << folder
<< "\">  -  </a>";
603 setParam(param
,"open",idstr
);
604 folder
= encodeUrl("treeview",param
);
605 setParam(param
,"open",NULL
);
606 os
<< "<li><a href=\"" << folder
<< "\">  +  </a>";
609 os
<< "      "
610 << "<b>" << getNodeName(node
) << "</b>      "
611 << createFCViewReference(node
);
613 if(node
->getCore() != NULL
)
615 os
<< "      Core: "
616 << createFCViewReference(node
->getCore());
620 if(param
.count(std::string(idstr
)))
623 Node::MFChildrenType::const_iterator nI
=
624 node
->getMFChildren()->begin();
626 for(; nI
!= node
->getMFChildren()->end(); ++nI
)
628 treeViewNode(os
,*nI
,param
);
631 RootGroup
*pRG
= dynamic_cast<RootGroup
*>(node
->getCore());
635 treeViewNode(os
, pRG
->getRoot(), param
);
642 /*-------------------------------------------------------------------------*/
643 /* web page handler */
647 void WebInterface::rootHandler( std::ostream
&os
,
651 os
<< "Content-Type: text/html\r\n"
656 os
<< "<h1>OpenSG Web Interface</h1>"
658 << "<li><a href=\"changelist\">ChangeList</a>"
659 << "<li><a href=\"treeview\">SceneGraph</a>"
660 << "<li><b>" << getNodeName(_systemContainer
) << "</b>      "
661 << createFCViewReference(_systemContainer
)
663 os
<< _footer
<< "</html>";
666 /*! View the current changelist
668 void WebInterface::changelistHandler(std::ostream
&os
,
673 ChangeList::idrefd_const_iterator createdI
;
674 ChangeList::changed_const_iterator changedI
;
675 ChangeList::idrefd_const_iterator destroyedI
;
676 FieldContainerPtr fcPtr
;
677 ChangeList
*changeList
;
678 std::string type
,mask
;
680 const int createdCols
=6;
681 const int changedCols
=3;
682 const int destroyedCols
=6;
686 changeList
=OSG::Thread::getCurrentChangeList();
693 os
<< "Content-Type: text/html\r\n"
696 << "<h1>ChangeList</h1>";
699 os
<< "<h2>Created</h2>"
701 for(col
= 0 ; col
< createdCols
; ++col
)
702 os
<< "<th>FieldContainer</th>";
704 for(col
= 0,createdI
= changeList
->beginCreated();
705 createdI
!= changeList
->endCreated(); createdI
++)
707 fcPtr
= FieldContainerFactory::the()->getContainer(*createdI
);
710 os
<< "<td>" << createFCViewReference(fcPtr
) << "</td>";
711 col
= (col
+1) % createdCols
;
715 while(col
&& col
++ < createdCols
)
716 os
<< "<td> </td>";
717 os
<< "</tr>\n</table>\n";
720 os
<< "<h2>Changed</h2>"
722 for(col
= 0 ; col
< changedCols
; ++col
)
723 os
<< "<th>FieldContainer</th><th>Change Mask</th>";
725 for(col
= 0,changedI
= changeList
->beginChanged();
726 changedI
!= changeList
->endChanged();
729 fcPtr
= FieldContainerFactory::the()->getContainer(changedI
->first
);
733 if(changedI
->second
== FieldBits::AllFields
)
738 for(unsigned int i
=0;i
<fcPtr
->getType().getNumFieldDescs();i
++)
740 FieldDescription
*desc
=fcPtr
->getType().getFieldDescription(i
+1);
741 if(desc
->getFieldMask() & changedI
->second
)
745 mask
+= desc
->getName().str();
753 << createFCViewReference(fcPtr
)
757 col
= (col
+1) % changedCols
;
761 while(col
&& col
++ < changedCols
)
762 os
<< "<td> </td>";
763 os
<< "</tr>\n</table>\n";
766 os
<< "<h2>Destroyed</h2>"
768 for(col
= 0 ; col
< destroyedCols
; ++col
)
769 os
<< "<th>FieldContainer</th>";
771 for(col
= 0,destroyedI
= changeList
->beginDestroyed();
772 destroyedI
!= changeList
->endDestroyed(); destroyedI
++)
774 fcPtr
= FieldContainerFactory::the()->getContainer(*destroyedI
);
777 os
<< "<td>" << createFCViewReference(fcPtr
,*destroyedI
) << "</td>";
778 col
= (col
+1) % destroyedCols
;
782 while(col
&& col
++ < destroyedCols
)
783 os
<< "<td> </td>";
784 os
<< "</tr>\n</table>\n";
785 os
<< _footer
<< "</html>";
789 /*! FieldContainer view handler
792 void WebInterface::fcViewHandler( std::ostream
&os
,
796 FieldContainer
*fcPtr
;//,*childFc;
797 std::string type
,value
;
800 // MFFieldContainerPtr *mfFCPtr;
802 if(!getParam(param
,"id"))
804 os
<< "Content-Type: text/html\r\n"
809 << _footer
<< "</html>";
813 id
= atoi(getParam(param
,"id"));
815 os
<< "Content-Type: text/html\r\n"
820 fcPtr
= FieldContainerFactory::the()->getContainer(id
);
829 << fcPtr
->getTypeName()
830 << " " << getNodeName(fcPtr
)
832 << "<table><tr><th>Field</th><th>Field Type</th><th> </th>"
833 << "<th>Value</th></tr>\n";
836 UInt32 numFields
= fcPtr
->getNumFields();
838 for(UInt32 field
= 1; field
<= numFields
; field
++)
840 GetFieldHandlePtr fHandle
= fcPtr
->getField(field
);
842 if(fHandle
== NULL
|| fHandle
->isValid() == false)
848 << fHandle
->getName()
850 << fHandle
->getType().getCName()
853 GetMapFieldHandlePtr sfMap
=
854 boost::dynamic_pointer_cast
<GetMapFieldHandle
>(fHandle
);
856 FieldContainerPtrSFieldBase::GetHandlePtr sfFCPtr
=
857 boost::dynamic_pointer_cast
<
858 FieldContainerPtrSFieldBase::GetHandle
>(fHandle
);
860 FieldContainerPtrMFieldBase::GetHandlePtr mfFCPtr
=
861 boost::dynamic_pointer_cast
<
862 FieldContainerPtrMFieldBase::GetHandle
>(fHandle
);
864 if(sfMap
!= NULL
&& sfMap
->isValid() == true)
868 AttachmentMap
&am
= static_cast<SFAttachmentMap
*>(field
)->getValue();
869 AttachmentMap::const_iterator mI
;
870 for(mI
= am
.begin() ; mI
!= am
.end() ; ++mI
)
874 os
<< createFCViewReference(mI
->second
);
880 if(sfFCPtr
!= NULL
|| mfFCPtr
!= NULL
)
884 if(mfFCPtr
!= NULL
&& mfFCPtr
->isValid() == true)
886 UInt32 mfSize
= (*mfFCPtr
)->size();
888 for(UInt32 j
= 0 ; j
< mfSize
; ++j
)
893 os
<< createFCViewReference((*(*mfFCPtr
))[j
]);
896 else if(sfFCPtr
!= NULL
&& sfFCPtr
->isValid() == true)
898 os
<< createFCViewReference((*sfFCPtr
)->getValue());
903 OutStream
outstream(os
);
905 os
<< "<form action=\"fcedit\">"
906 << "<input type=\"submit\" value=\"Edit\">"
907 << "<input type=\"hidden\" name=\"id\" value=\""
910 << "<input type=\"hidden\" name=\"field\" value=\""
911 << fHandle
->getDescription()->getFieldId()
916 fHandle
->pushValueToStream(outstream
); // << value;
920 os
<< "</td></tr>\n";
926 os
<< _footer
<< "</html>";
932 void WebInterface::fcEditHandler(std::ostream
&os
,
936 FieldContainer
*fcPtr
= NULL
;
937 std::string value
= "";
940 GetFieldHandlePtr fHandle
;
941 FieldDescriptionBase
*desc
= NULL
;
943 if(getParam(param
,"id"))
945 cid
= atoi(getParam(param
,"id"));
947 fcPtr
= FieldContainerFactory::the()->getContainer(cid
);
950 if(getParam(param
,"field"))
952 fid
= atoi(getParam(param
,"field"));
957 os
<< "Content-Type: text/html\r\n"
960 << "Unknown field container"
961 << _footer
<< "</html>";
966 fHandle
= fcPtr
->getField(fid
);
968 if(fHandle
== NULL
|| fHandle
->isValid() == false)
970 os
<< "Content-Type: text/html\r\n"
973 << "Unknown field in container"
974 << _footer
<< "</html>";
979 desc
= fcPtr
->getFieldDescription(fid
);
981 if(getParam(param
, "value"))
983 EditFieldHandlePtr curField
= fcPtr
->editField(fid
);
985 if(curField
!= NULL
&& curField
->isValid() == true)
986 curField
->pushValueFromCString(getParam(param
, "value"));
989 std::stringstream ss
;
993 fHandle
->pushValueToStream(oss
);
998 os
<< "Content-Type: text/html\r\n"
1002 << fcPtr
->getTypeName()
1006 << "<form action=\"fcedit\">"
1007 << "<textarea name=\"value\" cols=\"50\" rows=\"10\">"
1010 << "<input type=\"submit\" value=\" Change \">"
1011 << "<input type=\"hidden\" name=\"id\" value=\""
1014 << "<input type=\"hidden\" name=\"field\" value=\""
1018 << _footer
<< "</html>";
1021 /*! Show scenegraph tree. For each leave to open, a parameter with
1022 the container id must be set. The parameters open and closed
1023 are used to open or close folders.
1025 void WebInterface::treeViewHandler( std::ostream
&os
,
1029 ParameterT::iterator pI
;
1032 if(getParam(param
,"open"))
1034 setParam(param
,getParam(param
,"open"),"");
1035 setParam(param
,"open",NULL
);
1039 if(getParam(param
,"close"))
1041 setParam(param
,getParam(param
,"close"),NULL
);
1042 setParam(param
,"close",NULL
);
1046 os
<< "Content-Type: text/html\r\n"
1049 << "<h1>Scenegraph</h1>\n"
1052 treeViewNode(os
,_root
,param
);
1055 << _footer
<< "</html>";
1058 /*! Returns the name of a field container.
1060 const char *WebInterface::getNodeName(const FieldContainer
*fcPtr
)
1062 static const char *unnamed
= "";
1067 const AttachmentContainer
*acPtr
=
1068 dynamic_cast<const AttachmentContainer
*>(fcPtr
);
1073 const Char8
*name
= getName(acPtr
);
1081 /*! Returns the default html header.
1083 std::string
WebInterface::getDefaultHeader(void)
1085 std::stringstream header
;
1087 header
<< "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" "
1088 << "width=\"100%\">"
1089 << "<tr bgcolor=\"#E5E5E5\">"
1090 << "<td valign=center><a href=\"/\"><font color=\"#004faf\">"
1091 << "Home</font></a></td>"
1092 << "<td valign=center><a href=\"changelist\"><font color="
1093 << "\"#004faf\">Changelist</font></a></td>"
1094 << "<td valign=center><a href=\"treeview\"><font color"
1095 << "=\"#004faf\">Scenegraph</font></a></td>"
1098 return header
.str();