Merged revisions 1045-1120 via svnmerge. autotools
authorasmodai <asmodai@9efc2488-bf62-4759-914b-345cdb29e865>
Mon, 5 Nov 2007 13:40:16 +0000 (13:40 +0000)
committerasmodai <asmodai@9efc2488-bf62-4759-914b-345cdb29e865>
Mon, 5 Nov 2007 13:40:16 +0000 (13:40 +0000)
git-svn-id: svn://svn.open-ils.org/OpenSRF/branches/autotools@1121 9efc2488-bf62-4759-914b-345cdb29e865

102 files changed:
Makefile
bin/osrf_ctl.sh
examples/math_xul_client/math/content/math.xul
examples/opensrf.xml.example
examples/opensrf_core.xml.example
examples/srfsh.xml.example
include/objson/json2xml.h
include/objson/json_parser.h
include/objson/object.h
include/objson/xml2json.h
include/opensrf/osrfConfig.h
include/opensrf/osrf_app_session.h
include/opensrf/osrf_application.h
include/opensrf/osrf_cache.h
include/opensrf/osrf_json.h [new file with mode: 0644]
include/opensrf/osrf_json_utils.h [new file with mode: 0644]
include/opensrf/osrf_json_xml.h [new file with mode: 0644]
include/opensrf/osrf_legacy_json.h [new file with mode: 0644]
include/opensrf/osrf_message.h
include/opensrf/osrf_settings.h
include/opensrf/string_array.h
include/opensrf/utils.h
include/opensrf/xml_utils.h
install.conf
src/Makefile
src/c-apps/Makefile
src/c-apps/osrf_dbmath.c
src/c-apps/osrf_math.c
src/c-apps/osrf_version.c
src/gateway/Makefile
src/gateway/apachetools.c
src/gateway/osrf_json_gateway.c
src/java/Makefile
src/java/org/opensrf/ClientSession.java
src/java/org/opensrf/MethodException.java
src/java/org/opensrf/Request.java
src/java/org/opensrf/Session.java
src/java/org/opensrf/Sys.java
src/java/org/opensrf/net/xmpp/XMPPReader.java
src/java/org/opensrf/net/xmpp/XMPPSession.java
src/java/org/opensrf/test/TestClient.java
src/java/org/opensrf/test/TestLog.java [new file with mode: 0644]
src/java/org/opensrf/test/TestThread.java [new file with mode: 0644]
src/java/org/opensrf/util/FileLogger.java [new file with mode: 0644]
src/java/org/opensrf/util/Logger.java [new file with mode: 0644]
src/java/org/opensrf/util/OSRFObject.java
src/javascript/JSON.js [deleted file]
src/javascript/JSON_v0.js [new file with mode: 0644]
src/javascript/JSON_v1.js [new file with mode: 0644]
src/jserver/Makefile
src/jserver/osrf_chat.c
src/libopensrf/Makefile
src/libopensrf/Makefile.json [new file with mode: 0644]
src/libopensrf/basic_client.c
src/libopensrf/log.c
src/libopensrf/opensrf.c
src/libopensrf/osrf_app_session.c
src/libopensrf/osrf_application.c
src/libopensrf/osrf_cache.c
src/libopensrf/osrf_hash.c
src/libopensrf/osrf_json_object.c [new file with mode: 0644]
src/libopensrf/osrf_json_parser.c [new file with mode: 0644]
src/libopensrf/osrf_json_test.c [new file with mode: 0644]
src/libopensrf/osrf_json_tools.c [new file with mode: 0644]
src/libopensrf/osrf_json_xml.c [new file with mode: 0644]
src/libopensrf/osrf_legacy_json.c [new file with mode: 0644]
src/libopensrf/osrf_message.c
src/libopensrf/osrf_prefork.c
src/libopensrf/osrf_settings.c
src/libopensrf/osrf_stack.c
src/libopensrf/osrf_system.c
src/libopensrf/osrf_transgroup.c
src/libopensrf/sha.c
src/libopensrf/socket_bundle.c
src/libopensrf/string_array.c
src/libopensrf/transport_client.c
src/libopensrf/transport_message.c
src/libopensrf/transport_session.c
src/libopensrf/utils.c
src/objson/json_parser.c
src/objson/objson_test.c
src/perlmods/OpenSRF/AppSession.pm
src/perlmods/OpenSRF/Application.pm
src/perlmods/OpenSRF/DomainObject/oilsMessage.pm
src/perlmods/OpenSRF/DomainObject/oilsResponse.pm
src/perlmods/OpenSRF/Transport.pm
src/perlmods/OpenSRF/UnixServer.pm
src/perlmods/OpenSRF/Utils/JSON.pm
src/perlmods/OpenSRF/Utils/Logger.pm
src/python/osrf/gateway.py
src/python/osrf/json.py
src/python/osrf/log.py
src/python/osrf/net.py
src/python/osrf/net_obj.py
src/python/osrf/ses.py
src/python/osrf/stack.py
src/python/osrf/system.py
src/python/srfsh.py
src/router/Makefile
src/router/osrf_router.c
src/srfsh/Makefile
src/srfsh/srfsh.c

index e45075a..867173e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,9 @@ install:
 jserver-install:
        source install.conf && make -s -C src jserver-install
 
+javascript-install:
+       source install.conf && make -s -C src javascript-install
+
 clean:
        make -s -C src clean
 
index 8958204..6d65902 100755 (executable)
@@ -12,9 +12,17 @@ OPT_PID_DIR=""
 
 function usage {
        echo "";
-       echo "usage: $0 -d <pid_dir> -c <c_config> -a <action>";
+       echo "usage: $0 [OPTION]... -c <c_config> -a <action>";
        echo "";
-       echo "Actions include:"
+       echo "Mandatory parameters:";
+       echo -e "  -a\t\taction to perform";
+       echo -e "  -c\t\tfull path to C configuration file (opensrf_core.xml)";
+       echo "";
+       echo "Optional parameters:";
+       echo -e "  -d\t\tstore PID files in this directory";
+       echo -e "  -l\t\taccept 'localhost' as the fully-qualified domain name";
+       echo "";
+       echo "Actions include:";
        echo -e "\tstart_router"
        echo -e "\tstop_router"
        echo -e "\trestart_router"
@@ -31,9 +39,10 @@ function usage {
        echo -e "\tstart_all"
        echo -e "\trestart_all"
        echo "";
-    echo "Example:";
-    echo "  $0 -c opensrf_core.xml -a restart_all";
-    echo "";
+       echo "Examples:";
+       echo "  $0 -c opensrf_core.xml -a restart_all";
+       echo "  $0 -l -c opensrf_core.xml -a restart_all";
+       echo "";
        exit;
 }
 
@@ -41,11 +50,12 @@ function usage {
 # ---------------------------------------------------------------------------
 # Load the command line options and set the global vars
 # ---------------------------------------------------------------------------
-while getopts  "c:a:d:h" flag; do
+while getopts  "c:a:d:lh" flag; do
        case $flag in   
                "a")            OPT_ACTION="$OPTARG";;
                "c")            OPT_CONFIG="$OPTARG";;
                "d")            OPT_PID_DIR="$OPTARG";;
+               "l")            export OSRF_HOSTNAME="localhost";;
                "h"|*)  usage;;
        esac;
 done
index 1396a0b..d6a1371 100644 (file)
@@ -16,7 +16,7 @@
 
        <!-- OpenSRF -->
        <script>var myPackageDir = "math";</script>
-       <script src="OpenSRF/JSON.js" />
+       <script src="OpenSRF/JSON_v1.js" />
        <script src="OpenSRF/md5.js" />
        <script src="OpenSRF/opensrf_utils.js" />
        <script src="OpenSRF/opensrf_config.js" />
index fa2bc44..ad33887 100644 (file)
 
   <hosts>
 
-    <myhost.mydomain.org>
+    <localhost>
 <!-- ^-=-
        Must match the fully qualified domain name of the host 
        on Linux, this is usually the output of "hostname -f"
 
       </apps>
 
-    </myhost.mydomain.org>
+    </localhost>
 
   </hosts>
 
index d24121d..a46df77 100644 (file)
 -->
     <services>
       <service>opensrf.math</service>
-      <service>open-ils.cat</service>
-      <service>open-ils.search</service>
-      <service>open-ils.circ</service>
-      <service>open-ils.actor</service>
-      <service>open-ils.auth</service>
     </services>
 
     <!-- jabber login info -->
index e88ae6b..11b1b4d 100644 (file)
@@ -3,7 +3,7 @@
 <srfsh>
   <router_name>router</router_name>
   <domains>
-    <domain>127.0.0.1</domain>
+    <domain>localhost</domain>
   </domains>
   <username>myusername</username>
   <passwd>mypassword</passwd>
index 9eaa1c0..6539adf 100644 (file)
@@ -1,11 +1,10 @@
-
-#include <string.h>
-#include <stdio.h>
-
-/* the JSON parser, so we can read the response we're XMLizing */
-#include <objson/object.h>
-#include <objson/json_parser.h>
-#include <opensrf/utils.h>
-
-char* jsonObjectToXML(jsonObject*);
+/*
+ * Header to support legacy objson library
+ */
+#ifndef OBJSON_JSON2XML_H
+#define OBJSON_JSON2XML_H
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_xml.h>
+#include <opensrf/osrf_legacy_json.h>
+#endif
 
index a1f785b..2c982ee 100644 (file)
@@ -1,84 +1,9 @@
 /*
-Copyright (C) 2005  Georgia Public Library Service 
-Bill Erickson <highfalutin@gmail.com>
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-*/
-
-
-
-
-/* ---------------------------------------------------------------------------------------
-       JSON parser.
- * --------------------------------------------------------------------------------------- */
-#ifndef JSON_PARSER_H
-#define JSON_PARSER_H
-
-#include <stdio.h>
-#include <ctype.h>
-#include <objson/object.h>
-#include <opensrf/utils.h>
-
-
-
-/* Parses the given JSON string and returns the built object. 
- *     returns NULL (and prints parser error to stderr) on error.  
+ * Header to support legacy objson library
  */
-
-jsonObject* json_parse_string(char* string);
-
-jsonObject* jsonParseString(char* string);
-jsonObject* jsonParseStringFmt( char* string, ... );
-
-jsonObject* json_parse_file( const char* filename );
-
-jsonObject* jsonParseFile( const char* string );
-
-
-
-/* does the actual parsing work.  returns 0 on success.  -1 on error and
- * -2 if there was no object to build (string was all comments) 
- */
-int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* returns 0 on success and turns obj into a string object */
-int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* returns 0 on success and turns obj into a number or double object */
-int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* returns 0 on success and turns obj into an 'object' object */
-int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* returns 0 on success and turns object into an array object */
-int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* churns through whitespace and increments index as it goes.
- * eat_all == true means we should eat newlines, tabs
- */
-void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen);
-
-int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* removes comments from a json string.  if the comment contains a class hint
- * and class_hint isn't NULL, an allocated char* with the class name will be
- * shoved into *class_hint.  returns 0 on success, -1 on parse error.
- * 'index' is assumed to be at the second character (*) of the comment
- */
-int json_eat_comment(char* string, unsigned long* index, char** class_hint, int parse_class, int current_strlen);
-
-/* prints a useful error message to stderr. always returns -1 */
-int json_handle_error(char* string, unsigned long* index, char* err_msg);
-
-int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-
+#ifndef OBJSON_XML2JSON_H
+#define OBJSON_XML2JSON_H
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_xml.h>
+#include <opensrf/osrf_legacy_json.h>
 #endif
index 8d62c1a..e54f8ed 100644 (file)
@@ -1,286 +1,8 @@
 /*
-Copyright (C) 2005  Georgia Public Library Service 
-Bill Erickson <highfalutin@gmail.com>
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-*/
-
-
-/* ---------------------------------------------------------------------------------------
-       libjson
- * --------------------------------------------------------------------------------------- */
-
-#ifndef _JSON_OBJECT_H
-#define _JSON_OBJECT_H
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <opensrf/utils.h>
-
-/* json object types */
-#define JSON_HASH      0
-#define JSON_ARRAY     1
-#define JSON_STRING    2
-#define JSON_NUMBER    3
-#define JSON_NULL      4       
-#define JSON_BOOL      5
-
-
-/* top level generic object structure */
-struct _jsonObjectStruct {
-
-       /* how many sub-objects do we contain if we're an array or an object.  
-               Note that this includes null array elements in sparse arrays */
-       unsigned long size;
-
-       /* optional class hint */
-       char* classname;
-
-       /* see JSON types above */
-       int type;
-
-
-       /* our cargo */
-       union _jsonObjectValue {
-               struct _jsonObjectNodeStruct* c; /* our list of sub-objects if we're an array or a hash */
-               char*           s; /* string */
-               int                     b; /* bool */
-               double          n; /* number */
-       } value;
-       
-
-       /* client may provide a comment string which will be 
-        * added to the object when stringified */
-       char* comment;
-
-};
-typedef struct _jsonObjectStruct jsonObject;
-
-
-/** 
-       String parsing function.  This is assigned by the json_parser code.
-       to avoid circular dependency, declare the parse function here,
-       and have the json parse code set the variable to a real function 
-*/
-//jsonObject* (*jsonParseString) (char* str);
-
-
-/* this contains a single element of the object along with the elements 
- * index (if this object is an array) and key (if this object is a hash)
+ * Header to support legacy objson library
  */
-struct _jsonObjectNodeStruct {
-
-       unsigned long index; /* our array position */
-       char* key; /* our hash key */
-
-       jsonObject* item; /* our object */
-       struct _jsonObjectNodeStruct* next; /* pointer to the next object node */
-};
-typedef struct _jsonObjectNodeStruct jsonObjectNode;
-
-
-
-/* utility object for iterating over hash objects */
-struct _jsonObjectIteratorStruct {
-       const jsonObject* obj; /* the topic object */
-       jsonObjectNode* current; /* the current node within the object */
-};
-typedef struct _jsonObjectIteratorStruct jsonObjectIterator;
-
-
-/** Allocates a new iterator 
-       @param obj The object over which to iterate.
-*/
-jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj);
-
-/** 
-       De-allocates an iterator 
-       @param iter The iterator object to free
-*/
-void jsonObjectIteratorFree(jsonObjectIterator* iter);
-
-/** 
-       Returns the object_node currently pointed to by the iterator
-       and increments the pointer to the next node
-       @param iter The iterator in question.
- */
-jsonObjectNode* jsonObjectIteratorNext(jsonObjectIterator* iter);
-
-/** 
-       @param iter The iterator.
-       @return True if there is another node after the current node.
- */
-int jsonObjectIteratorHasNext(const jsonObjectIterator* iter);
-
-
-/** 
-       Allocates a new object. 
-       @param string The string data if this object is to be a string.  
-       if not, string should be NULL 
-       @return The newly allocated object or NULL on memory error.
-*/
-jsonObject* jsonNewObjectFmt(const char* string, ...);
-jsonObject* jsonNewObject(const char* string);
-
-/**
-       Allocates a new JSON number object.
-       @param num The number this object is to hold
-       @return The newly allocated object.
-*/
-jsonObject* jsonNewNumberObject( double num );
-
-
-/** 
-       Returns a pointer to the object at the given index.  This call is
-       only valid if the object has a type of JSON_ARRAY.
-       @param obj The object
-       @param index The position within the object
-       @return The object at the given index.
-*/
-jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index );
-
-
-/** 
-       Returns a pointer to the object with the given key 
-       @param obj The object
-       @param key The key
-       @return The object with the given key.
-*/
-jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key );
-
-/** 
-       De-allocates an object.  Note that this function should only be called 
-       on objects that are _not_ children of other objects or there will be
-       double-free's
-       @param obj The object to free.
-*/
-void jsonObjectFree(jsonObject* obj);
-
-
-/** 
-       Allocates a new object node.
-       @param obj The object to which the node will be appended.
-       @return The new object node.
-*/
-jsonObjectNode* jsonNewObjectNode(jsonObject* obj);
-
-/** 
-       De-allocates an object node 
-       @param obj The object node to de-allocate.
-*/
-void jsonObjectNodeFree(jsonObjectNode* obj);
-
-
-/** 
-       Pushes the given object onto the end of the list.  This coerces an object
-       into becoming an array.  _Only_ use this function on objects that you
-       want to become an array.
-       If obj is NULL, inserts a new NULL object into the list.
-       @return array size on success, -1 on error 
- */
-unsigned long jsonObjectPush(jsonObject* dest, jsonObject* newObj);
-
-/* removes (and deallocates) the object at the given index (if one exists) and inserts 
- * the new one.  returns the size on success, -1 on error 
- * If obj is NULL, inserts a new object into the list with is_null set to true
- */
-unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj);
-
-/* inserts the new object, overwriting (removing, deallocating) any 
- * previous object with the given key.
- * returns the size on success, -1 on error 
- * if 'obj' is NULL, a new object is inserted at key 'key' with 'is_null' 
- * set to true
- */
-unsigned long jsonObjectSetKey(jsonObject* dest, const char* key, jsonObject* newObj);
-
-/* removes the object at the given index and, if more items exist,
- * re-indexes (shifts down by 1) the rest of the objects in the array
- */
-unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index);
-
-/* removes (and deallocates) the object with key 'key' if it exists */
-unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key);
-
-/* returns a pointer to the string data held by this object if this object
-       is a string.  Otherwise returns NULL*/
-char* jsonObjectGetString(const jsonObject*);
-
-double jsonObjectGetNumber( const jsonObject* obj );
-
-/* sets the string data */
-void jsonObjectSetString(jsonObject* dest, const char* string);
-
-/* sets the number value for the object */
-void jsonObjectSetNumber(jsonObject* dest, double num);
-
-/* sets the class hint for this object */
-void jsonObjectSetClass(jsonObject* dest, const char* classname );
-
-/* converts an object to a json string.  client is responsible for freeing the return string */
-char* jsonObjectToJSON( const jsonObject* obj );
-
-/* set this object's comment string */
-void jsonObjectSetComment(jsonObject* dest, const char* classname);
-
-/* utility method.  starting at index 'index', shifts all indices down by one and 
- * decrements the objects size by 1 
- */
-void _jsonObjectShiftIndex(jsonObject* dest, unsigned long index);
-
-/* formats a JSON string from printing.  User must free returned string */
-char* jsonFormatString( const char* jsonString );
-
-jsonObject* jsonObjectClone( const jsonObject* o );
-
-/* tries to extract the string data from an object.
-       if object -> NULL (the C NULL)
-       if array ->     NULL  (the C NULL)
-       if null  -> NULL (the C NULL)
-       if true/false -> true/false
-       if string/number/double the string version of either of those
-       The caller is responsible for freeing the returned string
-       */
-char* jsonObjectToSimpleString( const jsonObject* o );
-
-int jsonBoolIsTrue( const jsonObject* o );
-
-
-/* ------------------------------------------------------------------------ */
-/* XPATH */
-
-/* provides an XPATH style search interface (e.g. /some/node/here) and 
-       return the object at that location if one exists.  Naturally,  
-       every element in the path must be a proper object ("hash" / {}).
-       Returns NULL if the specified node is not found 
-       Note also that the object returned is a clone and
-       must be freed by the caller
-*/
-jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ... );
-
-
-/* Utility method. finds any object in the tree that matches the path.  
-       Use this for finding paths that start with '//' */
-jsonObject* _jsonObjectFindPathRecurse( const jsonObject* o, char* root, char* path );
-
-/* returns a list of object whose key is 'root'.  These are used as
-       potential objects when doing a // search */
-jsonObject* __jsonObjectFindPathRecurse( const jsonObject* o, char* root );
-
-/* ------------------------------------------------------------------------ */
-
-
+#ifndef OBJSON_OBJECT_H
+#define OBJSON_OBJECT_H
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_legacy_json.h>
 #endif
-
-
index a9ded67..037fc75 100644 (file)
@@ -1,19 +1,11 @@
-
-#include <stdio.h>
-#include <string.h>
-#include <libxml/globals.h>
-#include <libxml/xmlerror.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <libxml/xmlmemory.h>
-
-#include <objson/object.h>
-#include <objson/json_parser.h>
-#include <opensrf/utils.h>
-#include <opensrf/osrf_list.h>
-
-
-jsonObject* jsonXMLToJSONObject(const char* xml);
-
+/*
+ * Header to support legacy objson library
+ */
+#ifndef OBJSON_XML2JSON_H
+#define OBJSON_XML2JSON_H
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_xml.h>
+#include <opensrf/osrf_legacy_json.h>
+#endif
 
 
index 75dbcfd..dbe59ed 100644 (file)
@@ -19,7 +19,7 @@ GNU General Public License for more details.
 #include <opensrf/xml_utils.h>
 #include <opensrf/utils.h>
 #include <opensrf/string_array.h>
-#include <objson/object.h>
+#include <opensrf/osrf_json.h>
 
 typedef struct {
        jsonObject* config;
index aae373c..561454e 100644 (file)
@@ -9,8 +9,7 @@
 #include <opensrf/osrf_hash.h>
 #include <opensrf/osrf_list.h>
 
-#include <objson/object.h>
-#include <objson/json_parser.h>
+#include <opensrf/osrf_json.h>
 
 
 
@@ -76,6 +75,9 @@ struct osrf_app_session_struct {
        /** SERVER or CLIENT */
        enum OSRF_SESSION_TYPE type;
 
+       /** the current locale for this session **/
+       char* session_locale;
+
        /* let the user use the session to store their own session data */
        void* userData;
 
@@ -100,6 +102,9 @@ osrf_app_session* osrf_app_client_session_init( char* remote_service );
 osrf_app_session* osrf_app_server_session_init( 
                char* session_id, char* our_app, char* remote_id );
 
+/** sets the default locale for a session **/
+char* osrf_app_session_set_locale( osrf_app_session*, const char* );
+
 /** returns a session from the global session hash */
 osrf_app_session* osrf_app_session_find_session( char* session_id );
 
@@ -115,6 +120,14 @@ int osrf_app_session_make_req(
                osrf_app_session* session, jsonObject* params, 
                char* method_name, int protocol, string_array* param_strings);
 
+int osrfAppSessionMakeLocaleRequest(
+               osrf_app_session* session, jsonObject* params, 
+               char* method_name, int protocol, string_array* param_strings, char* locale);
+
+int osrf_app_session_make_locale_req( 
+               osrf_app_session* session, jsonObject* params, 
+               char* method_name, int protocol, string_array* param_strings, char* locale);
+
 /** Sets the given request to complete state */
 void osrf_app_session_set_complete( osrf_app_session* session, int request_id );
 
index ac548a2..20bb5e0 100644 (file)
@@ -3,7 +3,7 @@
 #include <opensrf/osrf_app_session.h>
 #include <opensrf/osrf_hash.h>
 
-#include <objson/object.h>
+#include <opensrf/osrf_json.h>
 #include <stdio.h>
 #include <dlfcn.h>
 
index 5a755ff..cf7238d 100644 (file)
@@ -14,8 +14,7 @@ GNU General Public License for more details.
 */
 
 
-#include <objson/object.h>
-#include <objson/json_parser.h>
+#include <opensrf/osrf_json.h>
 #include <memcache.h>
 #include <opensrf/log.h>
 
@@ -81,3 +80,7 @@ int osrfCacheSetExpire( time_t seconds, char* key, ... );
 
 
 
+/**
+ * Clean up the global cache handles, etc.
+ */
+void osrfCacheCleanup();
diff --git a/include/opensrf/osrf_json.h b/include/opensrf/osrf_json.h
new file mode 100644 (file)
index 0000000..64844fd
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+
+#include <opensrf/utils.h>
+#include <opensrf/osrf_list.h>
+#include <opensrf/osrf_hash.h>
+
+#ifndef _JSON_H
+#define _JSON_H
+
+
+/* parser states */
+#define JSON_STATE_IN_OBJECT   0x1
+#define JSON_STATE_IN_ARRAY            0x2
+#define JSON_STATE_IN_STRING   0x4
+#define JSON_STATE_IN_UTF              0x8
+#define JSON_STATE_IN_ESCAPE   0x10
+#define JSON_STATE_IN_KEY              0x20
+#define JSON_STATE_IN_NULL             0x40
+#define JSON_STATE_IN_TRUE             0x80
+#define JSON_STATE_IN_FALSE            0x100
+#define JSON_STATE_IN_NUMBER   0x200
+#define JSON_STATE_IS_INVALID  0x400
+#define JSON_STATE_IS_DONE             0x800
+#define JSON_STATE_START_COMMEN        0x1000
+#define JSON_STATE_IN_COMMENT  0x2000
+#define JSON_STATE_END_COMMENT 0x4000
+
+
+/* object and array (container) states are pushed onto a stack so we
+ * can keep track of the object nest.  All other states are
+ * simply stored in the state field of the parser */
+#define JSON_STATE_SET(ctx,s) ctx->state |= s; /* set a state */
+#define JSON_STATE_REMOVE(ctx,s) ctx->state &= ~s; /* unset a state */
+#define JSON_STATE_CHECK(ctx,s) (ctx->state & s) ? 1 : 0 /* check if a state is set */
+#define JSON_STATE_POP(ctx) osrfListPop( ctx->stateStack ); /* remove a state from the stack */
+#define JSON_STATE_PUSH(ctx, state) osrfListPush( ctx->stateStack,(void*) state );/* push a state on the stack */
+#define JSON_STATE_PEEK(ctx) osrfListGetIndex(ctx->stateStack, ctx->stateStack->size -1) /* check which container type we're currently in */
+#define JSON_STATE_CHECK_STACK(ctx, s) (JSON_STATE_PEEK(ctx) == (void*) s ) ? 1 : 0  /* compare stack values */
+
+/* JSON types */
+#define JSON_HASH      0
+#define JSON_ARRAY     1
+#define JSON_STRING    2
+#define JSON_NUMBER    3
+#define JSON_NULL      4       
+#define JSON_BOOL      5
+
+#define JSON_PARSE_LAST_CHUNK 0x1 /* this is the last part of the string we're parsing */
+
+#define JSON_PARSE_FLAG_CHECK(ctx, f) (ctx->flags & f) ? 1 : 0 /* check if a parser state is set */
+
+#ifndef JSON_CLASS_KEY
+#define JSON_CLASS_KEY "__c"
+#endif
+#ifndef JSON_DATA_KEY
+#define JSON_DATA_KEY "__p"
+#endif
+
+
+struct jsonParserContextStruct {
+       int state;                                              /* what are we currently parsing */
+       const char* chunk;                              /* the chunk we're currently parsing */
+       int index;                                              /* where we are in parsing the current chunk */
+       int chunksize;                                  /* the size of the current chunk */
+       int flags;                                              /* parser flags */
+       osrfList* stateStack;           /* represents the nest of object/array states */
+       growing_buffer* buffer;         /* used to hold JSON strings, number, true, false, and null sequences */
+       growing_buffer* utfbuf;         /* holds the current unicode characters */
+       void* userData;                         /* opaque user pointer.  we ignore this */
+       struct jsonParserHandlerStruct* handler; /* the event handler struct */
+};
+typedef struct jsonParserContextStruct jsonParserContext;
+
+struct jsonParserHandlerStruct {
+       void (*handleStartObject)       (void* userData);
+       void (*handleObjectKey)         (void* userData, char* key);
+       void (*handleEndObject)         (void* userData);
+       void (*handleStartArray)        (void* userData);
+       void (*handleEndArray)          (void* userData);
+       void (*handleNull)                      (void* userData);
+       void (*handleString)                    (void* userData, char* string);
+       void (*handleBool)                      (void* userData, int boolval);
+       void (*handleNumber)                    (void* userData, double num);
+       void (*handleError)                     (void* userData, char* err, ...);
+};
+typedef struct jsonParserHandlerStruct jsonParserHandler;
+
+struct _jsonObjectStruct {
+       unsigned long size;     /* number of sub-items */
+       char* classname;                /* optional class hint (not part of the JSON spec) */
+       int type;                               /* JSON type */
+       struct _jsonObjectStruct* parent;       /* who we're attached to */
+       union __jsonValue {     /* cargo */
+               osrfHash*       h;              /* object container */
+               osrfList*       l;              /* array container */
+               char*           s;              /* string */
+               int                     b;              /* bool */
+//             double  n;              /* number */
+               double  n;              /* number */
+       } value;
+};
+typedef struct _jsonObjectStruct jsonObject;
+
+struct _jsonIteratorStruct {
+       jsonObject* obj; /* the object we're traversing */
+       osrfHashIterator* hashItr; /* the iterator for this hash */
+       char* key; /* if this object is an object, the current key */
+       unsigned long index; /* if this object is an array, the index */
+};
+typedef struct _jsonIteratorStruct jsonIterator;
+
+
+
+/** 
+ * Allocates a new parser context object
+ * @param handler The event handler struct
+ * @param userData Opaque user pointer which is available in callbacks
+ * and ignored by the parser
+ * @return An allocated parser context, NULL on error
+ */
+jsonParserContext* jsonNewParser( jsonParserHandler* handler, void* userData);
+
+/**
+ * Deallocates a parser context
+ * @param ctx The context object
+ */
+void jsonParserFree( jsonParserContext* ctx );
+
+/**
+ * Parse a chunk of data.
+ * @param ctx The parser context
+ * @param data The data to parse
+ * @param datalen The size of the chunk to parser
+ * @param flags Reserved
+ */
+int jsonParseChunk( jsonParserContext* ctx, const char* data, int datalen, int flags );
+
+
+/**
+ * Parses a JSON string;
+ * @param str The string to parser
+ * @return The resulting JSON object or NULL on error
+ */
+jsonObject* jsonParseString( const char* str );
+jsonObject* jsonParseStringRaw( const char* str );
+
+jsonObject* jsonParseStringFmt( const char* str, ... );
+
+/**
+ * Parses a JSON string;
+ * @param str The string to parser
+ * @return The resulting JSON object or NULL on error
+ */
+jsonObject* jsonParseStringHandleError( void (*errorHandler) (const char*), char* str, ... );
+
+
+
+/**
+ * Creates a new json object
+ * @param data The string data this object will hold if 
+ * this object happens to be a JSON_STRING, NULL otherwise
+ * @return The allocated json object.  Must be freed with 
+ * jsonObjectFree()
+ */
+jsonObject* jsonNewObject(const char* data);
+jsonObject* jsonNewObjectFmt(const char* data, ...);
+
+/**
+ * Creates a new object of the given type
+ */
+jsonObject* jsonNewObjectType(int type);
+
+/**
+ * Creates a new number object
+ */
+jsonObject* jsonNewNumberObject( double num );
+
+
+/**
+ * Creates a new json bool
+ */
+jsonObject* jsonNewBoolObject(int val);
+
+/**
+ * Deallocates an object
+ */
+void jsonObjectFree( jsonObject* o );
+
+/**
+ * Forces the given object to become an array (if it isn't already one) 
+ * and pushes the new object into the array
+ */
+unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo);
+
+/**
+ * Forces the given object to become a hash (if it isn't already one)
+ * and assigns the new object to the key of the hash
+ */
+unsigned long jsonObjectSetKey(
+               jsonObject* o, const char* key, jsonObject* newo);
+
+
+/**
+ * Turns the object into a JSON string.  The string must be freed by the caller */
+char* jsonObjectToJSON( const jsonObject* obj );
+char* jsonObjectToJSONRaw( const jsonObject* obj );
+
+
+/**
+ * Retrieves the object at the given key
+ */
+jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key );
+const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key );
+
+
+
+
+
+/** Allocates a new iterator 
+       @param obj The object over which to iterate.
+*/
+jsonIterator* jsonNewIterator(const jsonObject* obj);
+
+
+/** 
+       De-allocates an iterator 
+       @param iter The iterator object to free
+*/
+void jsonIteratorFree(jsonIterator* iter);
+
+/** 
+       Returns the object_node currently pointed to by the iterator
+       and increments the pointer to the next node
+       @param iter The iterator in question.
+ */
+jsonObject* jsonIteratorNext(jsonIterator* iter);
+
+
+/** 
+       @param iter The iterator.
+       @return True if there is another node after the current node.
+ */
+int jsonIteratorHasNext(const jsonIterator* iter);
+
+
+/** 
+       Returns a pointer to the object at the given index.  This call is
+       only valid if the object has a type of JSON_ARRAY.
+       @param obj The object
+       @param index The position within the object
+       @return The object at the given index.
+*/
+jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index );
+
+
+/* removes (and deallocates) the object at the given index (if one exists) and inserts 
+ * the new one.  returns the size on success, -1 on error 
+ * If obj is NULL, inserts a new object into the list with is_null set to true
+ */
+unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj);
+
+/* removes the object at the given index and, if more items exist,
+ * re-indexes (shifts down by 1) the rest of the objects in the array
+ */
+unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index);
+
+/* removes (and deallocates) the object with key 'key' if it exists */
+unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key);
+
+/* returns a pointer to the string data held by this object if this object
+       is a string.  Otherwise returns NULL*/
+char* jsonObjectGetString(const jsonObject*);
+
+double jsonObjectGetNumber( const jsonObject* obj );
+
+/* sets the string data */
+void jsonObjectSetString(jsonObject* dest, const char* string);
+
+/* sets the number value for the object */
+void jsonObjectSetNumber(jsonObject* dest, double num);
+
+/* sets the class hint for this object */
+void jsonObjectSetClass(jsonObject* dest, const char* classname );
+const char* jsonObjectGetClass(const jsonObject* dest);
+
+int jsonBoolIsTrue( jsonObject* boolObj );
+
+void jsonSetBool(jsonObject* bl, int val);
+
+jsonObject* jsonObjectClone( const jsonObject* o );
+
+
+/* tries to extract the string data from an object.
+       if object       -> NULL (the C NULL)
+       if array                ->      NULL  
+       if null         -> NULL 
+       if bool         -> NULL
+       if string/number the string version of either of those
+       The caller is responsible for freeing the returned string
+       */
+char* jsonObjectToSimpleString( const jsonObject* o );
+
+
+
+/* provides an XPATH style search interface (e.g. /some/node/here) and 
+       return the object at that location if one exists.  Naturally,  
+       every element in the path must be a proper object ("hash" / {}).
+       Returns NULL if the specified node is not found 
+       Note also that the object returned is a clone and
+       must be freed by the caller
+*/
+jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ... );
+
+
+/* formats a JSON string from printing.  User must free returned string */
+char* jsonFormatString( const char* jsonString );
+
+/* sets the error handler for all parsers */
+void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*));
+
+jsonObject* jsonParseFile( char* filename );
+
+/* ------------------------------------------------------------------------- */
+/**
+ * The following methods provide a facility for serializing and
+ * deserializing "classed" JSON objects.  To give a JSON object a 
+ * class, simply call jsonObjectSetClass().  
+ * Then, calling jsonObjectEncodeClass() will convert the JSON
+ * object (and any sub-objects) to a JSON object with class 
+ * wrapper objects like so:
+ * { _c : "classname", _d : <json_thing> }
+ * In this example _c is the class key and _d is the data (object)
+ * key.  The keys are defined by the constants 
+ * OSRF_JSON_CLASS_KEY and OSRF_JSON_DATA_KEY
+ * To revive a serialized object, simply call
+ * jsonObjectDecodeClass()
+ */
+
+
+/** Converts a class-wrapped object into an object with the
+ * classname set
+ * Caller must free the returned object 
+ */ 
+jsonObject* jsonObjectDecodeClass( const jsonObject* obj );
+
+
+/** Converts an object with a classname into a
+ * class-wrapped (serialized) object
+ * Caller must free the returned object 
+ */ 
+jsonObject* jsonObjectEncodeClass( const jsonObject* obj );
+
+/* ------------------------------------------------------------------------- */
+
+
+/**
+ *     Generates an XML representation of a JSON object */
+char* jsonObjectToXML(jsonObject*);
+
+
+/*
+ * Builds a JSON object from the provided XML 
+ */
+jsonObject* jsonXMLToJSONObject(const char* xml);
+
+
+#endif
diff --git a/include/opensrf/osrf_json_utils.h b/include/opensrf/osrf_json_utils.h
new file mode 100644 (file)
index 0000000..7b73474
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+
+/* ----------------------------------------------------------------------- */
+/* Clients need not include this file.  These are internal utilities only      */
+/* ----------------------------------------------------------------------- */
+
+#define JSON_EAT_WS(ctx)       \
+       while( ctx->index < ctx->chunksize ) {  \
+               if(!isspace(ctx->chunk[ctx->index])) break; \
+               ctx->index++;   \
+       } \
+       if( ctx->index >= ctx->chunksize ) return 0; \
+       c = ctx->chunk[ctx->index];
+
+#define JSON_CACHE_DATA(ctx, buf, size) \
+       while( (buf->n_used < size) && (ctx->index < ctx->chunksize) ) \
+               buffer_add_char(buf, ctx->chunk[ctx->index++]); 
+
+#define JSON_LOG_MARK __FILE__,__LINE__
+
+#define JSON_NUMBER_CHARS "0123456789.+-e"
+
+/**
+ * These are the callbacks through which the top level parser 
+ * builds objects via the push parser
+ */
+void _jsonHandleStartObject(void*);
+void _jsonHandleObjectKey(void*, char* key);
+void _jsonHandleEndObject(void*);
+void _jsonHandleStartArray(void*);
+void _jsonHandleEndArray(void*);
+void _jsonHandleNull(void*);
+void _jsonHandleString(void*, char* string);
+void _jsonHandleBool(void*, int boolval);
+void _jsonHandleNumber(void*, double num);
+void _jsonHandleError(void*, char* str, ...);
+
+struct jsonInternalParserStruct {
+       jsonParserContext* ctx;
+       jsonObject* obj;
+       jsonObject* current;
+       char* lastkey;
+       void (*handleError) (const char*);
+};
+typedef struct jsonInternalParserStruct jsonInternalParser;
+
+jsonInternalParser* _jsonNewInternalParser();
+void _jsonInternalParserFree(jsonInternalParser* p);
+
+/**
+ * Calls the defined error handler with the given error message.
+ * @return -1
+ */
+int _jsonParserError( jsonParserContext* ctx, char* err, ... );
+
+
+/**
+ *
+ * @return 0 on continue, 1 if it goes past the end of the string, -1 on error
+ */
+int _jsonParserHandleUnicode( jsonParserContext* ctx );
+
+
+/**
+ * @param type 0 for null, 1 for true, 2 for false
+ * @return 0 on continue, 1 if it goes past the end of the string, -1 on error
+ */
+int _jsonParserHandleMatch( jsonParserContext* ctx, int type );
+
+/**
+ * @return 0 on continue, 1 on end of chunk, -1 on error 
+ */
+int _jsonParserHandleString( jsonParserContext* ctx );
+
+/**
+ * @return 0 on continue, 1 on end of chunk, -1 on error 
+ */
+int _jsonParserHandleNumber( jsonParserContext* ctx );
+
+
+void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo );
+
+
+/* Utility method. finds any object in the tree that matches the path.  
+       Use this for finding paths that start with '//' */
+jsonObject* _jsonObjectFindPathRecurse( const jsonObject* o, char* root, char* path );
+
+
+/* returns a list of object whose key is 'root'.  These are used as
+       potential objects when doing a // search */
+jsonObject* __jsonObjectFindPathRecurse( const jsonObject* o, char* root );
+
+
diff --git a/include/opensrf/osrf_json_xml.h b/include/opensrf/osrf_json_xml.h
new file mode 100644 (file)
index 0000000..c01b1ac
--- /dev/null
@@ -0,0 +1,26 @@
+#ifdef OSRF_JSON_ENABLE_XML_UTILS
+
+#include <stdio.h>
+#include <string.h>
+#include <libxml/globals.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+
+#include <opensrf/osrf_json.h>
+#include <opensrf/utils.h>
+#include <opensrf/osrf_list.h>
+
+
+/**
+ *     Generates an XML representation of a JSON object */
+char* jsonObjectToXML(jsonObject*);
+
+
+/*
+ * Builds a JSON object from the provided XML 
+ */
+jsonObject* jsonXMLToJSONObject(const char* xml);
+
+#endif
diff --git a/include/opensrf/osrf_legacy_json.h b/include/opensrf/osrf_legacy_json.h
new file mode 100644 (file)
index 0000000..a825a8d
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+Copyright (C) 2005  Georgia Public Library Service 
+Bill Erickson <highfalutin@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+
+
+
+/* ---------------------------------------------------------------------------------------
+       JSON parser.
+ * --------------------------------------------------------------------------------------- */
+#ifndef LEGACY_JSON_H
+#define LEGACY_JSON_H
+
+#include <opensrf/osrf_json.h>
+#include <ctype.h>
+
+
+
+/* Parses the given JSON string and returns the built object. 
+ *     returns NULL (and prints parser error to stderr) on error.  
+ */
+
+jsonObject* json_parse_string(char* string);
+
+jsonObject* legacy_jsonParseString(const char* string);
+jsonObject* legacy_jsonParseStringFmt( const char* string, ... );
+
+jsonObject* json_parse_file( const char* filename );
+
+jsonObject* legacy_jsonParseFile( const char* string );
+
+
+
+/* does the actual parsing work.  returns 0 on success.  -1 on error and
+ * -2 if there was no object to build (string was all comments) 
+ */
+int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns obj into a string object */
+int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns obj into a number or double object */
+int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns obj into an 'object' object */
+int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns object into an array object */
+int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* churns through whitespace and increments index as it goes.
+ * eat_all == true means we should eat newlines, tabs
+ */
+void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen);
+
+int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* removes comments from a json string.  if the comment contains a class hint
+ * and class_hint isn't NULL, an allocated char* with the class name will be
+ * shoved into *class_hint.  returns 0 on success, -1 on parse error.
+ * 'index' is assumed to be at the second character (*) of the comment
+ */
+int json_eat_comment(char* string, unsigned long* index, char** class_hint, int parse_class, int current_strlen);
+
+/* prints a useful error message to stderr. always returns -1 */
+int json_handle_error(char* string, unsigned long* index, char* err_msg);
+
+int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+
+char* legacy_jsonObjectToJSON( const jsonObject* obj );
+
+
+
+/* LEGACY ITERATOR CODE ---------------------------------------------------  
+   ------------------------------------------------------------------------ */
+
+struct _jsonObjectNodeStruct {
+       unsigned long index; /* our array position */
+       char* key; /* our hash key */
+       jsonObject* item; /* our object */
+};
+typedef struct _jsonObjectNodeStruct jsonObjectNode;
+
+
+
+/* utility object for iterating over hash objects */
+struct _jsonObjectIteratorStruct {
+    jsonIterator* iterator;
+       const jsonObject* obj; /* the topic object */
+       jsonObjectNode* current; /* the current node within the object */
+    int done;
+};
+typedef struct _jsonObjectIteratorStruct jsonObjectIterator;
+
+
+/** Allocates a new iterator 
+       @param obj The object over which to iterate.
+*/
+jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj);
+
+/** 
+       De-allocates an iterator 
+       @param iter The iterator object to free
+*/
+void jsonObjectIteratorFree(jsonObjectIterator* iter);
+
+/** 
+       Returns the object_node currently pointed to by the iterator
+       and increments the pointer to the next node
+       @param iter The iterator in question.
+ */
+jsonObjectNode* jsonObjectIteratorNext(jsonObjectIterator* iter);
+
+/** 
+       @param iter The iterator.
+       @return True if there is another node after the current node.
+ */
+int jsonObjectIteratorHasNext(const jsonObjectIterator* iter);
+
+
+#endif
+
+
index 12e14c6..57f1aa5 100644 (file)
@@ -1,8 +1,7 @@
 #include <opensrf/string_array.h>
 #include <opensrf/utils.h>
 #include <opensrf/log.h>
-#include <objson/object.h>
-#include <objson/json_parser.h>
+#include <opensrf/osrf_json.h>
 
 
 /* libxml stuff for the config reader */
@@ -76,22 +75,44 @@ struct osrf_message_struct {
 
        char* full_param_string;
 
+       /* magical LOCALE hint */
+       char* sender_locale;
+
+       /* timezone offset from GMT of sender, in seconds */
+       int sender_tz_offset;
+
 };
 typedef struct osrf_message_struct osrf_message;
 typedef struct osrf_message_struct osrfMessage;
 
+/* Set the locale hint for this message.
+   default_locale is used if not set.
+   Returns NULL if msg or locale is not set, char* to msg->sender_locale on success.
+*/
+char* osrf_message_set_locale( osrf_message* msg, const char* locale );
+
+/* Set the default locale hint to be used for future outgoing messages.
+   Returns NULL if locale is NULL, const char* to default_locale otherwise.
+*/
+const char* osrf_message_set_default_locale( const char* locale );
+
+/* Get the current locale hint -- either the default or most recently received locale.
+   Returns const char* to current_locale.
+*/
+const char* osrf_message_get_current_locale(void);
 
 osrf_message* osrf_message_init( enum M_TYPE type, int thread_trace, int protocol );
 //void osrf_message_set_request_info( osrf_message*, char* param_name, json* params );
-void osrf_message_set_status_info( osrf_message*, char* status_name, char* status_text, int status_code );
-void osrf_message_set_result_content( osrf_message*, char* json_string );
+void osrf_message_set_status_info( osrf_message*,
+               const char* status_name, const char* status_text, int status_code );
+void osrf_message_set_result_content( osrf_message*, const char* json_string );
 void osrfMessageFree( osrfMessage* );
 void osrf_message_free( osrf_message* );
 char* osrf_message_to_xml( osrf_message* );
-char* osrf_message_serialize(osrf_message*);
+char* osrf_message_serialize(const osrf_message*);
 
 /* count is the max number of messages we'll put into msgs[] */
-int osrf_message_deserialize(char* json, osrf_message* msgs[], int count);
+int osrf_message_deserialize(const char* json, osrf_message* msgs[], int count);
 
 
 
@@ -101,10 +122,10 @@ int osrf_message_deserialize(char* json, osrf_message* msgs[], int count);
   */
 int osrf_message_from_xml( char* xml, osrf_message* msgs[] );
 
-void osrf_message_set_params( osrf_message* msg, jsonObject* o );
-void osrf_message_set_method( osrf_message* msg, char* method_name );
-void osrf_message_add_object_param( osrf_message* msg, jsonObject* o );
-void osrf_message_add_param( osrf_message*, char* param_string );
+void osrf_message_set_params( osrf_message* msg, const jsonObject* o );
+void osrf_message_set_method( osrf_message* msg, const char* method_name );
+void osrf_message_add_object_param( osrf_message* msg, const jsonObject* o );
+void osrf_message_add_param( osrf_message*, const char* param_string );
 
 
 jsonObject* osrfMessageGetResult( osrfMessage* msg );
@@ -113,7 +134,7 @@ jsonObject* osrfMessageGetResult( osrfMessage* msg );
   Returns the message as a jsonObject
   @return The jsonObject which must be freed by the caller.
   */
-jsonObject* osrfMessageToJSON( osrfMessage* msg );
+jsonObject* osrfMessageToJSON( const osrfMessage* msg );
 
 char* osrfMessageSerializeBatch( osrfMessage* msgs [], int count );
 
index 9aa0d47..94b399f 100644 (file)
@@ -12,8 +12,7 @@
 #include <opensrf/utils.h>
 #include <opensrf/osrf_app_session.h>
 
-#include <objson/object.h>
-#include <objson/json_parser.h>
+#include <opensrf/osrf_json.h>
 
 typedef struct { 
        char* hostname; 
@@ -21,11 +20,11 @@ typedef struct {
 } osrf_host_config;
 
 
-osrf_host_config* osrf_settings_new_host_config(char* hostname);
+osrf_host_config* osrf_settings_new_host_config(const char* hostname);
 void osrf_settings_free_host_config(osrf_host_config*);
-char* osrf_settings_host_value(char* path, ...);
-jsonObject* osrf_settings_host_value_object(char* format, ...);
-int osrf_settings_retrieve(char* hostname);
+char* osrf_settings_host_value(const char* path, ...);
+jsonObject* osrf_settings_host_value_object(const char* format, ...);
+int osrf_settings_retrieve(const char* hostname);
 
 #endif
 
index 77b3de0..70b0038 100644 (file)
@@ -2,17 +2,20 @@
 
 #include <opensrf/utils.h>
 #include <opensrf/log.h>
+#include <opensrf/osrf_list.h>
 
-#define STRING_ARRAY_MAX_SIZE 1024
+#define STRING_ARRAY_MAX_SIZE 4096
 
 #ifndef STRING_ARRAY_H
 #define STRING_ARRAY_H
 
+#define OSRF_STRING_ARRAY_FREE(arr)\
+    if(arr) {osrfListFree(arr->list); free(arr);}
+        
+
 struct string_array_struct {
-               char** array;   
-               int size;
-               int arr_size;
-               int total_string_size;
+    osrfList* list;
+    int size;
 };
 typedef struct string_array_struct string_array;
 typedef struct string_array_struct osrfStringArray;
index 7bbade0..ebf05a5 100644 (file)
@@ -41,6 +41,18 @@ GNU General Public License for more details.
                memset( ptr, 0, size );\
        } while(0)
 
+#ifndef NDEBUG
+// The original ... replace with noop once no more errors occur in NDEBUG mode
+#define osrf_clearbuf( s, n ) memset( s, 0, n )
+#else
+#define osrf_clearbuf( s, n ) \
+       do { \
+               char * clearbuf_temp_s = (s); \
+               size_t clearbuf_temp_n = (n); \
+               memset( clearbuf_temp_s, '!', clearbuf_temp_n ); \
+               clearbuf_temp_s[ clearbuf_temp_n - 1 ] = '\0'; \
+       } while( 0 )
+#endif
 
 #define OSRF_BUFFER_ADD(gb, data) \
        do {\
@@ -64,6 +76,10 @@ GNU General Public License for more details.
                }\
        }while(0)
 
+#define OSRF_BUFFER_RESET(gb) \
+    memset(gb->buf, 0, gb->size);\
+    gb->n_used = 0;
+
        
 
 
@@ -155,6 +171,7 @@ int set_proc_title( char* format, ... );
 int daemonize();
 
 void* safe_malloc(int size);
+void* safe_calloc(int size);
 
 // ---------------------------------------------------------------------------------
 // Generic growing buffer. Add data all you want
index 2b3a030..e0321a4 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _XML_UTILS_H
 #define _XML_UTILS_H
 
-#include <objson/object.h>
+#include <opensrf/osrf_json.h>
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 
index 35911d6..667f634 100644 (file)
@@ -12,3 +12,4 @@ export APXS2=/usr/bin/apxs2
 export APACHE2_HEADERS=/usr/include/apache2
 export APR_HEADERS=/usr/include/apr-1.0/
 export LIBXML2_HEADERS=/usr/include/libxml2/
+export OSRF_LEGACY_JSON=1
index 14823ab..04b627a 100644 (file)
@@ -1,17 +1,22 @@
 # TOP level 'src' makefile for OpenSRF
 
 
-export TMPDIR                  = $(TMP)/opensrf
-export OPENSRF                 = opensrf
-export BINDIR                  = $(PREFIX)/bin
-export LIBDIR                  = $(PREFIX)/lib
-export PERLDIR                 = $(LIBDIR)/perl5
-export INCLUDEDIR              = $(PREFIX)/include
-
-export LDLIBS                  += 
-export LDFLAGS                 += -Wl,-rpath=$(LIBDIR) -L $(TMPDIR) -L .
-export CFLAGS                  += -pipe -g -Wall -O2 -fPIC -I ../../include/ -I$(LIBXML2_HEADERS) -I$(APACHE2_HEADERS) \
-                                                               -I$(LIBXML2_HEADERS)/libxml -I$(APR_HEADERS) 
+export TMPDIR  = $(TMP)/opensrf
+export OPENSRF = opensrf
+export BINDIR  = $(PREFIX)/bin
+export LIBDIR  = $(PREFIX)/lib
+export PERLDIR = $(LIBDIR)/perl5
+export PYTHON25DIR     = $(LIBDIR)/python25
+export JSDIR   = $(LIBDIR)/javascript
+export INCLUDEDIR= $(PREFIX)/include
+
+export LDLIBS  += 
+export LDFLAGS += -Wl,-rpath=$(LIBDIR) -L $(TMPDIR) -L .
+export CFLAGS  += -D_LARGEFILE64_SOURCE -pipe -g -Wall -O2 -fPIC -I ../../include/ -I$(LIBXML2_HEADERS) -I$(APACHE2_HEADERS) -I$(APR_HEADERS) 
+
+ifeq ($(OSRF_LEGACY_JSON), 1)
+export LDLIBS += -lobjson
+endif
 
 all:   prep \
        opensrf \
@@ -26,8 +31,9 @@ install:      install-prep \
                router-install \
                srfsh-install \
                jserver-install \
+               javascript-install \
                perl-install \
-               objson-install
+               python25-install
 
 
 # --------------------------------------------------------------------------------
@@ -37,11 +43,7 @@ install:     install-prep \
 prep:
        mkdir -p $(TMPDIR)
 
-objson/libobjson.so:   prep
-       @echo $@
-       make -C objson
-
-opensrf:       objson/libobjson.so
+opensrf:       prep
        make -C libopensrf
        make -C c-apps
 
@@ -71,16 +73,14 @@ install-prep:
        @echo $@
        mkdir -p $(LIBDIR)
        mkdir -p $(BINDIR)
+       mkdir -p $(JSDIR)
        mkdir -p $(PERLDIR)
+       mkdir -p $(PYTHON25DIR)
        mkdir -p $(INCLUDEDIR)
        mkdir -p $(ETCDIR)
 
-objson-install:        install-prep 
-       @echo $@
-       make -C objson install
-
 # installs libopensrf.so, opensrf-c, headers, example configs, and osrf_ctl.sh
-opensrf-install:       objson-install
+opensrf-install:       install-prep
        @echo $@
        cp $(TMPDIR)/libopensrf.so $(LIBDIR)/libopensrf.so
        cp -r ../include/opensrf $(INCLUDEDIR)
@@ -90,6 +90,10 @@ opensrf-install:     objson-install
        cp ../examples/opensrf.xml.example $(ETCDIR)
        cp ../examples/opensrf_core.xml.example $(ETCDIR)
        cp ../examples/srfsh.xml.example $(ETCDIR)
+       if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then\
+               cp -r ../include/objson $(INCLUDEDIR);\
+               cp $(TMPDIR)/libobjson.so $(LIBDIR);\
+       fi
 
 gateway-install:       install-prep opensrf-install    
        @echo $@
@@ -107,10 +111,18 @@ jserver-install:  install-prep
        @echo $@
        make -C jserver install
 
+javascript-install:    install-prep
+       @echo $@
+       cp -r javascript/* $(JSDIR)/
+
 perl-install:  install-prep
        @echo $@
        cp -r perlmods/* $(PERLDIR)/
 
+python25-install:      install-prep
+       @echo $@
+       cp -r python/* $(PYTHON25DIR)/
+
 
 # --------------------------------------------------------------------------------
 # CLEAN        
@@ -120,7 +132,6 @@ clean:
        make -C router clean
        make -C gateway clean
        make -C jserver clean
-       make -C objson clean
        make -C srfsh clean
        make -C c-apps clean
        /bin/rm -rf $(TMPDIR) *.o
index ceda792..cd172bd 100644 (file)
@@ -1,5 +1,5 @@
-LDLIBS += -lobjson -lopensrf
-CFLAGS += -DOSRF_LOG_PARAMS
+LDLIBS += -lopensrf
+CFLAGS += -D_LARGEFILE64_SOURCE -DOSRF_LOG_PARAMS
 
 all:   osrf_math.so osrf_dbmath.so osrf_version.so
 
index 0b930cd..a88db87 100644 (file)
@@ -1,7 +1,7 @@
-#include "opensrf/osrf_app_session.h"
-#include "opensrf/osrf_application.h"
-#include "objson/object.h"
-#include "opensrf/log.h"
+#include <opensrf/osrf_app_session.h>
+#include <opensrf/osrf_application.h>
+#include <opensrf/osrf_json.h>
+#include <opensrf/log.h>
 
 #define MODULENAME "opensrf.dbmath"
 
index 63d168a..7c3cf29 100644 (file)
@@ -1,7 +1,7 @@
-#include "opensrf/osrf_app_session.h"
-#include "opensrf/osrf_application.h"
-#include "objson/object.h"
-#include "opensrf/log.h"
+#include <opensrf/osrf_app_session.h>
+#include <opensrf/osrf_application.h>
+#include <opensrf/osrf_json.h>
+#include <opensrf/log.h>
 
 #define MODULENAME "opensrf.math"
 
index 1affb70..9d07874 100644 (file)
@@ -1,6 +1,6 @@
 #include "opensrf/osrf_app_session.h"
 #include "opensrf/osrf_application.h"
-#include "objson/object.h"
+#include "opensrf/osrf_json.h"
 #include "opensrf/utils.h"
 #include "opensrf/log.h"
 
index 2120b23..3d60411 100644 (file)
@@ -1,6 +1,6 @@
 #CFLAGS        += -DASSUME_STATELESS -DOSRF_GATEWAY_NASTY_DEBUG
 CFLAGS += -DASSUME_STATELESS 
-LDLIBS += -lobjson -lopensrf
+LDLIBS += -lopensrf
 
 all: osrf_json_gateway.so copy
 
index d70bfe6..3570310 100644 (file)
@@ -24,7 +24,7 @@ string_array* apacheParseParms(request_rec* r) {
                if(ap_should_client_block(r)) {
 
                        char body[1025];
-                       memset(body,0,1025);
+                       memset(body,0,sizeof(body));
                        buffer = buffer_init(1025);
 
 
@@ -40,7 +40,7 @@ string_array* apacheParseParms(request_rec* r) {
                                }
 
                                buffer_add( buffer, body );
-                               memset(body,0,1025);
+                               memset(body,0,sizeof(body));
 
                                osrfLogDebug(OSRF_LOG_MARK, 
                                        "gateway read %d bytes: %d bytes of data so far", bread, buffer->n_used);
@@ -49,7 +49,7 @@ string_array* apacheParseParms(request_rec* r) {
 
                                if(buffer->n_used > APACHE_TOOLS_MAX_POST_SIZE) {
                                        osrfLogError(OSRF_LOG_MARK, "gateway received POST larger "
-                                               "than %d bytes. dropping reqeust", APACHE_TOOLS_MAX_POST_SIZE);
+                                               "than %d bytes. dropping request", APACHE_TOOLS_MAX_POST_SIZE);
                                        buffer_free(buffer);
                                        return NULL;
                                }
index 80bf722..cce7e8b 100644 (file)
@@ -2,17 +2,21 @@
 #include "opensrf/osrf_app_session.h"
 #include "opensrf/osrf_system.h"
 #include "opensrf/osrfConfig.h"
-#include "objson/object.h"
-#include "objson/json2xml.h"
-#include "objson/xml2json.h"
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_xml.h>
+#include <opensrf/osrf_legacy_json.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <unistd.h>
+#include <strings.h>
 
 
 #define MODULE_NAME "osrf_json_gateway_module"
 #define GATEWAY_CONFIG "OSRFGatewayConfig"
+#define DEFAULT_LOCALE "OSRFDefaultLocale"
 #define CONFIG_CONTEXT "gateway"
+#define JSON_PROTOCOL "OSRFGatewayLegacyJSON"
+#define GATEWAY_USE_LEGACY_JSON 1
 
 #define GATEWAY_DEFAULT_CONFIG "/openils/conf/opensrf_core.xml"
 
@@ -22,13 +26,25 @@ typedef struct {
        char* configfile;  /* our bootstrap config file */
 } osrf_json_gateway_config;
 
+typedef struct { 
+       int legacyJSON;
+} osrf_json_gateway_dir_config;
+
+
 module AP_MODULE_DECLARE_DATA osrf_json_gateway_module;
 
+char* osrf_json_default_locale = "en-US";
 char* osrf_json_gateway_config_file = NULL;
 int bootstrapped = 0;
 int numserved = 0;
 osrfStringArray* allowedServices = NULL;
 
+static const char* osrf_json_gateway_set_default_locale(cmd_parms *parms, void *config, const char *arg) {
+       if (arg)
+               osrf_json_default_locale = (char*) arg;
+       return NULL;
+}
+
 static const char* osrf_json_gateway_set_config(cmd_parms *parms, void *config, const char *arg) {
        osrf_json_gateway_config  *cfg;
        cfg = ap_get_module_config(parms->server->module_config, &osrf_json_gateway_module);
@@ -37,10 +53,20 @@ static const char* osrf_json_gateway_set_config(cmd_parms *parms, void *config,
        return NULL;
 }
 
+static const char* osrf_json_gateway_set_json_proto(cmd_parms *parms, void *config, const char *arg) {
+       osrf_json_gateway_dir_config* cfg = (osrf_json_gateway_dir_config*) config;
+       cfg->legacyJSON = (!strcasecmp((char*) arg, "false")) ? 0 : 1;
+       return NULL;
+}
+
 /* tell apache about our commands */
 static const command_rec osrf_json_gateway_cmds[] = {
        AP_INIT_TAKE1( GATEWAY_CONFIG, osrf_json_gateway_set_config, 
                        NULL, RSRC_CONF, "osrf json gateway config file"),
+       AP_INIT_TAKE1( DEFAULT_LOCALE, osrf_json_gateway_set_default_locale, 
+                       NULL, RSRC_CONF, "osrf json gateway default locale"),
+       AP_INIT_TAKE1( JSON_PROTOCOL, osrf_json_gateway_set_json_proto,
+                       NULL, ACCESS_CONF, "osrf json gateway config file"),
        {NULL}
 };
 
@@ -52,14 +78,20 @@ static void* osrf_json_gateway_create_config( apr_pool_t* p, server_rec* s) {
        return (void*) cfg;
 }
 
+static void* osrf_json_gateway_create_dir_config( apr_pool_t* p, char* dir) {
+       osrf_json_gateway_dir_config* cfg = (osrf_json_gateway_dir_config*) 
+                       apr_palloc(p, sizeof(osrf_json_gateway_dir_config));
+       cfg->legacyJSON = GATEWAY_USE_LEGACY_JSON;
+       return (void*) cfg;
+}
+
 
 static void osrf_json_gateway_child_init(apr_pool_t *p, server_rec *s) {
 
        char* cfg = osrf_json_gateway_config_file;
        char buf[32];
-       memset(buf, 0x0, 32);
        int t = time(NULL);
-       snprintf(buf, 32, "%d", t);
+       snprintf(buf, sizeof(buf), "%d", t);
 
        if( ! osrfSystemBootstrapClientResc( cfg, CONFIG_CONTEXT, buf ) ) {
                ap_log_error( APLOG_MARK, APLOG_ERR, 0, s, 
@@ -84,6 +116,25 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
        /* make sure we're needed first thing*/
        if (strcmp(r->handler, MODULE_NAME )) return DECLINED;
 
+
+       osrf_json_gateway_dir_config* dir_conf =  
+               ap_get_module_config(r->per_dir_config, &osrf_json_gateway_module);
+
+
+       /* provide 2 different JSON parsers and serializers to support legacy JSON */
+       jsonObject* (*parseJSONFunc) (const char*) = legacy_jsonParseString;
+       char* (*jsonToStringFunc) (const jsonObject*) = legacy_jsonObjectToJSON;
+
+       if(dir_conf->legacyJSON) {
+               ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, "Using legacy JSON");
+
+       } else {
+               ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, "Not using legacy JSON");
+               parseJSONFunc = jsonParseString;
+               jsonToStringFunc = jsonObjectToJSON;
+       }
+
+
        osrfLogDebug(OSRF_LOG_MARK, "osrf gateway: entered request handler");
 
        /* verify we are connected */
@@ -96,40 +147,42 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
 
        osrfLogSetAppname("osrf_json_gw");
 
+       char* osrf_locale       = NULL;
+       char* param_locale      = NULL; /* locale for this call */
        char* service           = NULL; /* service to connect to */
        char* method            = NULL; /* method to perform */
        char* format            = NULL; /* method to perform */
-       char* a_l                       = NULL; /* request api level */
-    char* input_format  = NULL; /* POST data format, defaults to 'format' */
-       int   isXML                     = 0;
-       int   api_level = 1;
+       char* a_l               = NULL; /* request api level */
+       char* input_format      = NULL; /* POST data format, defaults to 'format' */
+       int   isXML             = 0;
+       int   api_level         = 1;
 
        r->allowed |= (AP_METHOD_BIT << M_GET);
        r->allowed |= (AP_METHOD_BIT << M_POST);
 
        osrfLogDebug(OSRF_LOG_MARK, "osrf gateway: parsing URL params");
        string_array* mparams   = NULL;
-       string_array* params            = apacheParseParms(r); /* free me */
-       service         = apacheGetFirstParamValue( params, "service" );
-       method          = apacheGetFirstParamValue( params, "method" ); 
-       format          = apacheGetFirstParamValue( params, "format" ); 
-       input_format = apacheGetFirstParamValue( params, "input_format" ); 
+       string_array* params    = apacheParseParms(r); /* free me */
+       param_locale            = apacheGetFirstParamValue( params, "locale" );
+       service                 = apacheGetFirstParamValue( params, "service" );
+       method                  = apacheGetFirstParamValue( params, "method" ); 
+       format                  = apacheGetFirstParamValue( params, "format" ); 
+       input_format            = apacheGetFirstParamValue( params, "input_format" ); 
        a_l                     = apacheGetFirstParamValue( params, "api_level" ); 
-       mparams         = apacheGetParamValues( params, "param" ); /* free me */
-
-    if(format == NULL)
-        format = "json";
-    if(input_format == NULL)
-        input_format = format;
-
-   /* set the user defined timeout value */
-   int timeout = 60;
-   char* tout = apacheGetFirstParamValue( params, "timeout" ); /* request timeout in seconds */
-   if( tout ) {
-      timeout = atoi(tout);
-      osrfLogDebug(OSRF_LOG_MARK, "Client supplied timeout of %d", timeout);
-   }
-
+       mparams                 = apacheGetParamValues( params, "param" ); /* free me */
+
+       if(format == NULL)
+               format = "json";
+       if(input_format == NULL)
+               input_format = format;
+
+       /* set the user defined timeout value */
+       int timeout = 60;
+       char* tout = apacheGetFirstParamValue( params, "timeout" ); /* request timeout in seconds */
+       if( tout ) {
+               timeout = atoi(tout);
+               osrfLogDebug(OSRF_LOG_MARK, "Client supplied timeout of %d", timeout);
+       }
 
        if (a_l)
                api_level = atoi(a_l);
@@ -143,6 +196,38 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
 
        int ret = OK;
 
+       /* ----------------------------------------------------------------- */
+       /* Grab the requested locale using the Accept-Language header*/
+
+
+       if ( !param_locale ) {
+               if ( apr_table_get(r->headers_in, "X-OpenSRF-Language") ) {
+                       param_locale = strdup( apr_table_get(r->headers_in, "X-OpenSRF-Language") );
+               } else if ( apr_table_get(r->headers_in, "Accept-Language") ) {
+                       param_locale = strdup( apr_table_get(r->headers_in, "Accept-Language") );
+               }
+       }
+
+
+       if (param_locale) {
+               growing_buffer* osrf_locale_buf = buffer_init(16);      
+               if (index(param_locale, ',')) {
+                       int ind = index(param_locale, ',') - param_locale;
+                       int i;
+                       for ( i = 0; i < ind && i < 128; i++ )
+                               buffer_add_char( osrf_locale_buf, param_locale[i] );
+               } else {
+                       buffer_add( osrf_locale_buf, param_locale );
+               }
+
+               free(param_locale);
+               osrf_locale = buffer_release( osrf_locale_buf );
+       } else {
+               osrf_locale = strdup( osrf_json_default_locale );
+       }
+       /* ----------------------------------------------------------------- */
+
+
        if(!(service && method) || 
                !osrfStringArrayContains(allowedServices, service)) {
 
@@ -164,31 +249,39 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
                */
 
                osrfAppSession* session = osrf_app_client_session_init(service);
+               osrf_app_session_set_locale(session, osrf_locale);
 
                double starttime = get_timestamp_millis();
                int req_id = -1;
 
-        if(!strcasecmp(input_format, "json")) {
-                   req_id = osrf_app_session_make_req( session, NULL, method, api_level, mparams );
+               if(!strcasecmp(input_format, "json")) {
+                       jsonObject * arr = jsonNewObject(NULL);
+
+                       char* str;
+                       int i = 0;
 
-        } else {
+                       while( (str = osrfStringArrayGetString(mparams, i++)) ) 
+                               jsonObjectPush(arr, parseJSONFunc(str));
 
-            /**
-             * If we receive XML method params, convert each param to a JSON object
-             * and pass the array of JSON object params to the method */
-            if(!strcasecmp(input_format, "xml")) {
-                jsonObject* jsonParams = jsonNewObject(NULL);
+                       req_id = osrf_app_session_make_req( session, arr, method, api_level, NULL );
+               } else {
 
-                char* str;
-                int i = 0;
-                while( (str = osrfStringArrayGetString(mparams, i++)) ) {
-                    jsonObjectPush(jsonParams, jsonXMLToJSONObject(str));
-                }
+                       /**
+                       * If we receive XML method params, convert each param to a JSON object
+                       * and pass the array of JSON object params to the method */
+                       if(!strcasecmp(input_format, "xml")) {
+                               jsonObject* jsonParams = jsonNewObject(NULL);
 
-                       req_id = osrf_app_session_make_req( session, jsonParams, method, api_level, NULL );
-                jsonObjectFree(jsonParams);
-            }
-        }
+                               char* str;
+                               int i = 0;
+                               while( (str = osrfStringArrayGetString(mparams, i++)) ) {
+                                       jsonObjectPush(jsonParams, jsonXMLToJSONObject(str));
+                               }
+
+                               req_id = osrf_app_session_make_req( session, jsonParams, method, api_level, NULL );
+                               jsonObjectFree(jsonParams);
+                       }
+               }
 
 
                if( req_id == -1 ) {
@@ -209,7 +302,7 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
                char* str; int i = 0;
                while( (str = osrfStringArrayGetString(mparams, i++)) ) {
                        if( i == 1 ) {
-            OSRF_BUFFER_ADD(act, " ");
+                               OSRF_BUFFER_ADD(act, " ");
                                OSRF_BUFFER_ADD(act, str);
                        } else {
                                OSRF_BUFFER_ADD(act, ", ");
@@ -247,7 +340,7 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
                                if (isXML) {
                                        output = jsonObjectToXML( res );
                                } else {
-                                       output = jsonObjectToJSON( res );
+                                       output = jsonToStringFunc( res );
                                        if( morethan1 ) ap_rputs(",", r); /* comma between JSON array items */
                                }
                                ap_rputs(output, r);
@@ -283,18 +376,16 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
                                        "OpenSRF JSON Request returned error: %s -> %s", statusname, statustext );
                        int l = strlen(statusname) + strlen(statustext) + 32;
                        char buf[l];
-                       bzero(buf,l);
 
                        if (isXML)
-                               snprintf( buf, l, "<debug>\"%s : %s\"</debug>", statusname, statustext );
+                               snprintf( buf, sizeof(buf), "<debug>\"%s : %s\"</debug>", statusname, statustext );
 
                        else {
                                char bb[l];
-                               bzero(bb, l);
-                               snprintf(bb, l,  "%s : %s", statusname, statustext);
+                               snprintf(bb, sizeof(bb),  "%s : %s", statusname, statustext);
                                jsonObject* tmp = jsonNewObject(bb);
-                               char* j = jsonObjectToJSON(tmp);
-                               snprintf( buf, l, ",\"debug\": %s", j);
+                               char* j = jsonToStringFunc(tmp);
+                               snprintf( buf, sizeof(buf), ",\"debug\": %s", j);
                                free(j);
                                jsonObjectFree(tmp);
                        }
@@ -307,12 +398,11 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
 
                /* insert the status code */
                char buf[32];
-               bzero(buf,32);
 
                if (isXML)
-                       snprintf(buf, 32, "<status>%d</status>", statuscode );
+                       snprintf(buf, sizeof(buf), "<status>%d</status>", statuscode );
                else
-                       snprintf(buf, 32, ",\"status\":%d", statuscode );
+                       snprintf(buf, sizeof(buf), ",\"status\":%d", statuscode );
 
                ap_rputs( buf, r );
 
@@ -329,7 +419,7 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
        string_array_destroy(mparams);
 
        osrfLogDebug(OSRF_LOG_MARK, "Gateway served %d requests", ++numserved);
-   osrfLogClearXid();
+       osrfLogClearXid();
 
        return ret;
 }
@@ -344,7 +434,7 @@ static void osrf_json_gateway_register_hooks (apr_pool_t *p) {
 
 module AP_MODULE_DECLARE_DATA osrf_json_gateway_module = {
        STANDARD20_MODULE_STUFF,
-       NULL,
+       osrf_json_gateway_create_dir_config,
        NULL,
        osrf_json_gateway_create_config,
        NULL,
index 7bcd020..a04845c 100644 (file)
@@ -1,18 +1,43 @@
-#JAVA_LIBS = .:lib:ext/json-jdk1.5-2007-05-01.jar:ext/wstx-asl-3.2.1.jar:ext/stax-api-1.0.1.jar:ext/xercesImpl-1.4.4-2.jar
-JAVA_LIBDIR = .lib
+# dependencies
+STAX=stax-api-1.0.1.jar
+WSTX=wstx-lgpl-3.2.1.jar
+MEMCACHE=java_memcached-release_1.5.1.jar
+JSON=json.zip
+
+STAX_URL=http://woodstox.codehaus.org/$(STAX)
+WSTX_URL=http://woodstox.codehaus.org/3.2.1/$(WSTX)
+MEMCACHE_URL=http://img.whalin.com/memcached/jdk5/standard/$(MEMCACHE)
+JSON_URL=http://www.json.org/java/$(JSON)
+
+
 JAVAC=javac -J-Xmx256m
 JAVA=java -Xmx256m 
-JAVA_LIBS = .:$(JAVA_LIBDIR):ext/json-jdk1.5-2007-05-01.jar:ext/wstx-lgpl-3.2.1.jar:ext/stax-api-1.0.1.jar:ext/java_memcached-release_1.5.1.jar
+JAVA_LIBDIR = .lib
+JAVA_LIBS = .:$(JAVA_LIBDIR):ext/$(WSTX):ext/$(STAX):ext/$(MEMCACHE)
 JAVA_SRC = \
        org/opensrf/net/xmpp/*.java \
        org/opensrf/util/*.java \
        org/opensrf/*.java \
        org/opensrf/test/*.java 
 
-all:
+
+#------------------------------------------------------------------
+
+all:   jar
+
+
+dirs:
        mkdir -p $(JAVA_LIBDIR)
+
+opensrf:       deps
        $(JAVAC) -d $(JAVA_LIBDIR) -cp $(JAVA_LIBS) $(JAVA_SRC) 2>&1 
 
+jar:   opensrf
+       rm -f opensrf.jar
+       echo "creating opensrf.jar"
+       jar cf opensrf.jar -C .lib org
+       rm -r $(JAVA_LIBDIR)
+
 # only prints the first 30 lines of errors
 slim:
        mkdir -p $(JAVA_LIBDIR)
@@ -25,14 +50,15 @@ check:
        @echo -e "\nTruncating at 30 lines"
 
 run:
-       @$(JAVA) -cp $(JAVA_LIBS) $(JAVA_EXE) $(JAVA_ARGS)
+       $(JAVA) -cp $(JAVA_LIBS):opensrf.jar $(JAVA_EXE) $(JAVA_ARGS)
 
 deps:
        mkdir -p ext
-       wget 'http://woodstox.codehaus.org/stax-api-1.0.1.jar' -O ext/stax-api-1.0.1.jar
-       wget 'http://woodstox.codehaus.org/3.2.1/wstx-lgpl-3.2.1.jar' -O ext/wstx-lgpl-3.2.1.jar
-       wget 'http://img.whalin.com/memcached/jdk5/standard/java_memcached-release_1.5.1.jar' -O ext/java_memcached-release_1.5.1.jar
-       mkdir -p .tmp && cd .tmp && wget 'http://www.json.org/java/json.zip' && unzip json.zip
+       mkdir -p $(JAVA_LIBDIR)
+       if [ ! -f ext/$(STAX) ]; then wget '$(STAX_URL)' -O ext/$(STAX); fi 
+       if [ ! -f ext/wstx-lgpl-3.2.1.jar ]; then wget '$(WSTX_URL)' -O ext/$(WSTX); fi
+       if [ ! -f ext/java_memcached-release_1.5.1.jar ]; then wget '$(MEMCACHE_URL)' -O ext/$(MEMCACHE); fi
+       if [ ! -f .tmp/$(JSON) ]; then mkdir -p .tmp && cd .tmp && wget '$(JSON_URL)' && unzip $(JSON); fi
        $(JAVAC) -d $(JAVA_LIBDIR) .tmp/org/json/*.java
 
 docs:
@@ -40,8 +66,10 @@ docs:
        javadoc -classpath $(JAVA_LIBS) -d doc @files;
        rm files;
 
-clean:
-       rm -r $(JAVA_LIBDIR)
-       
+clean: 
+       rm -rf $(JAVA_LIBDIR) opensrf.jar
 
+dep_clean:
+       rm -rf ext .tmp
 
+       
index cc68505..bc3b8e2 100644 (file)
@@ -51,7 +51,7 @@ public class ClientSession extends Session {
         /** create a random thread */
         long time = new Date().getTime();
         Random rand = new Random(time);
-        setThread(rand.nextInt()+""+rand.nextInt()+""+time);
+        setThread(rand.nextInt()+""+rand.nextInt()+""+time+Thread.currentThread().getId());
 
         nextId = 0;
         requests = new HashMap<Integer, Request>();
@@ -115,6 +115,7 @@ public class ClientSession extends Session {
         Request req = findRequest(msg.getId());
         if(req == null) {
             /** LOG that we've received a result to a non-existant request */
+            System.err.println(msg.getId() +" has no corresponding request");
             return;
         }
         OSRFObject payload = (OSRFObject) msg.get("payload");
@@ -145,5 +146,19 @@ public class ClientSession extends Session {
         if(req == null) return;
         req.setComplete();
     }
+
+    public static Object atomicRequest(String service, String method, Object[] params) throws MethodException {
+        try {
+            ClientSession session = new ClientSession(service);
+            Request osrfRequest = session.request(method, params);
+            Result result = osrfRequest.recv(600000);
+            if(result.getStatusCode() != 200) 
+                throw new MethodException( 
+                    "Request "+service+":"+method+":"+" failed with status code " + result.getStatusCode());
+            return result.getContent();
+        } catch(Exception e) {
+            throw new MethodException(e);
+        }
+    }
 }
 
index bf47313..f87e638 100644 (file)
@@ -7,5 +7,8 @@ public class MethodException extends Exception {
     public MethodException(String info) {
         super(info);
     }
+    public MethodException(Throwable cause) {
+        super(cause);
+    }
 }
 
index e5d0ef6..8fe537f 100644 (file)
@@ -73,11 +73,16 @@ public class Request {
 
         Result result = null;
 
+        if((result = resultQueue.poll()) != null)
+            return result;
+
         if(millis < 0 && !complete) {
             /** wait potentially forever for a result to arrive */
-            session.waitForMessage(millis);
-            if((result = resultQueue.poll()) != null)
-                return result;
+            while(!complete) {
+                session.waitForMessage(millis);
+                if((result = resultQueue.poll()) != null)
+                    return result;
+            }
 
         } else {
 
index e2b41c3..f5045b3 100644 (file)
@@ -45,10 +45,9 @@ public abstract class Session {
         xmsg.setTo(remoteNode);
         xmsg.setThread(thread);
         xmsg.setBody(new JSONWriter(Arrays.asList(new Message[] {omsg})).write());
-        XMPPSession ses = XMPPSession.getGlobalSession();
 
         try {
-            XMPPSession.getGlobalSession().send(xmsg);
+            XMPPSession.getThreadSession().send(xmsg);
         } catch(XMPPException e) {
             connectState = ConnectState.DISCONNECTED;
             throw new SessionException("Error sending message to " + remoteNode, e);
@@ -63,7 +62,7 @@ public abstract class Session {
     public static void waitForMessage(long millis) throws SessionException, MethodException {
         try {
             Stack.processXMPPMessage(
-                XMPPSession.getGlobalSession().recv(millis));
+                XMPPSession.getThreadSession().recv(millis));
         } catch(XMPPException e) {
             throw new SessionException("Error waiting for message", e);
         }
index 8520838..fd2fcc6 100644 (file)
@@ -2,6 +2,9 @@ package org.opensrf;
 
 import org.opensrf.util.*;
 import org.opensrf.net.xmpp.*;
+import java.util.Random;
+import java.util.Date;
+import java.net.InetAddress;
 
 
 public class Sys {
@@ -16,6 +19,14 @@ public class Sys {
     public static void bootstrapClient(String configFile, String configContext) 
             throws ConfigException, SessionException  {
 
+        if(Logger.instance() == null) /* provide a sane default logger */
+            Logger.init(Logger.WARN, new Logger()); 
+
+        /** see if the current thread already has a connection */
+        XMPPSession existing = XMPPSession.getThreadSession();
+        if(existing != null && existing.connected())
+            return;
+
         /** create the config parser */
         Config config = new Config(configContext);
         config.parse(configFile);
@@ -27,11 +38,24 @@ public class Sys {
         String host = (String) config.getFirst("/domains/domain");
         int port = config.getInt("/port");
 
+
+        /** Create a random login resource string */
+        String res = "java_";
+        try {
+            res += InetAddress.getLocalHost().getHostAddress();
+        } catch(java.net.UnknownHostException e) {}
+        res += "_"+Math.abs(new Random(new Date().getTime()).nextInt()) 
+            + "_t"+ Thread.currentThread().getId();
+
+
         try {
+
             /** Connect to the Jabber network */
+            Logger.info("attempting to create XMPP session "+username+"@"+host+"/"+res);
             XMPPSession xses = new XMPPSession(host, port);
-            xses.connect(username, passwd, "test-java"); /* XXX */
-            XMPPSession.setGlobalSession(xses);
+            xses.connect(username, passwd, res);
+            XMPPSession.setThreadSession(xses);
+
         } catch(XMPPException e) {
             throw new SessionException("Unable to bootstrap client", e);
         }
@@ -41,7 +65,7 @@ public class Sys {
      * Shuts down the connection to the opensrf network
      */
     public static void shutdown() {
-        XMPPSession.getGlobalSession().disconnect();
+        XMPPSession.getThreadSession().disconnect();
     }
 }
 
index 01a4f91..54f0722 100644 (file)
@@ -8,6 +8,7 @@ import java.io.InputStream;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.Date;
 
+import com.ctc.wstx.stax.WstxInputFactory;
 
 /**
  * Slim XMPP Stream reader.  This reader only understands enough XMPP
@@ -118,7 +119,7 @@ public class XMPPReader implements Runnable {
      * Each reader should have exactly one dependent session thread. 
      */
     private synchronized void notifyCoreEvent() {
-        notify();
+        notifyAll();
     }
 
 
@@ -133,11 +134,13 @@ public class XMPPReader implements Runnable {
     public synchronized long waitCoreEvent(long timeout) {
 
         if(msgQueue.peek() != null || timeout == 0) return 0;
-
         long start = new Date().getTime();
+
         try{
-            if(timeout < 0) wait();
-            else wait(timeout);
+            if(timeout < 0) 
+                wait();
+            else 
+                wait(timeout);
         } catch(InterruptedException ie) {}
 
         return new Date().getTime() - start;
@@ -158,7 +161,8 @@ public class XMPPReader implements Runnable {
 
         try {
 
-            XMLInputFactory factory = XMLInputFactory.newInstance();
+            //XMLInputFactory factory = XMLInputFactory.newInstance();
+            XMLInputFactory factory = new com.ctc.wstx.stax.WstxInputFactory();
 
             /** disable as many unused features as possible to speed up the parsing */
             factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
index e8b639e..f9be7d2 100644 (file)
@@ -2,6 +2,9 @@ package org.opensrf.net.xmpp;
 
 import java.io.*;
 import java.net.Socket;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
 
 
 /**
@@ -21,6 +24,8 @@ public class XMPPSession {
 
     public static final String JABBER_DISCONNECT = "</stream:stream>";
 
+    private static Map threadConnections = new ConcurrentHashMap();
+
     /** jabber domain */
     private String host;
     /** jabber port */
@@ -59,24 +64,65 @@ public class XMPPSession {
     /**
      * Returns the global, process-wide session
      */
+    /*
     public static XMPPSession getGlobalSession() {
         return globalSession;
     }
+    */
+
+    public static XMPPSession getThreadSession() {
+        return (XMPPSession) threadConnections.get(new Long(Thread.currentThread().getId()));
+    }
+
+    /**
+     * Sets the given session as the global session for the current thread
+     * @param ses The session
+     */
+    public static void setThreadSession(XMPPSession ses) {
+        /* every time we create a new connection, clean up any dead threads. 
+         * this is cheaper than cleaning up the dead threads at every access. */
+        cleanupThreadSessions();
+        threadConnections.put(new Long(Thread.currentThread().getId()), ses);
+    }
+
+    /**
+     * Analyzes the threadSession data to see if there are any sessions
+     * whose controlling thread has gone away.  
+     */
+    private static void cleanupThreadSessions() {
+        Thread threads[] = new Thread[Thread.activeCount()]; 
+        Thread.enumerate(threads);
+        for(Iterator i = threadConnections.keySet().iterator(); i.hasNext(); ) {
+            boolean found = false;
+            Long id = (Long) i.next();
+            for(Thread t : threads) {
+                if(t.getId() == id.longValue()) {
+                    found = true;
+                    break;
+                }
+            }
+            if(!found) 
+                threadConnections.remove(id);
+        }
+    }
 
     /**
      * Sets the global, process-wide section
      */
+    /*
     public static void setGlobalSession(XMPPSession ses) {
         globalSession = ses;
     }
+    */
 
 
     /** true if this session is connected to the server */
     public boolean connected() {
         return (
-            reader != null && 
-            reader.getXMPPStreamState() == 
-                XMPPReader.XMPPStreamState.CONNECTED);
+                reader != null && 
+                reader.getXMPPStreamState() == XMPPReader.XMPPStreamState.CONNECTED &&
+                !socket.isClosed()
+            );
     }
 
 
@@ -113,29 +159,33 @@ public class XMPPSession {
         thread.setDaemon(true);
         thread.start();
 
-        /* send the initial jabber message */
-        sendConnect();
-        reader.waitCoreEvent(10000);
+        synchronized(reader) {
+            /* send the initial jabber message */
+            sendConnect();
+            reader.waitCoreEvent(10000);
+        }
         if( reader.getXMPPStreamState() != XMPPReader.XMPPStreamState.CONNECT_RECV ) 
             throw new XMPPException("unable to connect to jabber server");
 
-        /* send the basic auth message */
-        sendBasicAuth(); /* XXX add support for other auth mechanisms */
-        reader.waitCoreEvent(10000);
-        if(!connected())
+        synchronized(reader) {
+            /* send the basic auth message */
+            sendBasicAuth(); 
+            reader.waitCoreEvent(10000);
+        }
+        if(!connected()) 
             throw new XMPPException("Authentication failed");
     }
 
     /** Sends the initial jabber message */
     private void sendConnect() {
-        writer.printf(JABBER_CONNECT, host);
         reader.setXMPPStreamState(XMPPReader.XMPPStreamState.CONNECT_SENT);
+        writer.printf(JABBER_CONNECT, host);
     }
 
     /** Send the basic auth message */
     private void sendBasicAuth() {
-        writer.printf(JABBER_BASIC_AUTH, username, password, resource);
         reader.setXMPPStreamState(XMPPReader.XMPPStreamState.AUTH_SENT);
+        writer.printf(JABBER_BASIC_AUTH, username, password, resource);
     }
 
 
@@ -186,14 +236,17 @@ public class XMPPSession {
         } else {
 
             while(timeout >= 0) { /* wait at most 'timeout' milleseconds for a message to arrive */
+                msg = reader.popMessageQueue();
+                if( msg != null ) return msg;
                 timeout -= reader.waitCoreEvent(timeout);
                 msg = reader.popMessageQueue();
                 if( msg != null ) return msg;
                 checkConnected();
+                if(timeout == 0) break;
             }
         }
 
-        return null;
+        return reader.popMessageQueue();
     }
 
 
index 2625053..a1136cd 100644 (file)
@@ -7,48 +7,72 @@ import java.util.List;
 import java.util.ArrayList;
 import java.io.PrintStream;
 
-
 public class TestClient {
 
     public static void main(String args[]) throws Exception {
 
+        /** which opensrf service are we sending our request to */
+        String service; 
+        /** which opensrf method we're calling */
+        String method;
+        /** method params, captures from command-line args */
+        List<Object> params;
+        /** knows how to read JSON */
+        JSONReader reader;
+        /** opensrf request */
+        Request request;
+        /** request result */
+        Result result;
+        /** start time for the request */
+        long start;
+        /** for brevity */
         PrintStream out = System.out;
+
         if(args.length < 3) {
             out.println( "usage: org.opensrf.test.TestClient "+
                 "<osrfConfigFile> <service> <method> [<JSONparam1>, <JSONparam2>]");
             return;
         }
 
+        /** connect to the opensrf network,  default config context 
+         * for opensrf_core.xml is /config/opensrf */
         Sys.bootstrapClient(args[0], "/config/opensrf");
-        String service = args[1];
-        String method = args[2];
 
-        /** build the client session and send the request */
-        ClientSession session = new ClientSession(service);
-        List<Object> params = new ArrayList<Object>();
-        JSONReader reader;
-
-        for(int i = 3; i < args.length; i++) /* add the params */
+        /* grab the server, method, and any params from the command line */
+        service = args[1];
+        method = args[2];
+        params = new ArrayList<Object>();
+        for(int i = 3; i < args.length; i++) 
             params.add(new JSONReader(args[i]).read());
 
 
-        Result result;
+        /** build the client session */
+        ClientSession session = new ClientSession(service);
 
-        long start = new Date().getTime();
-        Request request = session.request(method, params);
+        /** kick off the timer */
+        start = new Date().getTime();
+
+        /** Create the request object from the session, method and params */
+        request = session.request(method, params);
 
         while( (result = request.recv(60000)) != null ) { 
             /** loop over the results and print the JSON version of the content */
 
-            if(result.getStatusCode() != 200) { /* make sure the request succeeded */
+            if(result.getStatusCode() != 200) { 
+                /** make sure the request succeeded */
                 out.println("status = " + result.getStatus());
                 out.println("status code = " + result.getStatusCode());
                 continue;
             }
 
-            out.println("result JSON: " + new JSONWriter(result.getContent()).write());
+            /** JSON-ify the resulting object and print it */
+            out.println("\nresult JSON: " + new JSONWriter(result.getContent()).write());
         }
+        
+        /** How long did the request take? */
         out.println("Request round trip took: " + (new Date().getTime() - start) + " ms.");
+
+        Sys.shutdown();
     }
 }
 
diff --git a/src/java/org/opensrf/test/TestLog.java b/src/java/org/opensrf/test/TestLog.java
new file mode 100644 (file)
index 0000000..1d60242
--- /dev/null
@@ -0,0 +1,15 @@
+package org.opensrf.test;
+import org.opensrf.util.Logger;
+import org.opensrf.util.FileLogger;
+
+
+/** Simple test class for tesing the logging functionality */
+public class TestLog {
+    public static void main(String args[]) {
+       Logger.init(Logger.DEBUG, new FileLogger("test.log")); 
+       Logger.error("Hello, world");
+       Logger.warn("Hello, world");
+       Logger.info("Hello, world");
+       Logger.debug("Hello, world");
+    }
+}
diff --git a/src/java/org/opensrf/test/TestThread.java b/src/java/org/opensrf/test/TestThread.java
new file mode 100644 (file)
index 0000000..bb4cf06
--- /dev/null
@@ -0,0 +1,68 @@
+package org.opensrf.test;
+import org.opensrf.*;
+import org.opensrf.util.*;
+import java.util.Map;
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.io.PrintStream;
+
+/**
+ * Connects to the opensrf network once per thread and runs
+ * and runs a series of request acccross all launched threads.
+ * The purpose is to verify that the java threaded client api 
+ * is functioning as expected
+ */
+public class TestThread implements Runnable {
+
+    String args[];
+
+    public TestThread(String args[]) {
+        this.args = args;
+    }
+
+    public void run() {
+
+        try {
+
+            Sys.bootstrapClient(args[0], "/config/opensrf");
+            ClientSession session = new ClientSession(args[3]);
+    
+            List params = new ArrayList<Object>();
+            for(int i = 5; i < args.length; i++) 
+                params.add(new JSONReader(args[3]).read());
+    
+            for(int i = 0; i < Integer.parseInt(args[2]); i++) {
+                System.out.println("thread " + Thread.currentThread().getId()+" sending request " + i);
+                Request request = session.request(args[4], params);
+                Result result = request.recv(3000);
+                if(result != null) {
+                    System.out.println("thread " + Thread.currentThread().getId()+ 
+                        " got result JSON: " + new JSONWriter(result.getContent()).write());
+                } else {
+                    System.out.println("* thread " + Thread.currentThread().getId()+ " got NO result");
+                }
+            }
+    
+            Sys.shutdown();
+        } catch(Exception e) {
+            System.err.println(e);
+        }
+    }
+
+    public static void main(String args[]) throws Exception {
+
+        if(args.length < 5) {
+            System.out.println( "usage: org.opensrf.test.TestClient "+
+                "<osrfConfigFile> <numthreads> <numiter> <service> <method> [<JSONparam1>, <JSONparam2>]");
+            return;
+        }
+
+        int numThreads = Integer.parseInt(args[1]);
+        for(int i = 0; i < numThreads; i++) 
+            new Thread(new TestThread(args)).start();
+    }
+}
+
+
+
diff --git a/src/java/org/opensrf/util/FileLogger.java b/src/java/org/opensrf/util/FileLogger.java
new file mode 100644 (file)
index 0000000..9eb838d
--- /dev/null
@@ -0,0 +1,44 @@
+package org.opensrf.util;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+
+
+public class FileLogger extends Logger {
+
+    /** File to log to */
+    private String filename;
+
+    /** 
+     * FileLogger constructor
+     * @param filename The path to the log file
+     */
+    public FileLogger(String filename) {
+        this.filename = filename;
+    }
+
+    /**
+     * Logs the mesage to a file.
+     * @param level The log level
+     * @param msg The mesage to log
+     */
+    protected synchronized void log(short level, String msg) {
+        if(level > logLevel) return;
+
+        BufferedWriter out = null;
+        try {
+            out = new BufferedWriter(new FileWriter(this.filename, true));
+            out.write(formatMessage(level, msg) + "\n");
+
+        } catch(Exception e) {
+            /** If we are unable to write our log message, go ahead and
+              * fall back to the default (stdout) logger */
+            Logger.init(logLevel, new Logger());
+            Logger.logByLevel(ERROR, "Unable to write to log file " + this.filename);
+            Logger.logByLevel(level, msg);
+        }
+
+        try {
+            out.close();
+        } catch(Exception e) {}
+    }
+}
diff --git a/src/java/org/opensrf/util/Logger.java b/src/java/org/opensrf/util/Logger.java
new file mode 100644 (file)
index 0000000..cc201fd
--- /dev/null
@@ -0,0 +1,130 @@
+package org.opensrf.util;
+import java.text.SimpleDateFormat;
+import java.text.FieldPosition;
+import java.util.Date;
+
+/**
+ * Basic OpenSRF logging API.  This default implementation
+ * logs to stderr.
+ */
+public class Logger {
+
+    /** Log levels */
+    public static final short ERROR = 1;
+    public static final short WARN  = 2;
+    public static final short INFO  = 3;
+    public static final short DEBUG = 4;
+
+    /** The global log instance */
+    private static Logger instance;
+    /** The global log level */
+    protected static short logLevel;
+
+    public Logger() {}
+
+    /** Sets the global Logger instance
+     * @param level The global log level.
+     * @param l The Logger instance to use
+     */
+    public static void init(short level, Logger l) {
+        instance = l;
+        logLevel = level;
+    }
+
+    /** 
+     * @return The global Logger instance
+     */
+    public static Logger instance() {
+        return instance;
+    }
+
+    /**
+     * Logs an error message
+     * @param msg The message to log
+     */
+    public static void error(String msg) {
+        instance.log(ERROR, msg);
+    }
+
+    /**
+     * Logs an warning message
+     * @param msg The message to log
+     */
+    public static void warn(String msg) {
+        instance.log(WARN, msg);
+    }
+
+    /**
+     * Logs an info message
+     * @param msg The message to log
+     */
+    public static void info(String msg) {
+        instance.log(INFO, msg);
+    }
+
+    /**
+     * Logs an debug message
+     * @param msg The message to log
+     */
+    public static void debug(String msg) {
+        instance.log(DEBUG, msg);
+    }
+
+    /** 
+     * Appends the text representation of the log level
+     * @param sb The stringbuffer to append to
+     * @param level The log level
+     */
+    protected static void appendLevelString(StringBuffer sb, short level) {
+        switch(level) {
+            case DEBUG:
+                sb.append("DEBG"); break;
+            case INFO:
+                sb.append("INFO"); break;
+            case WARN:
+                sb.append("WARN"); break;
+            case ERROR:
+                sb.append("ERR "); break;
+        }
+    }
+
+    /**
+     * Formats a message for logging.  Appends the current date+time
+     * and the log level string.
+     * @param level The log level
+     * @param msg The message to log
+     */
+    protected static String formatMessage(short level, String msg) {
+
+        StringBuffer sb = new StringBuffer();
+        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(
+            new Date(), sb, new FieldPosition(0));
+
+        sb.append(" [");
+        appendLevelString(sb, level);
+        sb.append("] ");
+        sb.append(msg);
+        return sb.toString();
+    }
+
+    /**
+     * Logs a message by passing the log level explicitly
+     * @param level The log level
+     * @param msg The message to log
+     */
+    public static void logByLevel(short level, String msg) {
+        instance.log(level, msg);
+    }
+
+    /**
+     * Performs the actual logging.  Subclasses should override 
+     * this method.
+     * @param level The log level
+     * @param msg The message to log
+     */
+    protected synchronized void log(short level, String msg) {
+        if(level > logLevel) return;
+        System.err.println(formatMessage(level, msg));
+    }
+}
+
index 1050706..af28f4a 100644 (file)
@@ -16,6 +16,7 @@ public class OSRFObject extends HashMap<String, Object> implements OSRFSerializa
     public OSRFObject() {
     }
 
+
     /**
      * Creates a new object with the provided registry
      */
@@ -24,6 +25,15 @@ public class OSRFObject extends HashMap<String, Object> implements OSRFSerializa
         registry = reg;
     }
 
+
+    /**
+     * Creates a new OpenSRF object based on the net class string
+     * */
+    public OSRFObject(String netClass) {
+        this(OSRFRegistry.getRegistry(netClass));
+    }
+
+
     /**
      * @return This object's registry
      */
diff --git a/src/javascript/JSON.js b/src/javascript/JSON.js
deleted file mode 100644 (file)
index e52a439..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-// in case we run on an implimentation that doesn't have "undefined";
-var undefined;
-
-function Cast (obj, class_constructor) {
-       try {
-               if (eval(class_constructor + '["_isfieldmapper"]')) {
-                       obj = eval("new " + class_constructor + "(obj)");
-               }
-       } catch( E ) {
-               alert( E + "\n");
-       } finally {
-               return obj;
-       }
-}
-
-function JSON2js (json) {
-
-       json = String(json).replace( /\/\*--\s*S\w*?\s*?\s+\w+\s*--\*\//g, 'Cast(');
-       json = String(json).replace( /\/\*--\s*E\w*?\s*?\s+(\w+)\s*--\*\//g, ', "$1")');
-
-       var obj;
-       if (json != '') {
-               try {
-                       eval( 'obj = ' + json );
-               } catch(E) {
-                       debug("Error building JSON object with string " + E + "\nString:\n" + json );
-                       return null;
-               }
-       }
-       return obj;
-}
-
-
-function object2Array(obj) {
-       if( obj == null ) return null;
-
-       var arr = new Array();
-       for( var i  = 0; i < obj.length; i++ ) {
-               arr[i] = obj[i];
-       }
-       return arr;
-}
-
-
-function js2JSON(arg) {
-       return _js2JSON(arg);
-}
-
-function _js2JSON(arg) {
-       var i, o, u, v;
-
-               switch (typeof arg) {
-                       case 'object':
-       
-                               if(arg) {
-       
-                                       if (arg._isfieldmapper) { /* magi-c-ast for fieldmapper objects */
-       
-                                               if( arg.a.constructor != Array ) {
-                                                       var arr = new Array();
-                                                       for( var i  = 0; i < arg.a.length; i++ ) {
-                                                               if( arg.a[i] == null ) {
-                                                                       arr[i] = null; continue;
-                                                               }
-       
-                                                               if( typeof arg.a[i] != 'object' ) { 
-                                                                       arr[i] = arg.a[i];
-       
-                                                               } else if( typeof arg.a[i] == 'object' 
-                                                                                       && arg.a[i]._isfieldmapper) {
-       
-                                                                       arr[i] = arg.a[i];
-       
-                                                               } else {
-                                                                       arr[i] = object2Array(arg.a[i]);                
-                                                               }
-                                                       }
-                                                       arg.a = arr;
-                                               }
-       
-                                               return "/*--S " + arg.classname + " --*/" + js2JSON(arg.a) + "/*--E " + arg.classname + " --*/";
-       
-                                       } else {
-       
-                                               if (arg.constructor == Array) {
-                                                       o = '';
-                                                       for (i = 0; i < arg.length; ++i) {
-                                                               v = js2JSON(arg[i]);
-                                                               if (o) {
-                                                                       o += ',';
-                                                               }
-                                                               if (v !== u) {
-                                                                       o += v;
-                                                               } else {
-                                                                       o += 'null';
-                                                               }
-                                                       }
-                                                       return '[' + o + ']';
-       
-                                               } else if (typeof arg.toString != 'undefined') {
-                                                       o = '';
-                                                       for (i in arg) {
-                                                               v = js2JSON(arg[i]);
-                                                               if (v !== u) {
-                                                                       if (o) {
-                                                                               o += ',';
-                                                                       }
-                                                                       o += js2JSON(i) + ':' + v;
-                                                               }
-                                                       }
-       
-                                                       o = '{' + o + '}';
-                                                       return o;
-       
-                                               } else {
-                                                       return;
-                                               }
-                                       }
-                               }
-                               return 'null';
-       
-                       case 'unknown':
-                       case 'number':
-                               return arg;
-       
-                       case 'undefined':
-                       case 'function':
-                               return u;
-       
-                       case 'string':
-                       default:
-                               return '"' + String(arg).replace(/(["\\])/g, '\\$1') + '"';
-               }
-
-}
diff --git a/src/javascript/JSON_v0.js b/src/javascript/JSON_v0.js
new file mode 100644 (file)
index 0000000..e52a439
--- /dev/null
@@ -0,0 +1,135 @@
+// in case we run on an implimentation that doesn't have "undefined";
+var undefined;
+
+function Cast (obj, class_constructor) {
+       try {
+               if (eval(class_constructor + '["_isfieldmapper"]')) {
+                       obj = eval("new " + class_constructor + "(obj)");
+               }
+       } catch( E ) {
+               alert( E + "\n");
+       } finally {
+               return obj;
+       }
+}
+
+function JSON2js (json) {
+
+       json = String(json).replace( /\/\*--\s*S\w*?\s*?\s+\w+\s*--\*\//g, 'Cast(');
+       json = String(json).replace( /\/\*--\s*E\w*?\s*?\s+(\w+)\s*--\*\//g, ', "$1")');
+
+       var obj;
+       if (json != '') {
+               try {
+                       eval( 'obj = ' + json );
+               } catch(E) {
+                       debug("Error building JSON object with string " + E + "\nString:\n" + json );
+                       return null;
+               }
+       }
+       return obj;
+}
+
+
+function object2Array(obj) {
+       if( obj == null ) return null;
+
+       var arr = new Array();
+       for( var i  = 0; i < obj.length; i++ ) {
+               arr[i] = obj[i];
+       }
+       return arr;
+}
+
+
+function js2JSON(arg) {
+       return _js2JSON(arg);
+}
+
+function _js2JSON(arg) {
+       var i, o, u, v;
+
+               switch (typeof arg) {
+                       case 'object':
+       
+                               if(arg) {
+       
+                                       if (arg._isfieldmapper) { /* magi-c-ast for fieldmapper objects */
+       
+                                               if( arg.a.constructor != Array ) {
+                                                       var arr = new Array();
+                                                       for( var i  = 0; i < arg.a.length; i++ ) {
+                                                               if( arg.a[i] == null ) {
+                                                                       arr[i] = null; continue;
+                                                               }
+       
+                                                               if( typeof arg.a[i] != 'object' ) { 
+                                                                       arr[i] = arg.a[i];
+       
+                                                               } else if( typeof arg.a[i] == 'object' 
+                                                                                       && arg.a[i]._isfieldmapper) {
+       
+                                                                       arr[i] = arg.a[i];
+       
+                                                               } else {
+                                                                       arr[i] = object2Array(arg.a[i]);                
+                                                               }
+                                                       }
+                                                       arg.a = arr;
+                                               }
+       
+                                               return "/*--S " + arg.classname + " --*/" + js2JSON(arg.a) + "/*--E " + arg.classname + " --*/";
+       
+                                       } else {
+       
+                                               if (arg.constructor == Array) {
+                                                       o = '';
+                                                       for (i = 0; i < arg.length; ++i) {
+                                                               v = js2JSON(arg[i]);
+                                                               if (o) {
+                                                                       o += ',';
+                                                               }
+                                                               if (v !== u) {
+                                                                       o += v;
+                                                               } else {
+                                                                       o += 'null';
+                                                               }
+                                                       }
+                                                       return '[' + o + ']';
+       
+                                               } else if (typeof arg.toString != 'undefined') {
+                                                       o = '';
+                                                       for (i in arg) {
+                                                               v = js2JSON(arg[i]);
+                                                               if (v !== u) {
+                                                                       if (o) {
+                                                                               o += ',';
+                                                                       }
+                                                                       o += js2JSON(i) + ':' + v;
+                                                               }
+                                                       }
+       
+                                                       o = '{' + o + '}';
+                                                       return o;
+       
+                                               } else {
+                                                       return;
+                                               }
+                                       }
+                               }
+                               return 'null';
+       
+                       case 'unknown':
+                       case 'number':
+                               return arg;
+       
+                       case 'undefined':
+                       case 'function':
+                               return u;
+       
+                       case 'string':
+                       default:
+                               return '"' + String(arg).replace(/(["\\])/g, '\\$1') + '"';
+               }
+
+}
diff --git a/src/javascript/JSON_v1.js b/src/javascript/JSON_v1.js
new file mode 100644 (file)
index 0000000..cfc0a36
--- /dev/null
@@ -0,0 +1,201 @@
+var JSON_CLASS_KEY     = '__c';
+var JSON_DATA_KEY      = '__p';
+
+
+
+function JSON_version() { return 'wrapper' }
+
+function JSON2js(text) {
+       return decodeJS(JSON2jsRaw(text));
+}
+
+function JSON2jsRaw(text) {
+       var obj;
+       eval('obj = ' + text);
+       return obj;
+}
+
+
+/* iterates over object, arrays, or fieldmapper objects */
+function jsIterate( arg, callback ) {
+       if( arg && typeof arg == 'object' ) {
+               if( arg.constructor == Array ) {
+                       for( var i = 0; i < arg.length; i++ ) 
+                               callback(arg, i);
+
+               }  else if( arg.constructor == Object ) {
+                               for( var i in arg ) 
+                                       callback(arg, i);
+
+               } else if( arg._isfieldmapper && arg.a ) {
+                       for( var i = 0; i < arg.a.length; i++ ) 
+                               callback(arg.a, i);
+               }
+       }
+}
+
+
+/* removes the class/paylod wrapper objects */
+function decodeJS(arg) {
+
+       if(arg == null) return null;
+
+       if(     arg && typeof arg == 'object' &&
+                       arg.constructor == Object &&
+                       arg[JSON_CLASS_KEY] ) {
+               eval('arg = new ' + arg[JSON_CLASS_KEY] + '(arg[JSON_DATA_KEY])');      
+       }
+
+       jsIterate( arg, 
+               function(o, i) {
+                       o[i] = decodeJS(o[i]);
+               }
+       );
+
+       return arg;
+}
+
+
+function jsClone(obj) {
+       if( obj == null ) return null;
+       if( typeof obj != 'object' ) return obj;
+
+       var newobj;
+       if (obj.constructor == Array) {
+               newobj = [];
+               for( var i = 0; i < obj.length; i++ ) 
+                       newobj[i] = jsClone(obj[i]);
+
+       } else if( obj.constructor == Object ) {
+               newobj = {};
+               for( var i in obj )
+                       newobj[i] = jsClone(obj[i]);
+
+       } else if( obj._isfieldmapper && obj.a ) {
+               eval('newobj = new '+obj.classname + '();');
+               for( var i = 0; i < obj.a.length; i++ ) 
+                       newobj.a[i] = jsClone(obj.a[i]);
+       }
+
+       return newobj;
+}
+       
+
+/* adds the class/paylod wrapper objects */
+function encodeJS(arg) {
+       if( arg == null ) return null;  
+       if( typeof arg != 'object' ) return arg;
+
+       if( arg._isfieldmapper ) {
+      var newarr = []
+      if(!arg.a) arg.a = [];
+               for( var i = 0; i < arg.a.length; i++ ) 
+                       newarr[i] = encodeJS(arg.a[i]);
+
+               var a = {};
+               a[JSON_CLASS_KEY] = arg.classname;
+               a[JSON_DATA_KEY] = newarr;
+      return a;
+       }
+
+       var newobj;
+
+       if(arg.length != undefined) {
+               newobj = [];
+               for( var i = 0; i < arg.length; i++ ) 
+         newobj.push(encodeJS(arg[i]));
+      return newobj;
+       } 
+   
+       newobj = {};
+       for( var i in arg )
+               newobj[i] = encodeJS(arg[i]);
+       return newobj;
+}
+
+/* turns a javascript object into a JSON string */
+function js2JSON(arg) {
+       return js2JSONRaw(encodeJS(arg));
+}
+
+function js2JSONRaw(arg) {
+
+       if( arg == null ) 
+               return 'null';
+
+       var o;
+
+       switch (typeof arg) {
+
+               case 'object':
+
+                       if (arg.constructor == Array) {
+                               o = '';
+                               jsIterate( arg,
+                                       function(obj, i) {
+                                               if (o) o += ',';
+                                               o += js2JSONRaw(obj[i]);
+                                       }
+                               );
+                               return '[' + o + ']';
+
+                       } else if (typeof arg.toString != 'undefined') {
+                               o = '';
+                               jsIterate( arg,
+                                       function(obj, i) {
+                                               if (o) o += ',';
+                                               o = o + js2JSONRaw(i) + ':' + js2JSONRaw(obj[i]);
+                                       }
+                               );
+                               return '{' + o + '}';
+
+                       } else {
+                               return 'null';
+                       }
+
+               case 'number': return arg;
+
+               case 'string':
+                       var s = String(arg);
+                       s = s.replace(/\\/g, '\\\\');
+                       s = s.replace(/"/g, '\\"');
+                       s = s.replace(/\t/g, "\\t");
+                       s = s.replace(/\n/g, "\\n");
+                       s = s.replace(/\r/g, "\\r");
+                       s = s.replace(/\f/g, "\\f");
+                       return '"' + s + '"';
+
+               default: return 'null';
+       }
+}
+
+
+function __tabs(c) { 
+       var s = ''; 
+       for( i = 0; i < c; i++ ) s += '\t';
+       return s;
+}
+
+function jsonPretty(str) {
+       if(!str) return "";
+       var s = '';
+       var d = 0;
+       for( var i = 0; i < str.length; i++ ) {
+               var c = str.charAt(i);
+               if( c == '{' || c == '[' ) {
+                       s += c + '\n' + __tabs(++d);
+               } else if( c == '}' || c == ']' ) {
+                       s += '\n' + __tabs(--d) + '\n';
+                       if( str.charAt(i+1) == ',' ) {
+                               s += '\n' + __tabs(d);
+                       }
+               } else if( c == ',' ) {
+                       s += ',\n' + __tabs(d);
+               } else {
+                       s += c;
+               }
+       }
+       return s;
+}
+
+
index e21cfe1..33f9ced 100644 (file)
@@ -1,4 +1,4 @@
-LDLIBS += -lopensrf -lobjson -lxml2
+LDLIBS += -lopensrf  -lxml2
 CFLAGS += -D_GNU_SOURCE
 
 all: chopchop
index 9a20bd6..9891698 100644 (file)
@@ -279,7 +279,7 @@ int osrfChatSend( osrfChatServer* cs, osrfChatNode* node, char* toAddr, char* fr
 
        int l = strlen(toAddr);
        char dombuf[l];
-       bzero(dombuf, l);
+       memset(dombuf, 0, sizeof(dombuf));
        jid_get_domain( toAddr, dombuf, l );    
 
        if( eq( dombuf, cs->domain ) ) { /* this is to a user we host */
@@ -595,8 +595,7 @@ int osrfChatHandleNewConnection( osrfChatNode* node, const char* name, const xml
 
 char* osrfChatMkAuthKey() {
        char keybuf[112];
-       bzero(keybuf, 112);
-       snprintf(keybuf, 111, "%d%ld%s", (int) time(NULL), (long) getpid(), getenv("HOSTNAME"));
+       snprintf(keybuf, sizeof(keybuf), "%d%ld%s", (int) time(NULL), (long) getpid(), getenv("HOSTNAME"));
        return strdup(shahash(keybuf));
 }
 
index dff2024..c6d5d53 100644 (file)
@@ -1,10 +1,17 @@
-# OSRF_LOG_PARAMS log all incoming method params at OSRF_INFO log level. 
-# OSRF_STRICT_PARAMS instructs the app handler to return an error if the number of method arguments
-#      provided to any method is not at least as large as the 'argc' setting for the method
+# ------------------------------------------------------------------
+# To build the standalone JSON lib libosrf_json.so:
+#      $ make libosrf_json.so
+# To build the standalone JSON lib libosrf_json.so with XML utils 
+#      support, use something like the following:
+#      $ CFLAGS="-DOSRF_JSON_ENABLE_XML_UTILS -I/usr/include/libxml2" LDLIBS=-lxml2 make libosrf_json.so
+#
+# The compiler flag -DOSRF_JSON_ALLOW_COMMENTS tells the parser to 
+# allow legacy JSON comments like /* comment */
+# ------------------------------------------------------------------
 
-CFLAGS +=  -DASSUME_STATELESS  -DOSRF_LOG_PARAMS -DOSRF_STRICT_PARAMS -rdynamic -fno-strict-aliasing
-LDLIBS += -lxml2 -lobjson -ldl -lmemcache 
-OSRF_INC = ../../include/opensrf/
+CFLAGS +=  -DASSUME_STATELESS  -DOSRF_STRICT_PARAMS -rdynamic -fno-strict-aliasing -I../../include -fPIC -Wall -DOSRF_JSON_ENABLE_XML_UTILS
+LDLIBS += -lxml2 -ldl -lmemcache 
+export OSRF_INC = ../../include/opensrf/
 
 TARGETS =      osrf_message.o \
                        osrf_app_session.o \
@@ -27,12 +34,24 @@ TARGETS =   osrf_message.o \
                        utils.o\
                        socket_bundle.o\
                        sha.o\
-                       string_array.o 
+                       string_array.o
 
+JSON_TARGETS =         osrf_json_object.o\
+                               osrf_json_parser.o \
+                               osrf_json_tools.o \
+                               osrf_legacy_json.o \
+                               osrf_json_xml.o
 
-all: opensrf
+# use these when building the standalone JSON module
+JSON_DEPS = osrf_list.o\
+                       osrf_hash.o\
+                       utils.o\
+                       log.o\
+                       md5.o\
+                       string_array.o
 
 
+all: opensrf
 
 # Build the OpenSRF C binary
 opensrf:       opensrf.o libopensrf.so
@@ -41,8 +60,26 @@ opensrf:     opensrf.o libopensrf.so
 
 
 # Build the OpenSRF library
-libopensrf.so: $(TARGETS)
-       $(CC) -shared -W1 $(LDFLAGS) $(LDLIBS) $(TARGETS) -o $(TMPDIR)/libopensrf.so
+libopensrf.so: $(TARGETS) json
+       if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then\
+               $(CC) -shared -W1 $(LDFLAGS) $(LDLIBS) $(TARGETS) -o $(TMPDIR)/libopensrf.so;\
+       else\
+               $(CC) -shared -W1 $(LDFLAGS) $(LDLIBS) $(TARGETS) $(JSON_TARGETS) -o $(TMPDIR)/libopensrf.so;\
+       fi; 
+
+
+json:  $(JSON_TARGETS) $(JSON_DEPS)
+       if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then \
+               $(CC) -shared -W1  $(CFLAGS) \
+                       $(LDFLAGS) $(JSON_TARGETS) $(JSON_DEPS) -o $(TMPDIR)/libobjson.so;\
+       fi;
+
+libosrf_json.so:       $(JSON_TARGETS) $(JSON_DEPS)
+       $(CC) -shared -W1 $(CFLAGS) \
+               $(LDFLAGS) $(LDLIBS) $(JSON_TARGETS) $(JSON_DEPS) -o $@
+
+
+osrf_json_test:        osrf_json_test.o $(JSON_TARGETS) $(JSON_DEPS)
 
 
 opensrf.o:     opensrf.c
@@ -66,8 +103,17 @@ utils.o:    utils.c $(OSRF_INC)/utils.h
 socket_bundle.o:       socket_bundle.c $(OSRF_INC)/socket_bundle.h
 sha.o: sha.c $(OSRF_INC)/sha.h
 string_array.o:        string_array.c $(OSRF_INC)/string_array.h
+osrf_json_object.o:    osrf_json_object.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_parser.o:    osrf_json_parser.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_tools.o:     osrf_json_tools.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_legacy_json.o:    osrf_legacy_json.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_xml.o:       osrf_json_xml.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_xml.h
+osrf_json_test.o:      osrf_json_test.c
 
 
 clean:
-       /bin/rm -f *.o libopensrf.so opensrf
+       /bin/rm -f *.o *.so opensrf osrf_json_test
+
+
+
 
diff --git a/src/libopensrf/Makefile.json b/src/libopensrf/Makefile.json
new file mode 100644 (file)
index 0000000..8de20a6
--- /dev/null
@@ -0,0 +1,40 @@
+#-DOSRF_JSON_ALLOW_COMMENTS 
+
+# ------------------------------------------------------------------
+# To build a standalone version of libosrf_json, something 
+# like the following should work:
+# $ CFLAGS="-fPIC -I /usr/include/libxml2 -I ../../include" \
+#      OSRF_INC="../../include/opensrf" LDLIBS="-lxml2" \
+#      make -f Makefile.json standalone
+# ------------------------------------------------------------------
+TARGETS = osrf_json_object.o osrf_json_parser.o osrf_json_tools.o osrf_legacy_json.o osrf_json_xml.o
+
+# these are only needed when compiling the standalone version
+EXT_TARGETS = osrf_list.o osrf_hash.o utils.o log.o md5.o string_array.o
+
+all:   $(TARGETS)
+       if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then \
+               $(CC) -shared -W1 $(LDFLAGS) $(TARGETS) -o $(TMPDIR)/libobjson.so;\
+       fi;
+
+standalone: $(TARGETS) $(EXT_TARGETS)
+       $(CC) -shared -W1 $(CFLAGS) $(LDFLAGS) $(LDLIBS) $(TARGETS) $(EXT_TARGETS) -o libosrf_json.so
+
+osrf_json_object.o:    osrf_json_object.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_parser.o:    osrf_json_parser.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_tools.o:     osrf_json_tools.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_legacy_json.o:    osrf_legacy_json.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_xml.o:       osrf_json_xml.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_xml.h
+
+
+osrf_list.o:   osrf_list.c $(OSRF_INC)/osrf_list.h
+osrf_hash.o:   osrf_hash.c $(OSRF_INC)/osrf_hash.h
+utils.o:       utils.c $(OSRF_INC)/utils.h
+md5.o: md5.c $(OSRF_INC)/md5.h
+log.o: log.c $(OSRF_INC)/log.h
+string_array.o:        string_array.c $(OSRF_INC)/string_array.h
+
+
+clean:
+       rm -f osrf_json*.o osrf_legacy_json.o libosrf_json.so
+
index 5cf8f76..a477ab0 100644 (file)
@@ -31,10 +31,10 @@ int main( int argc, char** argv ) {
                signal(SIGINT, sig_int);
                fprintf(stderr, "Listener: %ld\n", (long) getpid() );   
                char buf[300];
-               memset(buf, 0, 300);
+               osrf_clearbuf(buf, sizeof(buf));
                printf("=> ");
 
-               while( fgets( buf, 299, stdin) ) {
+               while( fgets( buf, sizeof(buf), stdin) ) {
 
                        // remove newline
                        buf[strlen(buf)-1] = '\0';
@@ -48,7 +48,7 @@ int main( int argc, char** argv ) {
                        client_send_message( client, send );
                        message_free( send );
                        printf("\n=> ");
-                       memset(buf, 0, 300);
+                       osrf_clearbuf(buf, sizeof(buf));
                }
                fprintf(stderr, "Killing child %d\n", pid );
                kill( pid, SIGKILL );
index 94c5359..8fb5094 100644 (file)
@@ -58,8 +58,7 @@ void osrfLogMkXid( void ) {
    if(_osrfLogIsClient) {
       static int _osrfLogXidInc = 0; /* increments with each new xid for uniqueness */
       char buf[32];
-      memset(buf, 0x0, 32);
-      snprintf(buf, 32, "%s%d", _osrfLogXidPfx, _osrfLogXidInc);
+      snprintf(buf, sizeof(buf), "%s%d", _osrfLogXidPfx, _osrfLogXidInc);
       _osrfLogSetXid(buf);
       _osrfLogXidInc++;
    }
@@ -74,8 +73,7 @@ void osrfLogSetIsClient(int is) {
    if(!is) return;
    /* go ahead and create the xid prefix so it will be consistent later */
    static char buff[32];
-   memset(buff, 0x0, 32);
-   snprintf(buff, 32, "%d%ld", (int)time(NULL), (long) getpid());
+   snprintf(buff, sizeof(buff), "%d%ld", (int)time(NULL), (long) getpid());
    _osrfLogXidPfx = buff;
 }
 
@@ -223,7 +221,7 @@ static void _osrfLogDetail( int level, const char* filename, int line, char* msg
 
    if( logtype == OSRF_LOG_TYPE_SYSLOG ) {
                char buf[1536];  
-               memset(buf, 0x0, 1536);
+               osrf_clearbuf(buf, sizeof(buf));
                /* give syslog some breathing room, and be cute about it */
                strncat(buf, msg, 1535);
                buf[1532] = '.';
index 3b6f6fd..e7215d2 100644 (file)
@@ -1,6 +1,4 @@
 #include <opensrf/osrf_system.h>
-//#include <opensrf/osrf_hash.h>
-//#include <opensrf/osrf_list.h>
 
 int main( int argc, char* argv[] ) {
 
index d89c445..bf852b2 100644 (file)
@@ -4,7 +4,6 @@
 /* the global app_session cache */
 osrfHash* osrfAppSessionCache = NULL;
 
-
 // --------------------------------------------------------------------------
 // --------------------------------------------------------------------------
 // Request API
@@ -111,6 +110,11 @@ osrf_message* _osrf_app_request_recv( osrf_app_request* req, int timeout ) {
                        osrf_message* ret_msg = req->result;
                        osrf_message* tmp_msg = ret_msg->next;
                        req->result = tmp_msg;
+                       if (ret_msg->sender_locale) {
+                               if (req->session->session_locale)
+                                       free(req->session->session_locale);
+                               req->session->session_locale = strdup(ret_msg->sender_locale);
+                       }
                        return ret_msg;
                }
 
@@ -125,6 +129,11 @@ osrf_message* _osrf_app_request_recv( osrf_app_request* req, int timeout ) {
                        osrf_message* ret_msg = req->result;
                        osrf_message* tmp_msg = ret_msg->next;
                        req->result = tmp_msg;
+                       if (ret_msg->sender_locale) {
+                               if (req->session->session_locale)
+                                       free(req->session->session_locale);
+                               req->session->session_locale = strdup(ret_msg->sender_locale);
+                       }
                        return ret_msg;
                }
                if( req->complete )
@@ -133,7 +142,7 @@ osrf_message* _osrf_app_request_recv( osrf_app_request* req, int timeout ) {
                if(req->reset_timeout) {
                        remaining = (time_t) timeout;
                        req->reset_timeout = 0;
-                       osrfLogDebug( OSRF_LOG_MARK, "Recevied a timeout reset");
+                       osrfLogDebug( OSRF_LOG_MARK, "Received a timeout reset");
                } else {
                        remaining -= (int) (time(NULL) - start);
                }
@@ -161,6 +170,18 @@ int _osrf_app_request_resend( osrf_app_request* req ) {
 // --------------------------------------------------------------------------
 
 /** returns a session from the global session hash */
+char* osrf_app_session_set_locale( osrf_app_session* session, const char* locale ) {
+       if (!session || !locale)
+               return NULL;
+
+       if(session->session_locale)
+               free(session->session_locale);
+
+       session->session_locale = strdup( locale );
+       return session->session_locale;
+}
+
+/** returns a session from the global session hash */
 osrf_app_session* osrf_app_session_find_session( char* session_id ) {
        if(session_id) return osrfHashGet(osrfAppSessionCache, session_id);
        return NULL;
@@ -183,6 +204,11 @@ osrf_app_session* osrfAppSessionClientInit( char* remote_service ) {
 
 osrf_app_session* osrf_app_client_session_init( char* remote_service ) {
 
+       if (!remote_service) {
+               osrfLogWarning( OSRF_LOG_MARK, "No remote service specified in osrf_app_client_session_init");
+               return NULL;
+       }
+
        osrf_app_session* session = safe_malloc(sizeof(osrf_app_session));      
 
        session->transport_handle = osrf_system_get_transport_client();
@@ -192,15 +218,32 @@ osrf_app_session* osrf_app_client_session_init( char* remote_service ) {
                return NULL;
        }
 
-       char target_buf[512];
-       target_buf[ 0 ] = '\0';
-
        osrfStringArray* arr = osrfNewStringArray(8);
        osrfConfigGetValueList(NULL, arr, "/domains/domain");
        char* domain = osrfStringArrayGetString(arr, 0);
+
+       if (!domain) {
+               osrfLogWarning( OSRF_LOG_MARK, "No domains specified in the OpenSRF config file");
+               free( session );
+               osrfStringArrayFree(arr);
+               return NULL;
+       }
+
        char* router_name = osrfConfigGetValue(NULL, "/router_name");
-       
-       int len = snprintf( target_buf, 512, "%s@%s/%s",  router_name, domain, remote_service );
+       if (!router_name) {
+               osrfLogWarning( OSRF_LOG_MARK, "No router name specified in the OpenSRF config file");
+               free( session );
+               osrfStringArrayFree(arr);
+               return NULL;
+       }
+
+       char target_buf[512];
+       target_buf[ 0 ] = '\0';
+
+       int len = snprintf( target_buf, sizeof(target_buf), "%s@%s/%s",
+                       router_name ? router_name : "(null)",
+                       domain ? domain : "(null)",
+                       remote_service ? remote_service : "(null)" );
        osrfStringArrayFree(arr);
        //free(domain);
        free(router_name);
@@ -216,6 +259,7 @@ osrf_app_session* osrf_app_client_session_init( char* remote_service ) {
        session->remote_id = strdup(target_buf);
        session->orig_remote_id = strdup(session->remote_id);
        session->remote_service = strdup(remote_service);
+       session->session_locale = NULL;
 
        #ifdef ASSUME_STATELESS
        session->stateless = 1;
@@ -227,9 +271,8 @@ osrf_app_session* osrf_app_client_session_init( char* remote_service ) {
 
        /* build a chunky, random session id */
        char id[256];
-       memset(id,0,256);
 
-       sprintf(id, "%f.%d%ld", get_timestamp_millis(), (int)time(NULL), (long) getpid());
+       snprintf(id, sizeof(id), "%f.%d%ld", get_timestamp_millis(), (int)time(NULL), (long) getpid());
        session->session_id = strdup(id);
        osrfLogDebug( OSRF_LOG_MARK,  "Building a new client session with id [%s] [%s]", 
                        session->remote_service, session->session_id );
@@ -296,6 +339,9 @@ void _osrf_app_session_free( osrf_app_session* session ){
        if( session->userDataFree && session->userData ) 
                session->userDataFree(session->userData);
        
+       if(session->session_locale)
+               free(session->session_locale);
+
        free(session->remote_id);
        free(session->orig_remote_id);
        free(session->session_id);
@@ -308,19 +354,42 @@ int osrfAppSessionMakeRequest(
                osrf_app_session* session, jsonObject* params, 
                char* method_name, int protocol, string_array* param_strings ) {
 
-       return osrf_app_session_make_req( session, params, 
-                       method_name, protocol, param_strings );
+       return osrf_app_session_make_locale_req( session, params, 
+                       method_name, protocol, param_strings, NULL );
+}
+
+int osrfAppSessionMakeLocaleRequest(
+               osrf_app_session* session, jsonObject* params, 
+               char* method_name, int protocol, string_array* param_strings, char* locale ) {
+
+       return osrf_app_session_make_locale_req( session, params, 
+                       method_name, protocol, param_strings, locale );
 }
 
 int osrf_app_session_make_req( 
                osrf_app_session* session, jsonObject* params, 
-               char* method_name, int protocol, string_array* param_strings ) {
+               char* method_name, int protocol, string_array* param_strings) {
+
+       return osrf_app_session_make_locale_req(session, params,
+                       method_name, protocol, param_strings, NULL);
+}
+
+int osrf_app_session_make_locale_req( 
+               osrf_app_session* session, jsonObject* params, 
+               char* method_name, int protocol, string_array* param_strings, char* locale ) {
        if(session == NULL) return -1;
 
-   osrfLogMkXid();
+       osrfLogMkXid();
 
        osrf_message* req_msg = osrf_message_init( REQUEST, ++(session->thread_trace), protocol );
        osrf_message_set_method(req_msg, method_name);
+
+       if (locale) {
+               osrf_message_set_locale(req_msg, locale);
+       } else if (session->session_locale) {
+               osrf_message_set_locale(req_msg, session->session_locale);
+       }
+
        if(params) {
                osrf_message_set_params(req_msg, params);
 
@@ -341,7 +410,7 @@ int osrf_app_session_make_req(
                return -1;
        }
 
-       osrfLogDebug( OSRF_LOG_MARK,  "Pushing [%d] onto requeust queue for session [%s] [%s]",
+       osrfLogDebug( OSRF_LOG_MARK,  "Pushing [%d] onto request queue for session [%s] [%s]",
                        req->request_id, session->remote_service, session->session_id );
        osrfListSet( session->request_queue, req, req->request_id ); 
        return req->request_id;
index 1f12b86..c70d446 100644 (file)
@@ -1,11 +1,7 @@
 #include <opensrf/osrf_application.h>
-#include <objson/object.h>
-
-//osrfApplication* __osrfAppList = NULL; 
 
 osrfHash* __osrfAppHash = NULL;
 
-
 int osrfAppRegisterApplication( char* appName, char* soFile ) {
        if(!appName || ! soFile) return -1;
        char* error;
@@ -16,7 +12,7 @@ int osrfAppRegisterApplication( char* appName, char* soFile ) {
 
        osrfApplication* app = safe_malloc(sizeof(osrfApplication));
        app->handle = dlopen (soFile, RTLD_NOW);
-   app->onExit = NULL;
+       app->onExit = NULL;
 
        if(!app->handle) {
                osrfLogWarning( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, dlerror() );
@@ -55,28 +51,27 @@ int osrfAppRegisterApplication( char* appName, char* soFile ) {
 
        osrfLogSetAppname(appName);
 
-   osrfAppSetOnExit(app, appName);
+       osrfAppSetOnExit(app, appName);
 
        return 0;
 }
 
 
 void osrfAppSetOnExit(osrfApplication* app, char* appName) {
-   if(!(app && appName)) return;
+       if(!(app && appName)) return;
 
        /* see if we can run the initialize method */
-   char* error;
+       char* error;
        void (*onExit) (void);
        *(void **) (&onExit) = dlsym(app->handle, "osrfAppChildExit");
 
        if( (error = dlerror()) != NULL ) {
-      osrfLogDebug(OSRF_LOG_MARK, "No exit handler defined for %s", appName);
-      return;
-   }
+               osrfLogDebug(OSRF_LOG_MARK, "No exit handler defined for %s", appName);
+               return;
+       }
 
-   osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
-   app->onExit = (*onExit);
-   //if( (ret = (*onExit)()) ) {
+       osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
+       app->onExit = (*onExit);
 }
 
 
@@ -106,14 +101,14 @@ int osrfAppRunChildInit(char* appname) {
 
 
 void osrfAppRunExitCode() { 
-   osrfHashIterator* itr = osrfNewHashIterator(__osrfAppHash);
-   osrfApplication* app;
-   while( (app = osrfHashIteratorNext(itr)) ) {
-      if( app->onExit ) {
-         osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s", itr->current);
-         app->onExit();
-      }
-   }
+       osrfHashIterator* itr = osrfNewHashIterator(__osrfAppHash);
+       osrfApplication* app;
+       while( (app = osrfHashIteratorNext(itr)) ) {
+               if( app->onExit ) {
+                       osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s", itr->current);
+                       app->onExit();
+               }
+       }
 }
 
 
index 940af11..6c83633 100644 (file)
@@ -96,4 +96,9 @@ int osrfCacheSetExpire( time_t seconds, char* key, ... ) {
        return -1;
 }
 
+void osrfCacheCleanup() {
+    if(__osrfCache) 
+        mc_free(__osrfCache);
+}
+
 
index e00f09c..5447466 100644 (file)
@@ -193,7 +193,7 @@ void osrfHashFree( osrfHash* hash ) {
        }
 
        osrfListFree(hash->hash);
-       osrfStringArrayFree(hash->keys);
+    OSRF_STRING_ARRAY_FREE(hash->keys);
        free(hash);
 }
 
@@ -201,7 +201,6 @@ void osrfHashFree( osrfHash* hash ) {
 
 osrfHashIterator* osrfNewHashIterator( osrfHash* hash ) {
        if(!hash) return NULL;
-       //osrfHashIterator* itr = safe_malloc(sizeof(osrfHashIterator));
        osrfHashIterator* itr;
        OSRF_MALLOC(itr, sizeof(osrfHashIterator));
        itr->hash = hash;
@@ -214,8 +213,7 @@ void* osrfHashIteratorNext( osrfHashIterator* itr ) {
        if(!(itr && itr->hash)) return NULL;
        if( itr->currentIdx >= itr->keys->size ) return NULL;
        free(itr->current);
-       itr->current = strdup(
-                       osrfStringArrayGetString(itr->keys, itr->currentIdx++));
+       itr->current = strdup(osrfStringArrayGetString(itr->keys, itr->currentIdx++));
        char* val = osrfHashGet( itr->hash, itr->current );
        return val;
 }
@@ -223,14 +221,12 @@ void* osrfHashIteratorNext( osrfHashIterator* itr ) {
 void osrfHashIteratorFree( osrfHashIterator* itr ) {
        if(!itr) return;
        free(itr->current);
-       //osrfStringArrayFree(itr->keys);
        free(itr);
 }
 
 void osrfHashIteratorReset( osrfHashIterator* itr ) {
        if(!itr) return;
        free(itr->current);
-       //osrfStringArrayFree(itr->keys);
        itr->keys = osrfHashKeysInc(itr->hash);
        itr->currentIdx = 0;
        itr->current = NULL;
diff --git a/src/libopensrf/osrf_json_object.c b/src/libopensrf/osrf_json_object.c
new file mode 100644 (file)
index 0000000..64c8efd
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+#include <limits.h>
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_utils.h>
+
+/* cleans up an object if it is morphing another object, also
+ * verifies that the appropriate storage container exists where appropriate */
+#define JSON_INIT_CLEAR(_obj_, newtype)                \
+       if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) {                        \
+               osrfHashFree(_obj_->value.h);                   \
+               _obj_->value.h = NULL;                                  \
+} else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) {      \
+               osrfListFree(_obj_->value.l);                   \
+               _obj_->value.l = NULL;                                  \
+} else if( _obj_->type == JSON_STRING && newtype != JSON_STRING ) { \
+               free(_obj_->value.s);                                           \
+               _obj_->value.s = NULL;                                  \
+} \
+       _obj_->type = newtype;\
+       if( newtype == JSON_HASH && _obj_->value.h == NULL ) {  \
+               _obj_->value.h = osrfNewHash();         \
+               _obj_->value.h->freeItem = _jsonFreeHashItem; \
+} else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) { \
+               _obj_->value.l = osrfNewList();         \
+               _obj_->value.l->freeItem = _jsonFreeListItem;\
+}
+
+static void add_json_to_buffer( const jsonObject* obj, growing_buffer * buf );
+
+jsonObject* jsonNewObject(const char* data) {
+
+       jsonObject* o;
+       OSRF_MALLOC(o, sizeof(jsonObject));
+       o->type = JSON_NULL;
+
+       if(data) {
+               o->type = JSON_STRING;
+               o->value.s = strdup(data);
+       }
+
+       return o;
+}
+
+jsonObject* jsonNewObjectFmt(const char* data, ...) {
+
+       jsonObject* o;
+       OSRF_MALLOC(o, sizeof(jsonObject));
+       o->type = JSON_NULL;
+
+       if(data) {
+               VA_LIST_TO_STRING(data);
+               o->type = JSON_STRING;
+               o->value.s = strdup(VA_BUF);
+       }
+
+       return o;
+}
+
+jsonObject* jsonNewNumberObject( double num ) {
+       jsonObject* o = jsonNewObject(NULL);
+       o->type = JSON_NUMBER;
+       o->value.n = num;
+       return o;
+}
+
+jsonObject* jsonNewBoolObject(int val) {
+    jsonObject* o = jsonNewObject(NULL);
+    o->type = JSON_BOOL;
+    jsonSetBool(o, val);
+    return o;
+}
+
+jsonObject* jsonNewObjectType(int type) {
+       jsonObject* o = jsonNewObject(NULL);
+       o->type = type;
+       return o;
+}
+
+void jsonObjectFree( jsonObject* o ) {
+
+       if(!o || o->parent) return;
+       free(o->classname);
+
+       switch(o->type) {
+               case JSON_HASH          : osrfHashFree(o->value.h); break;
+               case JSON_ARRAY : osrfListFree(o->value.l); break;
+               case JSON_STRING        : free(o->value.s); break;
+       }
+       free(o);
+}
+
+static void _jsonFreeHashItem(char* key, void* item){
+       if(!item) return;
+       jsonObject* o = (jsonObject*) item;
+       o->parent = NULL; /* detach the item */
+       jsonObjectFree(o);
+}
+static void _jsonFreeListItem(void* item){
+       if(!item) return;
+       jsonObject* o = (jsonObject*) item;
+       o->parent = NULL; /* detach the item */
+       jsonObjectFree(o);
+}
+
+void jsonSetBool(jsonObject* bl, int val) {
+    if(!bl) return;
+    JSON_INIT_CLEAR(bl, JSON_BOOL);
+    bl->value.b = val;
+}
+
+unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
+    if(!o) return -1;
+    if(!newo) newo = jsonNewObject(NULL);
+       JSON_INIT_CLEAR(o, JSON_ARRAY);
+       newo->parent = o;
+       osrfListPush( o->value.l, newo );
+       o->size = o->value.l->size;
+       return o->size;
+}
+
+unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
+    if(!dest) return -1;
+    if(!newObj) newObj = jsonNewObject(NULL);
+       JSON_INIT_CLEAR(dest, JSON_ARRAY);
+       newObj->parent = dest;
+       osrfListSet( dest->value.l, newObj, index );
+       dest->size = dest->value.l->size;
+       return dest->value.l->size;
+}
+
+unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
+    if(!o) return -1;
+    if(!newo) newo = jsonNewObject(NULL);
+       JSON_INIT_CLEAR(o, JSON_HASH);
+       newo->parent = o;
+       osrfHashSet( o->value.h, newo, key );
+       o->size = o->value.h->size;
+       return o->size;
+}
+
+jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key ) {
+       if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
+       return osrfHashGet( obj->value.h, key);
+}
+
+const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
+       if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
+       return osrfHashGet( obj->value.h, key);
+}
+
+char* jsonObjectToJSON( const jsonObject* obj ) {
+       jsonObject* obj2 = jsonObjectEncodeClass( obj );
+       char* json = jsonObjectToJSONRaw(obj2);
+       jsonObjectFree(obj2);
+       return json;
+}
+
+char* jsonObjectToJSONRaw( const jsonObject* obj ) {
+       if(!obj) return NULL;
+       growing_buffer* buf = buffer_init(32);
+       add_json_to_buffer( obj, buf );
+       return buffer_release( buf );
+}
+
+static void add_json_to_buffer( const jsonObject* obj, growing_buffer * buf ) {
+
+       switch(obj->type) {
+
+               case JSON_BOOL :
+                       if(obj->value.b) OSRF_BUFFER_ADD(buf, "true"); 
+                       else OSRF_BUFFER_ADD(buf, "false"); 
+                       break;
+
+               case JSON_NUMBER: {
+                       double x = obj->value.n;
+                       if( x <= INT_MAX && x >= INT_MIN && x == (int) x ) {
+                               INT_TO_STRING((int)x);
+                               OSRF_BUFFER_ADD(buf, INTSTR);
+
+                       } else {
+                               DOUBLE_TO_STRING(x);
+                               OSRF_BUFFER_ADD(buf, DOUBLESTR);
+                       }
+                       break;
+               }
+
+               case JSON_NULL:
+                       OSRF_BUFFER_ADD(buf, "null");
+                       break;
+
+               case JSON_STRING:
+                       OSRF_BUFFER_ADD_CHAR(buf, '"');
+                       char* data = obj->value.s;
+                       int len = strlen(data);
+                       
+                       char* output = uescape(data, len, 1);
+                       OSRF_BUFFER_ADD(buf, output);
+                       free(output);
+                       OSRF_BUFFER_ADD_CHAR(buf, '"');
+                       break;
+                       
+               case JSON_ARRAY: {
+                       OSRF_BUFFER_ADD_CHAR(buf, '[');
+                       if( obj->value.l ) {
+                               int i;
+                               for( i = 0; i != obj->value.l->size; i++ ) {
+                                       if(i > 0) OSRF_BUFFER_ADD(buf, ",");
+                                       add_json_to_buffer( OSRF_LIST_GET_INDEX(obj->value.l, i), buf );
+                               }
+                       }
+                       OSRF_BUFFER_ADD_CHAR(buf, ']');
+                       break;
+               }
+
+               case JSON_HASH: {
+
+                       OSRF_BUFFER_ADD_CHAR(buf, '{');
+                       osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
+                       jsonObject* item;
+                       int i = 0;
+
+                       while( (item = osrfHashIteratorNext(itr)) ) {
+                               if(i++ > 0) OSRF_BUFFER_ADD(buf, ",");
+                               buffer_fadd(buf, "\"%s\":", itr->current);
+                               add_json_to_buffer( item, buf );
+                       }
+
+                       osrfHashIteratorFree(itr);
+                       OSRF_BUFFER_ADD_CHAR(buf, '}');
+                       break;
+               }
+       }
+}
+
+
+jsonIterator* jsonNewIterator(const jsonObject* obj) {
+       if(!obj) return NULL;
+       jsonIterator* itr;
+       OSRF_MALLOC(itr, sizeof(jsonIterator));
+
+       itr->obj                = (jsonObject*) obj;
+       itr->index      = 0;
+       itr->key                = NULL;
+
+       if( obj->type == JSON_HASH )
+               itr->hashItr = osrfNewHashIterator(obj->value.h);
+       
+       return itr;
+}
+
+void jsonIteratorFree(jsonIterator* itr) {
+       if(!itr) return;
+       free(itr->key);
+       osrfHashIteratorFree(itr->hashItr);
+       free(itr);
+}
+
+jsonObject* jsonIteratorNext(jsonIterator* itr) {
+       if(!(itr && itr->obj)) return NULL;
+       if( itr->obj->type == JSON_HASH ) {
+               if(!itr->hashItr) return NULL;
+               jsonObject* item = osrfHashIteratorNext(itr->hashItr);
+               free(itr->key);
+               itr->key = strdup(itr->hashItr->current);
+               return item;
+       } else {
+               return jsonObjectGetIndex( itr->obj, itr->index++ );
+       }
+}
+
+int jsonIteratorHasNext(const jsonIterator* itr) {
+       if(!(itr && itr->obj)) return 0;
+       if( itr->obj->type == JSON_HASH )
+               return osrfHashIteratorHasNext( itr->hashItr );
+       return (itr->index < itr->obj->size) ? 1 : 0;
+}
+
+jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
+       if(!obj) return NULL;
+       return (obj->type == JSON_ARRAY) ? 
+        (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
+}
+
+
+
+unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
+       if( dest && dest->type == JSON_ARRAY ) {
+               osrfListRemove(dest->value.l, index);
+               return dest->value.l->size;
+       }
+       return -1;
+}
+
+
+unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
+       if( dest && key && dest->type == JSON_HASH ) {
+               osrfHashRemove(dest->value.h, key);
+               return 1;
+       }
+       return -1;
+}
+
+char* jsonObjectGetString(const jsonObject* obj) {
+       return (obj && obj->type == JSON_STRING) ? obj->value.s : NULL;
+}
+
+double jsonObjectGetNumber( const jsonObject* obj ) {
+       return (obj && obj->type == JSON_NUMBER) ? obj->value.n : 0;
+}
+
+void jsonObjectSetString(jsonObject* dest, const char* string) {
+       if(!(dest && string)) return;
+       JSON_INIT_CLEAR(dest, JSON_STRING);
+       free(dest->value.s);
+       dest->value.s = strdup(string);
+}
+
+void jsonObjectSetNumber(jsonObject* dest, double num) {
+       if(!dest) return;
+       JSON_INIT_CLEAR(dest, JSON_NUMBER);
+       dest->value.n = num;
+}
+
+void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
+       if(!(dest && classname)) return;
+       free(dest->classname);
+       dest->classname = strdup(classname);
+}
+const char* jsonObjectGetClass(const jsonObject* dest) {
+    if(!dest) return NULL;
+    return dest->classname;
+}
+
+jsonObject* jsonObjectClone( const jsonObject* o ) {
+    if(!o) return jsonNewObject(NULL);
+
+    int i;
+    jsonObject* arr; 
+    jsonObject* hash; 
+    jsonIterator* itr;
+    jsonObject* tmp;
+    jsonObject* result = NULL;
+
+    switch(o->type) {
+        case JSON_NULL:
+            result = jsonNewObject(NULL);
+            break;
+        case JSON_STRING:
+            result = jsonNewObject(jsonObjectGetString(o));
+            break;
+        case JSON_NUMBER:
+            result = jsonNewNumberObject(jsonObjectGetNumber(o));
+            break;
+        case JSON_BOOL:
+            result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
+            break;
+        case JSON_ARRAY:
+            arr = jsonNewObject(NULL);
+            arr->type = JSON_ARRAY;
+            for(i=0; i < o->size; i++) 
+                jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
+            result = arr;
+            break;
+        case JSON_HASH:
+            hash = jsonNewObject(NULL);
+            hash->type = JSON_HASH;
+            itr = jsonNewIterator(o);
+            while( (tmp = jsonIteratorNext(itr)) )
+                jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
+            jsonIteratorFree(itr);
+            result = hash;
+            break;
+    }
+
+    jsonObjectSetClass(result, jsonObjectGetClass(o));
+    return result;
+}
+
+int jsonBoolIsTrue( jsonObject* boolObj ) {
+    if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
+        return 1;
+    return 0;
+}
+
+
+char* jsonObjectToSimpleString( const jsonObject* o ) {
+       if(!o) return NULL;
+
+       char* value = NULL;
+
+       switch( o->type ) {
+
+               case JSON_NUMBER: {
+
+                       if( o->value.n == (int) o->value.n ) {
+                               INT_TO_STRING((int) o->value.n);        
+                               value = strdup(INTSTR);
+
+                       } else {
+                               DOUBLE_TO_STRING(o->value.n);
+                               value = strdup(DOUBLESTR);
+                       }
+
+                       break;
+               }
+
+               case JSON_STRING:
+                       value = strdup(o->value.s);
+       }
+
+       return value;
+}
+
+
diff --git a/src/libopensrf/osrf_json_parser.c b/src/libopensrf/osrf_json_parser.c
new file mode 100644 (file)
index 0000000..e7b3f26
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_utils.h>
+#include <ctype.h>
+
+
+/* if the client sets a global error handler, this will point to it */
+static void (*jsonClientErrorCallback) (const char*) = NULL;
+
+/* these are the handlers for our internal parser */
+static jsonParserHandler jsonInternalParserHandlerStruct = {
+       _jsonHandleStartObject,
+       _jsonHandleObjectKey,
+       _jsonHandleEndObject,
+       _jsonHandleStartArray,
+       _jsonHandleEndArray,
+       _jsonHandleNull,
+       _jsonHandleString,
+       _jsonHandleBool,
+       _jsonHandleNumber,
+       _jsonHandleError
+};
+static jsonParserHandler* 
+       jsonInternalParserHandler = &jsonInternalParserHandlerStruct; 
+
+
+jsonParserContext* jsonNewParser( jsonParserHandler* handler, void* userData) {
+       jsonParserContext* ctx;
+       OSRF_MALLOC(ctx, sizeof(jsonParserContext));
+       ctx->stateStack                 = osrfNewList();
+       ctx->buffer                                     = buffer_init(512);
+       ctx->utfbuf                                     = buffer_init(5);
+       ctx->handler                            = handler;
+       ctx->state                                      = 0;
+       ctx->index                                      = 0;
+       ctx->chunk                                      = NULL;
+       ctx->userData                           = userData;
+       return ctx;
+}
+
+void jsonParserFree( jsonParserContext* ctx ) {
+       if(!ctx) return;
+       buffer_free(ctx->buffer);
+       buffer_free(ctx->utfbuf);
+       osrfListFree(ctx->stateStack);
+       free(ctx);
+}
+
+
+void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*)) {
+       jsonClientErrorCallback = errorHandler;
+}
+
+
+int _jsonParserError( jsonParserContext* ctx, char* err, ... ) {
+       if( ctx->handler->handleError ) {
+               VA_LIST_TO_STRING(err);
+               int pre = ctx->index - 15;
+               int post= ctx->index + 15;
+               while( pre < 0 ) pre++;
+               while( post >= ctx->chunksize ) post--;
+               int l = post - pre;
+               char buf[l];
+               snprintf(buf, sizeof(buf), ctx->chunk + pre);
+               ctx->handler->handleError( ctx->userData, 
+                       "*JSON Parser Error\n - char  = %c\n "
+                       "- index = %d\n - near  => %s\n - %s", 
+                       ctx->chunk[ctx->index], ctx->index, buf, VA_BUF );
+       }
+       JSON_STATE_SET(ctx, JSON_STATE_IS_INVALID);
+       return -1;
+}
+
+
+int _jsonParserHandleUnicode( jsonParserContext* ctx ) {
+
+       /* collect as many of the utf characters as we can in this chunk */
+       JSON_CACHE_DATA(ctx, ctx->utfbuf, 4);
+
+       /* we ran off the end of the chunk */
+       if( ctx->utfbuf->n_used < 4 ) {
+               JSON_STATE_SET(ctx, JSON_STATE_IN_UTF);
+               return 1;
+       }
+
+       ctx->index--; /* push it back to index of the final utf char */
+
+       /* ----------------------------------------------------------------------- */
+       /* We have all of the escaped unicode data.  Write it to the buffer */
+       /* The following chunk is used with permission from 
+        * json-c http://oss.metaparadigm.com/json-c/ 
+        */
+       #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
+       unsigned char utf_out[4];
+       memset(utf_out, 0, sizeof(utf_out));
+       char* buf = ctx->utfbuf->buf;
+
+       unsigned int ucs_char =
+               (hexdigit(buf[0] ) << 12) +
+               (hexdigit(buf[1]) << 8) +
+               (hexdigit(buf[2]) << 4) +
+               hexdigit(buf[3]);
+
+       if (ucs_char < 0x80) {
+               utf_out[0] = ucs_char;
+               OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
+
+       } else if (ucs_char < 0x800) {
+               utf_out[0] = 0xc0 | (ucs_char >> 6);
+               utf_out[1] = 0x80 | (ucs_char & 0x3f);
+               OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
+
+       } else {
+               utf_out[0] = 0xe0 | (ucs_char >> 12);
+               utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
+               utf_out[2] = 0x80 | (ucs_char & 0x3f);
+               OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
+       }
+       /* ----------------------------------------------------------------------- */
+       /* ----------------------------------------------------------------------- */
+
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_UTF);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
+       OSRF_BUFFER_RESET(ctx->utfbuf);
+       return 0;
+}
+
+
+
+/* type : 0=null, 1=true, 2=false */
+int _jsonParserHandleMatch( jsonParserContext* ctx, int type ) {
+
+       switch(type) {
+
+               case 0: /* JSON null */
+
+                       /* first see if we have it all first */
+                       if( ctx->chunksize > (ctx->index + 3) ) {
+                               if( strncasecmp(ctx->chunk + ctx->index, "null", 4) ) 
+                                       return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
+                               if( ctx->handler->handleNull ) 
+                                       ctx->handler->handleNull(ctx->userData);
+                               ctx->index += 4;
+                               break;
+                       }
+
+                       JSON_CACHE_DATA(ctx, ctx->buffer, 4);
+                       if( ctx->buffer->n_used < 4 ) {
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_NULL);
+                               return 1;
+                       } 
+
+                       if( strncasecmp(ctx->buffer->buf, "null", 4) ) 
+                               return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
+                       if( ctx->handler->handleNull ) 
+                               ctx->handler->handleNull(ctx->userData);
+                       break;
+
+               case 1: /* JSON true */
+
+                       /* see if we have it all first */
+                       if( ctx->chunksize > (ctx->index + 3) ) {
+                               if( strncasecmp(ctx->chunk + ctx->index, "true", 4) ) 
+                                       return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
+                               if( ctx->handler->handleBool ) 
+                                       ctx->handler->handleBool(ctx->userData, 1);
+                               ctx->index += 4;
+                               break;
+                       }
+
+                       JSON_CACHE_DATA(ctx, ctx->buffer, 4);
+                       if( ctx->buffer->n_used < 4 ) {
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_TRUE);
+                               return 1;
+                       } 
+                       if( strncasecmp( ctx->buffer->buf, "true", 4 ) ) {
+                               return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
+                       }
+                       if( ctx->handler->handleBool ) 
+                               ctx->handler->handleBool(ctx->userData, 1);
+                       break;
+
+               case 2: /* JSON false */
+
+                       /* see if we have it all first */
+                       if( ctx->chunksize > (ctx->index + 4) ) {
+                               if( strncasecmp(ctx->chunk + ctx->index, "false", 5) ) 
+                                       return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
+                               if( ctx->handler->handleBool ) 
+                                       ctx->handler->handleBool(ctx->userData, 0);
+                               ctx->index += 5;
+                               break;
+                       }
+
+                       JSON_CACHE_DATA(ctx, ctx->buffer, 5);
+                       if( ctx->buffer->n_used < 5 ) {
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_FALSE);
+                               return 1;
+                       }
+                       if( strncasecmp( ctx->buffer->buf, "false", 5 ) ) 
+                               return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
+                       if( ctx->handler->handleBool ) 
+                               ctx->handler->handleBool(ctx->userData, 0);
+                       break;
+
+               default: 
+                       fprintf(stderr, "Invalid type flag\n");
+                       return -1;
+
+       }
+
+       ctx->index--; /* set it back to the index of the final sequence character */
+       OSRF_BUFFER_RESET(ctx->buffer);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NULL);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_TRUE);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_FALSE);
+
+       return 0;
+}
+
+
+int _jsonParserHandleString( jsonParserContext* ctx ) {
+
+       char c = ctx->chunk[ctx->index];
+
+       if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_ESCAPE) ) {
+
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_UTF) ) {
+
+                       return _jsonParserHandleUnicode( ctx );
+                                               
+               } else {
+
+                       switch(c) {
+
+                               /* handle all of the escape chars */
+                               case '\\': OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\\' ); break;
+                               case '"'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\"' ); break;
+                               case 't'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\t' ); break;
+                               case 'b'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\b' ); break;
+                               case 'f'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\f' ); break;
+                               case 'r'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\r' ); break;
+                               case 'n'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\n' ); break;
+                               case 'u'        : 
+                                       ctx->index++; /* progress to the first utf char */
+                                       return _jsonParserHandleUnicode( ctx );
+                               default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
+                       }
+               }
+
+               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
+               return 0;
+
+       } else {
+
+               switch(c) {
+
+                       case '"'        : /* this string is ending */
+                               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_KEY) ) {
+
+                                       /* object key */
+                                       if(ctx->handler->handleObjectKey) {
+                                               ctx->handler->handleObjectKey( 
+                                                       ctx->userData, ctx->buffer->buf);
+                                       }
+
+                               } else { /* regular json string */
+
+                                       if(ctx->handler->handleString) {
+                                               ctx->handler->handleString( 
+                                                       ctx->userData, ctx->buffer->buf );
+                                       }
+
+                               }
+
+                               OSRF_BUFFER_RESET(ctx->buffer); /* flush the buffer and states */
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_STRING);
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case '\\' : JSON_STATE_SET(ctx, JSON_STATE_IN_ESCAPE); break;
+                       default  : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
+               }
+       }
+       return 0;
+}
+
+
+int _jsonParserHandleNumber( jsonParserContext* ctx ) {
+       char c = ctx->chunk[ctx->index];
+
+       do {
+               OSRF_BUFFER_ADD_CHAR(ctx->buffer, c);
+               c = ctx->chunk[++(ctx->index)];
+       } while( strchr(JSON_NUMBER_CHARS, c) && ctx->index < ctx->chunksize );
+
+       /* if we're run off the end of the chunk and we're not parsing the last chunk,
+        * save the number and the state */
+       if( ctx->index >= ctx->chunksize && 
+                       ! JSON_PARSE_FLAG_CHECK(ctx, JSON_PARSE_LAST_CHUNK) ) {
+               JSON_STATE_SET(ctx, JSON_STATE_IN_NUMBER);
+               return 1;
+       }
+
+       /* make me more strict */
+       char* err = NULL;
+       double d = strtod(ctx->buffer->buf, &err);
+       if(err && err[0] != '\0') 
+               return _jsonParserError(ctx, "Invalid number sequence");
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NUMBER);
+       OSRF_BUFFER_RESET(ctx->buffer);
+       if(ctx->handler->handleNumber)
+               ctx->handler->handleNumber( ctx->userData, d );
+       ctx->index--; /* scooch back to the first non-digit number */
+       return 0;
+}
+
+
+
+
+int jsonParseChunk( jsonParserContext* ctx, const char* data, int datalen, int flags ) {
+
+       if( !( ctx && ctx->handler && data && datalen > 0 )) return -1;
+       ctx->chunksize  = datalen;
+       ctx->chunk              = data;
+       ctx->flags              = flags;
+       char c;
+
+       if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_INVALID) )
+               return _jsonParserError( ctx, "JSON Parser cannot continue after an error" );
+
+       if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_DONE) )
+               return _jsonParserError( ctx, "Extra content at end of JSON data" );
+
+       for( ctx->index = 0; (ctx->index < ctx->chunksize) && 
+                               (c = ctx->chunk[ctx->index]); ctx->index++ ) {
+
+               /* middle of parsing a string */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_STRING)) {
+                       if( _jsonParserHandleString(ctx) == -1 )
+                               return -1;
+                       continue;
+               }
+
+               /* middle of parsing a number */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_NUMBER) ) {
+                       if( _jsonParserHandleNumber(ctx) == -1 )
+                               return -1;
+                       continue;
+               }
+
+
+#ifdef OSRF_JSON_ALLOW_COMMENTS
+               /* we just saw a bare '/' character */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_START_COMMENT) ) {
+                       if(c == '*') {
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_START_COMMENT);
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
+                               continue;
+                       } else {
+                               return _jsonParserError( ctx, "Invalid comment initializer" );
+                       }
+               }
+
+               /* we're currently in the middle of a comment block */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_COMMENT) ) {
+                       if(c == '*') {
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_COMMENT);
+                               JSON_STATE_SET(ctx, JSON_STATE_END_COMMENT);
+                               continue;
+                       } else {
+                               continue;
+                       }
+               }
+
+               /* we're in a comment, and we just saw a '*' character */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_END_COMMENT) ) {
+                       if( c == '/' ) { /* comment is finished */
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
+                               continue;
+                       } else {
+                               /* looks like this isn't the end of the comment after all */
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
+                       }
+               }
+#endif
+
+               /* if we're in the middle of parsing a null/true/false sequence */
+               if( JSON_STATE_CHECK(ctx, (JSON_STATE_IN_NULL | 
+                                       JSON_STATE_IN_TRUE | JSON_STATE_IN_FALSE)) ) {
+
+                       int type = (JSON_STATE_CHECK(ctx, JSON_STATE_IN_NULL)) ? 0 :
+                               (JSON_STATE_CHECK(ctx, JSON_STATE_IN_TRUE)) ? 1 : 2;
+
+                       if( _jsonParserHandleMatch( ctx, type ) == -1 ) 
+                               return -1;
+                       continue;
+               }
+
+               JSON_EAT_WS(ctx);
+
+               /* handle all of the top level characters */
+               switch(c) {
+
+                       case '{' : /* starting an object */
+                               if( ctx->handler->handleStartObject) 
+                                       ctx->handler->handleStartObject( ctx->userData );
+                               JSON_STATE_PUSH(ctx, JSON_STATE_IN_OBJECT);
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case '}' : /* ending an object */
+                               if( ctx->handler->handleEndObject) 
+                                       ctx->handler->handleEndObject( ctx->userData ); 
+                JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
+                               JSON_STATE_POP(ctx);
+                               if( JSON_STATE_PEEK(ctx) == NULL )
+                                       JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
+                               break;
+
+                       case '[' : /* starting an array */
+                               if( ctx->handler->handleStartArray )
+                                       ctx->handler->handleStartArray( ctx->userData );
+                               JSON_STATE_PUSH(ctx, JSON_STATE_IN_ARRAY);
+                               break;
+
+                       case ']': /* ending an array */
+                               if( ctx->handler->handleEndArray )
+                                       ctx->handler->handleEndArray( ctx->userData );
+                               JSON_STATE_POP(ctx);
+                               if( JSON_STATE_PEEK(ctx) == NULL )
+                                       JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
+                               break;
+                               
+                       case ':' : /* done with the object key */
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case ',' : /* after object or array item */
+                               if( JSON_STATE_CHECK_STACK(ctx, JSON_STATE_IN_OBJECT) )
+                                       JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case 'n' :
+                       case 'N' : /* null */
+                               if( _jsonParserHandleMatch( ctx, 0 ) == -1)
+                                       return -1;
+                               break;
+
+                       case 't' :
+                       case 'T' :
+                               if( _jsonParserHandleMatch( ctx, 1 ) == -1 )
+                                       return -1;
+                               break;
+
+                       case 'f' :
+                       case 'F' :
+                               if( _jsonParserHandleMatch( ctx, 2 ) == -1)
+                                       return -1;
+                               break;
+
+                       case '"' : 
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_STRING);
+                               break;
+
+#ifdef OSRF_JSON_ALLOW_COMMENTS
+                       case '/' :
+                               JSON_STATE_SET(ctx, JSON_STATE_START_COMMENT);
+                               break;
+#endif
+
+                       default:
+                               if( strchr(JSON_NUMBER_CHARS, c) ) {
+                                       if( _jsonParserHandleNumber( ctx ) == -1 )
+                                               return -1;
+                               } else {
+                                       return _jsonParserError( ctx, "Invalid Token" );
+                               }
+               }
+       }
+
+       return 0;
+}
+
+
+jsonInternalParser* _jsonNewInternalParser() {
+       jsonInternalParser* p;
+       OSRF_MALLOC(p, sizeof(jsonInternalParser));
+       p->ctx = jsonNewParser( jsonInternalParserHandler, p );
+       p->obj          = NULL;
+       p->lastkey      = NULL;
+       return p;
+}
+
+void _jsonInternalParserFree(jsonInternalParser* p) {
+       if(!p) return;
+       jsonParserFree(p->ctx);
+       free(p->lastkey);
+       free(p);
+}
+
+static jsonObject* _jsonParseStringImpl(const char* str, void (*errorHandler) (const char*) ) {
+       jsonInternalParser* parser = _jsonNewInternalParser();
+       parser->handleError = errorHandler;
+       jsonParseChunk( parser->ctx, str, strlen(str),  JSON_PARSE_LAST_CHUNK );
+       jsonObject* obj = parser->obj;
+       _jsonInternalParserFree(parser);
+       return obj;
+}
+
+jsonObject* jsonParseStringHandleError( 
+               void (*errorHandler) (const char*), char* str, ... ) {
+       if(!str) return NULL;
+       VA_LIST_TO_STRING(str);
+       return _jsonParseStringImpl(VA_BUF, errorHandler);
+}
+
+jsonObject* jsonParseString( const char* str ) {
+       if(!str) return NULL;
+       jsonObject* obj =  _jsonParseStringImpl(str, NULL);
+       jsonObject* obj2 = jsonObjectDecodeClass(obj);
+       jsonObjectFree(obj);
+       return obj2;
+}
+
+jsonObject* jsonParseStringRaw( const char* str ) {
+       if(!str) return NULL;
+       return _jsonParseStringImpl(str, NULL);
+}
+
+jsonObject* jsonParseStringFmt( const char* str, ... ) {
+       if(!str) return NULL;
+       VA_LIST_TO_STRING(str);
+       return _jsonParseStringImpl(VA_BUF, NULL);
+}
+
+
+#define JSON_SHOVE_ITEM(ctx,type)  \
+       jsonInternalParser* p = (jsonInternalParser*) ctx;\
+       _jsonInsertParserItem(p, jsonNewObjectType(type));
+
+void _jsonHandleStartObject(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_HASH); }
+void _jsonHandleStartArray(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_ARRAY); }
+void _jsonHandleNull(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_NULL); }
+
+void _jsonHandleObjectKey(void* ctx, char* key) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       free(p->lastkey);
+       p->lastkey = strdup(key);
+}
+
+void _jsonHandleEndObject(void* ctx) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       p->current = p->current->parent;
+}
+
+void _jsonHandleEndArray(void* ctx) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       p->current = p->current->parent;
+}
+
+void _jsonHandleString(void* ctx, char* string) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       _jsonInsertParserItem(p, jsonNewObject(string));
+}
+
+void _jsonHandleBool(void* ctx, int boolval) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       jsonObject* obj = jsonNewObjectType(JSON_BOOL);
+       obj->value.b = boolval;
+       _jsonInsertParserItem(p, obj);
+}
+
+void _jsonHandleNumber(void* ctx, double num) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       _jsonInsertParserItem(p, jsonNewNumberObject(num));
+}
+
+void _jsonHandleError(void* ctx, char* str, ...) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       VA_LIST_TO_STRING(str);
+
+       if( p->handleError ) 
+               p->handleError(VA_BUF);
+       else 
+               if( jsonClientErrorCallback ) 
+                       jsonClientErrorCallback(VA_BUF);
+
+       else fprintf(stderr, "%s\n", VA_BUF);
+       jsonObjectFree(p->obj);
+       p->obj = NULL;
+}
+
+
+void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ) {
+
+       if( !p->obj ) {
+
+               /* new parser, set the new object to our object */
+               p->obj = p->current = newo;
+
+       } else {
+
+               /* insert the new object into the current container object */
+               switch(p->current->type) { 
+                       case JSON_HASH  : jsonObjectSetKey(p->current, p->lastkey, newo);  break;
+                       case JSON_ARRAY: jsonObjectPush(p->current, newo); break;
+                       default: fprintf(stderr, "%s:%d -> how?\n", JSON_LOG_MARK); 
+               } 
+
+               /* if the new object is a container object, make it our current container */
+               if( newo->type == JSON_ARRAY || newo->type == JSON_HASH )
+                       p->current = newo;      
+       }
+}
+
+
diff --git a/src/libopensrf/osrf_json_test.c b/src/libopensrf/osrf_json_test.c
new file mode 100644 (file)
index 0000000..4d1994d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Basic JSON test module.  Needs more strenous tests....
+ *
+ */
+#include <stdio.h>
+#include <opensrf/osrf_json.h>
+
+static void speedTest();
+
+
+int main(int argc, char* argv[]) {
+    /* XXX add support for command line test type specification */
+    speedTest(); 
+    return 0; 
+}
+
+
+
+static void speedTest() {
+
+    /* creates a giant json object, generating JSON strings
+     * of subobjects as it goes. */
+
+    int i,k;
+    int count = 50;
+    char buf[16];
+    char* jsonString;
+
+    jsonObject* array;
+    jsonObject* dupe;
+    jsonObject* hash = jsonNewObject(NULL);
+
+    for(i = 0; i < count; i++) {
+        
+        snprintf(buf, sizeof(buf), "key_%d", i);
+
+        array = jsonNewObject(NULL);
+        for(k = 0; k < count + i; k++) {
+            jsonObjectPush(array, jsonNewNumberObject(k));
+            jsonObjectPush(array, jsonNewObject(NULL));
+            jsonObjectPush(array, jsonNewObjectFmt("str %d-%d", i, k));
+        }
+        jsonObjectSetKey(hash, buf, array);
+
+        jsonString = jsonObjectToJSON(hash);
+        printf("%s\n\n", jsonString);
+        dupe = jsonParseString(jsonString);
+
+        jsonObjectFree(dupe);
+        free(jsonString);
+    }
+
+    jsonObjectFree(hash);
+}
+
diff --git a/src/libopensrf/osrf_json_tools.c b/src/libopensrf/osrf_json_tools.c
new file mode 100644 (file)
index 0000000..aaf65a5
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+#include <opensrf/osrf_json.h>
+
+jsonObject* _jsonObjectEncodeClass( const jsonObject* obj, int ignoreClass );
+
+
+jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ...);
+jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path);
+jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root);
+
+
+static char* __tabs(int count) {
+       growing_buffer* buf = buffer_init(24);
+       int i;
+       for(i=0;i<count;i++) OSRF_BUFFER_ADD(buf, "  ");
+    return buffer_release(buf);
+}
+
+char* jsonFormatString( const char* string ) {
+       if(!string) return strdup("");
+
+       growing_buffer* buf = buffer_init(64);
+       int i;
+       int depth = 0;
+       char* tab = NULL;
+
+       char c;
+       for(i=0; i!= strlen(string); i++) {
+               c = string[i];
+
+               if( c == '{' || c == '[' ) {
+
+                       tab = __tabs(++depth);
+                       buffer_fadd( buf, "%c\n%s", c, tab);
+                       free(tab);
+
+               } else if( c == '}' || c == ']' ) {
+
+                       tab = __tabs(--depth);
+                       buffer_fadd( buf, "\n%s%c", tab, c);
+                       free(tab);
+
+                       if(string[i+1] != ',') {
+                               tab = __tabs(depth);
+                               buffer_fadd( buf, "%s", tab );  
+                               free(tab);
+                       }
+
+               } else if( c == ',' ) {
+
+                       tab = __tabs(depth);
+                       buffer_fadd(buf, ",\n%s", tab);
+                       free(tab);
+
+               } else { buffer_add_char(buf, c); }
+
+       }
+
+    return buffer_release(buf);
+}
+
+
+
+jsonObject* jsonObjectDecodeClass( const jsonObject* obj ) {
+       if(!obj) return jsonNewObject(NULL);
+
+       jsonObject* newObj                       = NULL; 
+       const jsonObject* classObj       = NULL;
+       const jsonObject* payloadObj = NULL;
+       int i;
+
+       if( obj->type == JSON_HASH ) {
+
+               /* are we a special class object? */
+               if( (classObj = jsonObjectGetKeyConst( obj, JSON_CLASS_KEY )) ) {
+
+                       /* do we have a payload */
+                       if( (payloadObj = jsonObjectGetKeyConst( obj, JSON_DATA_KEY )) ) {
+                               newObj = jsonObjectDecodeClass( payloadObj ); 
+                               jsonObjectSetClass( newObj, jsonObjectGetString(classObj) );
+
+                       } else { /* class is defined but there is no payload */
+                               return NULL;
+                       }
+
+               } else { /* we're a regular hash */
+
+                       jsonIterator* itr = jsonNewIterator(obj);
+                       jsonObject* tmp;
+                       newObj = jsonNewObjectType(JSON_HASH);
+                       while( (tmp = jsonIteratorNext(itr)) ) {
+                               jsonObject* o = jsonObjectDecodeClass(tmp);
+                               jsonObjectSetKey( newObj, itr->key, o );
+                       }
+                       jsonIteratorFree(itr);
+               }
+
+       } else {
+
+               if( obj->type == JSON_ARRAY ) { /* we're an array */
+                       newObj = jsonNewObjectType(JSON_ARRAY);
+                       for( i = 0; i != obj->size; i++ ) {
+                               jsonObject* tmp = jsonObjectDecodeClass(jsonObjectGetIndex( obj, i ) );
+                               jsonObjectSetIndex( newObj, i, tmp );
+                       }
+
+               } else { /* not an aggregate type */
+                       newObj = jsonObjectClone(obj);
+               }
+       }
+               
+       return newObj;
+}
+
+jsonObject* jsonObjectEncodeClass( const jsonObject* obj ) {
+       return _jsonObjectEncodeClass( obj, 0 );
+}
+
+jsonObject* _jsonObjectEncodeClass( const jsonObject* obj, int ignoreClass ) {
+
+       //if(!obj) return NULL;
+       if(!obj) return jsonNewObject(NULL);
+       jsonObject* newObj = NULL;
+
+       if( obj->classname && ! ignoreClass ) {
+               newObj = jsonNewObjectType(JSON_HASH);
+
+               jsonObjectSetKey( newObj, 
+                       JSON_CLASS_KEY, jsonNewObject(obj->classname) ); 
+
+               jsonObjectSetKey( newObj, 
+                       JSON_DATA_KEY, _jsonObjectEncodeClass(obj, 1));
+
+       } else if( obj->type == JSON_HASH ) {
+
+               jsonIterator* itr = jsonNewIterator(obj);
+               jsonObject* tmp;
+               newObj = jsonNewObjectType(JSON_HASH);
+
+               while( (tmp = jsonIteratorNext(itr)) ) {
+                       jsonObjectSetKey( newObj, itr->key, 
+                                       _jsonObjectEncodeClass(tmp, 0));
+               }
+               jsonIteratorFree(itr);
+
+       } else if( obj->type == JSON_ARRAY ) {
+
+               newObj = jsonNewObjectType(JSON_ARRAY);
+               int i;
+               for( i = 0; i != obj->size; i++ ) {
+                       jsonObjectSetIndex( newObj, i, 
+                               _jsonObjectEncodeClass(jsonObjectGetIndex( obj, i ), 0 ));
+               }
+
+       } else {
+               newObj = jsonObjectClone(obj);
+       }
+
+       return newObj;
+}
+
+jsonObject* jsonParseFile( char* filename ) {
+       if(!filename) return NULL;
+       char* data = file_to_string(filename);
+       jsonObject* o = jsonParseString(data);
+       free(data);
+       return o;
+}
+
+
+
+jsonObject* jsonObjectFindPath( const jsonObject* obj, char* format, ...) {
+       if(!obj || !format || strlen(format) < 1) return NULL;  
+
+       VA_LIST_TO_STRING(format);
+       char* buf = VA_BUF;
+       char* token = NULL;
+       char* t = buf;
+       char* tt; /* strtok storage */
+
+       /* copy the path before strtok_r destroys it */
+       char* pathcopy = strdup(buf);
+
+       /* grab the root of the path */
+       token = strtok_r(t, "/", &tt);
+       if(!token) return NULL;
+
+       /* special case where path starts with //  (start anywhere) */
+       if(strlen(pathcopy) > 2 && pathcopy[0] == '/' && pathcopy[1] == '/') {
+               jsonObject* it = _jsonObjectFindPathRecurse(obj, token, pathcopy + 1);
+               free(pathcopy);
+               return it;
+       }
+
+       free(pathcopy);
+
+       t = NULL;
+       do { 
+               obj = jsonObjectGetKey(obj, token);
+       } while( (token = strtok_r(NULL, "/", &tt)) && obj);
+
+       return jsonObjectClone(obj);
+}
+
+/* --------------------------------------------------------------- */
+
+
+
+jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path) {
+
+       if(!obj || ! root || !path) return NULL;
+
+       /* collect all of the potential objects */
+       jsonObject* arr = __jsonObjectFindPathRecurse(obj, root);
+
+       /* container for fully matching objects */
+       jsonObject* newarr = jsonParseString("[]");
+       int i;
+
+       /* path is just /root or /root/ */
+       if( strlen(root) + 2 >= strlen(path) ) {
+               return arr;
+
+       } else {
+
+               /* gather all of the sub-objects that match the full path */
+               for( i = 0; i < arr->size; i++ ) {
+                       const jsonObject* a = jsonObjectGetIndex(arr, i);
+                       jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1); 
+
+                       if(thing) { //jsonObjectPush(newarr, thing);
+               if(thing->type == JSON_ARRAY) {
+               int i;
+                                       for( i = 0; i != thing->size; i++ )
+                                               jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i)));
+                                       jsonObjectFree(thing);
+
+                               } else {
+                                       jsonObjectPush(newarr, thing);
+                               }                                               
+                       }
+               }
+       }
+       
+       jsonObjectFree(arr);
+       return newarr;
+}
+
+jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root) {
+
+       jsonObject* arr = jsonParseString("[]");
+       if(!obj) return arr;
+
+       int i;
+
+       /* if the current object has a node that matches, add it */
+
+       jsonObject* o = jsonObjectGetKey(obj, root);
+       if(o) jsonObjectPush( arr, jsonObjectClone(o) );
+
+       jsonObject* tmp = NULL;
+       jsonObject* childarr;
+       jsonIterator* itr = jsonNewIterator(obj);
+
+       /* recurse through the children and find all potential nodes */
+       while( (tmp = jsonIteratorNext(itr)) ) {
+               childarr = __jsonObjectFindPathRecurse(tmp, root);
+               if(childarr && childarr->size > 0) {
+                       for( i = 0; i!= childarr->size; i++ ) {
+                               jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
+                       }
+               }
+               jsonObjectFree(childarr);
+       }
+
+       jsonIteratorFree(itr);
+
+       return arr;
+}
+
+
+
+
diff --git a/src/libopensrf/osrf_json_xml.c b/src/libopensrf/osrf_json_xml.c
new file mode 100644 (file)
index 0000000..283daf4
--- /dev/null
@@ -0,0 +1,365 @@
+#include <opensrf/osrf_json_xml.h>
+
+#ifdef OSRF_JSON_ENABLE_XML_UTILS
+
+struct osrfXMLGatewayParserStruct {
+    osrfList* objStack;
+    osrfList* keyStack;
+    jsonObject* obj;
+    short inString;
+    short inNumber;
+    short error;
+};
+typedef struct osrfXMLGatewayParserStruct osrfXMLGatewayParser;
+
+/** returns the attribute value with the given attribute name */
+static char* getXMLAttr(const xmlChar** atts, char* attr_name) {
+    int i;
+    if (atts != NULL) {
+        for(i = 0; (atts[i] != NULL); i++) {
+            if(strcmp((char*) atts[i++], attr_name) == 0) {
+                if(atts[i] != NULL) 
+                    return (char*) atts[i];
+            }
+        }
+    }
+    return NULL;
+}
+
+
+static void appendChild(osrfXMLGatewayParser* p, jsonObject* obj) {
+
+    if(p->obj == NULL) 
+        p->obj = obj;
+
+    if(p->objStack->size == 0)
+        return;
+    
+    jsonObject* parent = OSRF_LIST_GET_INDEX(p->objStack, p->objStack->size - 1);
+
+    if(parent->type == JSON_ARRAY) {
+        jsonObjectPush(parent, obj);
+    } else {
+        char* key = osrfListPop(p->keyStack);
+        jsonObjectSetKey(parent, key, obj);
+        free(key); /* the list is not setup for auto-freeing */
+    }
+}
+
+
+
+static void startElementHandler(
+    void *parser, const xmlChar *name, const xmlChar **atts) {
+
+    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+    jsonObject* obj;
+
+    char* hint = getXMLAttr(atts, "class_hint");
+
+    if(!strcmp((char*) name, "null")) {
+        appendChild(p, jsonNewObject(NULL));
+        return;
+    }
+
+    if(!strcmp((char*) name, "string")) {
+        p->inString = 1;
+        return;
+    }
+
+    if(!strcmp((char*) name, "element")) {
+       osrfListPush(p->keyStack, strdup(getXMLAttr(atts, "key")));
+       return;
+    }
+
+    if(!strcmp((char*) name, "object")) {
+        obj = jsonNewObject(NULL);
+        jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
+        obj->type = JSON_HASH;
+        appendChild(p, obj);
+        osrfListPush(p->objStack, obj);
+        return;
+    }
+
+    if(!strcmp((char*) name, "array")) {
+        obj = jsonNewObject(NULL);
+        jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
+        obj->type = JSON_ARRAY;
+        appendChild(p, obj);
+        osrfListPush(p->objStack, obj);
+        return;
+    }
+
+
+    if(!strcmp((char*) name, "number")) {
+        p->inNumber = 1;
+        return;
+    }
+
+    if(!strcmp((char*) name, "boolean")) {
+        obj = jsonNewObject(NULL);
+        obj->type = JSON_BOOL;
+        char* val = getXMLAttr(atts, "value");
+        if(val && !strcmp(val, "true"))
+            obj->value.b = 1;
+        
+        return;
+    }
+}
+
+static void endElementHandler( void *parser, const xmlChar *name) {
+    if(!strcmp((char*) name, "array") || !strcmp((char*) name, "object")) {
+        osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+        osrfListPop(p->objStack);
+    }
+}
+
+static void characterHandler(void *parser, const xmlChar *ch, int len) {
+
+    char data[len+1];
+    strncpy(data, (char*) ch, len);
+    data[len] = '\0';
+    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+
+    if(p->inString) {
+        appendChild(p, jsonNewObject(data));
+        p->inString = 0;
+        return;
+    }
+
+    if(p->inNumber) {
+        appendChild(p, jsonNewNumberObject(atof(data)));
+        p->inNumber = 0;
+        return;
+    }
+}
+
+static void parseWarningHandler(void *parser, const char* msg, ...) {
+    VA_LIST_TO_STRING(msg);
+    fprintf(stderr, "Parser warning %s\n", VA_BUF);
+    fflush(stderr);
+}
+
+static void parseErrorHandler(void *parser, const char* msg, ...) {
+
+    VA_LIST_TO_STRING(msg);
+    fprintf(stderr, "Parser error %s\n", VA_BUF);
+    fflush(stderr);
+
+    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+
+    /*  keyStack as strdup'ed strings.  The list may
+     *  not be empty, so tell it to free the items
+     *  when it's freed (from the main routine)
+     */
+    osrfListSetDefaultFree(p->keyStack);
+    jsonObjectFree(p->obj);
+
+    p->obj = NULL;
+    p->error = 1;
+}
+
+
+
+
+static xmlSAXHandler SAXHandlerStruct = {
+    NULL,                            /* internalSubset */
+    NULL,                            /* isStandalone */
+    NULL,                            /* hasInternalSubset */
+    NULL,                            /* hasExternalSubset */
+    NULL,                            /* resolveEntity */
+    NULL,                            /* getEntity */
+    NULL,                            /* entityDecl */
+    NULL,                            /* notationDecl */
+    NULL,                            /* attributeDecl */
+    NULL,                            /* elementDecl */
+    NULL,                            /* unparsedEntityDecl */
+    NULL,                            /* setDocumentLocator */
+    NULL,                            /* startDocument */
+    NULL,                            /* endDocument */
+    startElementHandler,        /* startElement */
+    endElementHandler,      /* endElement */
+    NULL,                            /* reference */
+    characterHandler,           /* characters */
+    NULL,                            /* ignorableWhitespace */
+    NULL,                            /* processingInstruction */
+    NULL,                            /* comment */
+    parseWarningHandler,     /* xmlParserWarning */
+    parseErrorHandler,       /* xmlParserError */
+    NULL,                            /* xmlParserFatalError : unused */
+    NULL,                            /* getParameterEntity */
+    NULL,                            /* cdataBlock; */
+    NULL,                            /* externalSubset; */
+    1,
+    NULL,
+    NULL,                            /* startElementNs */
+    NULL,                            /* endElementNs */
+    NULL                            /* xmlStructuredErrorFunc */
+};
+
+static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
+
+jsonObject* jsonXMLToJSONObject(const char* xml) {
+
+    osrfXMLGatewayParser parser;
+
+    /* don't define freeItem, since objects will be cleaned by freeing the parent */
+    parser.objStack = osrfNewList(); 
+    /* don't define freeItem, since the list eill end up empty if there are no errors*/
+    parser.keyStack = osrfNewList(); 
+    parser.obj = NULL;
+    parser.inString = 0;
+    parser.inNumber = 0;
+
+    xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt(SAXHandler, &parser, "", 0, NULL);
+    xmlParseChunk(ctxt, xml, strlen(xml), 1);
+
+    osrfListFree(parser.objStack);
+    osrfListFree(parser.keyStack);
+    xmlFreeParserCtxt(ctxt);
+    xmlCleanupCharEncodingHandlers();
+    xmlDictCleanup();
+    xmlCleanupParser();
+
+    return parser.obj;
+}
+
+
+
+
+
+
+static char* _escape_xml (char*);
+static int _recurse_jsonObjectToXML(jsonObject*, growing_buffer*);
+
+char* jsonObjectToXML(jsonObject* obj) {
+
+       growing_buffer * res_xml;
+       char * output;
+
+       res_xml = buffer_init(1024);
+
+       if (!obj)
+               return strdup("<null/>");
+       
+       _recurse_jsonObjectToXML( obj, res_xml );
+       output = buffer_data(res_xml);
+       
+       buffer_free(res_xml);
+
+       return output;
+
+}
+
+int _recurse_jsonObjectToXML(jsonObject* obj, growing_buffer* res_xml) {
+
+       char * hint = NULL;
+       char * bool_val = NULL;
+       int i = 0;
+       
+       if (obj->classname)
+               hint = strdup(obj->classname);
+
+       if(obj->type == JSON_NULL) {
+
+               if (hint)
+                       buffer_fadd(res_xml, "<null class_hint=\"%s\"/>",hint);
+               else
+                       buffer_add(res_xml, "<null/>");
+
+       } else if(obj->type == JSON_BOOL) {
+
+               if (obj->value.b)
+                       bool_val = strdup("true");
+               else
+                       bool_val = strdup("false");
+
+               if (hint)
+                       buffer_fadd(res_xml, "<boolean value=\"%s\" class_hint=\"%s\"/>", bool_val, hint);
+               else
+                       buffer_fadd(res_xml, "<boolean value=\"%s\"/>", bool_val);
+
+               free(bool_val);
+                
+       } else if (obj->type == JSON_STRING) {
+               if (hint) {
+                       char * t = _escape_xml(jsonObjectGetString(obj));
+                       buffer_fadd(res_xml,"<string class_hint=\"%s\">%s</string>", hint, t);
+                       free(t);
+               } else {
+                       char * t = _escape_xml(jsonObjectGetString(obj));
+                       buffer_fadd(res_xml,"<string>%s</string>", t);
+                       free(t);
+               }
+
+       } else if(obj->type == JSON_NUMBER) {
+               double x = jsonObjectGetNumber(obj);
+               if (hint) {
+                       if (x == (int)x)
+                               buffer_fadd(res_xml,"<number class_hint=\"%s\">%d</number>", hint, (int)x);
+                       else
+                               buffer_fadd(res_xml,"<number class_hint=\"%s\">%lf</number>", hint, x);
+               } else {
+                       if (x == (int)x)
+                               buffer_fadd(res_xml,"<number>%d</number>", (int)x);
+                       else
+                               buffer_fadd(res_xml,"<number>%lf</number>", x);
+               }
+
+       } else if (obj->type == JSON_ARRAY) {
+
+               if (hint) 
+                       buffer_fadd(res_xml,"<array class_hint=\"%s\">", hint);
+               else
+                               buffer_add(res_xml,"<array>");
+
+               for ( i = 0; i!= obj->size; i++ )
+                       _recurse_jsonObjectToXML(jsonObjectGetIndex(obj,i), res_xml);
+
+               buffer_add(res_xml,"</array>");
+
+       } else if (obj->type == JSON_HASH) {
+
+               if (hint)
+                       buffer_fadd(res_xml,"<object class_hint=\"%s\">", hint);
+               else
+                       buffer_add(res_xml,"<object>");
+
+               jsonIterator* itr = jsonNewIterator(obj);
+               jsonObject* tmp;
+               while( (tmp = jsonIteratorNext(itr)) ) {
+                       buffer_fadd(res_xml,"<element key=\"%s\">",itr->key);
+                       _recurse_jsonObjectToXML(tmp, res_xml);
+                       buffer_add(res_xml,"</element>");
+               }
+               jsonIteratorFree(itr);
+
+               buffer_add(res_xml,"</object>");
+       }
+
+       if (hint)
+               free(hint);
+
+       return 1;
+}
+
+char* _escape_xml (char* text) {
+       char* out;
+       growing_buffer* b = buffer_init(256);
+       int len = strlen(text);
+       int i;
+       for (i = 0; i < len; i++) {
+               if (text[i] == '&')
+                       buffer_add(b,"&amp;");
+               else if (text[i] == '<')
+                       buffer_add(b,"&lt;");
+               else if (text[i] == '>')
+                       buffer_add(b,"&gt;");
+               else
+                       buffer_add_char(b,text[i]);
+       }
+       out = buffer_data(b);
+       buffer_free(b);
+       return out;
+}
+
+#endif
diff --git a/src/libopensrf/osrf_legacy_json.c b/src/libopensrf/osrf_legacy_json.c
new file mode 100644 (file)
index 0000000..a81d46d
--- /dev/null
@@ -0,0 +1,881 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+
+#include <opensrf/osrf_legacy_json.h>
+
+/* keep a copy of the length of the current json string so we don't 
+ * have to calculate it in each function
+ */
+int current_strlen; 
+
+
+jsonObject* legacy_jsonParseString( const char* string) {
+       return json_parse_string( (char*) string );
+}
+
+jsonObject* legacy_jsonParseStringFmt( const char* string, ... ) {
+       VA_LIST_TO_STRING(string);
+       return json_parse_string( VA_BUF );
+}
+
+
+jsonObject* json_parse_string(char* string) {
+
+       if(string == NULL) return NULL;
+
+       current_strlen = strlen(string);
+
+       if(current_strlen == 0) 
+               return NULL;
+
+       unsigned long index = 0;
+
+       json_eat_ws(string, &index, 1, current_strlen); /* remove leading whitespace */
+       if(index == current_strlen) return NULL;
+
+       jsonObject* obj = jsonNewObject(NULL);
+
+       int status = _json_parse_string(string, &index, obj, current_strlen);
+       if(!status) return obj;
+
+       if(status == -2) {
+               jsonObjectFree(obj);
+               return NULL;
+       }
+
+       return NULL;
+}
+
+
+int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( !string || !index || *index >= current_strlen) return -2;
+
+       int status = 0; /* return code from parsing routines */
+       char* classname = NULL; /* object class hint */
+       json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
+
+       char c = string[*index];
+
+       /* remove any leading comments */
+       if( c == '/' ) { 
+
+               while(1) {
+                       (*index)++; /* move to second comment char */
+                       status = json_eat_comment(string, index, &classname, 1, current_strlen);
+                       if(status) return status;
+
+                       json_eat_ws(string, index, 1, current_strlen);
+                       c = string[*index];
+                       if(c != '/')
+                               break;
+               }
+       }
+
+       json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
+
+       if(*index >= current_strlen)
+               return -2;
+
+       switch(c) {
+                               
+               /* json string */
+               case '"': 
+                       (*index)++;
+                       status = json_parse_json_string(string, index, obj, current_strlen);
+                       break;
+
+               /* json array */
+               case '[':
+                       (*index)++;
+                       status = json_parse_json_array(string, index, obj, current_strlen);
+                       break;
+
+               /* json object */
+               case '{':
+                       (*index)++;
+                       status = json_parse_json_object(string, index, obj, current_strlen);
+                       break;
+
+               /* NULL */
+               case 'n':
+               case 'N':
+                       status = json_parse_json_null(string, index, obj, current_strlen);
+                       break;
+                       
+
+               /* true, false */
+               case 'f':
+               case 'F':
+               case 't':
+               case 'T':
+                       status = json_parse_json_bool(string, index, obj, current_strlen);
+                       break;
+
+               default:
+                       if(isdigit(c) || c == '.' || c == '-') { /* are we a number? */
+                               status = json_parse_json_number(string, index, obj, current_strlen);
+                               if(status) return status;
+                               break;
+                       }
+
+                       (*index)--;
+                       /* we should never get here */
+                       return json_handle_error(string, index, "_json_parse_string() final switch clause");
+       }       
+
+       if(status) return status;
+
+       json_eat_ws(string, index, 1, current_strlen);
+
+       if( *index < current_strlen ) {
+               /* remove any trailing comments */
+               c = string[*index];
+               if( c == '/' ) { 
+                       (*index)++;
+                       status = json_eat_comment(string, index, NULL, 0, current_strlen);
+                       if(status) return status;
+               }
+       }
+
+       if(classname){
+               jsonObjectSetClass(obj, classname);
+               free(classname);
+       }
+
+       return 0;
+}
+
+
+int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+
+       if(*index >= (current_strlen - 3)) {
+               return json_handle_error(string, index, 
+                       "_parse_json_null(): invalid null" );
+       }
+
+       if(!strncasecmp(string + (*index), "null", 4)) {
+               (*index) += 4;
+               obj->type = JSON_NULL;
+               return 0;
+       } else {
+               return json_handle_error(string, index,
+                       "_parse_json_null(): invalid null" );
+       }
+}
+
+/* should be at the first character of the bool at this point */
+int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || ! obj || *index >= current_strlen ) return -1;
+
+       char* ret = "json_parse_json_bool(): truncated bool";
+
+       if( *index >= (current_strlen - 5))
+               return json_handle_error(string, index, ret);
+       
+       if(!strncasecmp( string + (*index), "false", 5)) {
+               (*index) += 5;
+               obj->value.b = 0;
+               obj->type = JSON_BOOL;
+               return 0;
+       }
+
+       if( *index >= (current_strlen - 4))
+               return json_handle_error(string, index, ret);
+
+       if(!strncasecmp( string + (*index), "true", 4)) {
+               (*index) += 4;
+               obj->value.b = 1;
+               obj->type = JSON_BOOL;
+               return 0;
+       }
+
+       return json_handle_error(string, index, ret);
+}
+
+
+/* expecting the first character of the number */
+int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || ! obj || *index >= current_strlen ) return -1;
+
+       growing_buffer* buf = buffer_init(64);
+       char c = string[*index];
+
+       int done = 0;
+       int dot_seen = 0;
+
+       /* negative number? */
+       if(c == '-') { buffer_add(buf, "-"); (*index)++; }
+
+       c = string[*index];
+
+       while(*index < current_strlen) {
+
+               if(isdigit(c)) {
+                       buffer_add_char(buf, c);
+               }
+
+               else if( c == '.' ) {
+                       if(dot_seen) {
+                               buffer_free(buf);
+                               return json_handle_error(string, index, 
+                                       "json_parse_json_number(): malformed json number");
+                       }
+                       dot_seen = 1;
+                       buffer_add_char(buf, c);
+               } else {
+                       done = 1; break;
+               }
+
+               (*index)++;
+               c = string[*index];
+               if(done) break;
+       }
+
+       obj->type = JSON_NUMBER;
+       obj->value.n = strtod(buf->buf, NULL);
+       buffer_free(buf);
+       return 0;
+}
+
+/* index should point to the character directly following the '['.  when done
+ * index will point to the character directly following the ']' character
+ */
+int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+
+       if( ! string || ! obj || ! index || *index >= current_strlen ) return -1;
+
+       int status = 0;
+       int in_parse = 0; /* true if this array already contains one item */
+       obj->type = JSON_ARRAY;
+       int set = 0;
+       int done = 0;
+
+       while(*index < current_strlen) {
+
+               json_eat_ws(string, index, 1, current_strlen);
+
+               if(string[*index] == ']') {
+                       (*index)++;
+                       done = 1;
+                       break;
+               }
+
+               if(in_parse) {
+                       json_eat_ws(string, index, 1, current_strlen);
+                       if(string[*index] != ',') {
+                               return json_handle_error(string, index,
+                                       "json_parse_json_array(): array item not followed by a ','");
+                       }
+                       (*index)++;
+                       json_eat_ws(string, index, 1, current_strlen);
+               }
+
+               jsonObject* item = jsonNewObject(NULL);
+
+               #ifndef STRICT_JSON_READ
+               if(*index < current_strlen) {
+                       if(string[*index] == ',' || string[*index] == ']') {
+                               status = 0;
+                               set = 1;
+                       }
+               }
+               if(!set) status = _json_parse_string(string, index, item, current_strlen);
+
+               #else
+               status = _json_parse_string(string, index, item, current_strlen);
+               #endif
+
+               if(status) { jsonObjectFree(item); return status; }
+               jsonObjectPush(obj, item);
+               in_parse = 1;
+               set = 0;
+       }
+
+       if(!done)
+               return json_handle_error(string, index,
+                       "json_parse_json_array(): array not closed");
+
+       return 0;
+}
+
+
+/* index should point to the character directly following the '{'.  when done
+ * index will point to the character directly following the '}'
+ */
+int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || !obj || ! index || *index >= current_strlen ) return -1;
+
+       obj->type = JSON_HASH;
+       int status;
+       int in_parse = 0; /* true if we've already added one item to this object */
+       int set = 0;
+       int done = 0;
+
+       while(*index < current_strlen) {
+
+               json_eat_ws(string, index, 1, current_strlen);
+
+               if(string[*index] == '}') {
+                       (*index)++;
+                       done = 1;
+                       break;
+               }
+
+               if(in_parse) {
+                       if(string[*index] != ',') {
+                               return json_handle_error(string, index,
+                                       "json_parse_json_object(): object missing ',' between elements" );
+                       }
+                       (*index)++;
+                       json_eat_ws(string, index, 1, current_strlen);
+               }
+
+               /* first we grab the hash key */
+               jsonObject* key_obj = jsonNewObject(NULL);
+               status = _json_parse_string(string, index, key_obj, current_strlen);
+               if(status) return status;
+
+               if(key_obj->type != JSON_STRING) {
+                       return json_handle_error(string, index, 
+                               "_json_parse_json_object(): hash key not a string");
+               }
+
+               char* key = key_obj->value.s;
+
+               json_eat_ws(string, index, 1, current_strlen);
+
+               if(string[*index] != ':') {
+                       return json_handle_error(string, index, 
+                               "json_parse_json_object(): hash key not followed by ':' character");
+               }
+
+               (*index)++;
+
+               /* now grab the value object */
+               json_eat_ws(string, index, 1, current_strlen);
+               jsonObject* value_obj = jsonNewObject(NULL);
+
+#ifndef STRICT_JSON_READ
+               if(*index < current_strlen) {
+                       if(string[*index] == ',' || string[*index] == '}') {
+                               status = 0;
+                               set = 1;
+                       }
+               }
+               if(!set)
+                       status = _json_parse_string(string, index, value_obj, current_strlen);
+
+#else
+                status = _json_parse_string(string, index, value_obj, current_strlen);
+#endif
+
+               if(status) return status;
+
+               /* put the data into the object and continue */
+               jsonObjectSetKey(obj, key, value_obj);
+               jsonObjectFree(key_obj);
+               in_parse = 1;
+               set = 0;
+       }
+
+       if(!done)
+               return json_handle_error(string, index,
+                       "json_parse_json_object(): object not closed");
+
+       return 0;
+}
+
+
+
+/* when done, index will point to the character after the closing quote */
+int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || ! index || *index >= current_strlen ) return -1;
+
+       int in_escape = 0;      
+       int done = 0;
+       growing_buffer* buf = buffer_init(64);
+
+       while(*index < current_strlen) {
+
+               char c = string[*index]; 
+
+               switch(c) {
+
+                       case '\\':
+                               if(in_escape) {
+                                       buffer_add(buf, "\\");
+                                       in_escape = 0;
+                               } else 
+                                       in_escape = 1;
+                               break;
+
+                       case '"':
+                               if(in_escape) {
+                                       buffer_add(buf, "\"");
+                                       in_escape = 0;
+                               } else 
+                                       done = 1;
+                               break;
+
+                       case 't':
+                               if(in_escape) {
+                                       buffer_add(buf,"\t");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'b':
+                               if(in_escape) {
+                                       buffer_add(buf,"\b");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'f':
+                               if(in_escape) {
+                                       buffer_add(buf,"\f");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'r':
+                               if(in_escape) {
+                                       buffer_add(buf,"\r");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'n':
+                               if(in_escape) {
+                                       buffer_add(buf,"\n");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'u':
+                               if(in_escape) {
+                                       (*index)++;
+
+                                       if(*index >= (current_strlen - 4)) {
+                                               buffer_free(buf);
+                                               return json_handle_error(string, index,
+                                                       "json_parse_json_string(): truncated escaped unicode"); }
+
+                                       char buff[5];
+                                       osrf_clearbuf(buff, sizeof(buff));
+                                       memcpy(buff, string + (*index), 4);
+
+
+                                       /* ----------------------------------------------------------------------- */
+                                       /* ----------------------------------------------------------------------- */
+                                       /* The following chunk was borrowed with permission from 
+                                               json-c http://oss.metaparadigm.com/json-c/ */
+                                       unsigned char utf_out[3];
+                                       memset(utf_out, 0, sizeof(utf_out));
+
+                                       #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
+
+                                       unsigned int ucs_char =
+                                               (hexdigit(string[*index] ) << 12) +
+                                               (hexdigit(string[*index + 1]) << 8) +
+                                               (hexdigit(string[*index + 2]) << 4) +
+                                               hexdigit(string[*index + 3]);
+       
+                                       if (ucs_char < 0x80) {
+                                               utf_out[0] = ucs_char;
+                                               buffer_add(buf, (char*) utf_out);
+
+                                       } else if (ucs_char < 0x800) {
+                                               utf_out[0] = 0xc0 | (ucs_char >> 6);
+                                               utf_out[1] = 0x80 | (ucs_char & 0x3f);
+                                               buffer_add(buf, (char*) utf_out);
+
+                                       } else {
+                                               utf_out[0] = 0xe0 | (ucs_char >> 12);
+                                               utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
+                                               utf_out[2] = 0x80 | (ucs_char & 0x3f);
+                                               buffer_add(buf, (char*) utf_out);
+                                       }
+                                       /* ----------------------------------------------------------------------- */
+                                       /* ----------------------------------------------------------------------- */
+
+                                       (*index) += 3;
+                                       in_escape = 0;
+
+                               } else {
+
+                                       buffer_add_char(buf, c);
+                               }
+
+                               break;
+
+                       default:
+                               buffer_add_char(buf, c);
+               }
+
+               (*index)++;
+               if(done) break;
+       }
+
+       jsonObjectSetString(obj, buf->buf);
+       buffer_free(buf);
+       return 0;
+}
+
+
+void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen) {
+       if( ! string || ! index ) return;
+       if(*index >= current_strlen)
+               return;
+
+       if( eat_all ) { /* removes newlines, etc */
+               while(string[*index] == ' '     || 
+                               string[*index] == '\n'  ||
+                               string[*index] == '\t') 
+                       (*index)++;
+       }
+
+       else    
+               while(string[*index] == ' ') (*index)++;
+}
+
+
+/* index should be at the '*' character at the beginning of the comment.
+ * when done, index will point to the first character after the final /
+ */
+int json_eat_comment(char* string, unsigned long* index, char** buffer, int parse_class, int current_strlen) {
+       if( ! string || ! index || *index >= current_strlen ) return -1;
+       
+
+       if(string[*index] != '*' && string[*index] != '/' )
+               return json_handle_error(string, index, 
+                       "json_eat_comment(): invalid character after /");
+
+       /* chop out any // style comments */
+       if(string[*index] == '/') {
+               (*index)++;
+               char c = string[*index];
+               while(*index < current_strlen) {
+                       (*index)++;
+                       if(c == '\n') 
+                               return 0;
+                       c = string[*index];
+               }
+               return 0;
+       }
+
+       (*index)++;
+
+       int on_star                     = 0; /* true if we just saw a '*' character */
+
+       /* we're just past the '*' */
+       if(!parse_class) { /* we're not concerned with class hints */
+               while(*index < current_strlen) {
+                       if(string[*index] == '/') {
+                               if(on_star) {
+                                       (*index)++;
+                                       return 0;
+                               }
+                       }
+
+                       if(string[*index] == '*') on_star = 1;
+                       else on_star = 0;
+
+                       (*index)++;
+               }
+               return 0;
+       }
+
+
+
+       growing_buffer* buf = buffer_init(64);
+
+       int first_dash          = 0;
+       int second_dash = 0;
+       int third_dash          = 0;
+       int fourth_dash = 0;
+
+       int in_hint                     = 0;
+       int done                                = 0;
+
+       /*--S hint--*/   /* <-- Hints  look like this */
+       /*--E hint--*/
+
+       while(*index < current_strlen) {
+               char c = string[*index];
+
+               switch(c) {
+
+                       case '-':
+                               on_star = 0;
+                               if(third_dash)                  fourth_dash = 1;
+                               else if(in_hint)                third_dash      = 1;
+                               else if(first_dash)     second_dash = 1;
+                               else                                            first_dash = 1;
+                               break;
+
+                       case 'S':
+                               on_star = 0;
+                               if(second_dash && !in_hint) {
+                                       (*index)++;
+                                       json_eat_ws(string, index, 1, current_strlen);
+                                       (*index)--; /* this will get incremented at the bottom of the loop */
+                                       in_hint = 1;
+                                       break;
+                               } 
+
+                               if(second_dash && in_hint) {
+                                       buffer_add_char(buf, c);
+                                       break;
+                               }
+
+                       case 'E':
+                               on_star = 0;
+                               if(second_dash && !in_hint) {
+                                       (*index)++;
+                                       json_eat_ws(string, index, 1, current_strlen);
+                                       (*index)--; /* this will get incremented at the bottom of the loop */
+                                       in_hint = 1;
+                                       break;
+                               }
+
+                               if(second_dash && in_hint) {
+                                       buffer_add_char(buf, c);
+                                       break;
+                               }
+
+                       case '*':
+                               on_star = 1;
+                               break;
+
+                       case '/':
+                               if(on_star) 
+                                       done = 1;
+                               else
+                               on_star = 0;
+                               break;
+
+                       default:
+                               on_star = 0;
+                               if(in_hint)
+                                       buffer_add_char(buf, c);
+               }
+
+               (*index)++;
+               if(done) break;
+       }
+
+       if( buf->n_used > 0 && buffer)
+               *buffer = buffer_data(buf);
+
+       buffer_free(buf);
+       return 0;
+}
+
+int json_handle_error(char* string, unsigned long* index, char* err_msg) {
+
+       char buf[60];
+       osrf_clearbuf(buf, sizeof(buf));
+
+       if(*index > 30)
+               strncpy( buf, string + (*index - 30), 59 );
+       else
+               strncpy( buf, string, 59 );
+
+       fprintf(stderr, 
+                       "\nError parsing json string at charracter %c "
+                       "(code %d) and index %ld\nString length: %d\nMsg:\t%s\nNear:\t%s\nFull String:\t%s\n", 
+                       string[*index], string[*index], *index, current_strlen, err_msg, buf, string );
+
+       return -1;
+}
+
+
+jsonObject* legacy_jsonParseFile( const char* filename ) {
+       return json_parse_file( filename );
+}
+       
+jsonObject* json_parse_file(const char* filename) {
+       if(!filename) return NULL;
+       char* data = file_to_string(filename);
+       jsonObject* o = json_parse_string(data);
+       free(data);
+       return o;
+}
+
+
+
+char* legacy_jsonObjectToJSON( const jsonObject* obj ) {
+
+       if(obj == NULL) return strdup("null");
+
+       growing_buffer* buf = buffer_init(64);
+
+       /* add class hints if we have a class name */
+       if(obj->classname) {
+               buffer_add(buf,"/*--S ");
+               buffer_add(buf,obj->classname);
+               buffer_add(buf, "--*/");
+       }
+
+       switch( obj->type ) {
+
+               case JSON_BOOL: 
+                       if(obj->value.b) buffer_add(buf, "true"); 
+                       else buffer_add(buf, "false"); 
+                       break;
+
+               case JSON_NUMBER: {
+                       double x = obj->value.n;
+
+                       /* if the number does not need to be a double,
+                               turn it into an int on the way out */
+                       if( x == (int) x ) {
+                               INT_TO_STRING((int)x);  
+                               buffer_add(buf, INTSTR);
+
+                       } else {
+                               DOUBLE_TO_STRING(x);
+                               buffer_add(buf, DOUBLESTR);
+                       }
+                       break;
+               }
+
+               case JSON_NULL:
+                       buffer_add(buf, "null");
+                       break;
+
+               case JSON_STRING:
+                       buffer_add(buf, "\"");
+                       char* data = obj->value.s;
+                       int len = strlen(data);
+                       
+                       char* output = uescape(data, len, 1);
+                       buffer_add(buf, output);
+                       free(output);
+                       buffer_add(buf, "\"");
+                       break;
+
+               case JSON_ARRAY:
+                       buffer_add(buf, "[");
+                       int i;
+                       for( i = 0; i!= obj->size; i++ ) {
+                               const jsonObject* x = jsonObjectGetIndex(obj,i);
+                               char* data = legacy_jsonObjectToJSON(x);
+                               buffer_add(buf, data);
+                               free(data);
+                               if(i != obj->size - 1)
+                                       buffer_add(buf, ",");
+                       }
+                       buffer_add(buf, "]");
+                       break;  
+
+               case JSON_HASH:
+       
+                       buffer_add(buf, "{");
+                       jsonIterator* itr = jsonNewIterator(obj);
+                       jsonObject* tmp;
+       
+                       while( (tmp = jsonIteratorNext(itr)) ) {
+
+                               buffer_add(buf, "\"");
+
+                               char* key = itr->key;
+                               int len = strlen(key);
+                               char* output = uescape(key, len, 1);
+                               buffer_add(buf, output);
+                               free(output);
+
+                               buffer_add(buf, "\":");
+                               char* data =  legacy_jsonObjectToJSON(tmp);
+                               buffer_add(buf, data);
+                               if(jsonIteratorHasNext(itr))
+                                       buffer_add(buf, ",");
+                               free(data);
+                       }
+
+                       jsonIteratorFree(itr);
+                       buffer_add(buf, "}");
+                       break;
+               
+                       default:
+                               fprintf(stderr, "Unknown object type %d\n", obj->type);
+                               break;
+                               
+       }
+
+       /* close out the object hint */
+       if(obj->classname) {
+               buffer_add(buf, "/*--E ");
+               buffer_add(buf, obj->classname);
+               buffer_add(buf, "--*/");
+       }
+
+       char* data = buffer_data(buf);
+       buffer_free(buf);
+       return data;
+}
+
+
+
+static jsonObjectNode* makeNode(jsonObject* obj, unsigned long index, char* key) {
+    jsonObjectNode* node = safe_malloc(sizeof(jsonObjectNode));
+    node->item = obj;
+    node->index = index;
+    node->key = key;
+    return node;
+}
+
+jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj) {
+       if(!obj) return NULL;
+       jsonObjectIterator* itr = safe_malloc(sizeof(jsonObjectIterator));
+    itr->iterator = jsonNewIterator(obj);
+       itr->obj = obj;
+    itr->done = 0;
+    itr->current = NULL;
+       return itr;
+}
+
+jsonObjectNode* jsonObjectIteratorNext( jsonObjectIterator* itr ) {
+       if(itr == NULL || itr->done) return NULL;
+
+    if(itr->current) free(itr->current);
+    jsonObject* next = jsonIteratorNext(itr->iterator);
+    if(next == NULL) {
+        itr->current = NULL;
+        itr->done = 1;
+        return NULL;
+    }
+    itr->current = makeNode(next, itr->iterator->index, itr->iterator->key);
+    return itr->current;
+}
+
+void jsonObjectIteratorFree(jsonObjectIterator* iter) { 
+    if(iter->current) free(iter->current);
+    jsonIteratorFree(iter->iterator);
+       free(iter);
+}
+
+int jsonObjectIteratorHasNext(const jsonObjectIterator* itr) {
+       return (itr && itr->current);
+}
+
+
index 313216a..50938d1 100644 (file)
@@ -1,5 +1,8 @@
 #include <opensrf/osrf_message.h>
 
+static char default_locale[17] = "en-US\0\0\0\0\0\0\0\0\0\0\0\0";
+static char* current_locale = NULL;
+
 osrf_message* osrf_message_init( enum M_TYPE type, int thread_trace, int protocol ) {
 
        osrf_message* msg                       = (osrf_message*) safe_malloc(sizeof(osrf_message));
@@ -10,18 +13,36 @@ osrf_message* osrf_message_init( enum M_TYPE type, int thread_trace, int protoco
        msg->is_exception                       = 0;
        msg->_params                            = NULL;
        msg->_result_content            = NULL;
+       msg->sender_locale              = NULL;
 
        return msg;
 }
 
 
-void osrf_message_set_method( osrf_message* msg, char* method_name ) {
+const char* osrf_message_get_last_locale() {
+       return current_locale;
+}
+
+char* osrf_message_set_locale( osrf_message* msg, const char* locale ) {
+       if( msg == NULL || locale == NULL ) return NULL;
+       return msg->sender_locale = strdup( locale );
+}
+
+const char* osrf_message_set_default_locale( const char* locale ) {
+       if( locale == NULL ) return NULL;
+       if( strlen(locale) > sizeof(default_locale) - 1 ) return NULL;
+
+       strcpy( default_locale, locale );
+       return (const char*) default_locale;
+}
+
+void osrf_message_set_method( osrf_message* msg, const char* method_name ) {
        if( msg == NULL || method_name == NULL ) return;
        msg->method_name = strdup( method_name );
 }
 
 
-void osrf_message_add_object_param( osrf_message* msg, jsonObject* o ) {
+void osrf_message_add_object_param( osrf_message* msg, const jsonObject* o ) {
        if(!msg|| !o) return;
        if(!msg->_params)
                msg->_params = jsonParseString("[]");
@@ -30,16 +51,15 @@ void osrf_message_add_object_param( osrf_message* msg, jsonObject* o ) {
        free(j);
 }
 
-void osrf_message_set_params( osrf_message* msg, jsonObject* o ) {
+void osrf_message_set_params( osrf_message* msg, const jsonObject* o ) {
        if(!msg || !o) return;
 
        if(o->type != JSON_ARRAY) {
                osrfLogDebug( OSRF_LOG_MARK, "passing non-array to osrf_message_set_params(), fixing...");
-               jsonObject* clone = jsonObjectClone(o);
-               o = jsonNewObject(NULL);
-               jsonObjectPush(o, clone);
                if(msg->_params) jsonObjectFree(msg->_params);
-               msg->_params = o;
+               jsonObject* clone = jsonObjectClone(o);
+               msg->_params = jsonNewObject(NULL);
+               jsonObjectPush(msg->_params, clone);
                return;
        }
 
@@ -49,15 +69,15 @@ void osrf_message_set_params( osrf_message* msg, jsonObject* o ) {
 
 
 /* only works if parse_json_params is false */
-void osrf_message_add_param( osrf_message* msg, char* param_string ) {
+void osrf_message_add_param( osrf_message* msg, const char* param_string ) {
        if(msg == NULL || param_string == NULL) return;
        if(!msg->_params) msg->_params = jsonParseString("[]");
        jsonObjectPush(msg->_params, jsonParseString(param_string));
 }
 
 
-void osrf_message_set_status_info( 
-               osrf_message* msg, char* status_name, char* status_text, int status_code ) {
+void osrf_message_set_status_info( osrf_message* msg,
+               const char* status_name, const char* status_text, int status_code ) {
        if(!msg) return;
 
        if( status_name != NULL ) 
@@ -70,7 +90,7 @@ void osrf_message_set_status_info(
 }
 
 
-void osrf_message_set_result_content( osrf_message* msg, char* json_string ) {
+void osrf_message_set_result_content( osrf_message* msg, const char* json_string ) {
        if( msg == NULL || json_string == NULL) return;
        msg->result_string =    strdup(json_string);
        if(json_string) msg->_result_content = jsonParseString(json_string);
@@ -101,6 +121,9 @@ void osrf_message_free( osrf_message* msg ) {
        if( msg->method_name != NULL )
                free(msg->method_name);
 
+       if( msg->sender_locale != NULL )
+               free(msg->sender_locale);
+
        if( msg->_params != NULL )
                jsonObjectFree(msg->_params);
 
@@ -113,7 +136,7 @@ char* osrfMessageSerializeBatch( osrfMessage* msgs [], int count ) {
 
        char* j;
        int i = 0;
-       osrfMessage* msg = NULL;
+       const osrfMessage* msg = NULL;
        jsonObject* wrapper = jsonNewObject(NULL);
 
        while( ((msg = msgs[i]) && (i++ < count)) ) 
@@ -126,7 +149,7 @@ char* osrfMessageSerializeBatch( osrfMessage* msgs [], int count ) {
 }
 
 
-char* osrf_message_serialize(osrf_message* msg) {
+char* osrf_message_serialize(const osrf_message* msg) {
 
        if( msg == NULL ) return NULL;
        char* j = NULL;
@@ -144,18 +167,27 @@ char* osrf_message_serialize(osrf_message* msg) {
 }
 
 
-jsonObject* osrfMessageToJSON( osrfMessage* msg ) {
+jsonObject* osrfMessageToJSON( const osrfMessage* msg ) {
 
        jsonObject* json = jsonNewObject(NULL);
        jsonObjectSetClass(json, "osrfMessage");
        jsonObject* payload;
-       char sc[64]; memset(sc,0,64);
+       char sc[64];
+       osrf_clearbuf(sc, sizeof(sc));
 
        char* str;
 
        INT_TO_STRING(msg->thread_trace);
        jsonObjectSetKey(json, "threadTrace", jsonNewObject(INTSTR));
 
+       if (msg->sender_locale != NULL) {
+               jsonObjectSetKey(json, "locale", jsonNewObject(msg->sender_locale));
+       } else if (current_locale != NULL) {
+               jsonObjectSetKey(json, "locale", jsonNewObject(current_locale));
+       } else {
+               jsonObjectSetKey(json, "locale", jsonNewObject(default_locale));
+       }
+
        switch(msg->m_type) {
                
                case CONNECT: 
@@ -171,7 +203,7 @@ jsonObject* osrfMessageToJSON( osrfMessage* msg ) {
                        payload = jsonNewObject(NULL);
                        jsonObjectSetClass(payload, msg->status_name);
                        jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
-         sprintf(sc,"%d",msg->status_code);
+                       snprintf(sc, sizeof(sc), "%d", msg->status_code);
                        jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
                        jsonObjectSetKey(json, "payload", payload);
                        break;
@@ -193,7 +225,7 @@ jsonObject* osrfMessageToJSON( osrfMessage* msg ) {
                        payload = jsonNewObject(NULL);
                        jsonObjectSetClass(payload,"osrfResult");
                        jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
-         sprintf(sc,"%d",msg->status_code);
+                       snprintf(sc, sizeof(sc), "%d", msg->status_code);
                        jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
                        str = jsonObjectToJSON(msg->_result_content);
                        jsonObjectSetKey(payload, "content", jsonParseString(str));
@@ -206,7 +238,7 @@ jsonObject* osrfMessageToJSON( osrfMessage* msg ) {
 }
 
 
-int osrf_message_deserialize(char* string, osrf_message* msgs[], int count) {
+int osrf_message_deserialize(const char* string, osrf_message* msgs[], int count) {
 
        if(!string || !msgs || count <= 0) return 0;
        int numparsed = 0;
@@ -223,16 +255,16 @@ int osrf_message_deserialize(char* string, osrf_message* msgs[], int count) {
 
        for( x = 0; x < json->size && x < count; x++ ) {
 
-               jsonObject* message = jsonObjectGetIndex(json, x);
+               const jsonObject* message = jsonObjectGetIndex(json, x);
 
                if(message && message->type != JSON_NULL && 
                        message->classname && !strcmp(message->classname, "osrfMessage")) {
 
                        osrf_message* new_msg = safe_malloc(sizeof(osrf_message));
 
-                       jsonObject* tmp = jsonObjectGetKey(message, "type");
+                       const jsonObject* tmp = jsonObjectGetKeyConst(message, "type");
 
-                       char* t;
+                       const char* t;
                        if( ( t = jsonObjectGetString(tmp)) ) {
 
                                if(!strcmp(t, "CONNECT"))               new_msg->m_type = CONNECT;
@@ -242,23 +274,28 @@ int osrf_message_deserialize(char* string, osrf_message* msgs[], int count) {
                                if(!strcmp(t, "RESULT"))                new_msg->m_type = RESULT;
                        }
 
-                       tmp = jsonObjectGetKey(message, "threadTrace");
+                       tmp = jsonObjectGetKeyConst(message, "threadTrace");
                        if(tmp) {
                                char* tt = jsonObjectToSimpleString(tmp);
                                if(tt) {
                                        new_msg->thread_trace = atoi(tt);
                                        free(tt);
                                }
-                               /*
-                               if(tmp->type == JSON_NUMBER)
-                                       new_msg->thread_trace = (int) jsonObjectGetNumber(tmp);
-                               if(tmp->type == JSON_STRING)
-                                       new_msg->thread_trace = atoi(jsonObjectGetString(tmp));
-                                       */
                        }
 
+                       /* use the sender's locale, or the global default */
+                       if (current_locale)
+                               free( current_locale );
 
-                       tmp = jsonObjectGetKey(message, "protocol");
+                       tmp = jsonObjectGetKeyConst(message, "locale");
+                       if(tmp) {
+                               new_msg->sender_locale = jsonObjectToSimpleString(tmp);
+                               current_locale = strdup( new_msg->sender_locale );
+                       } else {
+                               current_locale = NULL;
+                       }
+
+                       tmp = jsonObjectGetKeyConst(message, "protocol");
 
                        if(tmp) {
                                char* proto = jsonObjectToSimpleString(tmp);
@@ -266,25 +303,18 @@ int osrf_message_deserialize(char* string, osrf_message* msgs[], int count) {
                                        new_msg->protocol = atoi(proto);
                                        free(proto);
                                }
-
-                               /*
-                               if(tmp->type == JSON_NUMBER)
-                                       new_msg->protocol = (int) jsonObjectGetNumber(tmp);
-                               if(tmp->type == JSON_STRING)
-                                       new_msg->protocol = atoi(jsonObjectGetString(tmp));
-                                       */
                        }
 
-                       tmp = jsonObjectGetKey(message, "payload");
+                       tmp = jsonObjectGetKeyConst(message, "payload");
                        if(tmp) {
                                if(tmp->classname)
                                        new_msg->status_name = strdup(tmp->classname);
 
-                               jsonObject* tmp0 = jsonObjectGetKey(tmp,"method");
+                               const jsonObject* tmp0 = jsonObjectGetKeyConst(tmp,"method");
                                if(jsonObjectGetString(tmp0))
                                        new_msg->method_name = strdup(jsonObjectGetString(tmp0));
 
-                               tmp0 = jsonObjectGetKey(tmp,"params");
+                               tmp0 = jsonObjectGetKeyConst(tmp,"params");
                                if(tmp0) {
                                        char* s = jsonObjectToJSON(tmp0);
                                        new_msg->_params = jsonParseString(s);
@@ -293,11 +323,11 @@ int osrf_message_deserialize(char* string, osrf_message* msgs[], int count) {
                                        free(s);
                                }
 
-                               tmp0 = jsonObjectGetKey(tmp,"status");
+                               tmp0 = jsonObjectGetKeyConst(tmp,"status");
                                if(jsonObjectGetString(tmp0))
                                        new_msg->status_text = strdup(jsonObjectGetString(tmp0));
 
-                               tmp0 = jsonObjectGetKey(tmp,"statusCode");
+                               tmp0 = jsonObjectGetKeyConst(tmp,"statusCode");
                                if(tmp0) {
                                        if(jsonObjectGetString(tmp0))
                                                new_msg->status_code = atoi(jsonObjectGetString(tmp0));
@@ -305,7 +335,7 @@ int osrf_message_deserialize(char* string, osrf_message* msgs[], int count) {
                                                new_msg->status_code = (int) jsonObjectGetNumber(tmp0);
                                }
 
-                               tmp0 = jsonObjectGetKey(tmp,"content");
+                               tmp0 = jsonObjectGetKeyConst(tmp,"content");
                                if(tmp0) {
                                        char* s = jsonObjectToJSON(tmp0);
                                        new_msg->_result_content = jsonParseString(s);
index 36b203c..4d4b996 100644 (file)
@@ -210,7 +210,7 @@ void prefork_child_process_request(prefork_child* child, char* data) {
 
                /* if no data was reveived within the timeout interval */
                if( !recvd && (end - start) >= keepalive ) {
-                       osrfLogInfo(OSRF_LOG_MARK, "No request was reveived in %d seconds, exiting stateful session", keepalive);
+                       osrfLogInfo(OSRF_LOG_MARK, "No request was received in %d seconds, exiting stateful session", keepalive);
                        osrfAppSessionStatus( 
                                        session, 
                                        OSRF_STATUS_TIMEOUT, 
@@ -547,9 +547,9 @@ void check_children( prefork_simple* forker, int forever ) {
 
                        /* now suck off the data */
                        char buf[64];
-                       memset( buf, 0, 64);
+                       osrf_clearbuf( buf, sizeof(buf) );
                        if( (n=read(cur_child->read_status_fd, buf, 63))  < 0 ) {
-                               osrfLogWarning( OSRF_LOG_MARK, "Read error afer select in child status read with errno %d", errno);
+                               osrfLogWarning( OSRF_LOG_MARK, "Read error after select in child status read with errno %d", errno);
                        }
 
                        osrfLogDebug( OSRF_LOG_MARK,  "Read %d bytes from status buffer: %s", n, buf );
@@ -566,7 +566,7 @@ void prefork_child_wait( prefork_child* child ) {
        int i,n;
        growing_buffer* gbuf = buffer_init( READ_BUFSIZE );
        char buf[READ_BUFSIZE];
-       memset( buf, 0, READ_BUFSIZE );
+       osrf_clearbuf( buf, sizeof(buf) );
 
        for( i = 0; i < child->max_requests; i++ ) {
 
@@ -579,7 +579,7 @@ void prefork_child_wait( prefork_child* child ) {
                        if(!gotdata)
                                set_fl(child->read_data_fd, O_NONBLOCK );
                        buffer_add( gbuf, buf );
-                       memset( buf, 0, READ_BUFSIZE );
+                       osrf_clearbuf( buf, sizeof(buf) );
                        gotdata = 1;
                }
 
index a732b08..9b9f944 100644 (file)
@@ -2,7 +2,7 @@
 
 osrf_host_config* config = NULL;
 
-char* osrf_settings_host_value(char* format, ...) {
+char* osrf_settings_host_value(const char* format, ...) {
        VA_LIST_TO_STRING(format);
 
        if( ! config ) {
@@ -18,7 +18,7 @@ char* osrf_settings_host_value(char* format, ...) {
        return val;
 }
 
-jsonObject* osrf_settings_host_value_object(char* format, ...) {
+jsonObject* osrf_settings_host_value_object(const char* format, ...) {
        VA_LIST_TO_STRING(format);
 
        if( ! config ) {
@@ -32,7 +32,7 @@ jsonObject* osrf_settings_host_value_object(char* format, ...) {
 }
 
 
-int osrf_settings_retrieve(char* hostname) {
+int osrf_settings_retrieve(const char* hostname) {
 
        if(!config) {
 
@@ -72,10 +72,11 @@ int osrf_settings_retrieve(char* hostname) {
        return 0;
 }
 
-osrf_host_config* osrf_settings_new_host_config(char* hostname) {
+osrf_host_config* osrf_settings_new_host_config(const char* hostname) {
        if(!hostname) return NULL;
        osrf_host_config* c = safe_malloc(sizeof(osrf_host_config));
        c->hostname = strdup(hostname);
+       c->config = NULL;
        return c;
 }
 
index 231ccbc..7a7ed16 100644 (file)
@@ -67,7 +67,7 @@ osrfAppSession* osrf_stack_transport_handler( transport_message* msg, char* my_s
 
        osrf_app_session_set_remote( session, msg->sender );
        osrf_message* arr[OSRF_MAX_MSGS_PER_PACKET];
-       memset(arr, 0, OSRF_MAX_MSGS_PER_PACKET );
+       memset(arr, 0, sizeof(arr));
        int num_msgs = osrf_message_deserialize(msg->body, arr, OSRF_MAX_MSGS_PER_PACKET);
 
        osrfLogDebug( OSRF_LOG_MARK,  "We received %d messages from %s", num_msgs, msg->sender );
@@ -92,7 +92,7 @@ osrfAppSession* osrf_stack_transport_handler( transport_message* msg, char* my_s
 
                        } else {
                                osrfLogWarning( OSRF_LOG_MARK, " * Jabber Error is for top level remote id [%s], no one "
-                                               "to send my message too!!!", session->remote_id );
+                                               "to send my message to!!!", session->remote_id );
                        }
                }
 
index e5596dd..de25a29 100644 (file)
@@ -4,9 +4,28 @@
 #include <signal.h>
 
 static int _osrfSystemInitCache( void );
+static void report_child_status( pid_t pid, int status );
+struct child_node;
+typedef struct child_node ChildNode;
+
+struct child_node
+{
+       ChildNode* pNext;
+       ChildNode* pPrev;
+       pid_t pid;
+       char* app;
+       char* libfile;
+};
+
+static ChildNode* child_list;
 
 static transport_client* osrfGlobalTransportClient = NULL;
 
+static void add_child( pid_t pid, const char* app, const char* libfile );
+static void delete_child( ChildNode* node );
+static void delete_all_children( void );
+static ChildNode* seek_child( pid_t pid );
+
 transport_client* osrfSystemGetTransportClient( void ) {
        return osrfGlobalTransportClient;
 }
@@ -122,12 +141,14 @@ int osrfSystemBootstrap( char* hostname, char* configfile, char* contextNode ) {
                                pid_t pid;
                
                                if( (pid = fork()) ) { 
-                                       // storage pid in local table for re-launching dead children...
-                                       osrfLogInfo( OSRF_LOG_MARK, "Launched application child %ld", (long) pid);
+                                       // store pid in local list for re-launching dead children...
+                                       add_child( pid, appname, libfile );
+                                       osrfLogInfo( OSRF_LOG_MARK, "Running application child %s: process id %ld",
+                                                                appname, (long) pid );
        
                                } else {
                
-                                       osrfLogError( OSRF_LOG_MARK, " * Running application %s\n", appname);
+                                       osrfLogInfo( OSRF_LOG_MARK, " * Running application %s\n", appname);
                                        if( osrfAppRegisterApplication( appname, libfile ) == 0 ) 
                                                osrf_prefork_run(appname);
        
@@ -140,7 +161,8 @@ int osrfSystemBootstrap( char* hostname, char* configfile, char* contextNode ) {
 
        while(1) {
                errno = 0;
-               pid_t pid = wait(NULL);
+               int status;
+               pid_t pid = wait( &status );
                if(-1 == pid) {
                        if(errno == ECHILD)
                                osrfLogError(OSRF_LOG_MARK, "We have no more live services... exiting");
@@ -148,13 +170,131 @@ int osrfSystemBootstrap( char* hostname, char* configfile, char* contextNode ) {
                                osrfLogError(OSRF_LOG_MARK, "Exiting top-level system loop with error: %s", strerror(errno));
                        break;
                } else {
-                       osrfLogError(OSRF_LOG_MARK, "We lost a top-level service process with PID %ld", pid);
+                       report_child_status( pid, status );
                }
        }
 
+       delete_all_children();
        return 0;
 }
 
+
+static void report_child_status( pid_t pid, int status )
+{
+       const char* app;
+       const char* libfile;
+       ChildNode* node = seek_child( pid );
+
+       if( node ) {
+               app     = node->app     ? node->app     : "[unknown]";
+               libfile = node->libfile ? node->libfile : "[none]";
+       } else
+               app = libfile = NULL;
+       
+       if( WIFEXITED( status ) )
+       {
+               int rc = WEXITSTATUS( status );  // return code of child process
+               if( rc )
+                       osrfLogError( OSRF_LOG_MARK, "Child process %ld (app %s) exited with return code %d",
+                                                 (long) pid, app, rc );
+               else
+                       osrfLogInfo( OSRF_LOG_MARK, "Child process %ld (app %s) exited normally",
+                                                 (long) pid, app );
+       }
+       else if( WIFSIGNALED( status ) )
+       {
+               osrfLogError( OSRF_LOG_MARK, "Child process %ld (app %s) killed by signal %d",
+                                         (long) pid, app, WTERMSIG( status) );
+       }
+       else if( WIFSTOPPED( status ) )
+       {
+               osrfLogError( OSRF_LOG_MARK, "Child process %ld (app %s) stopped by signal %d",
+                                         (long) pid, app, (int) WSTOPSIG( status ) );
+       }
+
+       delete_child( node );
+}
+
+/*----------- Routines to manage list of children --*/
+
+static void add_child( pid_t pid, const char* app, const char* libfile )
+{
+       /* Construct new child node */
+       
+       ChildNode* node = safe_malloc( sizeof( ChildNode ) );
+
+       node->pid = pid;
+
+       if( app )
+               node->app = strdup( app );
+       else
+               node->app = NULL;
+
+       if( libfile )
+               node->libfile = strdup( libfile );
+       else
+               node->libfile = NULL;
+       
+       /* Add new child node to the head of the list */
+
+       node->pNext = child_list;
+       node->pPrev = NULL;
+
+       if( child_list )
+               child_list->pPrev = node;
+
+       child_list = node;
+}
+
+static void delete_child( ChildNode* node ) {
+
+       /* Sanity check */
+
+       if( ! node )
+               return;
+       
+       /* Detach the node from the list */
+
+       if( node->pPrev )
+               node->pPrev->pNext = node->pNext;
+       else
+               child_list = node->pNext;
+
+       if( node->pNext )
+               node->pNext->pPrev = node->pPrev;
+
+       /* Deallocate the node and its payload */
+
+       free( node->app );
+       free( node->libfile );
+       free( node );
+}
+
+static void delete_all_children( void ) {
+
+       while( child_list )
+               delete_child( child_list );
+}
+
+static ChildNode* seek_child( pid_t pid ) {
+
+       /* Return a pointer to the child node for the */
+       /* specified process ID, or NULL if not found */
+       
+       ChildNode* node = child_list;
+       while( node ) {
+               if( node->pid == pid )
+                       break;
+               else
+                       node = node->pNext;
+       }
+
+       return node;
+}
+
+/*----------- End of routines to manage list of children --*/
+
+
 int osrf_system_bootstrap_client_resc( char* config_file, char* contextnode, char* resource ) {
 
        int failure = 0;
@@ -314,6 +454,7 @@ int osrf_system_disconnect_client( void ) {
 
 int osrf_system_shutdown( void ) {
        osrfConfigCleanup();
+    osrfCacheCleanup();
        osrf_system_disconnect_client();
        osrf_settings_free_host_config(NULL);
        osrfAppSessionCleanup();
index 72b203f..566267b 100644 (file)
@@ -89,7 +89,7 @@ int osrfTransportGroupSendMatch( osrfTransportGroup* grp, transport_message* msg
        if(!(grp && msg)) return -1;
 
        char domain[256];
-       bzero(domain, 256);
+       osrf_clearbuf(domain, sizeof(domain));
        jid_get_domain( msg->recipient, domain, 255 );
 
        osrfTransportGroupNode* node = osrfHashGet(grp->nodes, domain);
@@ -108,15 +108,15 @@ int osrfTransportGroupSend( osrfTransportGroup* grp, transport_message* msg ) {
        int bufsize = 256;
 
        char domain[bufsize];
-       bzero(domain, bufsize);
+       osrf_clearbuf(domain, sizeof(domain));
        jid_get_domain( msg->recipient, domain, bufsize - 1 );
 
        char msgrecip[bufsize];
-       bzero(msgrecip, bufsize);
+       osrf_clearbuf(msgrecip, sizeof(msgrecip));
        jid_get_username(msg->recipient, msgrecip, bufsize - 1);
 
        char msgres[bufsize];
-       bzero(msgres, bufsize);
+       osrf_clearbuf(msgres, sizeof(msgres));
        jid_get_resource(msg->recipient, msgres, bufsize - 1);
 
        char* firstdomain = NULL;
@@ -149,8 +149,7 @@ int osrfTransportGroupSend( osrfTransportGroup* grp, transport_message* msg ) {
                /* update the recipient domain if necessary */
 
                if(updateRecip) {
-                       bzero(newrcp, 1024);
-                       sprintf(newrcp, "%s@%s/%s", msgrecip, node->domain, msgres);
+                       snprintf(newrcp, sizeof(newrcp), "%s@%s/%s", msgrecip, node->domain, msgres);
                        free(msg->recipient);
                        msg->recipient = strdup(newrcp);
                }
index 14791bd..c48a4c9 100644 (file)
@@ -208,7 +208,7 @@ char *shahash(const char *str)
        INT64 length=0;
 
        int strsz;
-       static char final[40];
+       static char final[41];
        int *hashval;
 
        hashval = (int *)malloc(20);
index 6dc91b0..3a09132 100644 (file)
@@ -707,7 +707,7 @@ static int _socket_handle_client_data(socket_manager* mgr, socket_node* node) {
        int read_bytes;
        int sock_fd = node->sock_fd;
 
-       memset(buf, 0, RBUFSIZE);
+       osrf_clearbuf(buf, sizeof(buf));
        set_fl(sock_fd, O_NONBLOCK);
 
        osrfLogInternal( OSRF_LOG_MARK, "%ld : Received data at %f\n", (long) getpid(), get_timestamp_millis());
@@ -717,7 +717,7 @@ static int _socket_handle_client_data(socket_manager* mgr, socket_node* node) {
                if(mgr->data_received)
                        mgr->data_received(mgr->blob, mgr, sock_fd, buf, node->parent_id);
 
-               memset(buf, 0, RBUFSIZE);
+               osrf_clearbuf(buf, sizeof(buf));
        }
     int local_errno = errno; /* capture errno as set by recv() */
 
index 3197718..ca8393f 100644 (file)
@@ -8,18 +8,11 @@ string_array* init_string_array(int size) {
        if(size > STRING_ARRAY_MAX_SIZE)
                osrfLogError( OSRF_LOG_MARK, "init_string_array size is too large");
 
-       /*
-       string_array* arr = 
-               (string_array*) safe_malloc(sizeof(string_array));
-               */
        string_array* arr;
        OSRF_MALLOC( arr, sizeof(string_array));
-
-       //arr->array = (char**) safe_malloc(size * sizeof(char*));
-       OSRF_MALLOC(arr->array, size * sizeof(char*));
-
+    arr->list = osrfNewListSize(size);
+    osrfListSetDefaultFree(arr->list);
        arr->size = 0;
-       arr->arr_size = size;
        return arr;
 }
 
@@ -30,65 +23,39 @@ void osrfStringArrayAdd(osrfStringArray* arr, char* string) {
 
 void string_array_add(string_array* arr, char* str) {
        if(arr == NULL || str == NULL ) return;
-       if( strlen(str) < 1 ) return;
-
-       arr->size++;
-
        if( arr->size > STRING_ARRAY_MAX_SIZE ) 
                osrfLogError( OSRF_LOG_MARK, "string_array_add size is too large");
-
-       /* if necessary, double capacity */
-       if(arr->size >= arr->arr_size) {
-               arr->arr_size *= 2;
-               //char** tmp = (char**) safe_malloc(arr->arr_size * sizeof(char*));
-               char** tmp;
-               OSRF_MALLOC( tmp, arr->arr_size * sizeof(char*));
-               int i;
-
-               /* copy the string pointers over */
-               for( i = 0; i!= arr->size; i++ ) 
-                       tmp[i] = arr->array[i];
-
-               free(arr->array);
-               arr->array = tmp;
-       }
-
-       arr->array[arr->size - 1] = strdup(str);
+    osrfListPush(arr->list, strdup(str));
+    arr->size = arr->list->size;
 }
 
 char* osrfStringArrayGetString(osrfStringArray* arr, int index) {
-       return string_array_get_string(arr, index);
+    if(!arr) return NULL;
+    return OSRF_LIST_GET_INDEX(arr->list, index);
 }
 
 char* string_array_get_string(string_array* arr, int index) {
-       if(!arr || index < 0 || index >= arr->size ) return NULL;
-       return arr->array[index]; 
+    if(!arr) return NULL;
+    return OSRF_LIST_GET_INDEX(arr->list, index);
 }
 
 
 void osrfStringArrayFree(osrfStringArray* arr) {
-       string_array_destroy(arr);
+    OSRF_STRING_ARRAY_FREE(arr);
 }
 
 void string_array_destroy(string_array* arr) {
-       if(arr) {
-               int i = 0;
-               while( i < arr->size ) free(arr->array[i++]);
-               free(arr->array);
-               free(arr);
-       }
+    OSRF_STRING_ARRAY_FREE(arr);
 }
 
 
 int osrfStringArrayContains( osrfStringArray* arr, char* string ) {
        if(!(arr && string)) return 0;
-       
        int i;
-       for( i = 0; i != arr->size; i++ ) {
-               char* str = osrfStringArrayGetString(arr, i);
-               if(str) {
-                       if(!strcmp(str, string)) return 1;
-               }
+       for( i = 0; i < arr->size; i++ ) {
+        char* str = OSRF_LIST_GET_INDEX(arr->list, i);
+               if(str && !strcmp(str, string)) 
+            return 1;
        }
 
        return 0;
@@ -97,19 +64,29 @@ int osrfStringArrayContains( osrfStringArray* arr, char* string ) {
 void osrfStringArrayRemove( osrfStringArray* arr, char* tstr) {
        if(!(arr && tstr)) return;
        int i;
-       for( i = 0; i != arr->size; i++ ) {
-               char* str = osrfStringArrayGetString(arr, i);
-               if(str) {
-                       if(!strcmp(str, tstr)) {
-                               free(arr->array[i]);
-                               arr->array[i] = NULL;
-                               break;
-                       }
+    char* str;
+
+       for( i = 0; i < arr->size; i++ ) {
+        /* find and remove the string */
+        str = OSRF_LIST_GET_INDEX(arr->list, i);
+               if(str && !strcmp(str, tstr)) {
+            osrfListRemove(arr->list, i);
+                       break;
                }
        }
-       for( ; i != arr->size; i++ ) 
-               arr->array[i] = arr->array[i+1];
 
+    /* disable automatic item freeing on delete and shift
+     * items up in the array to fill in the gap
+     */
+    arr->list->freeItem = NULL;
+       for( ; i < arr->size - 1; i++ ) 
+        osrfListSet(arr->list, OSRF_LIST_GET_INDEX(arr->list, i+1) , i);
+
+    /* remove the last item since it was shifted up */
+    osrfListRemove(arr->list, i);
+
+    /* re-enable automatic item freeing in delete */
+    osrfListSetDefaultFree(arr->list);
        arr->size--;
 }
 
index 9a14291..5676a43 100644 (file)
@@ -23,7 +23,7 @@ int main( int argc, char** argv ) {
                if( recv->body ) {
                        int len = strlen(recv->body);
                        char buf[len + 20];
-                       memset( buf, 0, len + 20); 
+                       osrf_clearbuf( buf, 0, sizeof(buf)); 
                        sprintf( buf, "Echoing...%s", recv->body );
                        send = message_init( buf, "Echoing Stuff", "12345", recv->sender, "" );
                } else {
index 6488115..80b1ea9 100644 (file)
@@ -238,7 +238,7 @@ char* message_to_xml( const transport_message* msg ) {
                xmlAddChild( message_node, error_node );
                xmlNewProp( error_node, BAD_CAST "type", BAD_CAST msg->error_type );
                char code_buf[16];
-               memset( code_buf, 0, 16);
+               osrf_clearbuf( code_buf, sizeof(code_buf));
                sprintf(code_buf, "%d", msg->error_code );
                xmlNewProp( error_node, BAD_CAST "code", BAD_CAST code_buf  );
        }
@@ -303,6 +303,7 @@ void jid_get_username( const char* jid, char buf[], int size ) {
                if( jid[i] == 64 ) { /*ascii @*/
                        if(i > size)  i = size;
                        strncpy( buf, jid, i );
+                       buf[i] = '\0'; // strncpy doesn't provide the nul
                        return;
                }
        }
@@ -319,6 +320,7 @@ void jid_get_resource( const char* jid, char buf[], int size)  {
                        int rlen = len - (i+1);
                        if(rlen > size) rlen = size;
                        strncpy( buf, start, rlen );
+                       buf[rlen] = '\0'; // strncpy doesn't provide the nul
                }
        }
 }
@@ -343,6 +345,7 @@ void jid_get_domain( const char* jid, char buf[], int size ) {
                int dlen = index2 - index1;
                if(dlen > size) dlen = size;
                memcpy( buf, jid + index1, dlen );
+               buf[dlen] = '\0'; // memcpy doesn't provide the nul
        }
 }
 
index 4fb0290..684cd11 100644 (file)
@@ -183,8 +183,7 @@ int session_connect( transport_session* session,
                char* our_hostname = getenv("HOSTNAME");
                size1 = 150 + strlen( server );
                char stanza1[ size1 ]; 
-               memset( stanza1, 0, size1 );
-               sprintf( stanza1, 
+               snprintf( stanza1, sizeof(stanza1),
                                "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
                                "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
                                username, our_hostname );
@@ -207,14 +206,12 @@ int session_connect( transport_session* session,
        
                        int ss = session->session_id->n_used + strlen(password) + 5;
                        char hashstuff[ss];
-                       memset(hashstuff,0,ss);
-                       sprintf( hashstuff, "%s%s", session->session_id->buf, password );
+                       snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
 
                        char* hash = shahash( hashstuff );
                        size2 = 100 + strlen( hash );
                        char stanza2[ size2 ];
-                       memset( stanza2, 0, size2 );
-                       sprintf( stanza2, "<handshake>%s</handshake>", hash );
+                       snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
        
                        //if( ! tcp_send( session->sock_obj, stanza2 )  ) {
                        if( socket_send( session->sock_id, stanza2 )  ) {
@@ -228,8 +225,7 @@ int session_connect( transport_session* session,
                /* the first Jabber connect stanza */
                size1 = 100 + strlen( server );
                char stanza1[ size1 ]; 
-               memset( stanza1, 0, size1 );
-               sprintf( stanza1, 
+               snprintf( stanza1, sizeof(stanza1), 
                                "<stream:stream to='%s' xmlns='jabber:client' "
                                "xmlns:stream='http://etherx.jabber.org/streams'>",
                        server );
@@ -253,9 +249,7 @@ int session_connect( transport_session* session,
                        /* the second jabber connect stanza including login info*/
                        size2 = 150 + strlen( username ) + strlen(password) + strlen(resource);
                        char stanza2[ size2 ];
-                       memset( stanza2, 0, size2 );
-               
-                       sprintf( stanza2, 
+                       snprintf( stanza2, sizeof(stanza2), 
                                        "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
                                        "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
                                        username, password, resource );
@@ -273,17 +267,14 @@ int session_connect( transport_session* session,
 
                        int ss = session->session_id->n_used + strlen(password) + 5;
                        char hashstuff[ss];
-                       memset(hashstuff,0,ss);
-                       sprintf( hashstuff, "%s%s", session->session_id->buf, password );
+                       snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
 
                        char* hash = shahash( hashstuff );
 
                        /* the second jabber connect stanza including login info*/
                        size2 = 150 + strlen( hash ) + strlen(password) + strlen(resource);
                        char stanza2[ size2 ];
-                       memset( stanza2, 0, size2 );
-               
-                       sprintf( stanza2, 
+                       snprintf( stanza2, sizeof(stanza2), 
                                        "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
                                        "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
                                        username, hash, resource );
@@ -566,7 +557,6 @@ void characterHandler(
                void *session, const xmlChar *ch, int len) {
 
        char data[len+1];
-       memset( data, 0, len+1 );
        strncpy( data, (char*) ch, len );
        data[len] = 0;
 
@@ -597,7 +587,7 @@ void characterHandler(
 
        if( ses->state_machine->in_error ) {
                /* for now... */
-               osrfLogWarning( OSRF_LOG_MARK,  "ERROR Xml fragment: %s\n", ch );
+               osrfLogWarning( OSRF_LOG_MARK,  "ERROR XML fragment: %s\n", ch );
        }
 
 }
index 908145b..0323fb7 100644 (file)
@@ -22,6 +22,16 @@ inline void* safe_malloc( int size ) {
                osrfLogError( OSRF_LOG_MARK, "Out of Memory" );
                exit(99);
        }
+       memset( ptr, 0, size ); // remove this after safe_calloc transition
+       return ptr;
+}
+
+inline void* safe_calloc( int size ) {
+       void* ptr = (void*) malloc( size );
+       if( ptr == NULL ) {
+               osrfLogError( OSRF_LOG_MARK, "Out of Memory" );
+               exit(99);
+       }
        memset( ptr, 0, size );
        return ptr;
 }
@@ -45,8 +55,9 @@ int init_proc_title( int argc, char* argv[] ) {
        int i = 0;
        while( i < argc ) {
                int len = strlen( global_argv[i]);
-               bzero( global_argv[i++], len );
+               osrf_clearbuf( global_argv[i], len );
                global_argv_size += len;
+               i++;
        }
 
        global_argv_size -= 2;
@@ -55,7 +66,7 @@ int init_proc_title( int argc, char* argv[] ) {
 
 int set_proc_title( char* format, ... ) {
        VA_LIST_TO_STRING(format);
-       bzero( *(global_argv), global_argv_size );
+       osrf_clearbuf( *(global_argv), global_argv_size);
        return snprintf( *(global_argv), global_argv_size, VA_BUF );
 }
 
@@ -121,7 +132,7 @@ char* va_list_to_string(const char* format, ...) {
        len = va_list_size(format, args);
 
        char buf[len];
-       memset(buf, 0, len);
+       osrf_clearbuf(buf, sizeof(buf));
 
        va_start(a_copy, format);
        vsnprintf(buf, len - 1, format, a_copy);
@@ -164,7 +175,7 @@ int buffer_fadd(growing_buffer* gb, const char* format, ... ) {
        len = va_list_size(format, args);
 
        char buf[len];
-       memset(buf, 0, len);
+       osrf_clearbuf(buf, sizeof(buf));
 
        va_start(a_copy, format);
        vsnprintf(buf, len - 1, format, a_copy);
@@ -212,7 +223,7 @@ int buffer_add(growing_buffer* gb, char* data) {
 int buffer_reset( growing_buffer *gb){
        if( gb == NULL ) { return 0; }
        if( gb->buf == NULL ) { return 0; }
-       memset( gb->buf, 0, gb->size );
+       osrf_clearbuf( gb->buf, sizeof(gb->buf) );
        gb->n_used = 0;
        return 1;
 }
@@ -416,7 +427,7 @@ char* file_to_string(const char* filename) {
 
        int len = 1024;
        char buf[len];
-       bzero(buf, len);
+       osrf_clearbuf(buf, sizeof(buf));
        growing_buffer* gb = buffer_init(len);
 
        FILE* file = fopen(filename, "r");
@@ -425,9 +436,9 @@ char* file_to_string(const char* filename) {
                return NULL;
        }
 
-       while(fgets(buf, len - 1, file)) {
+       while(fgets(buf, sizeof(buf), file)) {
                buffer_add(gb, buf);
-               bzero(buf, len);
+               osrf_clearbuf(buf, sizeof(buf));
        }
 
        fclose(file);
@@ -454,13 +465,11 @@ char* md5sum( char* text, ... ) {
        MD5_stop (&ctx, digest);
 
        char buf[16];
-       memset(buf,0,16);
-
        char final[256];
-       memset(final,0,256);
+       osrf_clearbuf(final, sizeof(final));
 
        for ( i=0 ; i<16 ; i++ ) {
-               sprintf(buf, "%02x", digest[i]);
+               snprintf(buf, sizeof(buf), "%02x", digest[i]);
                strcat( final, buf );
        }
 
index 18cd2a8..84f9879 100644 (file)
@@ -482,7 +482,7 @@ int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj,
                                                        "json_parse_json_string(): truncated escaped unicode"); }
 
                                        char buff[5];
-                                       memset(buff,0,5);
+                                       memset(buff, 0, sizeof(buff));
                                        memcpy(buff, string + (*index), 4);
 
 
@@ -491,7 +491,7 @@ int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj,
                                        /* The following chunk was borrowed with permission from 
                                                json-c http://oss.metaparadigm.com/json-c/ */
                                        unsigned char utf_out[4];
-                                       memset(utf_out,0,4);
+                                       memset(utf_out, 0, sizeof(utf_out));
 
                                        #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
 
@@ -695,7 +695,7 @@ int json_eat_comment(char* string, unsigned long* index, char** buffer, int pars
 int json_handle_error(char* string, unsigned long* index, char* err_msg) {
 
        char buf[60];
-       memset(buf, 0, 60);
+       memset(buf, 0, sizeof(buf));
 
        if(*index > 30)
                strncpy( buf, string + (*index - 30), 59 );
index 5813780..61b7f9f 100644 (file)
@@ -183,11 +183,11 @@ int main() {
 
        char buf[10240];
        char smallbuf[512];
-       bzero(buf, 10240);
-       bzero(smallbuf, 512);
+       memset(buf, 0, sizeof(buf));
+       memset(smallbuf, 0, sizeof(smallbuf));
 
-       while(fgets(smallbuf, 512, F)) 
-               strcat(buf, smallbuf);
+       while(fgets(smallbuf, sizeof(smallbuf), F)) 
+               strncat(buf, smallbuf, sizeof(buf) - 1);
 
        /* dig our way into the JSON object we parsed, see test.json to get
           an idea of the object structure */
index 9b2017a..517fdf8 100644 (file)
@@ -156,6 +156,14 @@ sub last_sent_payload {
        return $self->{'last_sent_payload'};
 }
 
+sub session_locale {
+       my( $self, $type ) = @_;
+       if( $type ) {
+               return $self->{'session_locale'} = $type;
+       }
+       return $self->{'session_locale'};
+}
+
 sub last_sent_type {
        my( $self, $type ) = @_;
        if( $type ) {
@@ -192,9 +200,6 @@ sub stateless {
 }
 
 # When we're a client and we want to connect to a remote service
-# create( $app, username => $user, secret => $passwd );
-#    OR
-# create( $app, sysname => $user, secret => $shared_secret );
 sub create {
        my $class = shift;
        $class = ref($class) || $class;
@@ -202,6 +207,7 @@ sub create {
        my $app = shift;
         my $api_level = shift;
        my $quiet = shift;
+       my $locale = shift;
 
        $api_level = 1 if (!defined($api_level));
                                
@@ -237,6 +243,7 @@ sub create {
                           session_id  => $sess_id,
                           remote_id   => $r_id,
                           raise_error   => $quiet ? 0 : 1,
+                          session_locale   => $locale,
                           api_level   => $api_level,
                           orig_remote_id   => $r_id,
                                peer_handle => $peer_handle,
@@ -494,6 +501,7 @@ sub send {
        
                $msg->api_level($self->api_level);
                $msg->payload($payload) if $payload;
+               $msg->sender_locale($self->session_locale) if $self->session_locale;
        
                push @doc, $msg;
 
@@ -742,7 +750,7 @@ sub recv {
        $logger->debug( "Number of matched responses: " . @list, DEBUG );
        $self->queue_wait(0); # check for statuses
        
-       return $list[0] unless (wantarray);
+       return $list[0] if (!wantarray);
        return @list;
 }
 
index 71e62a4..08bd106 100644 (file)
@@ -1,4 +1,5 @@
 package OpenSRF::Application;
+# vim:noet:ts=4
 use vars qw/$_app $log @_METHODS $thunk $server_class/;
 
 use base qw/OpenSRF/;
@@ -98,7 +99,7 @@ sub handler {
         my @p = $app_msg->params;
                my $method_name = $app_msg->method;
                my $method_proto = $session->last_message_api_level;
-        $logger->info("CALL: $method_name [".join(', ',@p)."]");
+               $log->info("CALL: $method_name [". (@p ? join(', ',@p) : '') ."]");
 
                my $coderef = $app->method_lookup( $method_name, $method_proto, 1, 1 );
 
index 1374e28..19a4c12 100644 (file)
@@ -86,6 +86,24 @@ sub api_level {
        return $self->{api_level};
 }
 
+=head2 OpenSRF::DomainObject::oilsMessage->sender_locale( [$locale] );
+
+=over 4
+
+Sets or gets the current message locale hint.  Useful for telling the
+server how you see the world.
+
+=back
+
+=cut
+
+sub sender_locale {
+       my $self = shift;
+       my $val = shift;
+       $self->{sender_locale} = $val if (defined $val);
+       return $self->{sender_locale};
+}
+
 =head2 OpenSRF::DomainObject::oilsMessage->threadTrace( [$new_threadTrace] );
 
 =over 4
@@ -163,12 +181,14 @@ sub handler {
        my $session = shift;
 
        my $mtype = $self->type;
+       my $locale = $self->sender_locale;
        my $api_level = $self->api_level || 1;;
        my $tT = $self->threadTrace;
 
        $session->last_message_type($mtype);
        $session->last_message_api_level($api_level);
        $session->last_threadTrace($tT);
+       $session->session_locale($locale);
 
        $log->debug(" Received api_level => [$api_level], MType => [$mtype], ".
                        "from [".$session->remote_id."], threadTrace[".$self->threadTrace."]");
index bcaf96f..aeaee77 100644 (file)
@@ -201,7 +201,7 @@ $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
 =head1 ABSTRACT
 
 Implements the STATUS_CONTINUE message, informing the client that it should
-continue to wait for a response to it's request.
+continue to wait for a response to its request.
 
 =head1 SEE ALSO
 
@@ -316,11 +316,11 @@ $client->send( 'ERROR', OpenSRF::DomainObject::oilsException->new( status => "AR
 
 The base class for Exception messages sent between client and server.  This
 is implemented on top of the C<OpenSRF::DomainObject::oilsResponse> class, and 
-sets the default B<status> to C<Exception occured> and B<statusCode> to C<STATUS_BADREQUEST>.
+sets the default B<status> to C<Exception occurred> and B<statusCode> to C<STATUS_BADREQUEST>.
 
 =cut
 
-$status = 'Exception occured';
+$status = 'Exception occurred';
 $statusCode = STATUS_INTERNALSERVERERROR;
 
 #-------------------------------------------------------------------------------
@@ -372,7 +372,7 @@ OpenSRF::Utils::JSON->register_class_hint( hint => 'osrfMethodException', name =
 
 =head1 NAME
 
-OpenSRF::DomainObject::oilsMehtodException
+OpenSRF::DomainObject::oilsMethodException
 
 =head1 SYNOPSIS
 
@@ -387,7 +387,7 @@ $client->send( 'ERROR', new OpenSRF::DomainObject::oilsMethodException );
 
 =head1 ABSTRACT
 
-The class for Exceptions that occur durring the B<CONNECT> phase of a session.  This
+The class for Exceptions that occur during the B<CONNECT> phase of a session.  This
 is implemented on top of the C<OpenSRF::DomainObject::oilsException> class, and 
 sets the default B<status> to C<Connect Request Failed> and B<statusCode> to C<STATUS_NOTFOUND>.
 
@@ -398,7 +398,7 @@ B<OpenSRF::DomainObject::oilsException>
 =cut
 
 
-$status = 'A server error occured during method execution';
+$status = 'A server error occurred during method execution';
 $statusCode = STATUS_INTERNALSERVERERROR;
 
 # -------------------------------------------
index 9af9282..fbecf58 100644 (file)
@@ -90,7 +90,7 @@ sub handler {
        my $body        = $helper->get_body();
        my $type        = $helper->get_msg_type();
 
-   $logger->set_osrf_xid($helper->get_osrf_xid);
+       $logger->set_osrf_xid($helper->get_osrf_xid);
 
        if (defined($type) and $type eq 'error') {
                throw OpenSRF::EX::Session ("$remote_id IS NOT CONNECTED TO THE NETWORK!!!");
index 472bb44..2792db5 100644 (file)
@@ -146,7 +146,7 @@ sub process_request {
                $logger->debug( "Looping on zombies " . $x++ , DEBUG);
        }
 
-       $logger->debug( "Timed out, disconnected, or auth failed" );
+       $logger->debug( "Timed out, disconnected, or authentication failed" );
        $app_session->kill_me if ($app_session);
 
        $0 = $orig;
@@ -203,7 +203,7 @@ sub configure_hook {
        # boot a client
        OpenSRF::System->bootstrap_client( client_name => "system_client" );
 
-       $logger->debug( "Setting application implementaion for $app", DEBUG );
+       $logger->debug( "Setting application implementation for $app", DEBUG );
        my $client = OpenSRF::Utils::SettingsClient->new();
        my $imp = $client->config_value("apps", $app, "implementation");
        OpenSRF::Application::server_class($app);
index b417425..c78ec48 100644 (file)
@@ -85,7 +85,79 @@ sub _json_hint_to_class {
        return $hint;
 }
 
+
+my $JSON_CLASS_KEY = '__c';
+my $JSON_PAYLOAD_KEY = '__p';
+
 sub JSON2perl {
+       my( $class, $string ) = @_;
+       my $perl = $class->rawJSON2perl($string);
+       return $class->JSONObject2Perl($perl);
+}
+
+sub perl2JSON {
+       my( $class, $obj ) = @_;
+       my $json = $class->perl2JSONObject($obj);
+       return $class->rawPerl2JSON($json);
+}
+
+sub JSONObject2Perl {
+       my $class = shift;
+       my $obj = shift;
+       my $ref = ref($obj);
+       if( $ref eq 'HASH' ) {
+               if( defined($obj->{$JSON_CLASS_KEY})) {
+                       my $cls = $obj->{$JSON_CLASS_KEY};
+            $cls =~ s/^\s+//o;
+            $cls =~ s/\s+$//o;
+                       if( $obj = $class->JSONObject2Perl($obj->{$JSON_PAYLOAD_KEY}) ) {
+                               $cls = $class->lookup_class($cls) || $cls;
+                               return bless(\$obj, $cls) unless ref($obj); 
+                               return bless($obj, $cls);
+                       }
+                       return undef;
+               }
+               $obj->{$_} = $class->JSONObject2Perl($obj->{$_}) for (keys %$obj);
+       } elsif( $ref eq 'ARRAY' ) {
+               $obj->[$_] = $class->JSONObject2Perl($obj->[$_]) for(0..scalar(@$obj) - 1);
+       }
+       return $obj;
+}
+
+sub perl2JSONObject {
+       my $class = shift;
+       my $obj = shift;
+       my $ref = ref($obj);
+
+       return $obj unless $ref;
+       my $newobj;
+
+       if( $ref eq 'HASH' ) {
+               $newobj = {};
+               $newobj->{$_} = $class->perl2JSONObject( $obj->{$_} ) for (keys %$obj);
+       } elsif( $ref eq 'ARRAY' ) {
+               $newobj = [];
+               $newobj->[$_] = $class->perl2JSONObject( $obj->[$_] ) for(0..scalar(@$obj) - 1 );
+       } elsif( $ref ) {
+               if(UNIVERSAL::isa($obj, 'HASH')) {
+                       $newobj = {};
+                       $newobj->{$_} = $class->perl2JSONObject( $obj->{$_} ) for (keys %$obj);
+                       bless( $newobj, ref($obj) );
+                       #bless($obj, 'HASH'); # so our parser won't add the hints
+               } elsif(UNIVERSAL::isa($obj, 'ARRAY')) {
+                       $newobj = [];
+                       $newobj->[$_] = $class->perl2JSONObject( $obj->[$_] ) for(0..scalar(@$obj) - 1);
+                       bless( $newobj, ref($obj) );
+                       #bless($obj, 'ARRAY'); # so our parser won't add the hints
+               }
+               $ref = $class->lookup_hint($ref) || $ref;
+               $newobj = { $JSON_CLASS_KEY => $ref, $JSON_PAYLOAD_KEY => $newobj };
+       } 
+       return $newobj; 
+}
+
+
+sub rawJSON2perl {
        my $class = shift;
        local $_ = shift;
 
@@ -97,8 +169,8 @@ sub JSON2perl {
        s/\\u([0-9a-fA-F]{4})/chr(hex($1))/esog;
 
        # handle class blessings
-       s/\/\*--\s*S\w*?\s+\S+\s*--\*\// bless(/sog;
-       s/(\]|\}|")\s*\/\*--\s*E\w*?\s+(\S+)\s*--\*\//$1 => _json_hint_to_class("$1", "$2")) /sog;
+#      s/\/\*--\s*S\w*?\s+\S+\s*--\*\// bless(/sog;
+#      s/(\]|\}|")\s*\/\*--\s*E\w*?\s+(\S+)\s*--\*\//$1 => _json_hint_to_class("$1", "$2")) /sog;
 
        my $re = qr/((?<!\\)"(?>(?<=\\)"|[^"])*(?<!\\)")/;
        # Grab strings...
@@ -112,17 +184,18 @@ sub JSON2perl {
        s/:/ => /sog;
 
        # Do numbers...
-       #s/\b(-?\d+\.?\d*)\b/ OpenSRF::Utils::JSON::number::new($1) /sog;
+       #s/\b(-?\d+\.?\d*)\b/ JSON::number::new($1) /sog;
 
        # Change javascript stuff to perl...
        s/null/ undef /sog;
-       s/true/ bless( {}, "OpenSRF::Utils::JSON::bool::true") /sog;
-       s/false/ bless( {}, "OpenSRF::Utils::JSON::bool::false") /sog;
+       s/true/ bless( {}, "JSON::bool::true") /sog;
+       s/false/ bless( {}, "JSON::bool::false") /sog;
 
        my $ret;
        return eval '$ret = '.$_;
 }
 
+
 my $_json_index;
 sub ___JSON2perl {
        my $class = shift;
@@ -638,7 +711,7 @@ sub old_JSON2perl {
                } elsif ($element =~ /^\/\*/) {
                        next;
                } elsif ($element =~ /^\d/) {
-                       $output .= "do { OpenSRF::Utils::JSON::number::new($element) }";
+                       $output .= "do { JSON::number::new($element) }";
                        next;
                } elsif ($element eq '{' or $element eq '[') {
                        $casting_depth++;
@@ -654,10 +727,10 @@ sub old_JSON2perl {
                        $output .= ' => ';
                        next;
                } elsif ($element eq 'true') {
-                       $output .= 'bless( {}, "OpenSRF::Utils::JSON::bool::true")';
+                       $output .= 'bless( {}, "JSON::bool::true")';
                        next;
                } elsif ($element eq 'false') {
-                       $output .= 'bless( {}, "OpenSRF::Utils::JSON::bool::false")';
+                       $output .= 'bless( {}, "JSON::bool::false")';
                        next;
                }
                
@@ -667,26 +740,28 @@ sub old_JSON2perl {
        return eval $output;
 }
 
-sub perl2JSON {
+
+sub rawPerl2JSON {
        my ($class, $perl, $strict) = @_;
 
        my $output = '';
        if (!defined($perl)) {
                $output = '' if $strict;
                $output = 'null' unless $strict;
-       } elsif (ref($perl) and ref($perl) =~ /^OpenSRF::Utils::JSON/) {
+       } elsif (ref($perl) and ref($perl) =~ /^JSON/) {
                $output .= $perl;
-       } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) {
-               $output .= '/*--S '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
-               if (lc($_class_map{classes}{ref($perl)}{type}) eq 'hash') {
-                       my %hash =  %$perl;
-                       $output .= perl2JSON(undef,\%hash, $strict);
-               } elsif (lc($_class_map{classes}{ref($perl)}{type}) eq 'array') {
-                       my @array =  @$perl;
-                       $output .= perl2JSON(undef,\@array, $strict);
-               }
-               $output .= '/*--E '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
-       } elsif (ref($perl) and ref($perl) =~ /HASH/) {
+#      } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) {
+#              $output .= '/*--S '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
+#              if (lc($_class_map{classes}{ref($perl)}{type}) eq 'hash') {
+#                      my %hash =  %$perl;
+#                      $output .= rawPerl2JSON(undef,\%hash, $strict);
+#              } elsif (lc($_class_map{classes}{ref($perl)}{type}) eq 'array') {
+#                      my @array =  @$perl;
+#                      $output .= rawPerl2JSON(undef,\@array, $strict);
+#              }
+#              $output .= '/*--E '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
+#      } elsif (ref($perl) and ref($perl) =~ /HASH/) {
+       } elsif (UNIVERSAL::isa($perl, 'HASH')) {
                $output .= '{';
                my $c = 0;
                for my $key (sort keys %$perl) {
@@ -701,27 +776,28 @@ sub perl2JSON {
                        $outkey =~ s/\n/\\n/sgo;
                        $outkey =~ s/([\x{0080}-\x{fffd}])/sprintf('\u%0.4x',ord($1))/sgoe;
 
-                       $output .= '"'.$outkey.'":'. perl2JSON(undef,$$perl{$key}, $strict);
+                       $output .= '"'.$outkey.'":'. rawPerl2JSON(undef,$$perl{$key}, $strict);
                        $c++;
                }
                $output .= '}';
-       } elsif (ref($perl) and ref($perl) =~ /ARRAY/) {
+#      } elsif (ref($perl) and ref($perl) =~ /ARRAY/) {
+       } elsif (UNIVERSAL::isa($perl, 'ARRAY')) {
                $output .= '[';
                my $c = 0;
                for my $part (@$perl) {
                        $output .= ',' if ($c); 
                        
-                       $output .= perl2JSON(undef,$part, $strict);
+                       $output .= rawPerl2JSON(undef,$part, $strict);
                        $c++;
                }
                $output .= ']';
        } elsif (ref($perl) and ref($perl) =~ /CODE/) {
-               $output .= perl2JSON(undef,$perl->(), $strict);
+               $output .= rawPerl2JSON(undef,$perl->(), $strict);
        } elsif (ref($perl) and ("$perl" =~ /^([^=]+)=(\w+)/o)) {
                my $type = $2;
                my $name = $1;
                OpenSRF::Utils::JSON->register_class_hint(name => $name, hint => $name, type => lc($type));
-               $output .= perl2JSON(undef,$perl, $strict);
+               $output .= rawPerl2JSON(undef,$perl, $strict);
        } else {
                $perl = NFC($perl);
                $perl =~ s{\\}{\\\\}sgo;
@@ -750,7 +826,7 @@ sub perl2prettyJSON {
        if (!defined($perl)) {
                $output = "   "x$depth unless($nospace);
                $output .= 'null';
-       } elsif (ref($perl) and ref($perl) =~ /^OpenSRF::Utils::JSON/) {
+       } elsif (ref($perl) and ref($perl) =~ /^JSON/) {
                $output = "   "x$depth unless($nospace);
                $output .= $perl;
        } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) {
index 8734115..7ee1eba 100644 (file)
@@ -1,4 +1,5 @@
 package OpenSRF::Utils::Logger;
+# vim:ts=4:noet:
 use strict;
 use vars qw($AUTOLOAD @EXPORT_OK %EXPORT_TAGS);
 use Exporter;
@@ -29,7 +30,7 @@ push @EXPORT_OK, '$logger';
 %EXPORT_TAGS = ( level => [ qw/ NONE ERROR WARN INFO DEBUG INTERNAL / ], logger => [ '$logger' ] );
 
 my $config;                                                    # config handle
-my $loglevel;                                          # global log level
+my $loglevel = INFO();                         # global log level
 my $logfile;                                           # log file
 my $facility;                                          # syslog facility
 my $actfac;                                                    # activity log syslog facility
@@ -101,8 +102,12 @@ sub set_config {
         $actfile = $config->bootstrap->actlog || $config->bootstrap->logfile;
     }
 
-
-       $isclient = (OpenSRF::Utils::Config->current->bootstrap->client =~ /^true$/iog) ?  1 : 0;
+       my $client = OpenSRF::Utils::Config->current->bootstrap->client();
+       if (!$client) {
+               $isclient = 0;
+               return;
+       }
+       $isclient = ($client =~ /^true$/iog) ?  1 : 0;
 }
 
 sub _fac_to_const {
index 98c8686..ee2fd36 100644 (file)
@@ -2,16 +2,20 @@ from xml.dom import minidom
 from xml.sax import handler, make_parser, saxutils
 from json import *
 from net_obj import *
+from log import *
 import urllib, urllib2, sys, re
 
 defaultHost = None
-#paramRegex = re.compile('\%27')
 
 class GatewayRequest:
     def __init__(self, service, method, params=[]):
         self.service = service
         self.method = method
         self.params = params
+        self.path = 'gateway'
+
+    def setPath(self, path):
+        self.path = path
 
     def send(self):
         params = self.buildPOSTParams()
@@ -45,8 +49,33 @@ class GatewayRequest:
     setDefaultHost = staticmethod(setDefaultHost)
 
     def buildURL(self):
-        return 'http://%s/gateway' % defaultHost
+        return 'http://%s/%s' % (defaultHost, self.path)
+
+class JSONGatewayRequest(GatewayRequest):
+    def __init__(self, service, method, *params):
+        GatewayRequest.__init__(self, service, method, list(params))
+
+    def getFormat(self):
+        return 'json'
+
+    def getInputFormat(self):
+        return self.getFormat()
 
+    def handleResponse(self, response):
+        s = response.read()
+        obj = osrfJSONToObject(s)
+        if obj['status'] != 200:
+            sys.stderr.write('JSON gateway returned status %d:\n%s\n' % (obj['status'], s))
+            return None
+
+        # the gateway wraps responses in an array to handle streaming data
+        # if there is only one item in the array, it (probably) wasn't a streaming request
+        p = obj['payload']
+        if len(p) > 1: return p
+        return p[0]
+
+    def encodeParam(self, param):
+        return osrfObjectToJSON(param)
 
 class XMLGatewayRequest(GatewayRequest):
 
@@ -63,7 +92,12 @@ class XMLGatewayRequest(GatewayRequest):
         handler = XMLGatewayParser()
         parser = make_parser()
         parser.setContentHandler(handler)
-        parser.parse(response)
+        try:
+            parser.parse(response)
+        except Exception, e:
+            osrfLogErr('Error parsing gateway XML: %s' % str(e))
+            return None
+
         return handler.getResult()
 
     def encodeParam(self, param):
index 8845b92..a0856bd 100644 (file)
@@ -4,10 +4,21 @@ from osrf.const import OSRF_JSON_PAYLOAD_KEY, OSRF_JSON_CLASS_KEY
 
 class osrfJSONNetworkEncoder(simplejson.JSONEncoder):
     def default(self, obj):
+
         if isinstance(obj, osrfNetworkObject):
+            reg = obj.getRegistry()
+            data = obj.getData()
+
+            # re-encode the object as an array if necessary
+            if reg.wireProtocol == 'array':
+                d = []
+                for k in reg.keys:
+                    d.append(data.get(k)) 
+                data = d
+
             return { 
-                OSRF_JSON_CLASS_KEY: obj.getRegistry().hint,
-                OSRF_JSON_PAYLOAD_KEY: self.default(obj.getData())
+                OSRF_JSON_CLASS_KEY: reg.hint,
+                OSRF_JSON_PAYLOAD_KEY: self.default(data)
             }   
         return obj
 
@@ -77,9 +88,15 @@ def osrfFormatJSON(json):
     instring = False
     inescape = False
     done = False
+    eatws = False
 
     for c in json:
 
+        if eatws: # simpljson adds a pesky space after array and object items
+            if c == ' ': 
+                continue
+
+        eatws = False
         done = False
         if (c == '{' or c == '[') and not instring:
             t += 1
@@ -94,6 +111,10 @@ def osrfFormatJSON(json):
         if c == ',' and not instring:
             r += c + '\n' + __tabs(t)
             done = True
+            eatws = True
+
+        if c == ':' and not instring:
+            eatws = True
 
         if c == '"' and not inescape:
             instring = not instring
index 187a34d..df63093 100644 (file)
@@ -13,7 +13,7 @@
 # GNU General Public License for more details.
 # -----------------------------------------------------------------------
 
-import traceback, sys, os, re
+import traceback, sys, os, re, threading
 from osrf.const import *
 
 loglevel = 4
@@ -50,6 +50,8 @@ def __osrfLog(level, msg):
     try:
         import syslog
     except:
+        if level == OSRF_LOG_ERR:
+            sys.stderr.write('ERR ' + msg)
         return
         
     global loglevel
@@ -66,8 +68,11 @@ def __osrfLog(level, msg):
     if level == OSRF_LOG_WARN: lvl = 'WARN'; slvl=syslog.LOG_WARNING
     if level == OSRF_LOG_ERR:  lvl = 'ERR '; slvl=syslog.LOG_ERR
 
+
+    # XXX when file logging is implemented, wrap io in a semaphore for thread safety
+
     file = frgx.sub('',tb[0])
-    msg = '[%s:%d:%s:%s] %s' % (lvl, os.getpid(), file, tb[1], msg)
+    msg = '[%s:%d:%s:%s:%s] %s' % (lvl, os.getpid(), file, tb[1], threading.currentThread().getName(), msg)
     syslog.syslog(slvl, msg)
 
     if level == OSRF_LOG_ERR:
index db0b131..a6fddf8 100644 (file)
@@ -19,129 +19,133 @@ from pyxmpp.message import Message
 from pyxmpp.jid import JID
 from socket import gethostname
 from osrf.log import *
-import os, time
+import os, time, threading
 import logging
 
+threadSessions = {}
+
 # - log jabber activity (for future reference)
 #logger=logging.getLogger()
 #logger.addHandler(logging.StreamHandler())
 #logger.addHandler(logging.FileHandler('j.log'))
 #logger.setLevel(logging.DEBUG)
 
-__network = None
 def osrfSetNetworkHandle(handle):
-       """Sets the global network connection handle."""
-       global __network
-       __network = handle
+    """ Sets the thread-specific network handle"""
+    threadSessions[threading.currentThread().getName()] = handle
 
 def osrfGetNetworkHandle():
-       """Returns the global network connection handle."""
-       global __network
-       return __network
+    """ Returns the thread-specific network connection handle."""
+    return threadSessions.get(threading.currentThread().getName())
 
 
 class osrfNetworkMessage(object):
-       """Network message
-
-       attributes:
-
-       sender - message sender
-       to - message recipient
-       body - the body of the message
-       thread - the message thread
-       """
-
-       def __init__(self, message=None, **args):
-               if message:
-                       self.body = message.get_body()
-                       self.thread = message.get_thread()
-                       self.to = message.get_to()
-                       if message.xmlnode.hasProp('router_from') and message.xmlnode.prop('router_from') != '':
-                               self.sender = message.xmlnode.prop('router_from')
-                       else: self.sender = message.get_from().as_utf8()
-               else:
-                       if args.has_key('sender'): self.sender = args['sender']
-                       if args.has_key('to'): self.to = args['to']
-                       if args.has_key('body'): self.body = args['body']
-                       if args.has_key('thread'): self.thread = args['thread']
+    """Network message
+
+    attributes:
+
+    sender - message sender
+    to - message recipient
+    body - the body of the message
+    thread - the message thread
+    """
+
+    def __init__(self, message=None, **args):
+        if message:
+            self.body = message.get_body()
+            self.thread = message.get_thread()
+            self.to = message.get_to()
+            if message.xmlnode.hasProp('router_from') and message.xmlnode.prop('router_from') != '':
+                self.sender = message.xmlnode.prop('router_from')
+            else: self.sender = message.get_from().as_utf8()
+        else:
+            if args.has_key('sender'): self.sender = args['sender']
+            if args.has_key('to'): self.to = args['to']
+            if args.has_key('body'): self.body = args['body']
+            if args.has_key('thread'): self.thread = args['thread']
 
 
 class osrfNetwork(JabberClient):
-       def __init__(self, **args):
-               self.isconnected = False
-
-               # Create a unique jabber resource
-               resource = 'osrf_client'
-               if args.has_key('resource'):
-                       resource = args['resource']
-               resource += '_' + gethostname()+':'+ str(os.getpid()) 
-               self.jid = JID(args['username'], args['host'], resource)
-
-               osrfLogDebug("initializing network with JID %s and host=%s, port=%s, username=%s" % 
-                       (self.jid.as_utf8(), args['host'], args['port'], args['username']))
-
-               #initialize the superclass
-               JabberClient.__init__(self, self.jid, args['password'], args['host'])
-               self.queue = []
-
-       def connect(self):
-               JabberClient.connect(self)
-               while not self.isconnected:
-                       stream = self.get_stream()
-                       act = stream.loop_iter(10)
-                       if not act: self.idle()
-
-       def setRecvCallback(self, func):
-               """The callback provided is called when a message is received.
-               
-                       The only argument to the function is the received message. """
-               self.recvCallback = func
-
-       def session_started(self):
-               osrfLogInfo("Successfully connected to the opensrf network")
-               self.authenticated()
-               self.stream.set_message_handler("normal",self.message_received)
-               self.isconnected = True
-
-       def send(self, message):
-               """Sends the provided network message."""
-               osrfLogInternal("jabber sending to %s: %s" % (message.to, message.body))
-               msg = Message(None, None, message.to, None, None, None, message.body, message.thread)
-               self.stream.send(msg)
-       
-       def message_received(self, stanza):
-               """Handler for received messages."""
-               osrfLogInternal("jabber received a message of type %s" % stanza.get_type())
-               if stanza.get_type()=="headline":
-                       return True
-               # check for errors
-               osrfLogInternal("jabber received message from %s : %s" 
-                       % (stanza.get_from().as_utf8(), stanza.get_body()))
-               self.queue.append(osrfNetworkMessage(stanza))
-               return True
-
-       def recv(self, timeout=120):
-               """Attempts to receive a message from the network.
-
-               timeout - max number of seconds to wait for a message.  
-               If no message is received in 'timeout' seconds, None is returned. """
-
-               msg = None
-               if len(self.queue) == 0:
-                       while timeout >= 0 and len(self.queue) == 0:
-                               starttime = time.time()
-                               osrfLogInternal("going into stream loop at " + str(starttime))
-                               act = self.get_stream().loop_iter(timeout)
-                               endtime = time.time() - starttime
-                               timeout -= endtime
-                               osrfLogInternal("exiting stream loop after %s seconds" % str(endtime))
-                               osrfLogInternal("act = %s : queue length = %d" % (act, len(self.queue)) )
-                               if not act: self.idle()
-
-               # if we've acquired a message, handle it
-               if len(self.queue) > 0:
-                       self.recvCallback(self.queue.pop(0))
-               return None
+    def __init__(self, **args):
+        self.isconnected = False
+
+        # Create a unique jabber resource
+        resource = 'python'
+        if args.has_key('resource'):
+            resource = args['resource']
+        resource += '_' + gethostname()+':'+ str(os.getpid()) + '_'+ threading.currentThread().getName().lower()
+        self.jid = JID(args['username'], args['host'], resource)
+
+        osrfLogDebug("initializing network with JID %s and host=%s, port=%s, username=%s" % 
+            (self.jid.as_utf8(), args['host'], args['port'], args['username']))
+
+        #initialize the superclass
+        JabberClient.__init__(self, self.jid, args['password'], args['host'])
+        self.queue = []
+
+        self.recvCallback = None
+
+    def connect(self):
+        JabberClient.connect(self)
+        while not self.isconnected:
+            stream = self.get_stream()
+            act = stream.loop_iter(10)
+            if not act: self.idle()
+
+    def setRecvCallback(self, func):
+        """The callback provided is called when a message is received.
+        
+            The only argument to the function is the received message. """
+        self.recvCallback = func
+
+    def session_started(self):
+        osrfLogInfo("Successfully connected to the opensrf network")
+        self.authenticated()
+        self.stream.set_message_handler("normal",self.message_received)
+        self.isconnected = True
+
+    def send(self, message):
+        """Sends the provided network message."""
+        osrfLogInternal("jabber sending to %s: %s" % (message.to, message.body))
+        msg = Message(None, None, message.to, None, None, None, message.body, message.thread)
+        self.stream.send(msg)
+    
+    def message_received(self, stanza):
+        """Handler for received messages."""
+        osrfLogInternal("jabber received a message of type %s" % stanza.get_type())
+        if stanza.get_type()=="headline":
+            return True
+        # check for errors
+        osrfLogInternal("jabber received message from %s : %s" 
+            % (stanza.get_from().as_utf8(), stanza.get_body()))
+        self.queue.append(osrfNetworkMessage(stanza))
+        return True
+
+    def recv(self, timeout=120):
+        """Attempts to receive a message from the network.
+
+        timeout - max number of seconds to wait for a message.  
+        If a message is received in 'timeout' seconds, the message is passed to 
+        the recvCallback is called and True is returned.  Otherwise, false is returned."""
+
+        msg = None
+        if len(self.queue) == 0:
+            while timeout >= 0 and len(self.queue) == 0:
+                starttime = time.time()
+                osrfLogInternal("going into stream loop at " + str(starttime))
+                act = self.get_stream().loop_iter(timeout)
+                endtime = time.time() - starttime
+                timeout -= endtime
+                osrfLogInternal("exiting stream loop after %s seconds" % str(endtime))
+                osrfLogInternal("act = %s : queue length = %d" % (act, len(self.queue)) )
+                if not act: self.idle()
+
+        # if we've acquired a message, handle it
+        if len(self.queue) > 0:
+            self.recvCallback(self.queue.pop(0))
+            return True
+
+        return False
 
 
 
index 8ae3b9b..dafc1f2 100644 (file)
@@ -2,20 +2,11 @@ from osrf.const import OSRF_JSON_PAYLOAD_KEY, OSRF_JSON_CLASS_KEY
 from xml.sax import saxutils
 
 
-class osrfNetworkObject(object):
-    ''' Base class for all network serializable objects '''
-    pass
-
-def osrfNewObjectFromHint(hint):
-    try:
-        obj = None
-        exec('obj = osrfNetworkObject.%s()' % hint)
-        return obj
-    except AttributeError:
-        return osrfNetworkObject.__unknown()
-
+# -----------------------------------------------------------
+# Define the global network-class registry
+# -----------------------------------------------------------
 
-''' Global object registry '''
+# Global object registry 
 objectRegistry = {}
 
 class osrfNetworkRegistry(object):
@@ -37,54 +28,77 @@ class osrfNetworkRegistry(object):
     getRegistry = staticmethod(getRegistry)
 
 
-def __makeNetworkAccessor(cls, key):
-    '''  Creates and accessor/mutator method for the given class.  
+# -----------------------------------------------------------
+# Define the base class for all network-serializable objects
+# -----------------------------------------------------------
 
-        'key' is the name the method will have and represents
-        the field on the object whose data we are accessing
-        ''' 
-    def accessor(self, *args):
-        if len(args) != 0:
-            self.__data[key] = args[0]
-        return self.__data.get(key)
-    setattr(cls, key, accessor)
+class osrfNetworkObject(object):
+    ''' Base class for all network serializable objects '''
 
+    # link to our registry object for this registered class
+    registry = None
 
+    def __init__(self, data=None):
+        ''' If this is an array, we pull data out of the data array
+            (if there is any) and translate that into a hash internally '''
 
-def __makeGetRegistry(cls, registry):
-    ''' Wraps the registry for this class inside an accessor method '''
-    def get(self):
-        return registry
-    setattr(cls, 'getRegistry', get)
+        self._data = data
+        if not data: self._data = {}
+        if isinstance(data, list):
+            self.importArrayData(list)
 
-def __makeGetData(cls):
-    ''' Wraps the stored data in an accessor method '''
-    def get(self):
-        return self.__data
-    setattr(cls, 'getData', get)
+    def importArrayData(self, data):
+        ''' If an array-based object is created with an array
+            of data, cycle through and load the data '''
 
-def __makeSetField(cls):
-    ''' Creates a generic mutator for fields by fieldname '''
-    def set(self, field, value):
-        self.__data[field] = value
-    setattr(cls, 'setField', set)
-        
+        self._data = {}
+        if len(data) == 0: return
 
-def __osrfNetworkObjectInit(self, data={}):
-    ''' __init__ method for osrNetworkObjects.
-        If this is an array, we pull data out of the data array
-        (if there is any) and translate that into a hash internally
-        '''
-    self.__data = data
-    if isinstance(data, list) and len(data) > 0:
         reg = self.getRegistry()
         if reg.wireProtocol == 'array':
-            self.__data = {}
             for i in range(len(reg.keys)):
-                try:
-                    self.__data[reg.keys[i]] = data[i]
-                except:
-                    self.__data[reg.keys[i]] = None
+                if len(data) > i: break
+                self.setField(reg.keys[i], data[i])
+
+    def getData(self):
+        ''' Returns the full dataset for this object as a dict '''
+        return self._data
+
+    def setField(self, field, value):
+        self._data[field] = value
+
+    def getField(self, field):
+        return self._data.get(field)
+
+    def getRegistry(cls):
+        ''' Returns the registry object for this registered class '''
+        return cls.registry
+    getRegistry = classmethod(getRegistry)
+
+
+def osrfNewObjectFromHint(hint):
+    ''' Given a hint, this will create a new object of that 
+        type and return it.  If this hint is not registered,
+        an object of type osrfNetworkObject.__unknown is returned'''
+    try:
+        obj = None
+        exec('obj = osrfNetworkObject.%s()' % hint)
+        return obj
+    except AttributeError:
+        return osrfNetworkObject.__unknown()
+
+
+
+
+def __makeNetworkAccessor(cls, key):
+    ''' Creates and accessor/mutator method for the given class.  
+        'key' is the name the method will have and represents
+        the field on the object whose data we are accessing ''' 
+    def accessor(self, *args):
+        if len(args) != 0:
+            self.setField(key, args[0])
+        return self.getField(key)
+    setattr(cls, key, accessor)
 
 
 def osrfNetworkRegisterHint(hint, keys, type='hash'):
@@ -111,16 +125,10 @@ def osrfNetworkRegisterHint(hint, keys, type='hash'):
     for k in keys:
         __makeNetworkAccessor(cls, k)
 
-    # assign our custom init function
-    setattr(cls, '__init__', __osrfNetworkObjectInit)
-    __makeGetRegistry(cls, registry)
-    __makeGetData(cls)
-    __makeSetField(cls)
-
-
     # attach our new class to the osrfNetworkObject 
     # class so others can access it
     setattr(osrfNetworkObject, hint , cls)
+    cls.registry = registry
 
 
 
index 462ae08..5b9c422 100644 (file)
@@ -19,7 +19,7 @@ from osrf.conf import osrfConfigValue
 from osrf.net import osrfNetworkMessage, osrfGetNetworkHandle
 from osrf.log import *
 from osrf.const import *
-import random, sys, os, time
+import random, sys, os, time, threading
 
 
 # -----------------------------------------------------------------------
@@ -35,10 +35,16 @@ osrfNetworkRegisterHint('osrfMethodException', ['status', 'statusCode'], 'hash')
 class osrfSession(object):
     """Abstract session superclass."""
 
+    ''' Global cache of in-service sessions '''
+    sessionCache = {}
+
     def __init__(self):
         # by default, we're connected to no one
         self.state = OSRF_APP_SESSION_DISCONNECTED
 
+    def findSession(threadTrace):
+        return osrfSession.sessionCache.get(threadTrace)
+    findSession = staticmethod(findSession)
 
     def wait(self, timeout=120):
         """Wait up to <timeout> seconds for data to arrive on the network"""
@@ -58,7 +64,7 @@ class osrfSession(object):
 
     def cleanup(self):
         """Removes the session from the global session cache."""
-        del osrfClientSession.sessionCache[self.thread]
+        del osrfSession.sessionCache[self.thread]
 
 class osrfClientSession(osrfSession):
     """Client session object.  Use this to make server requests."""
@@ -78,7 +84,8 @@ class osrfClientSession(osrfSession):
         self.origRemoteId = self.remoteId
 
         # generate a random message thread
-        self.thread = "%s%s%s" % (os.getpid(), str(random.randint(100,100000)), str(time.time()))
+        self.thread = "%s%s%s%s" % (os.getpid(), 
+            str(random.randint(100,100000)), str(time.time()),threading.currentThread().getName().lower())
 
         # how many requests this session has taken part in
         self.nextId = 0 
@@ -87,7 +94,7 @@ class osrfClientSession(osrfSession):
         self.requests = {}
 
         # cache this session in the global session cache
-        osrfClientSession.sessionCache[self.thread] = self
+        osrfSession.sessionCache[self.thread] = self
 
     def resetRequestTimeout(self, rid):
         req = self.findRequest(rid)
@@ -190,13 +197,6 @@ class osrfClientSession(osrfSession):
 
 
 
-osrfSession.sessionCache = {}
-def osrfFindSession(thread):
-    """Finds a session in the global cache."""
-    try:
-        return osrfClientSession.sessionCache[thread]
-    except: return None
-
 class osrfRequest(object):
     """Represents a single OpenSRF request.
         A request is made and any resulting respones are 
index 0bf4892..4002812 100644 (file)
 from osrf.json import *
 from osrf.log import *
 from osrf.ex import *
-from osrf.ses import osrfFindSession, osrfClientSession, osrfServerSession
+from osrf.ses import osrfSession, osrfClientSession, osrfServerSession
 from osrf.const import *
 from time import time
 
 
 def osrfPushStack(netMessage):
-   ses = osrfFindSession(netMessage.thread)
+   ses = osrfSession.findSession(netMessage.thread)
 
    if not ses:
       # This is an incoming request from a client, create a new server session
index bfc5463..a70f22d 100644 (file)
@@ -14,7 +14,7 @@
 # -----------------------------------------------------------------------
 
 from osrf.conf import osrfConfig, osrfConfigValue, osrfConfigValueNoEx
-from osrf.net import osrfNetwork, osrfSetNetworkHandle
+from osrf.net import osrfNetwork, osrfSetNetworkHandle, osrfGetNetworkHandle
 from osrf.stack import osrfPushStack
 from osrf.log import *
 from osrf.set import osrfLoadSettings
@@ -24,6 +24,10 @@ import sys
 def osrfConnect(configFile, configContext):
     """ Connects to the opensrf network """
 
+    if osrfGetNetworkHandle():
+        ''' This thread already has a handle '''
+        return
+
     # parse the config file
     configParser = osrfConfig(configFile, configContext)
     configParser.parseConfig()
@@ -49,3 +53,5 @@ def osrfConnect(configFile, configContext):
 
 
 
+
+
index 70a278e..3c9cff9 100755 (executable)
@@ -11,73 +11,73 @@ from osrf.conf import osrfConfigValue
 # main listen loop
 # -------------------------------------------------------------------
 def do_loop():
-       while True:
+    while True:
 
-               try:
-                       #line = raw_input("srfsh% ")
-                       line = raw_input("\033[01;32msrfsh\033[01;34m% \033[00m")
-                       if not len(line): 
-                               continue
-                       if lower(line) == 'exit' or lower(line) == 'quit': 
-                               break
-                       parts = split(line)
+        try:
+            #line = raw_input("srfsh% ")
+            line = raw_input("\033[01;32msrfsh\033[01;34m% \033[00m")
+            if not len(line): 
+                continue
+            if lower(line) == 'exit' or lower(line) == 'quit': 
+                break
+            parts = split(line)
 
-                       command = parts[0]
-               
-                       if command == 'request':
-                               parts.pop(0)
-                               handle_request(parts)
-                               continue
+            command = parts[0]
+        
+            if command == 'request':
+                parts.pop(0)
+                handle_request(parts)
+                continue
 
-                       if command == 'math_bench':
-                               parts.pop(0)
-                               handle_math_bench(parts)
-                               continue
+            if command == 'math_bench':
+                parts.pop(0)
+                handle_math_bench(parts)
+                continue
 
-                       if command == 'help':
-                               handle_help()
-                               continue
+            if command == 'help':
+                handle_help()
+                continue
 
-                       if command == 'set':
-                               parts.pop(0)
-                               handle_set(parts)
+            if command == 'set':
+                parts.pop(0)
+                handle_set(parts)
 
-                       if command == 'get':
-                               parts.pop(0)
-                               handle_get(parts)
+            if command == 'get':
+                parts.pop(0)
+                handle_get(parts)
 
 
 
-               except KeyboardInterrupt:
-                       print ""
+        except KeyboardInterrupt:
+            print ""
 
-               except EOFError:
-                       print "exiting..."
-                       sys.exit(0)
+        except EOFError:
+            print "exiting..."
+            sys.exit(0)
 
 
 # -------------------------------------------------------------------
 # Set env variables to control behavior
 # -------------------------------------------------------------------
 def handle_set(parts):
-       m = re.compile('(.*)=(.*)').match(parts[0])
-       key = m.group(1)
-       val = m.group(2)
-       set_var(key, val)
-       print "%s = %s" % (key, val)
+    m = re.compile('(.*)=(.*)').match(parts[0])
+    key = m.group(1)
+    val = m.group(2)
+    set_var(key, val)
+    print "%s = %s" % (key, val)
 
 def handle_get(parts):
-       try:
-               print get_var(parts[0])
-       except:
-               print ""
+    try:
+        print get_var(parts[0])
+    except:
+        print ""
 
 
 # -------------------------------------------------------------------
 # Prints help info
 # -------------------------------------------------------------------
 def handle_help():
-       print """
+    print """
   help
     - show this menu
 
@@ -93,88 +93,88 @@ def handle_help():
   Environment variables:
     SRFSH_OUTPUT = pretty - print pretty JSON and key/value pairs for network objects
                  = raw - print formatted JSON 
-       """
+    """
 
-               
+        
 
 
 # -------------------------------------------------------------------
 # performs an opesnrf request
 # -------------------------------------------------------------------
 def handle_request(parts):
-       service = parts.pop(0)
-       method = parts.pop(0)
-       jstr = '[%s]' % join(parts)
-       params = None
+    service = parts.pop(0)
+    method = parts.pop(0)
+    jstr = '[%s]' % join(parts)
+    params = None
 
-       try:
-               params = osrfJSONToObject(jstr)
-       except:
-               print "Error parsing JSON: %s" % jstr
-               return
+    try:
+        params = osrfJSONToObject(jstr)
+    except:
+        print "Error parsing JSON: %s" % jstr
+        return
 
-       ses = osrfClientSession(service)
+    ses = osrfClientSession(service)
 
-       end = None
-       start = time.time()
+    end = None
+    start = time.time()
 
-       req = ses.request2(method, tuple(params))
+    req = ses.request2(method, tuple(params))
 
 
-       while True:
-               resp = req.recv(timeout=120)
-               if not end:
-                       total = time.time() - start
-               if not resp: break
+    while True:
+        resp = req.recv(timeout=120)
+        if not end:
+            total = time.time() - start
+        if not resp: break
 
-               otp = get_var('SRFSH_OUTPUT')
-               if otp == 'pretty':
-                       print osrfDebugNetworkObject(resp.content())
-               else:
-                       print osrfFormatJSON(osrfObjectToJSON(resp.content()))
+        otp = get_var('SRFSH_OUTPUT')
+        if otp == 'pretty':
+            print "\n" + osrfDebugNetworkObject(resp.content())
+        else:
+            print osrfFormatJSON(osrfObjectToJSON(resp.content()))
 
-       req.cleanup()
-       ses.cleanup()
+    req.cleanup()
+    ses.cleanup()
 
-       print '-'*60
-       print "Total request time: %f" % total
-       print '-'*60
+    print '-'*60
+    print "Total request time: %f" % total
+    print '-'*60
 
 
 def handle_math_bench(parts):
 
-       count = int(parts.pop(0))
-       ses = osrfClientSession('opensrf.math')
-       times = []
-
-       for i in range(100):
-               if i % 10: sys.stdout.write('.')
-               else: sys.stdout.write( str( i / 10 ) )
-       print "";
-
-
-       for i in range(count):
-       
-               starttime = time.time()
-               req = ses.request('add', 1, 2)
-               resp = req.recv(timeout=2)
-               endtime = time.time()
-       
-               if resp.content() == 3:
-                       sys.stdout.write("+")
-                       sys.stdout.flush()
-                       times.append( endtime - starttime )
-               else:
-                       print "What happened? %s" % str(resp.content())
-       
-               req.cleanup()
-               if not ( (i+1) % 100):
-                       print ' [%d]' % (i+1)
-       
-       ses.cleanup()
-       total = 0
-       for i in times: total += i
-       print "\naverage time %f" % (total / len(times))
+    count = int(parts.pop(0))
+    ses = osrfClientSession('opensrf.math')
+    times = []
+
+    for i in range(100):
+        if i % 10: sys.stdout.write('.')
+        else: sys.stdout.write( str( i / 10 ) )
+    print "";
+
+
+    for i in range(count):
+    
+        starttime = time.time()
+        req = ses.request('add', 1, 2)
+        resp = req.recv(timeout=2)
+        endtime = time.time()
+    
+        if resp.content() == 3:
+            sys.stdout.write("+")
+            sys.stdout.flush()
+            times.append( endtime - starttime )
+        else:
+            print "What happened? %s" % str(resp.content())
+    
+        req.cleanup()
+        if not ( (i+1) % 100):
+            print ' [%d]' % (i+1)
+    
+    ses.cleanup()
+    total = 0
+    for i in times: total += i
+    print "\naverage time %f" % (total / len(times))
 
 
 
@@ -183,87 +183,87 @@ def handle_math_bench(parts):
 # Defines the tab-completion handling and sets up the readline history 
 # -------------------------------------------------------------------
 def setup_readline():
-       class SrfshCompleter(object):
-               def __init__(self, words):
-                       self.words = words
-                       self.prefix = None
-       
-               def complete(self, prefix, index):
-                       if prefix != self.prefix:
-                               # find all words that start with this prefix
-                               self.matching_words = [
-                                       w for w in self.words if w.startswith(prefix)
-                               ]
-                               self.prefix = prefix
-                               try:
-                                       return self.matching_words[index]
-                               except IndexError:
-                                       return None
-       
-       words = 'request', 'help', 'exit', 'quit', 'opensrf.settings', 'opensrf.math', 'set'
-       completer = SrfshCompleter(words)
-       readline.parse_and_bind("tab: complete")
-       readline.set_completer(completer.complete)
-
-       histfile = os.path.join(get_var('HOME'), ".srfsh_history")
-       try:
-           readline.read_history_file(histfile)
-       except IOError:
-               pass
-       atexit.register(readline.write_history_file, histfile)
+    class SrfshCompleter(object):
+        def __init__(self, words):
+            self.words = words
+            self.prefix = None
+    
+        def complete(self, prefix, index):
+            if prefix != self.prefix:
+                # find all words that start with this prefix
+                self.matching_words = [
+                    w for w in self.words if w.startswith(prefix)
+                ]
+                self.prefix = prefix
+                try:
+                    return self.matching_words[index]
+                except IndexError:
+                    return None
+    
+    words = 'request', 'help', 'exit', 'quit', 'opensrf.settings', 'opensrf.math', 'set'
+    completer = SrfshCompleter(words)
+    readline.parse_and_bind("tab: complete")
+    readline.set_completer(completer.complete)
+
+    histfile = os.path.join(get_var('HOME'), ".srfsh_history")
+    try:
+        readline.read_history_file(histfile)
+    except IOError:
+        pass
+    atexit.register(readline.write_history_file, histfile)
 
 def do_connect():
-       file = os.path.join(get_var('HOME'), ".srfsh.xml")
-       print_green("Connecting to opensrf...")
-       osrfConnect(file, 'srfsh')
-       print_red('OK\n')
+    file = os.path.join(get_var('HOME'), ".srfsh.xml")
+    print_green("Connecting to opensrf...")
+    osrfConnect(file, 'srfsh')
+    print_red('OK\n')
 
 def load_plugins():
-       # Load the user defined external plugins
-       # XXX Make this a real module interface, with tab-complete words, commands, etc.
-       plugins = osrfConfigValue('plugins')
-       plugins = osrfConfigValue('plugins.plugin')
-       if not isinstance(plugins, list):
-               plugins = [plugins]
-
-       for module in plugins:
-               name = module['module']
-               init = module['init']
-               print_green("Loading module %s..." % name)
-
-               try:
-                       str = 'from %s import %s\n%s()' % (name, init, init)
-                       exec(str)
-                       print_red('OK\n')
-
-               except Exception, e:
-                       sys.stderr.write("\nError importing plugin %s, with init symbol %s: \n%s\n" % (name, init, e))
+    # Load the user defined external plugins
+    # XXX Make this a real module interface, with tab-complete words, commands, etc.
+    plugins = osrfConfigValue('plugins')
+    plugins = osrfConfigValue('plugins.plugin')
+    if not isinstance(plugins, list):
+        plugins = [plugins]
+
+    for module in plugins:
+        name = module['module']
+        init = module['init']
+        print_green("Loading module %s..." % name)
+
+        try:
+            str = 'from %s import %s\n%s()' % (name, init, init)
+            exec(str)
+            print_red('OK\n')
+
+        except Exception, e:
+            sys.stderr.write("\nError importing plugin %s, with init symbol %s: \n%s\n" % (name, init, e))
 
 def set_vars():
-       if not get_var('SRFSH_OUTPUT'):
-               set_var('SRFSH_OUTPUT', 'pretty')
+    if not get_var('SRFSH_OUTPUT'):
+        set_var('SRFSH_OUTPUT', 'pretty')
 
 
 def set_var(key, val):
-       os.environ[key] = val
+    os.environ[key] = val
 
 
 def get_var(key):
-       try: return os.environ[key]
-       except: return ''
-       
-       
+    try: return os.environ[key]
+    except: return ''
+    
+    
 def print_green(str):
-       sys.stdout.write("\033[01;32m")
-       sys.stdout.write(str)
-       sys.stdout.write("\033[00m")
-       sys.stdout.flush()
+    sys.stdout.write("\033[01;32m")
+    sys.stdout.write(str)
+    sys.stdout.write("\033[00m")
+    sys.stdout.flush()
 
 def print_red(str):
-       sys.stdout.write("\033[01;31m")
-       sys.stdout.write(str)
-       sys.stdout.write("\033[00m")
-       sys.stdout.flush()
+    sys.stdout.write("\033[01;31m")
+    sys.stdout.write(str)
+    sys.stdout.write("\033[00m")
+    sys.stdout.flush()
 
 
 
index e156743..f545b66 100644 (file)
@@ -1,6 +1,6 @@
 #MALLOC_CHECK_=1 # XXX debug only
 
-LDLIBS += -lxml2 -lopensrf -lobjson
+LDLIBS += -lxml2 -lopensrf 
 CFLAGS += -D_ROUTER
 
 all: opensrf_router 
index 4fdc053..3c12745 100644 (file)
@@ -119,7 +119,7 @@ void osrfRouterHandleIncoming( osrfRouter* router ) {
                        /* if the sender is not a trusted server, drop the message */
                        int len = strlen(msg->sender) + 1;
                        char domain[len];
-                       bzero(domain, len);
+                       memset(domain, 0, sizeof(domain));
                        jid_get_domain( msg->sender, domain, len - 1 );
 
                        if(osrfStringArrayContains( router->trustedServers, domain)) 
@@ -150,7 +150,7 @@ int osrfRouterClassHandleIncoming( osrfRouter* router, char* classname, osrfRout
                        /* if the client is not from a trusted domain, drop the message */
                        int len = strlen(msg->sender) + 1;
                        char domain[len];
-                       bzero(domain, len);
+                       memset(domain, 0, sizeof(domain));
                        jid_get_domain( msg->sender, domain, len - 1 );
 
                        if(osrfStringArrayContains( router->trustedClients, domain)) {
@@ -486,7 +486,7 @@ int osrfRouterHandleAppRequest( osrfRouter* router, transport_message* msg ) {
 
        int T = 32;
        osrfMessage* arr[T];
-       memset(arr, 0, );
+       memset(arr, 0, sizeof(arr));
 
        int num_msgs = osrf_message_deserialize( msg->body, arr, T );
        osrfMessage* omsg = NULL;
@@ -520,7 +520,7 @@ int osrfRouterRespondConnect( osrfRouter* router, transport_message* msg, osrfMe
 
        osrfMessage* success = osrf_message_init( STATUS, omsg->thread_trace, omsg->protocol );
 
-       osrfLogDebug( OSRF_LOG_MARK, "router recevied a CONNECT message from %s", msg->sender );
+       osrfLogDebug( OSRF_LOG_MARK, "router received a CONNECT message from %s", msg->sender );
 
        osrf_message_set_status_info( 
                success, "osrfConnectStatus", "Connection Successful", OSRF_STATUS_OK );
index 9b04bd5..efea8b2 100644 (file)
@@ -1,6 +1,6 @@
 # if EXEC_DEFAULT is defined, then srfsh will send all unknown commands to the shell for execution
 
-LDLIBS += -lobjson -lreadline -lxml2 -lopensrf -lncurses 
+LDLIBS += -lreadline -lxml2 -lopensrf -lncurses 
 LDFLAGS        += -DEXEC_DEFAULT
 
 all: srfsh
index 6c4cb54..a659c14 100644 (file)
@@ -47,7 +47,7 @@ static int parse_request( char* request );
 static int handle_router( char* words[] );
 
 /* utility method for print time data */
-/* static int handle_time( char* words[] ); */
+static int handle_time( char* words[] ); 
 
 /* handles app level requests */
 static int handle_request( char* words[], int relay );
@@ -80,8 +80,7 @@ int main( int argc, char* argv[] ) {
        char* home = getenv("HOME");
        int l = strlen(home) + 36;
        char fbuf[l];
-       memset(fbuf, 0, l);
-       sprintf(fbuf,"%s/.srfsh.xml",home);
+       snprintf(fbuf, sizeof(fbuf), "%s/.srfsh.xml", home);
        
        if(!access(fbuf, R_OK)) {
                if( ! osrf_system_bootstrap_client(fbuf, "srfsh") ) {
@@ -182,33 +181,18 @@ int main( int argc, char* argv[] ) {
                write_history(history_file);
 
        free(request);
+       free(login_session);
 
        osrf_system_shutdown();
        return 0;
 }
 
-/*
-static void sig_child_handler( int s ) {
-       child_dead = 1;
-}
-*/
-
-/*
-void sig_int_handler( int s ) {
-       printf("\n");
-       caught_sigint = 1;
-       signal(SIGINT,sig_int_handler);
-}
-*/
-
 static int load_history( void ) {
 
        char* home = getenv("HOME");
        int l = strlen(home) + 24;
        char fbuf[l];
-
-       memset(fbuf, 0, l);
-       sprintf(fbuf,"%s/.srfsh_history",home);
+       snprintf(fbuf, sizeof(fbuf), "%s/.srfsh_history", home);
        history_file = strdup(fbuf);
 
        if(!access(history_file, W_OK | R_OK )) {
@@ -279,10 +263,8 @@ static int parse_request( char* request ) {
        if( !strcmp(words[0],"router") ) 
                ret_val = handle_router( words );
 
-       /*
        else if( !strcmp(words[0],"time") ) 
                ret_val = handle_time( words );
-               */
 
        else if (!strcmp(words[0],"request"))
                ret_val = handle_request( words, 0 );
@@ -337,14 +319,14 @@ static int handle_introspect(char* words[]) {
                static const char text[] = "request %s opensrf.system.method %s";
                len = sizeof( text ) + strlen( words[1] ) + strlen( words[2] );
                char buf[len];
-               sprintf( buf, text, words[1], words[2] );
+               snprintf( buf, sizeof(buf), text, words[1], words[2] );
                return parse_request( buf );
 
        } else {
                static const char text[] = "request %s opensrf.system.method.all";
                len = sizeof( text ) + strlen( words[1] );
                char buf[len];
-               sprintf( buf, text, words[1] );
+               snprintf( buf, sizeof(buf), text, words[1] );
                return parse_request( buf );
 
        }
@@ -363,17 +345,14 @@ static int handle_login( char* words[]) {
                int orgloci = (orgloc) ? atoi(orgloc) : 0;
                if(!type) type = "opac";
 
-               char buf[256];
-               memset(buf,0,256);
-
-               char buf2[256];
-               memset(buf2,0,256);
+               char login_text[] = "request open-ils.auth open-ils.auth.authenticate.init \"%s\"";
+               size_t len = sizeof( login_text ) + strlen(username) + 1;
 
-               sprintf( buf, 
-                               "request open-ils.auth open-ils.auth.authenticate.init \"%s\"", username );
-               parse_request(buf); 
+               char buf[len];
+               snprintf( buf, sizeof(buf), login_text, username );
+               parse_request(buf);
 
-               char* hash;
+               const char* hash;
                if(last_result && last_result->_result_content) {
                        jsonObject* r = last_result->_result_content;
                        hash = jsonObjectGetString(r);
@@ -382,19 +361,12 @@ static int handle_login( char* words[]) {
 
                char* pass_buf = md5sum(password);
 
-               char both_buf[256];
-               memset(both_buf,0,256);
-               sprintf(both_buf,"%s%s",hash, pass_buf);
+               size_t both_len = strlen( hash ) + strlen( pass_buf ) + 1;
+               char both_buf[both_len];
+               snprintf(both_buf, sizeof(both_buf), "%s%s", hash, pass_buf);
 
                char* mess_buf = md5sum(both_buf);
 
-               /*
-               sprintf( buf2, "request open-ils.auth open-ils.auth.authenticate.complete "
-                               "{ \"username\" : \"%s\", \"password\" : \"%s\", "
-                               "\"type\" : \"%s\", \"org\" : %d, \"workstation\": \"%s\"}", 
-                               username, mess_buf, type, orgloci, workstation );
-                               */
-
                growing_buffer* argbuf = buffer_init(64);
                buffer_fadd(argbuf, 
                                "request open-ils.auth open-ils.auth.authenticate.complete "
@@ -411,19 +383,26 @@ static int handle_login( char* words[]) {
                parse_request( argbuf->buf );
                buffer_free(argbuf);
 
-               jsonObject* x = last_result->_result_content;
+               if( login_session != NULL )
+                       free( login_session );
+
+               const jsonObject* x = last_result->_result_content;
                double authtime = 0;
                if(x) {
-                       char* authtoken = jsonObjectGetString(
-                                       jsonObjectGetKey(jsonObjectGetKey(x,"payload"), "authtoken"));
+                       const char* authtoken = jsonObjectGetString(
+                                       jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtoken"));
                        authtime  = jsonObjectGetNumber(
-                                       jsonObjectGetKey(jsonObjectGetKey(x,"payload"), "authtime"));
-                       if(authtoken) login_session = strdup(authtoken);
-                       else login_session = NULL;
+                                       jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtime"));
+
+                       if(authtoken)
+                               login_session = strdup(authtoken);
+                       else
+                               login_session = NULL;
                }
                else login_session = NULL;
 
-               printf("Login Session: %s.  Session timeout: %f\n", login_session, authtime );
+               printf("Login Session: %s.  Session timeout: %f\n",
+                          (login_session ? login_session : "(none)"), authtime );
                
                return 1;
 
@@ -487,8 +466,19 @@ static int handle_print( char* words[]) {
                        }
                }
 
+               if(!strcmp(variable,"raw_print")) {
+                       if(raw_print) {
+                               printf("raw_print = true\n");
+                               return 1;
+                       } else {
+                               printf("raw_print = false\n");
+                               return 1;
+                       }
+               }
+
                if(!strcmp(variable,"login")) {
-                       printf("login session = %s\n", login_session );
+                       printf("login session = %s\n",
+                                  login_session ? login_session : "(none)" );
                        return 1;
                }
 
@@ -558,7 +548,7 @@ int send_request( char* server,
        jsonObject* params = NULL;
        if( !relay ) {
                if( buffer != NULL && buffer->n_used > 0 ) 
-                       params = json_parse_string(buffer->buf);
+                       params = jsonParseString(buffer->buf);
        } else {
                if(!last_result || ! last_result->_result_content) { 
                        printf("We're not going to call 'relay' with no result params\n");
@@ -616,22 +606,25 @@ int send_request( char* server,
        
                                char* content;
        
-                               if( pretty_print && omsg->_result_content ) {
+                               if( pretty_print ) {
                                        char* j = jsonObjectToJSON(omsg->_result_content);
                                        //content = json_printer(j); 
                                        content = jsonFormatString(j);
                                        free(j);
-                               } else
-                                       content = jsonObjectGetString(omsg->_result_content);
-       
+                               } else {
+                                       const char * temp_content = jsonObjectGetString(omsg->_result_content);
+                                       if( ! temp_content )
+                                               temp_content = "[null]";
+                                       content = strdup( temp_content );
+                               }
+                               
                                printf( "\nReceived Data: %s\n", content ); 
                                free(content);
        
                        } else {
 
                                char code[16];
-                               memset(code, 0, 16);
-                               sprintf( code, "%d", omsg->status_code );
+                               snprintf( code, sizeof(code), "%d", omsg->status_code );
                                buffer_add( resp_buffer, code );
 
                                printf( "\nReceived Exception:\nName: %s\nStatus: %s\nStatus: %s\n", 
@@ -654,9 +647,14 @@ int send_request( char* server,
                                        //content = json_printer(j); 
                                        content = jsonFormatString(j);
                                        free(j);
-                               } else
-                                       content = jsonObjectGetString(omsg->_result_content);
-       
+                               } else {
+                                       const char * temp_content = jsonObjectGetString(omsg->_result_content);
+                                       if( temp_content )
+                                               content = strdup( temp_content );
+                                       else
+                                               content = NULL;
+                               }
+
                                buffer_add( resp_buffer, "\nReceived Data: " ); 
                                buffer_add( resp_buffer, content );
                                buffer_add( resp_buffer, "\n" );
@@ -670,8 +668,7 @@ int send_request( char* server,
                                buffer_add( resp_buffer, omsg->status_text );
                                buffer_add( resp_buffer, "\nStatus: " );
                                char code[16];
-                               memset(code, 0, 16);
-                               sprintf( code, "%d", omsg->status_code );
+                               snprintf( code, sizeof(code), "%d", omsg->status_code );
                                buffer_add( resp_buffer, code );
                        }
                }
@@ -705,30 +702,15 @@ int send_request( char* server,
 
 }
 
-/*
 static int handle_time( char* words[] ) {
-
-       if( ! words[1] ) {
-
-               char buf[36];
-               memset(buf,0,36);
-               get_timestamp(buf);
-               printf( "%s\n", buf );
-               return 1;
-       }
-
-       if( words[1] ) {
-               time_t epoch = (time_t)atoi( words[1] );
-               char* localtime = strdup( ctime( &epoch ) );
-               printf( "%s => %s", words[1], localtime );
-               free(localtime);
-               return 1;
-       }
-
-       return 0;
-
+       if(!words[1]) {
+               printf("%f\n", get_timestamp_millis());
+    } else {
+        time_t epoch = (time_t) atoi(words[1]);
+               printf("%s", ctime(&epoch));
+    }
+       return 1;
 }
-*/
 
                
 
@@ -737,9 +719,10 @@ static int router_query_servers( const char* router_server ) {
        if( ! router_server || strlen(router_server) == 0 ) 
                return 0;
 
-       char rbuf[256];
-       memset(rbuf,0,256);
-       sprintf(rbuf,"router@%s/router", router_server );
+       const static char router_text[] = "router@%s/router";
+       size_t len = sizeof( router_text ) + strlen( router_server ) + 1;
+       char rbuf[len];
+       snprintf(rbuf, sizeof(rbuf), router_text, router_server );
                
        transport_message* send = 
                message_init( "servers", NULL, NULL, rbuf, NULL );
@@ -775,47 +758,47 @@ static int print_help( void ) {
                        "---------------------------------------------------------------------------------\n"
                        "Commands:\n"
                        "---------------------------------------------------------------------------------\n"
-                       "help                   - Display this message\n"
-                       "!<command> [args] - Forks and runs the given command in the shell\n"
+                       "help                   - Display this message\n"
+                       "!<command> [args]      - Forks and runs the given command in the shell\n"
                /*
                        "time                   - Prints the current time\n"
-                       "time <timestamp>       - Formats seconds since epoch into readable format\n"   
+                       "time <timestamp>       - Formats seconds since epoch into readable format\n"
                */
                        "set <variable> <value> - set a srfsh variable (e.g. set pretty_print true )\n"
-                       "print <variable>               - Displays the value of a srfsh variable\n"
+                       "print <variable>       - Displays the value of a srfsh variable\n"
                        "---------------------------------------------------------------------------------\n"
 
                        "router query servers <server1 [, server2, ...]>\n"
-                       "       - Returns stats on connected services\n"
+                       "       - Returns stats on connected services\n"
                        "\n"
                        "\n"
                        "request <service> <method> [ <json formatted string of params> ]\n"
-                       "       - Anything passed in will be wrapped in a json array,\n"
-                       "               so add commas if there is more than one param\n"
+                       "       - Anything passed in will be wrapped in a json array,\n"
+                       "               so add commas if there is more than one param\n"
                        "\n"
                        "\n"
                        "relay <service> <method>\n"
-                       "       - Performs the requested query using the last received result as the param\n"
+                       "       - Performs the requested query using the last received result as the param\n"
                        "\n"
                        "\n"
                        "math_bench <num_batches> [0|1|2]\n"
-                       "       - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
-                       "                2 means reconnect after every request\n"
+                       "       - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
+                       "                2 means reconnect after every request\n"
                        "\n"
                        "introspect <service>\n"
-                       "       - prints the API for the service\n"
+                       "       - prints the API for the service\n"
                        "\n"
                        "\n"
                        "---------------------------------------------------------------------------------\n"
                        " Commands for Open-ILS\n"
                        "---------------------------------------------------------------------------------\n"
                        "login <username> <password>\n"
-                       "       -       Logs into the 'server' and displays the session id\n"
-                       "       - To view the session id later, enter: print login\n"
+                       "       - Logs into the 'server' and displays the session id\n"
+                       "       - To view the session id later, enter: print login\n"
                        "---------------------------------------------------------------------------------\n"
                        "\n"
                        "\n"
-                       "Note: long output is piped through 'less'.  To search in 'less', type: /<search>\n"
+                       "Note: long output is piped through 'less'. To search in 'less', type: /<search>\n"
                        "---------------------------------------------------------------------------------\n"
                        "\n"
                        );
@@ -850,7 +833,7 @@ static int do_math( int count, int style ) {
        osrf_app_session* session = osrf_app_client_session_init(  "opensrf.math" );
        osrf_app_session_connect(session);
 
-       jsonObject* params = json_parse_string("[]");
+       jsonObject* params = jsonParseString("[]");
        jsonObjectPush(params,jsonNewObject("1"));
        jsonObjectPush(params,jsonNewObject("2"));
 
@@ -858,7 +841,7 @@ static int do_math( int count, int style ) {
        char* answers[] = { "3", "-1", "2", "0.500000" };
 
        float times[ count * 4 ];
-       memset(times,0,count*4);
+       memset(times, 0, sizeof(times));
 
        int k;
        for(k=0;k!=100;k++) {