summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdwin Eefting <edwin@datux.nl>2012-01-18 21:01:19 (GMT)
committer Edwin Eefting <edwin@datux.nl>2012-01-18 21:01:19 (GMT)
commit76056e47d56a7d972062f1346eb8b50fd59abeb4 (patch)
tree37309335f5c35aaf913ea427723e1535a499af32
parent537459871eaf3f0765ad6b0bf05d854611c73cf2 (diff)
refactored paper module
-rw-r--r--.cproject211
-rw-r--r--.project3
-rw-r--r--libs/cclient.h2
-rw-r--r--libs/csharedobject.h1
-rw-r--r--libs/utils.cpp80
-rw-r--r--libs/utils.h29
-rw-r--r--main.cpp2
-rw-r--r--modules/paper.module/CMakeLists.txt2
-rw-r--r--modules/paper.module/cpaperclient.cpp45
-rw-r--r--modules/paper.module/cpaperclient.h44
-rw-r--r--modules/paper.module/cpaperobject.cpp606
-rw-r--r--modules/paper.module/cpaperobject.h105
-rw-r--r--modules/paper.module/module.cpp783
13 files changed, 1139 insertions, 774 deletions
diff --git a/.cproject b/.cproject
index 85eda42..fda9e0b 100644
--- a/.cproject
+++ b/.cproject
@@ -15,6 +15,34 @@
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <configuration artifactName="synapse" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.base.186494460" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
+ <folderInfo id="cdt.managedbuild.toolchain.gnu.base.186494460.1739805831" name="/" resourcePath="">
+ <toolChain id="cdt.managedbuild.toolchain.gnu.base.1289815074" name="cdt.managedbuild.toolchain.gnu.base" superClass="cdt.managedbuild.toolchain.gnu.base">
+ <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.2000294945" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
+ <builder enableAutoBuild="true" id="cdt.managedbuild.target.gnu.builder.base.810871418" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.archiver.base.1493352238" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.124900079" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
+ <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.380391325" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+ </tool>
+ <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.16322708" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
+ <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1198899992" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+ </tool>
+ <tool id="cdt.managedbuild.tool.gnu.c.linker.base.1957613558" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.2087338148" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base">
+ <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1157006781" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
+ <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+ <additionalInput kind="additionalinput" paths="$(LIBS)"/>
+ </inputType>
+ </tool>
+ <tool id="cdt.managedbuild.tool.gnu.assembler.base.1416114639" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
+ <inputType id="cdt.managedbuild.tool.gnu.assembler.input.462417503" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+ </tool>
+ </toolChain>
+ </folderInfo>
+ </configuration>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
@@ -346,24 +374,173 @@
</scannerInfoProvider>
</profile>
</scannerConfigBuildInfo>
+ <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.186494460;cdt.managedbuild.toolchain.gnu.base.186494460.1739805831;cdt.managedbuild.tool.gnu.c.compiler.base.16322708;cdt.managedbuild.tool.gnu.c.compiler.input.1198899992">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
+ <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="makefileGenerator">
+ <runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ </scannerConfigBuildInfo>
+ <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.186494460;cdt.managedbuild.toolchain.gnu.base.186494460.1739805831;cdt.managedbuild.tool.gnu.cpp.compiler.base.124900079;cdt.managedbuild.tool.gnu.cpp.compiler.input.380391325">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/>
+ <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="makefileGenerator">
+ <runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+ <buildOutputProvider>
+ <openAction enabled="true" filePath=""/>
+ <parser enabled="true"/>
+ </buildOutputProvider>
+ <scannerInfoProvider id="specsFile">
+ <runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
+ <parser enabled="true"/>
+ </scannerInfoProvider>
+ </profile>
+ </scannerConfigBuildInfo>
</storageModule>
- <storageModule moduleId="cdtBuildSystem" version="4.0.0">
- <configuration buildProperties="" id="cdt.managedbuild.toolchain.gnu.base.186494460" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
- <folderInfo id="cdt.managedbuild.toolchain.gnu.base.186494460.1739805831" name="/" resourcePath="">
- <toolChain id="cdt.managedbuild.toolchain.gnu.base.1289815074" name="cdt.managedbuild.toolchain.gnu.base" superClass="cdt.managedbuild.toolchain.gnu.base">
- <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.2000294945" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
- <builder id="cdt.managedbuild.target.gnu.builder.base.810871418" managedBuildOn="false" name="Gnu Make Builder.Default" superClass="cdt.managedbuild.target.gnu.builder.base"/>
- <tool id="cdt.managedbuild.tool.gnu.archiver.base.1493352238" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
- <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.124900079" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"/>
- <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.16322708" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base"/>
- <tool id="cdt.managedbuild.tool.gnu.c.linker.base.1957613558" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
- <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.2087338148" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"/>
- <tool id="cdt.managedbuild.tool.gnu.assembler.base.1416114639" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base"/>
- </toolChain>
- </folderInfo>
- </configuration>
- </storageModule>
- <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
diff --git a/.project b/.project
index 3402632..ab8b1db 100644
--- a/.project
+++ b/.project
@@ -7,7 +7,6 @@
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
- <triggers>clean,full,incremental,</triggers>
<arguments>
<dictionary>
<key>?name?</key>
@@ -39,7 +38,7 @@
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
- <value>false</value>
+ <value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
diff --git a/libs/cclient.h b/libs/cclient.h
index 5b23ce7..5106b06 100644
--- a/libs/cclient.h
+++ b/libs/cclient.h
@@ -20,6 +20,8 @@
#ifndef CCLIENT_H_
#define CCLIENT_H_
+#include "cmsg.h"
+
namespace synapse
{
using namespace std;
diff --git a/libs/csharedobject.h b/libs/csharedobject.h
index 4a89aa0..e34a5ba 100644
--- a/libs/csharedobject.h
+++ b/libs/csharedobject.h
@@ -21,6 +21,7 @@
#define CSHAREDOBJECT_H_
#include "exception/cexception.h"
+#include <set>
namespace synapse
{
diff --git a/libs/utils.cpp b/libs/utils.cpp
new file mode 100644
index 0000000..4e3bc78
--- /dev/null
+++ b/libs/utils.cpp
@@ -0,0 +1,80 @@
+#include "utils.h"
+#include "boost/filesystem/fstream.hpp"
+#include "boost/regex.hpp"
+
+
+namespace utils
+{
+ using namespace boost::posix_time;
+ using boost::filesystem::ofstream;
+ using boost::filesystem::ifstream;
+
+ //replace a bunch of regular expressions in a file.
+ //the regex Cvar is a hasharray:
+ // regex => replacement
+ void regexReplaceFile(const string & inFilename, const string & outFilename, synapse::CvarMap & regex)
+ {
+ //read input
+ stringbuf inBuf;
+ ifstream inStream;
+ inStream.exceptions ( ifstream::failbit| ifstream::badbit );
+ inStream.open(inFilename);
+ inStream.get(inBuf,'\0');
+ inStream.close();
+
+ //build regex and formatter
+ string regexStr;
+ stringstream formatStr;
+ int count=1;
+ for(synapse::CvarMap::iterator I=regex.begin(); I!=regex.end(); I++)
+ {
+ if (count>1)
+ regexStr+="|";
+
+ regexStr+="("+ I->first +")";
+ formatStr << "(?{" << count << "}" << I->second.str() << ")";
+ count++;
+ }
+
+ //apply regexs
+ string outBuf;
+ outBuf=regex_replace(
+ inBuf.str(),
+ boost::regex(regexStr),
+ formatStr.str(),
+ boost::match_default | boost::format_all
+ );
+
+ //write output
+ ofstream outStream;
+ outStream.exceptions ( ofstream::eofbit | ofstream::failbit | ofstream::badbit );
+ outStream.open(outFilename);
+ outStream << outBuf;
+ outStream.close();
+ }
+
+ //return a random readable string, for use as key or uniq id
+ struct drand48_data gRandomBuffer;
+ bool gRandomBufferReady=false;
+
+ string randomStr(int length)
+ {
+ if (!gRandomBufferReady)
+ {
+ gRandomBufferReady=true;
+ srand48_r(time(NULL), &gRandomBuffer);
+ }
+
+ long int r;
+ string chars("abcdefghijklmnopqrstuvwxyz0123456789");
+ string s;
+ for (int i=0; i<length; i++)
+ {
+ mrand48_r(&gRandomBuffer, &r);
+ s=s+chars[abs(r) % chars.length()];
+ }
+ return(s);
+ }
+
+
+}
diff --git a/libs/utils.h b/libs/utils.h
new file mode 100644
index 0000000..5b31605
--- /dev/null
+++ b/libs/utils.h
@@ -0,0 +1,29 @@
+/*
+ * utils.h
+ *
+ * Created on: Jan 18, 2012
+ * Author: psy
+ */
+
+#ifndef UTILS_H_
+#define UTILS_H_
+
+#include <string>
+#include "cvar.h"
+
+namespace utils
+{
+ using namespace std;
+ using namespace synapse;
+
+ //replace a bunch of regular expressions in a file.
+ //the regex Cvar is a hasharray:
+ // regex => replacement
+ void regexReplaceFile(const string & inFilename, const string & outFilename, synapse::CvarMap & regex);
+
+ //return a random readable string, for use as key or uniq id
+ string randomStr(int length);
+}
+
+
+#endif /* UTILS_H_ */
diff --git a/main.cpp b/main.cpp
index 4967ca9..95a5780 100644
--- a/main.cpp
+++ b/main.cpp
@@ -25,6 +25,7 @@
int main(int argc, char *argv[])
{
+
//since CVAR is so crucial and complex, we selftest it first:
if (!synapse::Cvar::selfTest())
{
@@ -33,6 +34,7 @@ int main(int argc, char *argv[])
}
{
+
synapse::CmessageMan messageMan;
if (argc==2)
{
diff --git a/modules/paper.module/CMakeLists.txt b/modules/paper.module/CMakeLists.txt
index 5e81524..64d0339 100644
--- a/modules/paper.module/CMakeLists.txt
+++ b/modules/paper.module/CMakeLists.txt
@@ -12,7 +12,7 @@ MESSAGE(STATUS "Module name of ${CMAKE_CURRENT_BINARY_DIR} is: '${module_name}'"
#automaticly add all sourcefiles to current module
file(GLOB sources *.cpp)
-ADD_LIBRARY(${module_name} MODULE ${sources})
+ADD_LIBRARY(${module_name} MODULE ${sources} ../../libs/utils.cpp)
#need boost librarys? add them here:
INCLUDE(FindBoost)
diff --git a/modules/paper.module/cpaperclient.cpp b/modules/paper.module/cpaperclient.cpp
new file mode 100644
index 0000000..18470ac
--- /dev/null
+++ b/modules/paper.module/cpaperclient.cpp
@@ -0,0 +1,45 @@
+#include "cpaperclient.h"
+
+namespace paper
+{
+
+ CpaperClient::CpaperClient()
+ {
+ mLastElementId=0;
+ mAuthView=false;
+ mAuthChange=false;
+ mAuthOwner=false;
+ mAuthCursor=false;
+ mAuthChat=false;
+
+
+ }
+
+
+ //sends a message to the client, only if the client has view-rights
+ //otherwise the message is ignored.
+ void CpaperClient::sendFiltered(Cmsg & msg)
+ {
+ if (mAuthView)
+ msg.send();
+
+ }
+
+ //authorizes the client withclient with key and rights
+ void CpaperClient::authorize(Cvar & rights)
+ {
+ mAuthView=rights["view"];
+ mAuthChange=rights["change"];
+ mAuthOwner=rights["owner"];
+ mAuthCursor=rights["cursor"];
+ mAuthChat=rights["chat"];
+
+ //inform the client of its new rights
+ Cmsg out;
+ out.event="paper_Authorized";
+ out.dst=id;
+ out.map()=rights;
+ out.send();
+
+ }
+}
diff --git a/modules/paper.module/cpaperclient.h b/modules/paper.module/cpaperclient.h
new file mode 100644
index 0000000..ff0d840
--- /dev/null
+++ b/modules/paper.module/cpaperclient.h
@@ -0,0 +1,44 @@
+#ifndef CPAPERCLIENT
+#define CPAPERCLIENT
+
+
+#include "cclient.h"
+#include "cvar.h"
+#include "cmsg.h"
+
+namespace paper
+{
+ using namespace std;
+
+ //a client of a paper object
+ class CpaperClient : public synapse::Cclient
+ {
+ friend class CpaperObject;
+ private:
+
+ public:
+ Cvar mCursor;
+ int mLastElementId;
+
+
+ //authorized functions
+ bool mAuthCursor;
+ bool mAuthChat;
+ bool mAuthView;
+ bool mAuthChange;
+ bool mAuthOwner;
+
+
+
+ CpaperClient();
+
+
+ //sends a message to the client, only if the client has view-rights
+ //otherwise the message is ignored.
+ void sendFiltered(Cmsg & msg);
+
+ //authorizes the client withclient with key and rights
+ void authorize(Cvar & rights);
+ };
+}
+#endif
diff --git a/modules/paper.module/cpaperobject.cpp b/modules/paper.module/cpaperobject.cpp
new file mode 100644
index 0000000..41d672e
--- /dev/null
+++ b/modules/paper.module/cpaperobject.cpp
@@ -0,0 +1,606 @@
+#include "cpaperobject.h"
+#include "utils.h"
+//#include "boost/filesystem/operations.hpp"
+#include "boost/filesystem/fstream.hpp"
+//#include "boost/filesystem.hpp"
+
+
+namespace paper
+{
+ using namespace boost;
+// using namespace boost::posix_time;
+ using boost::filesystem::ofstream;
+// using boost::filesystem::ifstream;
+
+ //called when the drawing-data of the drawing is modified. (e.g. new export is needed)
+
+ void CpaperObject::changed()
+ {
+ mDrawing["changeTime"]=time(NULL);
+ mDrawing["exported"]=false;
+ mDrawing["version"]=mDrawing["version"]+1;
+ mDrawing.changed();
+ }
+
+
+ CpaperObject::CpaperObject()
+ {
+ mExporting=false;
+ }
+
+
+ //send message to all clients that are joined, using filtering.
+ void CpaperObject::sendAllFiltered(Cmsg & msg)
+ {
+ CclientMap::iterator I;
+ for (I=clientMap.begin(); I!=clientMap.end(); I++)
+ {
+ msg.dst=I->first;
+ try
+ {
+ I->second.sendFiltered(msg);
+ }
+ catch(...)
+ {
+ ; //expected raceconditions do occur during session ending, so ignore send errors
+ }
+ }
+ }
+
+ //get filenames, relative to wwwdir, or relative to synapse main dir.
+ //these probably are going to give different results when papers are made private.
+ string CpaperObject::getSvgFilename(bool www)
+ {
+ stringstream filename;
+ if (!www)
+ filename << "wwwdir";
+ filename << mDrawing["path"].str() << "paper.svg";
+ //cache breaker
+ if (www)
+ filename << "?" << mDrawing["version"];
+
+ return (filename.str());
+ }
+
+ string CpaperObject::getPngFilename(bool www)
+ {
+ stringstream filename;
+ if (!www)
+ filename << "wwwdir";
+ filename << mDrawing["path"].str() << "paper.png";
+ //cache breaker
+ if (www)
+ filename << "?" << mDrawing["version"];
+
+ return (filename.str());
+ }
+
+ string CpaperObject::getThumbFilename(bool www)
+ {
+ stringstream filename;
+ if (!www)
+ filename << "wwwdir";
+ filename << mDrawing["path"].str() << "thumb.png";
+ //cache breaker
+ if (www)
+ filename << "?" << mDrawing["version"];
+
+ return (filename.str());
+ }
+
+ string CpaperObject::getHtmlFilename(bool www)
+ {
+ stringstream filename;
+ if (!www)
+ filename << "wwwdir";
+ filename << mDrawing["path"].str() << "edit.html";
+
+ return (filename.str());
+ }
+
+ void CpaperObject::createHtml()
+ {
+ //Since we need to add all kinds of metadata to the paper-html file, we need to parse the html file and fill in some marcros
+ synapse::CvarMap regex;
+ regex["%id%"]=id;
+ regex["%png%"]=getPngFilename(true);
+ regex["%thumb%"]=getThumbFilename(true);
+ regex["%svg%"]=getSvgFilename(true);
+
+ utils::regexReplaceFile("wwwdir/paper/edit.html", getHtmlFilename(), regex);
+
+ }
+
+ //the paper is created for the first time.
+ void CpaperObject::create()
+ {
+ //1000r will be the root svg element with its settings
+ //NOTE: svgweb doesnt support a numeric svg-root, hence the added r
+ //NOTE: This is a STL ordered MAP, we need to keep the correct order, so hence the 1000.
+ mDrawing["data"]["1000r"]["element"]="svg";
+ mDrawing["data"]["1000r"]["version"]="1.2";
+ mDrawing["data"]["1000r"]["baseProfile"]="tiny";
+ mDrawing["data"]["1000r"]["viewBox"]="0 0 17777 10000";
+
+ mDrawing["data"]["1000r"]["xmlns"]="http://www.w3.org/2000/svg";
+ mDrawing["data"]["1000r"]["xmlns:xlink"]="http://www.w3.org/1999/xlink";
+ //we dont use this YET:
+ //drawing["data"]["1000r"]["xmlns:ev"]="http://www.w3.org/2001/xml-events";
+
+ mDrawing["data"]["1000r"]["stroke-linecap"]="round";
+ mDrawing["data"]["1000r"]["stroke-linejoin"]="round";
+
+ //drawing.setAttribute("preserveAspectRatio", "none");
+ // drawing.setAttribute("pointer-events","all");
+ // drawing.setAttribute("color-rendering","optimizeSpeed");
+ // drawing.setAttribute("shape-rendering","optimizeSpeed");
+ // drawing.setAttribute("text-rendering","optimizeSpeed");
+ // drawing.setAttribute("image-rendering","optimizeSpeed");
+
+ //we start at 1001 so the order stays correct in the stl map. once we get to 10000 the order gets screwed up, but that probably never happens ;)
+ mDrawing["lastElementId"]=1000;
+
+ //create appropriate files
+ mDrawing["path"]="/p/"+utils::randomStr(8)+"/";
+ filesystem::create_directory("wwwdir" + mDrawing["path"].str());
+ createHtml();
+
+
+ }
+
+
+ //called by the object manager to get interesting metadata about this object
+ void CpaperObject::getInfo(Cvar & var)
+ {
+ synapse::CsharedObject<CpaperClient>::getInfo(var);
+ var["changeTime"]=mDrawing["changeTime"];
+ var["clients"]=clientMap.size();
+ var["htmlPath"]=getHtmlFilename(true);
+ var["thumbPath"]=getThumbFilename(true);
+ var["version"]=mDrawing["version"];
+ }
+
+
+ //called when exporting is done
+ void CpaperObject::exported()
+ {
+ mExporting=false;
+ if (!mDrawing["exported"])
+ {
+ mDrawing["exported"]=true;
+ mDrawing["hasThumb"]=true;
+ mDrawing.changed();
+
+
+ }
+
+ //inform clients the export is ready
+ Cmsg out;
+ out.event="paper_Exported";
+ out["svgPath"]=getSvgFilename(true);
+ out["pngPath"]=getPngFilename(true);
+ out["thumbPath"]=getThumbFilename(true);
+ sendAllFiltered(out);
+
+ }
+
+ //export the drawing to svg and png files.
+ //sends various a paper_Exported event when done.
+ void CpaperObject::saveExport(bool send)
+ {
+ //already exporting, dont start it again
+ if (mExporting)
+ return;
+
+ //its already exported, dont do anything but send out the event.
+ if (mDrawing["exported"])
+ {
+ if (send)
+ exported();
+ }
+ else
+ //export the drawing
+ {
+ mExporting=true;
+
+ //export to svg
+ ofstream svgStream;
+ svgStream.exceptions ( ofstream::eofbit | ofstream::failbit | ofstream::badbit );
+ svgStream.open(getSvgFilename());
+
+ //svg header
+ svgStream << "<?xml version=\"1.0\" standalone=\"no\"?>\n";
+ svgStream << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n\n";
+
+ //export all elements
+ for(Cvar::iterator elementI=mDrawing["data"].begin(); elementI!=mDrawing["data"].end(); elementI++)
+ {
+ //start element
+ svgStream << "<" << elementI->second["element"].str();
+
+ //add id
+ svgStream << " id=\"" << elementI->first << "\"";
+
+ //fill in all element attributes
+ for(Cvar::iterator I=elementI->second.begin(); I!=elementI->second.end(); I++)
+ {
+ if (I->first!="element" && I->first!="text")
+ svgStream << " " << I->first << "=\"" << I->second.str() << "\"";
+ }
+
+ //text content?
+ if (elementI->second.isSet("text"))
+ {
+ //add text content and closing tag
+ svgStream << ">" << elementI->second["text"].str() << "</" << elementI->second["element"].str() << ">\n";
+ }
+ //root svg element?
+ else if (elementI->second["element"].str()=="svg")
+ {
+ //close, but dont end element
+ svgStream << ">\n";
+ }
+ else
+ {
+ //end element
+ svgStream << "/>\n";
+ }
+ }
+
+ svgStream << "<title>internetpapier.nl tekening #" << id << "</title>\n";
+
+ //close svg element
+ svgStream << "\n</svg>\n";
+
+ svgStream.close();
+
+
+ //now let imagemagic convert it to some nice pngs :)
+ Cmsg out;
+ out.src=0;
+ out.dst=0;
+ out.event="exec_Start";
+ out["id"]["paperId"]=id;
+ // out["id"]["path"]=getPngFilename(true);
+ // out["id"]["type"]="png";
+ out["queue"]=1;
+ out["cmd"]="nice convert -density 4 MSVG:" + getSvgFilename() + " " + getPngFilename() + " "+
+ "&& nice convert -scale 150 " + getPngFilename() + " " + getThumbFilename();
+ out.send();
+ }
+ }
+
+ void CpaperObject::execEnded(Cvar & var)
+ {
+ //for now executing is only for exporting (to be changed)
+ exported();
+ }
+
+
+ void CpaperObject::execError(Cvar & var)
+ {
+ execEnded(var);
+ }
+
+ void CpaperObject::save(string path)
+ {
+ mDrawing.save(path);
+ saveExport(false);
+ }
+
+ void CpaperObject::load(string path)
+ {
+ mDrawing.load(path);
+ }
+
+ //authenticates clientId with key
+ void CpaperObject::authenticate(int clientId, string key)
+ {
+ //key doesnt exists?
+ if (!mDrawing["auth"].isSet(key))
+ {
+ Cmsg out;
+ out.event="paper_AuthWrongKey";
+ out.dst=clientId;
+ out.send();
+ return;
+ }
+
+ //if the key exists, authorize the client
+ getClient(clientId).authorize(mDrawing["auth"][key]);
+ }
+
+ //changes authentication keys and authorization
+ //rights is just a hasharray
+ //Specifying clientId checks if the client is allowed to give these rights
+ //specifying an empty rights-array will delete the key
+ void CpaperObject::changeAuth(int clientId, string key, Cvar & rights)
+ {
+ if (!getClient(clientId).mAuthOwner)
+ throw(synapse::runtime_error("Only the owner can change the security settings of this drawing."));
+
+ if (rights.map().size()!=0)
+ {
+ mDrawing["auth"][key]["view"]=rights["view"];
+ mDrawing["auth"][key]["change"]=rights["change"];
+ mDrawing["auth"][key]["owner"]=rights["owner"];
+ mDrawing["auth"][key]["cursor"]=rights["cursor"];
+ mDrawing["auth"][key]["chat"]=rights["chat"];
+ if (rights.isSet("description"))
+ mDrawing["auth"][key]["description"]=rights["description"];
+ }
+ else
+ {
+ mDrawing["auth"].map().erase(key);
+ }
+
+ mDrawing.changed();
+
+ }
+
+ //get all authorisation and authentication info
+ void CpaperObject::getAuth(int clientId)
+ {
+ if (!getClient(clientId).mAuthOwner)
+ throw(synapse::runtime_error("Only the owner can get the security settings of this drawing."));
+
+
+ throw(synapse::runtime_error("STUB"));
+ }
+
+ //send the drawing commands to all clients, except clientId
+ //(use clientId 0 if your want the message send to all clients)
+ void CpaperObject::serverDraw(Cmsg out, int clientId )
+ {
+ out.event="paper_ServerDraw";
+ out.src=0;
+
+ //send to all connected clients, but not back to the original clientId
+ for (CclientMap::iterator I=clientMap.begin(); I!=clientMap.end(); I++)
+ {
+ if (I->first!=clientId)
+ {
+ out.dst=I->first;
+ try
+ {
+ I->second.sendFiltered(out);
+ }
+ catch(...)
+ {
+ //ignore send errors (expectable race conditions do occur with session ending)
+ ;
+ }
+ }
+ }
+ }
+
+
+ //get an iterator to specified element id.
+ //throws error if not found.
+ Cvar::iterator CpaperObject::getElement(const string & id)
+ {
+ Cvar::iterator elementI;
+ elementI=mDrawing["data"].map().find(id);
+ if (elementI==mDrawing["data"].map().end())
+ throw(synapse::runtime_error("Specified element id not found."));
+ return (elementI);
+ }
+
+
+ //transform the specified elementid into a message-data that can be send to clients
+ //sets:
+ //["element"]=elementtype
+ //["set"][attributename]=attributevalue
+ //["id"]=id
+ //["beforeId"]=id of element this element comes before
+ void CpaperObject::element2msg(const string & id, Cmsg & msg)
+ {
+ //get an iterator to requested id
+ Cvar::iterator elementI=getElement(id);
+
+ //fill in element type
+ msg["element"]=elementI->second["element"];
+ msg["id"]=id;
+
+ //fill in all element attributes
+ for(Cvar::iterator I=elementI->second.begin(); I!=elementI->second.end(); I++)
+ {
+ if (I->first!="element")
+ msg["set"][I->first]=I->second;
+ }
+
+ //is there an item after this?
+ elementI++;
+ if (elementI!=mDrawing["data"].end())
+ {
+ //yes, so fill in beforeId, so the objects stay in the correct order
+ msg["beforeId"]=elementI->first;
+ }
+ }
+
+ //resend all data to dst
+ void CpaperObject::reload(int dst)
+ {
+ if (!getClient(dst).mAuthView)
+ throw(synapse::runtime_error("You're not authorized to view this drawing."));
+
+ Cmsg out;
+ out.event="paper_ServerDraw";
+ out.dst=dst;
+
+ //indicate start of complete reload
+ out["cmd"]="reload";
+ out.send();
+
+ //send all elements
+ for(Cvar::iterator elementI=mDrawing["data"].begin(); elementI!=mDrawing["data"].end(); elementI++)
+ {
+ out.clear();
+ out["cmd"]="update";
+ element2msg(elementI->first, out);
+ out.send();
+ }
+
+ //send all cursors
+ for(CclientMap::iterator I=clientMap.begin(); I!=clientMap.end(); I++)
+ {
+ out.clear();
+ out["cursor"].map()=I->second.mCursor;
+ out["src"]=I->first;
+ out.send();
+ }
+
+ //send chat log
+ for(CvarList::iterator I=mDrawing["chat"].list().begin(); I!=mDrawing["chat"].list().end(); I++)
+ {
+ out.clear();
+ out["chat"]=*I;
+ out.send();
+ }
+
+
+ out.clear();
+ out["cmd"]="ready";
+ out.send();
+ }
+
+ //process drawing data received from a client store it, and relay it to other clients via serverDraw
+ //if a client is not authorized to do certain stuff, an exception is thrown
+ void CpaperObject::clientDraw(Cmsg & msg)
+ {
+ msg["src"]=msg.src;
+
+ //received cursor information?
+ if (msg.isSet("cursor"))
+ {
+ if (!getClient(msg.src).mAuthCursor)
+ throw(synapse::runtime_error("You're not allowed to send cursor updates"));
+
+ //store the new fields in the clients cursor object:
+ Cvar & cursor=getClient(msg.src).mCursor;
+ for(Cvar::iterator I=msg["cursor"].begin(); I!=msg["cursor"].end(); I++)
+ {
+ cursor[I->first]=I->second.str();
+ }
+ }
+
+ //received chat?
+ if (msg.isSet("chat"))
+ {
+ if (!getClient(msg.src).mAuthChat)
+ throw(synapse::runtime_error("You're not allowed to chat"));
+
+ //store in chat log for this object
+ mDrawing["chat"].list().push_back(msg["chat"]);
+ }
+
+ //received drawing commands?
+ if (msg["cmd"].str()=="update")
+ {
+
+ if (!getClient(msg.src).mAuthChange)
+ throw(synapse::runtime_error("You're not authorized to change this drawing."));
+
+ //id specified?
+ if (msg.isSet("id"))
+ {
+ //add new object?
+ if (msg["id"].str()=="new")
+ {
+ //figure out a fresh new id
+ mDrawing["lastElementId"]=mDrawing["lastElementId"]+1;
+
+ //store the new id in the message
+ msg["id"]=mDrawing["lastElementId"];
+
+ //create new element
+ mDrawing["data"][msg["id"]]["element"]=msg["element"].str();
+
+ }
+
+ //store the last id for this client
+ getClient(msg.src).mLastElementId=msg["id"];
+ }
+ //id not specified, automaticly add the last used id from this client
+ else
+ {
+ msg["id"]=getClient(msg.src).mLastElementId;
+ }
+
+
+ Cvar::iterator elementI=getElement(msg["id"]);
+
+ //add the data from the 'add' field to the drawing data
+ for(Cvar::iterator I=msg["add"].begin(); I!=msg["add"].end(); I++)
+ {
+ //this basically means: drawing["data"][key]+=value;
+ elementI->second[I->first].str()+=I->second.str();
+ }
+
+ //set the data from the 'set' field over the drawing data
+ for(Cvar::iterator I=msg["set"].begin(); I!=msg["set"].end(); I++)
+ {
+ elementI->second[I->first]=I->second.str();
+ }
+
+ changed();
+ }
+ //send refresh of requested element?
+ else if (msg["cmd"].str()=="refresh")
+ {
+ //id not set? the use the last one that was used by this client
+ if (!msg.isSet("id"))
+ {
+ if (getClient(msg.src).mLastElementId)
+ msg["id"]=getClient(msg.src).mLastElementId;
+ }
+
+ //reply with a serverdraw to this client only
+ Cmsg out;
+ out=msg;
+ out.event="paper_ServerDraw";
+ out.dst=msg.src;
+ out.src=0;
+ out["cmd"]="update";
+ if (msg.isSet("id"))
+ element2msg(msg["id"].str(), out);
+ getClient(msg.src).sendFiltered(out);
+
+ return;
+ }
+ //delete object?
+ else if (msg["cmd"].str()=="delete")
+ {
+ if (!getClient(msg.src).mAuthChange)
+ throw(synapse::runtime_error("You're not authorized to change this drawing."));
+
+ //delete it
+ Cvar::iterator elementI=getElement(msg["id"]);
+ mDrawing["data"].map().erase(elementI);
+ changed();
+
+ getClient(msg.src).mLastElementId=0;
+ }
+ //request a reload of all the data
+ else if (msg["cmd"].str()=="reload")
+ {
+ reload(msg.src);
+ return;
+ }
+
+ //relay the command to other clients
+ serverDraw(msg,msg.src);
+ }
+
+ void CpaperObject::addClient(int id)
+ {
+ //let the base class do its work:
+ synapse::CsharedObject<CpaperClient>::addClient(id);
+ }
+
+ bool CpaperObject::isIdle()
+ {
+ return (!mExporting && synapse::CsharedObject<CpaperClient>::isIdle());
+
+ }
+}
diff --git a/modules/paper.module/cpaperobject.h b/modules/paper.module/cpaperobject.h
new file mode 100644
index 0000000..c16ec2a
--- /dev/null
+++ b/modules/paper.module/cpaperobject.h
@@ -0,0 +1,105 @@
+#ifndef CPAPEROBJECT
+#define CPAPEROBJECT
+
+#include "cpaperclient.h"
+#include "csharedobject.h"
+#include "cconfig.h"
+
+#include <string>
+
+namespace paper
+{
+ using namespace std;
+
+ //a server side piece of paper
+ class CpaperObject : public synapse::CsharedObject<CpaperClient>
+ {
+ private:
+ bool mExporting;
+ synapse::Cconfig mDrawing;
+
+ //called when the drawing-data of the drawing is modified. (e.g. new export is needed)
+ void changed();
+
+
+ public:
+ CpaperObject();
+
+ //send message to all clients that are joined, using filtering.
+ void sendAllFiltered(Cmsg & msg);
+
+ //get filenames, relative to wwwdir, or relative to synapse main dir.
+ //these probably are going to give different results when papers are made private.
+ string getSvgFilename(bool www=false);
+ string getPngFilename(bool www=false);
+ string getThumbFilename(bool www=false);
+ string getHtmlFilename(bool www=false);
+
+ void createHtml();
+
+ //the paper is created for the first time.
+ void create();
+
+ //called by the object manager to get interesting metadata about this object
+ void getInfo(Cvar & var);
+
+
+ //called when exporting is done
+ void exported();
+
+ //export the drawing to svg and png files.
+ //sends various a paper_Exported event when done.
+ void saveExport(bool send=true);
+
+ void execEnded(Cvar & var);
+
+ void execError(Cvar & var);
+
+ void save(string path);
+
+ void load(string path);
+
+ //authenticates clientId with key
+ void authenticate(int clientId, string key);
+
+ //changes authentication keys and authorization
+ //rights is just a hasharray
+ //Specifying clientId checks if the client is allowed to give these rights
+ //specifying an empty rights-array will delete the key
+ void changeAuth(int clientId, string key, Cvar & rights);
+
+ //get all authorisation and authentication info
+ void getAuth(int clientId);
+
+ //send the drawing commands to all clients, except clientId
+ //(use clientId 0 if your want the message send to all clients)
+ void serverDraw(Cmsg out, int clientId=0 );;
+
+ //get an iterator to specified element id.
+ //throws error if not found.
+ Cvar::iterator getElement(const string & id);
+
+ //transform the specified elementid into a message-data that can be send to clients
+ //sets:
+ //["element"]=elementtype
+ //["set"][attributename]=attributevalue
+ //["id"]=id
+ //["beforeId"]=id of element this element comes before
+ void element2msg(const string & id, Cmsg & msg);
+
+ //resend all data to dst
+ void reload(int dst);
+
+ //process drawing data received from a client store it, and relay it to other clients via serverDraw
+ //if a client is not authorized to do certain stuff, an exception is thrown
+ void clientDraw(Cmsg & msg);
+
+ virtual void addClient(int id);
+
+ virtual bool isIdle();
+
+ };
+};
+
+#endif
+
diff --git a/modules/paper.module/module.cpp b/modules/paper.module/module.cpp
index 9380c5e..85cef1c 100644
--- a/modules/paper.module/module.cpp
+++ b/modules/paper.module/module.cpp
@@ -25,15 +25,10 @@ Internet paper.
#include <set>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "cconfig.h"
-#include "boost/filesystem/operations.hpp"
-#include "boost/filesystem/fstream.hpp"
-#include "boost/filesystem.hpp"
#include <boost/regex.hpp>
#include <stdlib.h>
-//we use the generic shared object management classes.
-#include "cclient.h"
-#include "csharedobject.h"
+#include "cpaperobject.h"
#include "cobjectman.h"
#include "exception/cexception.h"
@@ -42,13 +37,8 @@ namespace paper
{
using namespace std;
- using namespace boost;
- using namespace boost::posix_time;
- using boost::filesystem::ofstream;
- using boost::filesystem::ifstream;
- struct drand48_data gRandomBuffer;
bool gShutdown;
//most recent changed paper list
synapse::Cconfig gPaperIndex;
@@ -59,7 +49,6 @@ namespace paper
Cmsg out;
gShutdown=false;
- srand48_r(time(NULL), &gRandomBuffer);
gPaperIndex.load("var/paper/index");
//this module is single threaded
@@ -141,754 +130,12 @@ namespace paper
}
- //TODO: move this to a generic lib
- //replace a bunch of regular expressions in a file.
- //the regex Cvar is a hasharray:
- // regex => replacement
- void regexReplaceFile(const string & inFilename, const string & outFilename, synapse::CvarMap & regex)
- {
- //read input
- stringbuf inBuf;
- ifstream inStream;
- inStream.exceptions ( ifstream::failbit| ifstream::badbit );
- inStream.open(inFilename);
- inStream.get(inBuf,'\0');
- inStream.close();
-
- //build regex and formatter
- string regexStr;
- stringstream formatStr;
- int count=1;
- for(synapse::CvarMap::iterator I=regex.begin(); I!=regex.end(); I++)
- {
- if (count>1)
- regexStr+="|";
-
- regexStr+="("+ I->first +")";
- formatStr << "(?{" << count << "}" << I->second.str() << ")";
- count++;
- }
-
- //apply regexs
- string outBuf;
- outBuf=regex_replace(
- inBuf.str(),
- boost::regex(regexStr),
- formatStr.str(),
- boost::match_default | boost::format_all
- );
-
- //write output
- ofstream outStream;
- outStream.exceptions ( ofstream::eofbit | ofstream::failbit | ofstream::badbit );
- outStream.open(outFilename);
- outStream << outBuf;
- outStream.close();
- }
-
- //return a random readable string, for use as key or uniq id
- string randomStr(int length)
- {
- long int r;
- string chars("abcdefghijklmnopqrstuvwxyz0123456789");
- string s;
- for (int i=0; i<length; i++)
- {
- mrand48_r(&gRandomBuffer, &r);
- s=s+chars[abs(r) % chars.length()];
- }
- return(s);
- }
-
-
-
-
- //a client of a paper object
- class CpaperClient : public synapse::Cclient
- {
- friend class CpaperObject;
- private:
-
- public:
- Cvar mCursor;
- int mLastElementId;
-
-
- //authorized functions
- bool mAuthCursor;
- bool mAuthChat;
- bool mAuthView;
- bool mAuthChange;
- bool mAuthOwner;
-
-
-
- CpaperClient()
- {
- mLastElementId=0;
- mAuthView=false;
- mAuthChange=false;
- mAuthOwner=false;
- mAuthCursor=false;
- mAuthChat=false;
-
-
- }
-
-
- //sends a message to the client, only if the client has view-rights
- //otherwise the message is ignored.
- void sendFiltered(Cmsg & msg)
- {
- if (mAuthView)
- msg.send();
-
- }
-
- //authorizes the client withclient with key and rights
- void authorize(Cvar & rights)
- {
- mAuthView=rights["view"];
- mAuthChange=rights["change"];
- mAuthOwner=rights["owner"];
- mAuthCursor=rights["cursor"];
- mAuthChat=rights["chat"];
-
- //inform the client of its new rights
- Cmsg out;
- out.event="paper_Authorized";
- out.dst=id;
- out.map()=rights;
- out.send();
-
- }
- };
-
-
- //a server side piece of paper
- class CpaperObject : public synapse::CsharedObject<CpaperClient>
- {
- private:
- bool mExporting;
- synapse::Cconfig mDrawing;
-
- //called when the drawing-data of the drawing is modified. (e.g. new export is needed)
-
- void changed()
- {
- mDrawing["changeTime"]=time(NULL);
- mDrawing["exported"]=false;
- mDrawing["version"]=mDrawing["version"]+1;
- mDrawing.changed();
- }
-
-
- public:
- CpaperObject()
- {
- mExporting=false;
- }
-
-
- //send message to all clients that are joined, using filtering.
- void sendAllFiltered(Cmsg & msg)
- {
- CclientMap::iterator I;
- for (I=clientMap.begin(); I!=clientMap.end(); I++)
- {
- msg.dst=I->first;
- try
- {
- I->second.sendFiltered(msg);
- }
- catch(...)
- {
- ; //expected raceconditions do occur during session ending, so ignore send errors
- }
- }
- }
- //get filenames, relative to wwwdir, or relative to synapse main dir.
- //these probably are going to give different results when papers are made private.
- string getSvgFilename(bool www=false)
- {
- stringstream filename;
- if (!www)
- filename << "wwwdir";
- filename << mDrawing["path"].str() << "paper.svg";
- //cache breaker
- if (www)
- filename << "?" << mDrawing["version"];
-
- return (filename.str());
- }
- string getPngFilename(bool www=false)
- {
- stringstream filename;
- if (!www)
- filename << "wwwdir";
- filename << mDrawing["path"].str() << "paper.png";
- //cache breaker
- if (www)
- filename << "?" << mDrawing["version"];
-
- return (filename.str());
- }
- string getThumbFilename(bool www=false)
- {
- stringstream filename;
- if (!www)
- filename << "wwwdir";
- filename << mDrawing["path"].str() << "thumb.png";
- //cache breaker
- if (www)
- filename << "?" << mDrawing["version"];
-
- return (filename.str());
- }
-
- string getHtmlFilename(bool www=false)
- {
- stringstream filename;
- if (!www)
- filename << "wwwdir";
- filename << mDrawing["path"].str() << "edit.html";
- return (filename.str());
- }
- void createHtml()
- {
- //Since we need to add all kinds of metadata to the paper-html file, we need to parse the html file and fill in some marcros
- synapse::CvarMap regex;
- regex["%id%"]=id;
- regex["%png%"]=getPngFilename(true);
- regex["%thumb%"]=getThumbFilename(true);
- regex["%svg%"]=getSvgFilename(true);
- regexReplaceFile("wwwdir/paper/edit.html", getHtmlFilename(), regex);
-
- }
-
- //the paper is created for the first time.
- void create()
- {
- //1000r will be the root svg element with its settings
- //NOTE: svgweb doesnt support a numeric svg-root, hence the added r
- //NOTE: This is a STL ordered MAP, we need to keep the correct order, so hence the 1000.
- mDrawing["data"]["1000r"]["element"]="svg";
- mDrawing["data"]["1000r"]["version"]="1.2";
- mDrawing["data"]["1000r"]["baseProfile"]="tiny";
- mDrawing["data"]["1000r"]["viewBox"]="0 0 17777 10000";
-
- mDrawing["data"]["1000r"]["xmlns"]="http://www.w3.org/2000/svg";
- mDrawing["data"]["1000r"]["xmlns:xlink"]="http://www.w3.org/1999/xlink";
- //we dont use this YET:
- //drawing["data"]["1000r"]["xmlns:ev"]="http://www.w3.org/2001/xml-events";
-
- mDrawing["data"]["1000r"]["stroke-linecap"]="round";
- mDrawing["data"]["1000r"]["stroke-linejoin"]="round";
-
- //drawing.setAttribute("preserveAspectRatio", "none");
-// drawing.setAttribute("pointer-events","all");
-// drawing.setAttribute("color-rendering","optimizeSpeed");
-// drawing.setAttribute("shape-rendering","optimizeSpeed");
-// drawing.setAttribute("text-rendering","optimizeSpeed");
-// drawing.setAttribute("image-rendering","optimizeSpeed");
-
- //we start at 1001 so the order stays correct in the stl map. once we get to 10000 the order gets screwed up, but that probably never happens ;)
- mDrawing["lastElementId"]=1000;
-
- //create appropriate files
- mDrawing["path"]="/p/"+randomStr(8)+"/";
- filesystem::create_directory("wwwdir" + mDrawing["path"].str());
- createHtml();
-
-
- }
-
-
- //called by the object manager to get interesting metadata about this object
- void getInfo(Cvar & var)
- {
- synapse::CsharedObject<CpaperClient>::getInfo(var);
- var["changeTime"]=mDrawing["changeTime"];
- var["clients"]=clientMap.size();
- var["htmlPath"]=getHtmlFilename(true);
- var["thumbPath"]=getThumbFilename(true);
- }
-
-
- //called when exporting is done
- void exported()
- {
- mExporting=false;
- if (!mDrawing["exported"])
- {
- mDrawing["exported"]=true;
- mDrawing["hasThumb"]=true;
- mDrawing.changed();
-
- //update the recent-changed list?
- if (mDrawing["version"]>0)
- {
- //delete ourself from the list first
- for (synapse::CvarList::iterator paperI=gPaperIndex["list"].list().begin(); paperI!=gPaperIndex["list"].list().end(); paperI++)
- {
- if ((*paperI)["objectId"]==id)
- {
- gPaperIndex["list"].list().erase(paperI);
- break;
- }
- }
-
- //now re-add the updated version of ourself in the front position
- Cvar paperInfo;
- getInfo(paperInfo);
- gPaperIndex["list"].list().push_front(paperInfo);
-
- //never more then this amount in the index
- if (gPaperIndex["list"].list().size()>100)
- gPaperIndex["list"].list().pop_back();
-
- gPaperIndex.changed();
- }
-
- }
-
- //inform clients the export is ready
- Cmsg out;
- out.event="paper_Exported";
- out["svgPath"]=getSvgFilename(true);
- out["pngPath"]=getPngFilename(true);
- out["thumbPath"]=getThumbFilename(true);
- sendAllFiltered(out);
-
- }
-
- //export the drawing to svg and png files.
- //sends various a paper_Exported event when done.
- void saveExport(bool send=true)
- {
- //already exporting, dont start it again
- if (mExporting)
- return;
-
- //its already exported, dont do anything but send out the event.
- if (mDrawing["exported"])
- {
- if (send)
- exported();
- }
- else
- //export the drawing
- {
- mExporting=true;
-
- //export to svg
- ofstream svgStream;
- svgStream.exceptions ( ofstream::eofbit | ofstream::failbit | ofstream::badbit );
- svgStream.open(getSvgFilename());
-
- //svg header
- svgStream << "<?xml version=\"1.0\" standalone=\"no\"?>\n";
- svgStream << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n\n";
-
- //export all elements
- for(Cvar::iterator elementI=mDrawing["data"].begin(); elementI!=mDrawing["data"].end(); elementI++)
- {
- //start element
- svgStream << "<" << elementI->second["element"].str();
-
- //add id
- svgStream << " id=\"" << elementI->first << "\"";
-
- //fill in all element attributes
- for(Cvar::iterator I=elementI->second.begin(); I!=elementI->second.end(); I++)
- {
- if (I->first!="element" && I->first!="text")
- svgStream << " " << I->first << "=\"" << I->second.str() << "\"";
- }
-
- //text content?
- if (elementI->second.isSet("text"))
- {
- //add text content and closing tag
- svgStream << ">" << elementI->second["text"].str() << "</" << elementI->second["element"].str() << ">\n";
- }
- //root svg element?
- else if (elementI->second["element"].str()=="svg")
- {
- //close, but dont end element
- svgStream << ">\n";
- }
- else
- {
- //end element
- svgStream << "/>\n";
- }
- }
-
- svgStream << "<title>internetpapier.nl tekening #" << id << "</title>\n";
-
- //close svg element
- svgStream << "\n</svg>\n";
-
- svgStream.close();
-
-
- //now let imagemagic convert it to some nice pngs :)
- Cmsg out;
- out.src=0;
- out.dst=0;
- out.event="exec_Start";
- out["id"]["paperId"]=id;
-// out["id"]["path"]=getPngFilename(true);
-// out["id"]["type"]="png";
- out["queue"]=1;
- out["cmd"]="nice convert -density 4 MSVG:" + getSvgFilename() + " " + getPngFilename() + " "+
- "&& nice convert -scale 150 " + getPngFilename() + " " + getThumbFilename();
- out.send();
- }
- }
-
- void execEnded(Cvar & var)
- {
- //for now executing is only for exporting (to be changed)
- exported();
- }
-
-
- void execError(Cvar & var)
- {
- execEnded(var);
- }
-
- void save(string path)
- {
- mDrawing.save(path);
- saveExport(false);
- }
-
- void load(string path)
- {
- mDrawing.load(path);
- }
-
- //authenticates clientId with key
- void authenticate(int clientId, string key)
- {
- //key doesnt exists?
- if (!mDrawing["auth"].isSet(key))
- {
- Cmsg out;
- out.event="paper_AuthWrongKey";
- out.dst=clientId;
- out.send();
- return;
- }
-
- //if the key exists, authorize the client
- getClient(clientId).authorize(mDrawing["auth"][key]);
- }
-
- //changes authentication keys and authorization
- //rights is just a hasharray
- //Specifying clientId checks if the client is allowed to give these rights
- //specifying an empty rights-array will delete the key
- void changeAuth(int clientId, string key, Cvar & rights)
- {
- if (!getClient(clientId).mAuthOwner)
- throw(synapse::runtime_error("Only the owner can change the security settings of this drawing."));
-
- if (rights.map().size()!=0)
- {
- mDrawing["auth"][key]["view"]=rights["view"];
- mDrawing["auth"][key]["change"]=rights["change"];
- mDrawing["auth"][key]["owner"]=rights["owner"];
- mDrawing["auth"][key]["cursor"]=rights["cursor"];
- mDrawing["auth"][key]["chat"]=rights["chat"];
- if (rights.isSet("description"))
- mDrawing["auth"][key]["description"]=rights["description"];
- }
- else
- {
- mDrawing["auth"].map().erase(key);
- }
-
- mDrawing.changed();
-
- }
-
- //get all authorisation and authentication info
- void getAuth(int clientId)
- {
- if (!getClient(clientId).mAuthOwner)
- throw(synapse::runtime_error("Only the owner can get the security settings of this drawing."));
-
-
- throw(synapse::runtime_error("STUB"));
- }
-
- //send the drawing commands to all clients, except clientId
- //(use clientId 0 if your want the message send to all clients)
- void serverDraw(Cmsg out, int clientId=0 )
- {
- out.event="paper_ServerDraw";
- out.src=0;
-
- //send to all connected clients, but not back to the original clientId
- for (CclientMap::iterator I=clientMap.begin(); I!=clientMap.end(); I++)
- {
- if (I->first!=clientId)
- {
- out.dst=I->first;
- try
- {
- I->second.sendFiltered(out);
- }
- catch(...)
- {
- //ignore send errors (expectable race conditions do occur with session ending)
- ;
- }
- }
- }
- }
-
-
- //get an iterator to specified element id.
- //throws error if not found.
- Cvar::iterator getElement(const string & id)
- {
- Cvar::iterator elementI;
- elementI=mDrawing["data"].map().find(id);
- if (elementI==mDrawing["data"].map().end())
- throw(synapse::runtime_error("Specified element id not found."));
- return (elementI);
- }
-
-
- //transform the specified elementid into a message-data that can be send to clients
- //sets:
- //["element"]=elementtype
- //["set"][attributename]=attributevalue
- //["id"]=id
- //["beforeId"]=id of element this element comes before
- void element2msg(const string & id, Cmsg & msg)
- {
- //get an iterator to requested id
- Cvar::iterator elementI=getElement(id);
-
- //fill in element type
- msg["element"]=elementI->second["element"];
- msg["id"]=id;
-
- //fill in all element attributes
- for(Cvar::iterator I=elementI->second.begin(); I!=elementI->second.end(); I++)
- {
- if (I->first!="element")
- msg["set"][I->first]=I->second;
- }
-
- //is there an item after this?
- elementI++;
- if (elementI!=mDrawing["data"].end())
- {
- //yes, so fill in beforeId, so the objects stay in the correct order
- msg["beforeId"]=elementI->first;
- }
- }
-
- //resend all data to dst
- void reload(int dst)
- {
- if (!getClient(dst).mAuthView)
- throw(synapse::runtime_error("You're not authorized to view this drawing."));
-
- Cmsg out;
- out.event="paper_ServerDraw";
- out.dst=dst;
-
- //indicate start of complete reload
- out["cmd"]="reload";
- out.send();
-
- //send all elements
- for(Cvar::iterator elementI=mDrawing["data"].begin(); elementI!=mDrawing["data"].end(); elementI++)
- {
- out.clear();
- out["cmd"]="update";
- element2msg(elementI->first, out);
- out.send();
- }
-
- //send all cursors
- for(CclientMap::iterator I=clientMap.begin(); I!=clientMap.end(); I++)
- {
- out.clear();
- out["cursor"].map()=I->second.mCursor;
- out["src"]=I->first;
- out.send();
- }
-
- //send chat log
- for(CvarList::iterator I=mDrawing["chat"].list().begin(); I!=mDrawing["chat"].list().end(); I++)
- {
- out.clear();
- out["chat"]=*I;
- out.send();
- }
-
-
- out.clear();
- out["cmd"]="ready";
- out.send();
- }
-
- //process drawing data received from a client store it, and relay it to other clients via serverDraw
- //if a client is not authorized to do certain stuff, an exception is thrown
- void clientDraw(Cmsg & msg)
- {
- msg["src"]=msg.src;
-
- //received cursor information?
- if (msg.isSet("cursor"))
- {
- if (!getClient(msg.src).mAuthCursor)
- throw(synapse::runtime_error("You're not allowed to send cursor updates"));
-
- //store the new fields in the clients cursor object:
- Cvar & cursor=getClient(msg.src).mCursor;
- for(Cvar::iterator I=msg["cursor"].begin(); I!=msg["cursor"].end(); I++)
- {
- cursor[I->first]=I->second.str();
- }
- }
-
- //received chat?
- if (msg.isSet("chat"))
- {
- if (!getClient(msg.src).mAuthChat)
- throw(synapse::runtime_error("You're not allowed to chat"));
-
- //store in chat log for this object
- mDrawing["chat"].list().push_back(msg["chat"]);
- }
-
- //received drawing commands?
- if (msg["cmd"].str()=="update")
- {
-
- if (!getClient(msg.src).mAuthChange)
- throw(synapse::runtime_error("You're not authorized to change this drawing."));
-
- //id specified?
- if (msg.isSet("id"))
- {
- //add new object?
- if (msg["id"].str()=="new")
- {
- //figure out a fresh new id
- mDrawing["lastElementId"]=mDrawing["lastElementId"]+1;
-
- //store the new id in the message
- msg["id"]=mDrawing["lastElementId"];
-
- //create new element
- mDrawing["data"][msg["id"]]["element"]=msg["element"].str();
-
- }
-
- //store the last id for this client
- getClient(msg.src).mLastElementId=msg["id"];
- }
- //id not specified, automaticly add the last used id from this client
- else
- {
- msg["id"]=getClient(msg.src).mLastElementId;
- }
-
-
- Cvar::iterator elementI=getElement(msg["id"]);
-
- //add the data from the 'add' field to the drawing data
- for(Cvar::iterator I=msg["add"].begin(); I!=msg["add"].end(); I++)
- {
- //this basically means: drawing["data"][key]+=value;
- elementI->second[I->first].str()+=I->second.str();
- }
-
- //set the data from the 'set' field over the drawing data
- for(Cvar::iterator I=msg["set"].begin(); I!=msg["set"].end(); I++)
- {
- elementI->second[I->first]=I->second.str();
- }
-
- changed();
- }
- //send refresh of requested element?
- else if (msg["cmd"].str()=="refresh")
- {
- //id not set? the use the last one that was used by this client
- if (!msg.isSet("id"))
- {
- if (getClient(msg.src).mLastElementId)
- msg["id"]=getClient(msg.src).mLastElementId;
- }
-
- //reply with a serverdraw to this client only
- Cmsg out;
- out=msg;
- out.event="paper_ServerDraw";
- out.dst=msg.src;
- out.src=0;
- out["cmd"]="update";
- if (msg.isSet("id"))
- element2msg(msg["id"].str(), out);
- getClient(msg.src).sendFiltered(out);
-
- return;
- }
- //delete object?
- else if (msg["cmd"].str()=="delete")
- {
- if (!getClient(msg.src).mAuthChange)
- throw(synapse::runtime_error("You're not authorized to change this drawing."));
-
- //delete it
- Cvar::iterator elementI=getElement(msg["id"]);
- mDrawing["data"].map().erase(elementI);
- changed();
-
- getClient(msg.src).mLastElementId=0;
- }
- //request a reload of all the data
- else if (msg["cmd"].str()=="reload")
- {
- reload(msg.src);
- return;
- }
-
- //relay the command to other clients
- serverDraw(msg,msg.src);
- }
-
- virtual void addClient(int id)
- {
- //let the base class do its work:
- synapse::CsharedObject<CpaperClient>::addClient(id);
- }
-
- virtual bool isIdle()
- {
- return (!mExporting && synapse::CsharedObject<CpaperClient>::isIdle());
-
- }
-
- };
@@ -1086,7 +333,35 @@ namespace paper
{
if (msg.isSet("id"))
{
+ //tell the object the execution is complete
gObjectMan.getObject(msg["id"]["paperId"]).execEnded(msg["id"]);
+
+ //update the recently-changed index:
+ Cvar paperInfo;
+ gObjectMan.getObject(msg["id"]["paperId"]).getInfo(paperInfo);
+
+ if (paperInfo["version"]>0)
+ {
+ //delete ourself from the list first
+ for (synapse::CvarList::iterator paperI=gPaperIndex["list"].list().begin(); paperI!=gPaperIndex["list"].list().end(); paperI++)
+ {
+ if ((*paperI)["objectId"]==msg["id"]["paperId"])
+ {
+ gPaperIndex["list"].list().erase(paperI);
+ break;
+ }
+ }
+
+ //now re-add the updated version of ourself in the front position
+ gPaperIndex["list"].list().push_front(paperInfo);
+
+ //never more then this amount in the index
+ if (gPaperIndex["list"].list().size()>100)
+ gPaperIndex["list"].list().pop_back();
+
+ gPaperIndex.changed();
+ }
+
}
}