00001 /* 00002 This file is part of the Virtual Object System of 00003 the Interreality project (http://interreality.org). 00004 00005 Copyright (C) 2001-2003 Peter Amstutz 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Lesser General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Lesser General Public License for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public 00018 License along with this library; if not, write to the Free Software 00019 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00020 00021 Peter Amstutz <tetron@interreality.org> 00022 */ 00023 #ifndef _MESSAGE_HH_ 00024 #define _MESSAGE_HH_ 00025 00026 #include <vos/corelibs/vos/vosdefs.hh> 00027 #include <vos/corelibs/vos/refcount.hh> 00028 #include <vos/corelibs/vos/msglexer.hh> 00029 00030 #include <stdexcept> 00031 #include <string> 00032 #include <deque> 00033 00034 // #define LEAKYSET_DEBUG 00035 00036 /** @file 00037 Defines Message. 00038 */ 00039 00040 namespace VOS 00041 { 00042 class Site; 00043 class MessageContext; 00044 00045 /** @class Message message.hh vos/corelibs/vos/message.hh 00046 * 00047 * This class handles storing, generating and parsing of the messages 00048 * that are sent between virtual objects. 00049 */ 00050 class VOS_API Message : public virtual RefCounted 00051 { 00052 public: 00053 /** Thrown when getField() is passed an out-of-range parameter */ 00054 class NoSuchFieldError : public runtime_error { 00055 public: 00056 NoSuchFieldError(const string& s) : runtime_error(s) { } 00057 }; 00058 00059 /** A key-value field pair used to store the fields of the message */ 00060 struct Field { 00061 string key; /**< the key or tag for this field */ 00062 string value; /**< the value of this field */ 00063 string source; /**< the value of this field before doing substitution */ 00064 bool quoted; 00065 }; 00066 private: 00067 string type; 00068 string to; 00069 string from; 00070 string method; 00071 string nonce; 00072 double mtime; 00073 string dependsOn; 00074 Site* source_site; 00075 MessageContext* messageContext; 00076 string* formattedString; 00077 deque<Field> fields; 00078 void* parse_extra; 00079 00080 string parse_buffer; 00081 string* (*format_func)(const string& type, 00082 const string& to, 00083 const string& from, 00084 const string& method, 00085 const string& nonce, 00086 const string& dependsOn, 00087 double mtime, 00088 const deque<Field>& fields, 00089 bool withLength); 00090 int (*parse_func)(Message& m, 00091 void* extra, 00092 const string& str); 00093 static int refcount_debug_counter; 00094 #ifdef LEAKYSET_DEBUG 00095 static set<Message*> leakySet; 00096 #endif 00097 public: 00098 int refcount_debug; 00099 int incoming_debug; 00100 00101 /** Create a new message. */ 00102 Message(); 00103 00104 /** Copy a message. */ 00105 Message(Message& m); 00106 00107 /** Destroy this message. */ 00108 ~Message(); 00109 00110 /** Set what type of message this is. You probably want it to be either 00111 "message" or "update". 00112 @param s the types 00113 */ 00114 void setType(const string& s); 00115 00116 /** Set whom this message is intended for. This should probably 00117 be the URL string of the object to which the message is destined. 00118 @param s the to field 00119 */ 00120 void setTo(const string& s); 00121 00122 /** Set whom this message is sent by. This should probably 00123 be the URL string of the object which is generating this message. 00124 @param s the from field 00125 */ 00126 void setFrom(const string& s); 00127 00128 /** Set the method action this message is expressing. 00129 @param s the method field 00130 */ 00131 void setMethod(const string& s); 00132 00133 /** Set the nonce field. The nonce is used to match a match a 00134 reply against a request (because the reply bears the same 00135 nonce as the originating message.) 00136 @param s the nonce field 00137 */ 00138 void setNonce(const string& s); 00139 00140 /** Set the time field, in seconds. This is used by the message 00141 delivery scheduler to determine when to deliver this message, 00142 useful for scripted events in MessageBlock objects. 00143 @param time the time field 00144 */ 00145 void setTime(double time); 00146 00147 /** Set the source site. This is not a field in the message 00148 itself, but rather is used to set the site originating this 00149 message (that is to say, which socket the message was received 00150 on.) This is compared against the "from" field to provide a 00151 simple filter against really obvious spoofing. You do NOT 00152 need to set this if you are creating a new message to be sent; 00153 it is only used for messages received from the network. 00154 @param s the source site 00155 */ 00156 void setSourceSite(Site* source_site); 00157 00158 /** Indicate the message block this message is contained in. Does 00159 not actually add itself to the message block, however (you 00160 really want to be using MessageBlock::insertMessage()) 00161 @param mb the message block 00162 */ 00163 void setMessageContext(MessageContext* mb); 00164 00165 void setDependency(const string& nonce); 00166 00167 /** @return the type field. setType() has a bit more information about this field. */ 00168 const string& getType() const; 00169 00170 /** Get the to field. 00171 @return the to field setTo() has a bit more information about this field. */ 00172 string getTo() const; 00173 00174 /** Get the from field. 00175 @return the from field setFrom() has a bit more information about this field. */ 00176 string getFrom() const; 00177 00178 /** Get the method field. 00179 @return the method field setMethod() has a bit more information about this field. */ 00180 string getMethod() const; 00181 00182 /** Get the nonce field. 00183 @return the nonce field setNonce() has a bit more information about this field. */ 00184 string getNonce() const; 00185 00186 /** Get whether there is anything in the to field. 00187 @return if there is a to field setTo() has a bit more information about this field. */ 00188 bool hasTo() const; 00189 00190 /** Get whether there is anything in the from field. 00191 @return if there is a from field setFrom() has a bit more information about this field. */ 00192 bool hasFrom() const; 00193 00194 /** Get whether there is anything in the method field. 00195 @return if there is a method field setMethod() has a bit more information about this field. */ 00196 bool hasMethod() const; 00197 00198 /** Get whether there is anything in the nonce field. 00199 @return if there is a nonce field setNonce() has a bit more information about this field. */ 00200 bool hasNonce() const; 00201 00202 /** Get whether there is anything in the dependency field. 00203 @return if there is a dependency field setDependency() has a bit more information about this field. */ 00204 bool hasDependency() const; 00205 00206 /** Get time field, used for scheduling. 00207 @return the time field. getTime() has a bit more information about this field. */ 00208 double getTime() const; 00209 00210 /** Get the source site. This is the site that actually generated 00211 this message. MAY BE ZERO IF THE MESSAGE WAS GENERATED 00212 LOCALLY. If so, you'll need to do the following to determine 00213 the source site: 00214 Site::findSite(URL(themsg.getFrom()).getHostAndPort()) 00215 @return the source site, if any; NOTE YOU MUST CALL release() WHEN DONE OR USE vRef. 00216 */ 00217 Site* getSourceSite(); 00218 00219 /** Get the message block this message is contained in. May be NULL. 00220 @return the message block; NOTE YOU MUST CALL release() WHEN DONE OR USE vRef. 00221 */ 00222 MessageContext* getMessageContext(); 00223 00224 /** Get the nonce of the OUTGOING REPLIES that this message depends on before 00225 it can be delivered, used to determine message context. 00226 This is a comma-separeted @em no spaces! 00227 */ 00228 string getDependency(); 00229 00230 /** Get the number of ordinary fields. 00231 @return the number of ordinary fields in this message */ 00232 int getNumFields() const; 00233 00234 /** Have the message fill in the nonce field with a random nonce. */ 00235 void generateNonce(); 00236 00237 /** Get the first field which matches the supplied key. Note that 00238 this field list is SEPERATE from the type/to/from/method/nonce 00239 fields. 00240 @param key the tag to match 00241 @return the first instance of a field which matchs that tag 00242 @throw NoSuchFieldError if the field is not found 00243 */ 00244 const struct Field& getField(const string& key) throw (NoSuchFieldError); 00245 00246 /** Get the first field which matches the supplied key. Note that 00247 this field list is SEPERATE from the type/to/from/method/nonce 00248 fields. 00249 @param n the field at position n, where n is the array offset from 0. 00250 @return the first instance of a field which matchs that tag 00251 @throw NoSuchFieldError if the field is not found 00252 */ 00253 const struct Field& getField(int n) throw (NoSuchFieldError); 00254 00255 /** Insert a new field. 00256 @note On positions: if a position is zero or positive, its 00257 meaning is as you would expect, expressing the offset into an 00258 array of fields. However, if the position is negative, it 00259 expresses the offset from the end of the list. This means for 00260 a list of length m, -1 is the last item in the list (equal to 00261 position position m - 1) and -m is the first item (equal to 00262 positive position 0). If the position is positive, the field 00263 is inserted such that it now occupies that position, and all 00264 fields starting from the previous occupant of that position 00265 onward are moved up one. If the position in negative, the 00266 field is similarly inserted so that it now occupies that 00267 position. For example, position -1 will append the field to 00268 the end of the list, position -2 will insert the field in the 00269 second-to-last position, etc. 00270 @param n the position, as explained above 00271 @param key the key (or tag) to associate the value with 00272 @param val the value of this field 00273 @param whether this field is "quoted", eg suppress $(foo) substition 00274 */ 00275 void insertField(int n, const string& key, const string& val, bool quoted = false); 00276 00277 /** Insert a new field. 00278 @note On positions: if a position is zero or positive, its 00279 meaning is as you would expect, expressing the offset into an 00280 array of fields. However, if the position is negative, it 00281 expresses the offset from the end of the list. This means for 00282 a list of length m, -1 is the last item in the list (equal to 00283 position position m - 1) and -m is the first item (equal to 00284 positive position 0). If the position is positive, the field 00285 is inserted such that it now occupies that position, and all 00286 fields starting from the previous occupant of that position 00287 onward are moved up one. If the position in negative, the 00288 field is similarly inserted so that it now occupies that 00289 position. For example, position -1 will append the field to 00290 the end of the list, position -2 will insert the field in the 00291 second-to-last position, etc. 00292 @param n the position, as explained above 00293 @param key the key (or tag) to associate the value with 00294 @param val the value of this field 00295 */ 00296 void insertField(int n, const string& key, double val); 00297 00298 /** Insert a new field. 00299 @note On positions: if a position is zero or positive, its 00300 meaning is as you would expect, expressing the offset into an 00301 array of fields. However, if the position is negative, it 00302 expresses the offset from the end of the list. This means for 00303 a list of length m, -1 is the last item in the list (equal to 00304 position position m - 1) and -m is the first item (equal to 00305 positive position 0). If the position is positive, the field 00306 is inserted such that it now occupies that position, and all 00307 fields starting from the previous occupant of that position 00308 onward are moved up one. If the position in negative, the 00309 field is similarly inserted so that it now occupies that 00310 position. For example, position -1 will append the field to 00311 the end of the list, position -2 will insert the field in the 00312 second-to-last position, etc. 00313 @param n the position, as explained above 00314 @param key the key (or tag) to associate the value with 00315 @param val the value of this field 00316 */ 00317 void insertField(int n, const string& key, int val); 00318 00319 /** Remove an existing field at some position. See insertField() 00320 for details about the legal numerical values of the position. 00321 @param n the position 00322 */ 00323 void removeField(int n); 00324 00325 /** Remove the first instance of a field with the supplied key. 00326 @param s the key of the field to remove 00327 */ 00328 void removeField(const string& s); 00329 00330 /** Set the function which converts this message structure into a 00331 string. End users don't need to call this, as it is set to 00332 the default when the message is constructed. 00333 @note The more object-oriented way of doing this would be to 00334 create a function object type. This interface may change. */ 00335 void setMappingFunc(string* (*func)(const string& type, 00336 const string& to, 00337 const string& from, 00338 const string& method, 00339 const string& nonce, 00340 const string& dependsOn, 00341 double mtime, 00342 const deque<Message::Field>& fields, 00343 bool withLength)); 00344 00345 /* Set the function which will parse some string into a message 00346 structure. End users don't need to call this, as it is set to 00347 the default when the message is constructed. 00348 @note The more object-oriented way of doing this would be to 00349 create a function object type. This interface may change. 00350 00351 *** parsing moved to MessageBlock class 00352 00353 void setParseFunc(int (*func)(void* extra, 00354 const string& s), 00355 void* extra, 00356 void (*delete_extra)(void* extra)); 00357 */ 00358 00359 /** Get the message formatted with the function supplied 00360 in setMappingFunc(). 00361 @return the formatted string 00362 */ 00363 const string& getFormattedString(bool withLength=true); 00364 00365 /** Print out a string value for the message with substitutions done, 00366 to make it easier to tell what the processed message actually looked like. 00367 */ 00368 string getLoggableString(); 00369 00370 /* Add some data to be parsed using the parse function supplied 00371 in setParseFunc(). 00372 @return the number of characters read 00373 00374 *** parsing moved to MessageBlock class 00375 int parseUpdate(const string& data); 00376 */ 00377 00378 /** Internal debugging function. */ 00379 #ifdef LEAKYSET_DEBUG 00380 static void printLeakySet(); 00381 #endif 00382 00383 friend int msgFlexLexer::yylex(); 00384 }; 00385 00386 /** Converts a message into an XML format message */ 00387 string* xmlFormatting(const string& type, 00388 const string& to, 00389 const string& from, 00390 const string& method, 00391 const string& nonce, 00392 const string& dependsOn, 00393 double mtime, 00394 const deque<Message::Field>& fields, 00395 bool withLength); 00396 } 00397 00398 #endif