Merged libopensrf source directories (libtransport, libstack, and utils) into a singl...
authorerickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Thu, 21 Jun 2007 16:19:20 +0000 (16:19 +0000)
committererickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Thu, 21 Jun 2007 16:19:20 +0000 (16:19 +0000)
Moved opensrf headers to trunk/include/opensrf
Moved objson headers to trunk/include/objson
Updated #include's throughout to be fully qualified.  e.g. <opensrf/utils.h>, <objson/object.h>
Removed old, unused trunk/src/xinclude code
Cleaned up Makefiles to support new directory layout

git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@957 9efc2488-bf62-4759-914b-345cdb29e865

127 files changed:
Makefile
include/objson/json2xml.h [new file with mode: 0644]
include/objson/json_parser.h [new file with mode: 0644]
include/objson/object.h [new file with mode: 0644]
include/objson/xml2json.h [new file with mode: 0644]
include/opensrf/log.h [new file with mode: 0644]
include/opensrf/md5.h [new file with mode: 0644]
include/opensrf/osrfConfig.h [new file with mode: 0644]
include/opensrf/osrf_app_session.h [new file with mode: 0644]
include/opensrf/osrf_application.h [new file with mode: 0644]
include/opensrf/osrf_big_hash.h [new file with mode: 0644]
include/opensrf/osrf_big_list.h [new file with mode: 0644]
include/opensrf/osrf_cache.h [new file with mode: 0644]
include/opensrf/osrf_hash.h [new file with mode: 0644]
include/opensrf/osrf_list.h [new file with mode: 0644]
include/opensrf/osrf_message.h [new file with mode: 0644]
include/opensrf/osrf_prefork.h [new file with mode: 0644]
include/opensrf/osrf_settings.h [new file with mode: 0644]
include/opensrf/osrf_stack.h [new file with mode: 0644]
include/opensrf/osrf_system.h [new file with mode: 0644]
include/opensrf/osrf_transgroup.h [new file with mode: 0644]
include/opensrf/sha.h [new file with mode: 0644]
include/opensrf/socket_bundle.h [new file with mode: 0644]
include/opensrf/string_array.h [new file with mode: 0644]
include/opensrf/transport_client.h [new file with mode: 0644]
include/opensrf/transport_message.h [new file with mode: 0644]
include/opensrf/transport_session.h [new file with mode: 0644]
include/opensrf/utils.h [new file with mode: 0644]
include/opensrf/xml_utils.h [new file with mode: 0644]
src/Makefile
src/libopensrf/basic_client.c [new file with mode: 0644]
src/libopensrf/component.c [new file with mode: 0644]
src/libopensrf/log.c [new file with mode: 0644]
src/libopensrf/md5.c [new file with mode: 0644]
src/libopensrf/opensrf.c [new file with mode: 0644]
src/libopensrf/osrfConfig.c [new file with mode: 0644]
src/libopensrf/osrf_app_session.c [new file with mode: 0644]
src/libopensrf/osrf_application.c [new file with mode: 0644]
src/libopensrf/osrf_big_hash.c [new file with mode: 0644]
src/libopensrf/osrf_big_list.c [new file with mode: 0644]
src/libopensrf/osrf_cache.c [new file with mode: 0644]
src/libopensrf/osrf_hash.c [new file with mode: 0644]
src/libopensrf/osrf_list.c [new file with mode: 0644]
src/libopensrf/osrf_message.c [new file with mode: 0644]
src/libopensrf/osrf_prefork.c [new file with mode: 0644]
src/libopensrf/osrf_settings.c [new file with mode: 0644]
src/libopensrf/osrf_stack.c [new file with mode: 0644]
src/libopensrf/osrf_system.c [new file with mode: 0644]
src/libopensrf/osrf_transgroup.c [new file with mode: 0644]
src/libopensrf/sha.c [new file with mode: 0644]
src/libopensrf/socket_bundle.c [new file with mode: 0644]
src/libopensrf/socket_test.c [new file with mode: 0644]
src/libopensrf/string_array.c [new file with mode: 0644]
src/libopensrf/transport_client.c [new file with mode: 0644]
src/libopensrf/transport_message.c [new file with mode: 0644]
src/libopensrf/transport_session.c [new file with mode: 0644]
src/libopensrf/utils.c [new file with mode: 0644]
src/libopensrf/xml_utils.c [new file with mode: 0644]
src/libstack/Makefile [deleted file]
src/libstack/opensrf.c [deleted file]
src/libstack/osrfConfig.c [deleted file]
src/libstack/osrfConfig.h [deleted file]
src/libstack/osrf_app_session.c [deleted file]
src/libstack/osrf_app_session.h [deleted file]
src/libstack/osrf_application.c [deleted file]
src/libstack/osrf_application.h [deleted file]
src/libstack/osrf_big_hash.c [deleted file]
src/libstack/osrf_big_hash.h [deleted file]
src/libstack/osrf_big_list.c [deleted file]
src/libstack/osrf_big_list.h [deleted file]
src/libstack/osrf_cache.c [deleted file]
src/libstack/osrf_cache.h [deleted file]
src/libstack/osrf_hash.c [deleted file]
src/libstack/osrf_hash.h [deleted file]
src/libstack/osrf_list.c [deleted file]
src/libstack/osrf_list.h [deleted file]
src/libstack/osrf_message.c [deleted file]
src/libstack/osrf_message.h [deleted file]
src/libstack/osrf_prefork.c [deleted file]
src/libstack/osrf_prefork.h [deleted file]
src/libstack/osrf_settings.c [deleted file]
src/libstack/osrf_settings.h [deleted file]
src/libstack/osrf_stack.c [deleted file]
src/libstack/osrf_stack.h [deleted file]
src/libstack/osrf_system.c [deleted file]
src/libstack/osrf_system.h [deleted file]
src/libstack/osrf_transgroup.c [deleted file]
src/libstack/osrf_transgroup.h [deleted file]
src/libtransport/Makefile [deleted file]
src/libtransport/basic_client.c [deleted file]
src/libtransport/component.c [deleted file]
src/libtransport/transport_client.c [deleted file]
src/libtransport/transport_client.h [deleted file]
src/libtransport/transport_message.c [deleted file]
src/libtransport/transport_message.h [deleted file]
src/libtransport/transport_session.c [deleted file]
src/libtransport/transport_session.h [deleted file]
src/objson/Makefile
src/objson/json2xml.c
src/objson/json2xml.h [deleted file]
src/objson/json_parser.c
src/objson/json_parser.h [deleted file]
src/objson/object.c
src/objson/object.h [deleted file]
src/objson/objson_test.c
src/objson/xml2json.c
src/objson/xml2json.h [deleted file]
src/srfsh/srfsh.h
src/utils/Makefile [deleted file]
src/utils/fieldmapper-c.pl [deleted file]
src/utils/log.c [deleted file]
src/utils/log.h [deleted file]
src/utils/md5.c [deleted file]
src/utils/md5.h [deleted file]
src/utils/sha.c [deleted file]
src/utils/sha.h [deleted file]
src/utils/socket_bundle.c [deleted file]
src/utils/socket_bundle.h [deleted file]
src/utils/socket_test.c [deleted file]
src/utils/string_array.c [deleted file]
src/utils/string_array.h [deleted file]
src/utils/utils.c [deleted file]
src/utils/utils.h [deleted file]
src/utils/xml_utils.c [deleted file]
src/utils/xml_utils.h [deleted file]
src/xinclude/Makefile [deleted file]
src/xinclude/mod_xinclude.c [deleted file]

index a23c57c..8441861 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,18 @@
 all:
-       @source install.conf && make -s -C src all
+       @echo -e "\n * Run 'make verbose' to see full make output\n"
+       source install.conf && make -s -C src all
+
+verbose:
+       source install.conf && make -C src all
 
 jserver:
-       @source install.conf && make -s -C src jserver
+       source install.conf && make -s -C src jserver
 
 install:
-       @source install.conf && make -s -C src install
+       source install.conf && make -s -C src install
 
 jserver-install:
-       @source install.conf && make -s -C src jserver-install
+       source install.conf && make -s -C src jserver-install
 
 clean:
-       @make -s -C src clean
+       make -s -C src clean
diff --git a/include/objson/json2xml.h b/include/objson/json2xml.h
new file mode 100644 (file)
index 0000000..9eaa1c0
--- /dev/null
@@ -0,0 +1,11 @@
+
+#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*);
+
diff --git a/include/objson/json_parser.h b/include/objson/json_parser.h
new file mode 100644 (file)
index 0000000..ede5d91
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+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 <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.  
+ */
+
+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);
+
+/* returns true if c is 0-9 */
+int is_number(char c);
+
+int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+
+#endif
diff --git a/include/objson/object.h b/include/objson/object.h
new file mode 100644 (file)
index 0000000..8d62c1a
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+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)
+ */
+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 );
+
+/* ------------------------------------------------------------------------ */
+
+
+#endif
+
+
diff --git a/include/objson/xml2json.h b/include/objson/xml2json.h
new file mode 100644 (file)
index 0000000..a9ded67
--- /dev/null
@@ -0,0 +1,19 @@
+
+#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);
+
+
+
diff --git a/include/opensrf/log.h b/include/opensrf/log.h
new file mode 100644 (file)
index 0000000..4aafad3
--- /dev/null
@@ -0,0 +1,80 @@
+#include <opensrf/utils.h>
+
+#include <syslog.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#ifndef OSRF_LOG_INCLUDED
+#define OSRF_LOG_INCLUDED
+
+/* log levels */
+#define OSRF_LOG_ERROR 1
+#define OSRF_LOG_WARNING 2
+#define OSRF_LOG_INFO 3
+#define OSRF_LOG_DEBUG 4
+#define OSRF_LOG_INTERNAL 5
+#define OSRF_LOG_ACTIVITY -1
+
+#define OSRF_LOG_TYPE_FILE 1
+#define OSRF_LOG_TYPE_SYSLOG 2
+
+#define OSRF_LOG_MARK __FILE__, __LINE__
+
+
+/* Initializes the logger. */
+void osrfLogInit( int type, const char* appname, int maxlevel );
+
+/** Sets the systlog facility for the regular logs */
+void osrfLogSetSyslogFacility( int facility );
+
+/** Sets the systlog facility for the activity logs */
+void osrfLogSetSyslogActFacility( int facility );
+
+/** Sets the log file to use if we're logging to a file */
+void osrfLogSetFile( const char* logfile );
+
+/* once we know which application we're running, call this method to
+ * set the appname so log lines can include the app name */
+void osrfLogSetAppname( const char* appname );
+
+/** Set or Get the global log level.  Any log statements with a higher level
+ * than "level" will not be logged */
+void osrfLogSetLevel( int loglevel );
+int osrfLogGetLevel( void );
+
+/* Log an error message */
+void osrfLogError( const char* file, int line, const char* msg, ... );
+
+/* Log a warning message */
+void osrfLogWarning( const char* file, int line, const char* msg, ... );
+
+/* log an info message */
+void osrfLogInfo( const char* file, int line, const char* msg, ... );
+
+/* Log a debug message */
+void osrfLogDebug( const char* file, int line, const char* msg, ... );
+
+/* Log an internal debug message */
+void osrfLogInternal( const char* file, int line, const char* msg, ... );
+
+/* Log an activity message */
+void osrfLogActivity( const char* file, int line, const char* msg, ... );
+
+void osrfLogCleanup();
+
+void osrfLogClearXid();
+void osrfLogSetXid(char* xid);
+void osrfLogMkXid();
+void osrfLogSetIsClient(int is);
+char* osrfLogGetXid();
+
+/* sets the activity flag */
+void osrfLogSetActivityEnabled( int enabled );
+
+/* returns the int representation of the log facility based on the facility name
+ * if the facility name is invalid, LOG_LOCAL0 is returned 
+ */
+int osrfLogFacilityToInt( char* facility );
+
+#endif
diff --git a/include/opensrf/md5.h b/include/opensrf/md5.h
new file mode 100644 (file)
index 0000000..53dd2b1
--- /dev/null
@@ -0,0 +1,35 @@
+/* --- The MD5 routines --- */
+
+/* MD5 routines, after Ron Rivest */
+/* Written by David Madore <david.madore@ens.fr>, with code taken in
+ * part from Colin Plumb. */
+/* Public domain (1999/11/24) */
+
+/* Note: these routines do not depend on endianness. */
+
+/* === The header === */
+
+/* Put this in md5.h if you don't like having everything in one big
+ * file. */
+
+#ifndef _DMADORE_MD5_H
+#define _DMADORE_MD5_H
+
+struct md5_ctx {
+  /* The four chaining variables */
+  unsigned long buf[4];
+  /* Count number of message bits */
+  unsigned long bits[2];
+  /* Data being fed in */
+  unsigned long in[16];
+  /* Our position within the 512 bits (always between 0 and 63) */
+  int b;
+};
+
+void MD5_transform (unsigned long buf[4], const unsigned long in[16]);
+void MD5_start (struct md5_ctx *context);
+void MD5_feed (struct md5_ctx *context, unsigned char inb);
+void MD5_stop (struct md5_ctx *context, unsigned char digest[16]);
+
+#endif /* not defined _DMADORE_MD5_H */
+
diff --git a/include/opensrf/osrfConfig.h b/include/opensrf/osrfConfig.h
new file mode 100644 (file)
index 0000000..75dbcfd
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+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.
+*/
+
+#ifndef _OSRF_CONFIG_H
+#define _OSRF_CONFIG_H
+
+#include <opensrf/xml_utils.h>
+#include <opensrf/utils.h>
+#include <opensrf/string_array.h>
+#include <objson/object.h>
+
+typedef struct {
+       jsonObject* config;
+       char* configContext;
+} osrfConfig;
+
+
+/**
+       Parses a new config file.  Caller is responsible for freeing the returned
+               config object when finished.  
+       @param configFile The XML config file to parse.
+       @param configContext Optional root of the subtree in the config file where 
+       we will look for values. If it's not provided,  searches will be 
+       performed from the root of the config file
+       @return The config object if the file parses successfully.  Otherwise
+               it returns NULL;
+*/
+osrfConfig* osrfConfigInit(char* configFile, char* configContext);
+
+/**
+       @return True if we have a default config defined
+*/
+int osrfConfigHasDefaultConfig();
+
+/**
+       Replaces the config object's objson object.  This is useful
+       if you have an ojbson object already and not an XML config
+       file to parse.
+       @param cfg The config object to alter
+       @param obj The objson objet to use when searching values
+*/
+void osrfConfigReplaceConfig(osrfConfig* cfg, const jsonObject* obj);
+
+/** Deallocates a config object 
+       @param cfg The config object to free
+*/
+void osrfConfigFree(osrfConfig* cfg);
+
+
+/* Assigns the default config file.  This file will be used whenever
+       NULL is passed to config retrieval functions 
+       @param cfg The config object to use as the default config
+*/
+void osrfConfigSetDefaultConfig(osrfConfig* cfg);
+
+/* frees the default config if one exists */
+void osrfConfigCleanup();
+
+
+/** 
+       Returns the value in the config found at 'path'.
+       If the value found at 'path' is a long or a double,
+       the value is stringified and then returned.
+       The caller must free the returned char* 
+
+       if there is a configContext, then it will be appended to 
+       the front of the path like so: //<configContext>/<path>
+       if no configContext was provided to osfConfigSetFile, then 
+       the path is interpreted literally.
+       @param cfg The config file to search or NULL if the default
+               config should be used
+       @param path The search path
+*/
+char* osrfConfigGetValue(osrfConfig* cfg, char* path, ...);
+
+/** 
+       Puts the list of values found at 'path' into the pre-allocated 
+       string array.  
+       Note that the config node found at 'path' must be an array.
+       @param cfg The config file to search or NULL if the default
+               config should be used
+       @param arr An allocated string_array where the values will
+               be stored
+       @param path The search path
+       @return the number of values added to the string array;
+*/
+
+int osrfConfigGetValueList(osrfConfig* cfg, osrfStringArray* arr, char* path, ...);
+
+
+#endif
diff --git a/include/opensrf/osrf_app_session.h b/include/opensrf/osrf_app_session.h
new file mode 100644 (file)
index 0000000..aae373c
--- /dev/null
@@ -0,0 +1,234 @@
+#ifndef _OSRF_APP_SESSION
+#define _OSRF_APP_SESSION
+
+#include <opensrf/transport_client.h>
+#include <opensrf/osrf_message.h>
+#include <opensrf/osrf_system.h>
+#include <opensrf/string_array.h>
+#include <opensrf/osrfConfig.h>
+#include <opensrf/osrf_hash.h>
+#include <opensrf/osrf_list.h>
+
+#include <objson/object.h>
+#include <objson/json_parser.h>
+
+
+
+#define        DEF_RECV_TIMEOUT 6 /* receive timeout */
+#define        DEF_QUEUE_SIZE  
+
+enum OSRF_SESSION_STATE { OSRF_SESSION_CONNECTING, OSRF_SESSION_CONNECTED, OSRF_SESSION_DISCONNECTED };
+enum OSRF_SESSION_TYPE { OSRF_SESSION_SERVER, OSRF_SESSION_CLIENT };
+
+/* entry point for data into the stack.  gets set in osrf_stack.c */
+int (*osrf_stack_entry_point) (transport_client* client, int timeout, int* recvd );
+
+struct osrf_app_request_struct {
+       /** Our controlling session */
+       struct osrf_app_session_struct* session;
+
+       /** our "id" */
+       int request_id;
+       /** True if we have received a 'request complete' message from our request */
+       int complete;
+       /** Our original request payload */
+       osrf_message* payload; 
+       /** List of responses to our request */
+       osrf_message* result;
+
+       /* if set to true, then a call that is waiting on a response, will reset the 
+               timeout and set this variable back to false */
+       int reset_timeout;
+};
+typedef struct osrf_app_request_struct osrf_app_request;
+typedef struct osrf_app_request_struct osrfAppRequest;
+
+struct osrf_app_session_struct {
+
+       /** Our messag passing object */
+       transport_client* transport_handle;
+       /** Cache of active app_request objects */
+
+       //osrf_app_request* request_queue;
+
+       osrfList* request_queue;
+
+       /** The original remote id of the remote service we're talking to */
+       char* orig_remote_id;
+       /** The current remote id of the remote service we're talking to */
+       char* remote_id;
+
+       /** Who we're talking to if we're a client.  
+               what app we're serving if we're a server */
+       char* remote_service;
+
+       /** The current request thread_trace */
+       int thread_trace;
+       /** Our ID */
+       char* session_id;
+
+       /* true if this session does not require connect messages */
+       int stateless;
+
+       /** The connect state */
+       enum OSRF_SESSION_STATE state;
+
+       /** SERVER or CLIENT */
+       enum OSRF_SESSION_TYPE type;
+
+       /* let the user use the session to store their own session data */
+       void* userData;
+
+       void (*userDataFree) (void*);
+};
+typedef struct osrf_app_session_struct osrf_app_session;
+typedef struct osrf_app_session_struct osrfAppSession;
+
+
+
+// -------------------------------------------------------------------------- 
+// PUBLIC API ***
+// -------------------------------------------------------------------------- 
+
+/** Allocates a initializes a new app_session */
+osrf_app_session* osrfAppSessionClientInit( char* remote_service );
+osrf_app_session* osrf_app_client_session_init( char* remote_service );
+
+/** Allocates and initializes a new server session.  The global session cache
+  * is checked to see if this session already exists, if so, it's returned 
+  */
+osrf_app_session* osrf_app_server_session_init( 
+               char* session_id, char* our_app, char* remote_id );
+
+/** returns a session from the global session hash */
+osrf_app_session* osrf_app_session_find_session( char* session_id );
+
+/** Builds a new app_request object with the given payload andn returns
+  * the id of the request.  This id is then used to perform work on the
+  * requeset.
+  */
+int osrfAppSessionMakeRequest(
+               osrf_app_session* session, jsonObject* params, 
+               char* method_name, int protocol, string_array* param_strings);
+
+int osrf_app_session_make_req( 
+               osrf_app_session* session, jsonObject* params, 
+               char* method_name, int protocol, string_array* param_strings);
+
+/** Sets the given request to complete state */
+void osrf_app_session_set_complete( osrf_app_session* session, int request_id );
+
+/** Returns true if the given request is complete */
+int osrf_app_session_request_complete( osrf_app_session* session, int request_id );
+
+/** Does a recv call on the given request */
+osrf_message* osrfAppSessionRequestRecv(
+               osrf_app_session* session, int request_id, int timeout );
+osrf_message* osrf_app_session_request_recv( 
+               osrf_app_session* session, int request_id, int timeout );
+
+/** Removes the request from the request set and frees the reqest */
+void osrf_app_session_request_finish( osrf_app_session* session, int request_id );
+
+/** Resends the orginal request with the given request id */
+int osrf_app_session_request_resend( osrf_app_session*, int request_id );
+
+/** Resets the remote connection target to that of the original*/
+void osrf_app_session_reset_remote( osrf_app_session* );
+
+/** Sets the remote target to 'remote_id' */
+void osrf_app_session_set_remote( osrf_app_session* session, char* remote_id );
+
+/** pushes the given message into the result list of the app_request
+  * whose request_id matches the messages thread_trace 
+  */
+int osrf_app_session_push_queue( osrf_app_session*, osrf_message* msg );
+
+/** Attempts to connect to the remote service. Returns 1 on successful 
+  * connection, 0 otherwise.
+  */
+int osrf_app_session_connect( osrf_app_session* );
+int osrfAppSessionConnect( osrf_app_session* );
+
+/** Sends a disconnect message to the remote service.  No response is expected */
+int osrf_app_session_disconnect( osrf_app_session* );
+
+/**  Waits up to 'timeout' seconds for some data to arrive.
+  * Any data that arrives will be processed according to its
+  * payload and message type.  This method will return after
+  * any data has arrived.
+  */
+int osrf_app_session_queue_wait( osrf_app_session*, int timeout, int* recvd );
+
+/** Disconnects (if client), frees any attached app_reuqests, removes the session from the 
+  * global session cache and frees the session.  Needless to say, only call this when the
+  * session is completey done.
+  */
+void osrf_app_session_destroy ( osrf_app_session* );
+void osrfAppSessionFree( osrfAppSession* );
+
+
+
+// --------------------------------------------------------------------------
+// --------------------------------------------------------------------------
+// Request functions
+// --------------------------------------------------------------------------
+
+/** Allocations and initializes a new app_request object */
+osrf_app_request* _osrf_app_request_init( osrf_app_session* session, osrf_message* msg );
+
+/** Frees memory used by an app_request object */
+void _osrf_app_request_free( void * req );
+
+/** Pushes the given message onto the list of 'responses' to this request */
+void _osrf_app_request_push_queue( osrf_app_request*, osrf_message* payload );
+
+/** Checks the receive queue for messages.  If any are found, the first
+  * is popped off and returned.  Otherwise, this method will wait at most timeout 
+  * seconds for a message to appear in the receive queue.  Once it arrives it is returned.
+  * If no messages arrive in the timeout provided, null is returned.
+  */
+osrf_message* _osrf_app_request_recv( osrf_app_request* req, int timeout );
+
+/** Resend this requests original request message */
+int _osrf_app_request_resend( osrf_app_request* req );
+
+
+/* tells the request to reset it's wait timeout */
+void osrf_app_session_request_reset_timeout( osrf_app_session* session, int req_id );
+
+// --------------------------------------------------------------------------
+// --------------------------------------------------------------------------
+// Session functions 
+// --------------------------------------------------------------------------
+
+/** Returns the app_request with the given thread_trace (request_id) */
+osrf_app_request* _osrf_app_session_get_request( osrf_app_session*, int thread_trace );
+
+/** frees memory held by a session. Note: We delete all requests in the request list */
+void _osrf_app_session_free( osrf_app_session* );
+
+/** adds a session to the global session cache */
+void _osrf_app_session_push_session( osrf_app_session* );
+
+/** Adds an app_request to the request set */
+void _osrf_app_session_push_request( osrf_app_session*, osrf_app_request* req );
+
+/** Removes an app_request from this session request set, freeing the request object */
+void _osrf_app_session_remove_request( osrf_app_session*, osrf_app_request* req );
+
+/** Send the given message */
+int _osrf_app_session_send( osrf_app_session*, osrf_message* msg );
+
+int osrfAppSessionSendBatch( osrf_app_session*, osrf_message* msgs[], int size );
+
+int osrfAppRequestRespond( osrfAppSession* ses, int requestId, jsonObject* data ); 
+int osrfAppRequestRespondComplete( osrfAppSession* ses, int requestId, jsonObject* data ); 
+
+int osrfAppSessionStatus( osrfAppSession* ses, int type, char* name, int reqId, char* message );
+
+void osrfAppSessionCleanup();
+
+
+
+#endif
diff --git a/include/opensrf/osrf_application.h b/include/opensrf/osrf_application.h
new file mode 100644 (file)
index 0000000..ac548a2
--- /dev/null
@@ -0,0 +1,234 @@
+#include <opensrf/utils.h>
+#include <opensrf/log.h>
+#include <opensrf/osrf_app_session.h>
+#include <opensrf/osrf_hash.h>
+
+#include <objson/object.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+/**
+  All OpenSRF methods take the signature
+  int methodName( osrfMethodContext* );
+  If a negative number is returned, it means an unknown error occured and an exception
+  will be returned to the client automatically.
+  If a positive number is returned, it means that libopensrf should send a 'Request Complete'
+  message following any messages sent by the method.
+  If 0 is returned, it tells libopensrf that the method completed successfully and 
+  there is no need to send any further data to the client.
+  */
+
+
+
+/** 
+  This macro verifies methods receive the correct parameters */
+#define _OSRF_METHOD_VERIFY_CONTEXT(d) \
+       if(!d) return -1; \
+       if(!d->session) { osrfLogError( OSRF_LOG_MARK,  "Session is NULL in app reqeust" ); return -1; }\
+       if(!d->method) { osrfLogError( OSRF_LOG_MARK,  "Method is NULL in app reqeust" ); return -1; }\
+       if(d->method->argc) {\
+               if(!d->params) { osrfLogError( OSRF_LOG_MARK,  "Params is NULL in app reqeust %s", d->method->name ); return -1; }\
+               if( d->params->type != JSON_ARRAY ) { \
+                       osrfLogError( OSRF_LOG_MARK,  "'params' is not a JSON array for method %s", d->method->name);\
+                       return -1; }\
+       }\
+       if( !d->method->name ) { osrfLogError( OSRF_LOG_MARK,  "Method name is NULL"); return -1; } 
+
+#ifdef OSRF_LOG_PARAMS 
+#define OSRF_METHOD_VERIFY_CONTEXT(d) \
+       _OSRF_METHOD_VERIFY_CONTEXT(d); \
+       char* __j = jsonObjectToJSON(d->params);\
+       if(__j) { \
+               osrfLogInfo( OSRF_LOG_MARK,  "CALL:     %s %s - %s", d->session->remote_service, d->method->name, __j);\
+               free(__j); \
+       } 
+#else
+#define OSRF_METHOD_VERIFY_CONTEXT(d) _OSRF_METHOD_VERIFY_CONTEXT(d); 
+#endif
+
+
+
+/* used internally to make sure the method description provided is OK */
+#define OSRF_METHOD_VERIFY_DESCRIPTION(app, d) \
+       if(!app) return -1; \
+       if(!d) return -1;\
+       if(!d->name) { osrfLogError( OSRF_LOG_MARK,  "No method name provided in description" ), return -1; } \
+       if(!d->symbol) { osrfLogError( OSRF_LOG_MARK,  "No method symbol provided in description" ), return -1; } \
+       if(!d->notes) d->notes = ""; \
+       if(!d->paramNotes) d->paramNotes = "";\
+       if(!d->returnNotes) d->returnNotes = "";
+
+
+
+
+/* Some well known parameters */
+#define OSRF_SYSMETHOD_INTROSPECT                              "opensrf.system.method"
+#define OSRF_SYSMETHOD_INTROSPECT_ATOMIC               "opensrf.system.method.atomic"
+#define OSRF_SYSMETHOD_INTROSPECT_ALL                  "opensrf.system.method.all"
+#define OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC   "opensrf.system.method.all.atomic"
+#define OSRF_SYSMETHOD_ECHO                                            "opensrf.system.echo"
+#define OSRF_SYSMETHOD_ECHO_ATOMIC                             "opensrf.system.echo.atomic"
+
+#define OSRF_METHOD_SYSTEM                     1
+#define OSRF_METHOD_STREAMING          2
+#define OSRF_METHOD_ATOMIC                     4
+#define OSRF_METHOD_CACHABLE           8
+
+       
+
+struct _osrfApplicationStruct {
+       void* handle;                                                                   /* the lib handle */
+       osrfHash* methods;
+   void (*onExit) (void);
+};
+typedef struct _osrfApplicationStruct osrfApplication;
+
+
+struct _osrfMethodStruct {
+       char* name;                                     /* the method name */
+       char* symbol;                           /* the symbol name (function) */
+       char* notes;                            /* public method documentation */
+       int argc;                                       /* how many args this method expects */
+       //char* paramNotes;                     /* Description of the params expected for this method */
+       int options;                            /* describes the various options for this method */
+       void* userData;                         /* You can put your weeeeeeed in it ... */
+
+       /*
+       int sysmethod;                          
+       int streaming;                          
+       int atomic;                                     
+       int cachable;                           
+       */
+}; 
+typedef struct _osrfMethodStruct osrfMethod;
+
+struct _osrfMethodContextStruct {
+       osrfAppSession* session;        /* the current session */
+       osrfMethod* method;                     /* the requested method */      
+       jsonObject* params;                     /* the params to the method */
+       int request;                                    /* request id */
+       jsonObject* responses;          /* array of cached responses. */
+};
+typedef struct _osrfMethodContextStruct osrfMethodContext;
+
+
+
+/** 
+  Register an application
+  @param appName The name of the application
+  @param soFile The library (.so) file that implements this application
+  @return 0 on success, -1 on error
+  */
+int osrfAppRegisterApplication( char* appName, char* soFile );
+
+/**
+  Register a method
+  Any method with  the OSRF_METHOD_STREAMING option set will have a ".atomic"
+  version of the method registered automatically
+  @param appName The name of the application that implements the method
+  @param methodName The fully qualified name of the method
+  @param symbolName The symbol name (function) that implements the method
+  @param notes Public documentation for this method.
+  @params argc The number of arguments this method expects 
+  @param streaming True if this is a streaming method that requires an atomic version
+  @return 0 on success, -1 on error
+  */
+int osrfAppRegisterMethod( char* appName, char* methodName, 
+               char* symbolName, char* notes, int argc, int options );
+
+
+int osrfAppRegisterExtendedMethod( char* appName, char* methodName, 
+               char* symbolName, char* notes, int argc, int options, void* );
+
+osrfMethod* _osrfAppBuildMethod( char* methodName, 
+               char* symbolName, char* notes, int argc, int options, void* );
+
+/**
+  Finds the given app in the list of apps
+  @param name The name of the application
+  @return The application pointer or NULL if there is no such application
+  */
+osrfApplication* _osrfAppFindApplication( char* name );
+
+/**
+  Finds the given method for the given app
+  @param appName The application
+  @param methodName The method to find
+  @return A method pointer or NULL if no such method 
+  exists for the given application
+  */
+osrfMethod* _osrfAppFindMethod( char* appName, char* methodName );
+
+/**
+  Finds the given method for the given app
+  @param app The application object
+  @param methodName The method to find
+  @return A method pointer or NULL if no such method 
+  exists for the given application
+  */
+osrfMethod* __osrfAppFindMethod( osrfApplication* app, char* methodName );
+
+
+/**
+  Runs the specified method for the specified application.
+  @param appName The name of the application who's method to run
+  @param methodName The name of the method to run
+  @param ses The app session attached to this request
+  @params reqId The request id for this request
+  @param params The method parameters
+  */
+int osrfAppRunMethod( char* appName, char* methodName, 
+               osrfAppSession* ses, int reqId, jsonObject* params );
+
+
+/**
+  Trys to run the requested method as a system method.
+  A system method is a well known method that all
+  servers implement.  
+  @param context The current method context
+  @return 0 if the method is run successfully, return < 0 means
+  the method was not run, return > 0 means the method was run
+  and the application code now needs to send a 'request complete' 
+  message
+  */
+int __osrfAppRunSystemMethod(osrfMethodContext* context);
+
+/**
+  Registers all of the system methods for this app so that they may be
+  treated the same as other methods */
+int __osrfAppRegisterSysMethods( char* app );
+
+
+
+/**
+  Responds to the client with a method exception
+  @param ses The current session
+  @param request The request id
+  @param msg The debug message to send to the client
+  @return 0 on successfully sending of the message, -1 otherwise
+  */
+int osrfAppRequestRespondException( osrfAppSession* ses, int request, char* msg, ... );
+
+int __osrfAppPostProcess( osrfMethodContext* context, int retcode );
+
+
+int osrfAppRespond( osrfMethodContext* context, jsonObject* data );
+int _osrfAppRespond( osrfMethodContext* context, jsonObject* data, int complete );
+int osrfAppRespondComplete( osrfMethodContext* context, jsonObject* data );
+
+/* OSRF_METHOD_ATOMIC and/or OSRF_METHOD_CACHABLE and/or 0 for no special options */
+//int osrfAppProcessMethodOptions( char* method );
+
+int osrfAppIntrospect( osrfMethodContext* ctx );
+int osrfAppIntrospectAll( osrfMethodContext* ctx );
+int osrfAppEcho( osrfMethodContext* ctx );
+
+
+/**
+ * Tells the backend process to run its child init function */
+int osrfAppRunChildInit(char* appname);
+void osrfAppSetOnExit(osrfApplication* app, char* appName);
+void osrfAppRunExitCode();
+
+
diff --git a/include/opensrf/osrf_big_hash.h b/include/opensrf/osrf_big_hash.h
new file mode 100644 (file)
index 0000000..a738755
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef OSRF_HASH_H
+#define OSRF_HASH_H
+
+#include <Judy.h>
+#include <opensrf/utils.h>
+#include <opensrf/string_array.h>
+
+#define OSRF_HASH_MAXKEY 256
+
+struct __osrfBigHashStruct {
+       Pvoid_t hash;                                                   /* the hash */
+       void (*freeItem) (char* key, void* item);       /* callback for freeing stored items */
+};
+typedef struct __osrfBigHashStruct osrfBigHash;
+
+
+struct __osrfBigHashIteratorStruct {
+       char* current;
+       osrfBigHash* hash;
+};
+typedef struct __osrfBigHashIteratorStruct osrfBigHashIterator;
+
+/**
+  Allocates a new hash object
+  */
+osrfBigHash* osrfNewBigHash();
+
+/**
+  Sets the given key with the given item
+  if "freeItem" is defined and an item already exists at the given location, 
+  then old item is freed and the new item is put into place.
+  if "freeItem" is not defined and an item already exists, the old item
+  is returned.
+  @return The old item if exists and there is no 'freeItem', returns NULL
+  otherwise
+  */
+void* osrfBigHashSet( osrfBigHash* hash, void* item, const char* key, ... );
+
+/**
+  Removes an item from the hash.
+  if 'freeItem' is defined it is used and NULL is returned,
+  else the freed item is returned
+  */
+void* osrfBigHashRemove( osrfBigHash* hash, const char* key, ... );
+
+void* osrfBigHashGet( osrfBigHash* hash, const char* key, ... );
+
+
+/**
+  @return A list of strings representing the keys of the hash. 
+  caller is responsible for freeing the returned string array 
+  with osrfStringArrayFree();
+  */
+osrfStringArray* osrfBigHashKeys( osrfBigHash* hash );
+
+/**
+  Frees a hash
+  */
+void osrfBigHashFree( osrfBigHash* hash );
+
+/**
+  @return The number of items in the hash
+  */
+unsigned long osrfBigHashGetCount( osrfBigHash* hash );
+
+
+
+
+/**
+  Creates a new list iterator with the given list
+  */
+osrfBigHashIterator* osrfNewBigHashIterator( osrfBigHash* hash );
+
+/**
+  Returns the next non-NULL item in the list, return NULL when
+  the end of the list has been reached
+  */
+void* osrfBigHashIteratorNext( osrfBigHashIterator* itr );
+
+/**
+  Deallocates the given list
+  */
+void osrfBigHashIteratorFree( osrfBigHashIterator* itr );
+
+void osrfBigHashIteratorReset( osrfBigHashIterator* itr );
+
+#endif
diff --git a/include/opensrf/osrf_big_list.h b/include/opensrf/osrf_big_list.h
new file mode 100644 (file)
index 0000000..ebe3da7
--- /dev/null
@@ -0,0 +1,142 @@
+#ifndef OSRF_BIG_LIST_H
+#define OSRF_BIG_LIST_H
+
+
+#include <stdio.h>
+#include <opensrf/utils.h>
+#include <Judy.h>
+
+/**
+  Items are stored as void*'s so it's up to the user to
+  manage the data wisely.  Also, if the 'freeItem' callback is defined for the list,
+  then, it will be used on any item that needs to be freed, so don't mix data
+  types in the list if you want magic freeing */
+
+struct __osrfBigListStruct {
+       Pvoid_t list;                                                   /* the list */
+       int size;                                                               /* how many items in the list including NULL items between non-NULL items */    
+       void (*freeItem) (void* item);  /* callback for freeing stored items */
+};
+typedef struct __osrfBigListStruct osrfBigList;
+
+
+struct __osrfBigBigListIteratorStruct {
+       osrfBigList* list;
+       unsigned long current;
+};
+typedef struct __osrfBigBigListIteratorStruct osrfBigBigListIterator;
+
+
+/**
+  Creates a new list iterator with the given list
+  */
+osrfBigBigListIterator* osrfNewBigListIterator( osrfBigList* list );
+
+/**
+  Returns the next non-NULL item in the list, return NULL when
+  the end of the list has been reached
+  */
+void* osrfBigBigListIteratorNext( osrfBigBigListIterator* itr );
+
+/**
+  Deallocates the given list
+  */
+void osrfBigBigListIteratorFree( osrfBigBigListIterator* itr );
+
+void osrfBigBigListIteratorReset( osrfBigBigListIterator* itr );
+
+
+/**
+  Allocates a new list
+  @param compress If true, the list will compress empty slots on delete.  If item positionality
+  is not important, then using this feature is reccomended to keep the list from growing indefinitely.
+  if item positionality is not important.
+  @return The allocated list
+  */
+osrfBigList* osrfNewBigList();
+
+/**
+  Pushes an item onto the end of the list.  This always finds the highest index
+  in the list and pushes the new item into the list after it.
+  @param list The list
+  @param item The item to push
+  @return 0 on success, -1 on failure
+  */
+int osrfBigListPush( osrfBigList* list, void* item );
+
+
+/**
+ * Removes the last item in the list
+ * See osrfBigListRemove for details on how the removed item is handled
+ * @return The item, unless 'freeItem' exists, then returns NULL
+ */
+void* osrfBigListPop( osrfBigList* list );
+
+/**
+  Puts the given item into the list at the specified position.  If there
+  is already an item at the given position and the list has it's 
+  "freeItem" function defined, then it will be used to free said item.
+  If no 'freeItem' callback is defined, then the displaced item will
+  be returned;
+  @param list The list
+  @param item The item to put into the list
+  @param position The position to place the item in
+  @return NULL in successfully inserting the new item and freeing
+  any displaced items.  Returns the displaced item if no "freeItem"
+  callback is defined.
+       */
+void* osrfBigListSet( osrfBigList* list, void* item, unsigned long position );
+
+/**
+  Returns the item at the given position
+  @param list The list
+  @param postiont the position
+  */
+void* osrfBigListGetIndex( osrfBigList* list, unsigned long  position );
+
+/**
+  Frees the list and all list items (if the list has a "freeItem" function defined )
+  @param list The list
+  */
+void osrfBigListFree( osrfBigList* list );
+
+/**
+  Removes the list item at the given index
+  @param list The list
+  @param position The position of the item to remove
+  @return A pointer to the item removed if "freeItem" is not defined
+  for this list, returns NULL if it is.
+  */
+void* osrfBigListRemove( osrfBigList* list, int position );
+
+/**
+  Finds the list item whose void* is the same as the one passed in
+  @param list The list
+  @param addr The pointer connected to the list item we're to find
+  @return the index of the item, or -1 if the item was not found
+  */
+int osrfBigListFind( osrfBigList* list, void* addr );
+
+
+void __osrfBigListSetSize( osrfBigList* list );
+
+
+/**
+  @return The number of non-null items in the list
+  */
+unsigned long osrfBigListGetCount( osrfBigList* list );
+
+/**
+ * May be used as a default memory freeing call
+ * Just calls free() on list items
+ */
+void osrfBigListVanillaFree( void* item );
+
+/**
+ * Tells the list to just call 'free()' on each item when
+ * an item or the whole list is destroyed
+ */
+void osrfBigListSetDefaultFree( osrfBigList* list );
+
+
+#endif
diff --git a/include/opensrf/osrf_cache.h b/include/opensrf/osrf_cache.h
new file mode 100644 (file)
index 0000000..5a755ff
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+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.
+*/
+
+
+#include <objson/object.h>
+#include <objson/json_parser.h>
+#include <memcache.h>
+#include <opensrf/log.h>
+
+/**
+  osrfCache is a globally shared cache API
+  */
+
+
+/**
+  Initialize the cache.
+  @param serverStrings An array of "ip:port" strings to use as cache servers
+  @param size The size of the serverStrings array
+  @param maxCacheSeconds The maximum amount of time an object / string may
+       be cached.  Negative number means there is no limit
+  */
+int osrfCacheInit( char* serverStrings[], int size, time_t maxCacheSeconds );
+
+
+/**
+  Puts an object into the cache
+  @param key The cache key
+  @param obj The object to cache
+  @param seconds The amount of time to cache the data, negative number means
+       to cache up to 'maxCacheSeconds' as set by osrfCacheInit()
+  @return 0 on success, -1 on error
+  */
+int osrfCachePutObject( char* key, const jsonObject* obj, time_t seconds );
+
+/**
+  Puts a string into the cache
+  @param key The cache key
+  @param value The string to cache
+  @param seconds The amount of time to cache the data, negative number means
+       to cache up to 'maxCacheSeconds' as set by osrfCacheInit()
+  @return 0 on success, -1 on error
+  */
+int osrfCachePutString( char* key, const char* value, time_t seconds);
+
+/**
+  Grabs an object from the cache.
+  @param key The cache key
+  @return The object (which must be freed) if it exists, otherwise returns NULL
+  */
+jsonObject* osrfCacheGetObject( char* key, ... );
+
+/**
+  Grabs a string from the cache.
+  @param key The cache key
+  @return The string (which must be freed) if it exists, otherwise returns NULL
+  */
+char* osrfCacheGetString( char* key, ... );
+
+/**
+  Removes the item with the given key from the cache.
+  @return 0 on success, -1 on error.
+  */
+int osrfCacheRemove( char* key, ... );
+
+/**
+ * Sets the expire time to 'seconds' for the given key
+ */
+int osrfCacheSetExpire( time_t seconds, char* key, ... );
+
+
+
diff --git a/include/opensrf/osrf_hash.h b/include/opensrf/osrf_hash.h
new file mode 100644 (file)
index 0000000..132e43e
--- /dev/null
@@ -0,0 +1,115 @@
+#ifndef OSRF_HASH_H
+#define OSRF_HASH_H
+
+#include <opensrf/utils.h>
+#include <opensrf/string_array.h>
+#include <opensrf/osrf_list.h>
+
+/* 0x100 is a good size for small hashes */
+//#define OSRF_HASH_LIST_SIZE 0x100  /* size of the main hash list */
+#define OSRF_HASH_LIST_SIZE 0x10  /* size of the main hash list */
+
+
+/* used internally */
+#define OSRF_HASH_NODE_FREE(h, n) \
+       if(h && n) { \
+               if(h->freeItem) h->freeItem(n->key, n->item);\
+               free(n->key); free(n); \
+       }
+
+
+struct __osrfHashStruct {
+       osrfList* hash; /* this hash */
+       void (*freeItem) (char* key, void* item);       /* callback for freeing stored items */
+       unsigned int size;
+       osrfStringArray* keys;
+};
+typedef struct __osrfHashStruct osrfHash;
+
+struct _osrfHashNodeStruct {
+       char* key;
+       void* item;
+};
+typedef struct _osrfHashNodeStruct osrfHashNode;
+
+
+struct __osrfHashIteratorStruct {
+       char* current;
+       int currentIdx;
+       osrfHash* hash;
+       osrfStringArray* keys;
+};
+typedef struct __osrfHashIteratorStruct osrfHashIterator;
+
+osrfHashNode* osrfNewHashNode(char* key, void* item);
+void* osrfHashNodeFree(osrfHash*, osrfHashNode*);
+
+/**
+  Allocates a new hash object
+  */
+osrfHash* osrfNewHash();
+
+/**
+  Sets the given key with the given item
+  if "freeItem" is defined and an item already exists at the given location, 
+  then old item is freed and the new item is put into place.
+  if "freeItem" is not defined and an item already exists, the old item
+  is returned.
+  @return The old item if exists and there is no 'freeItem', returns NULL
+  otherwise
+  */
+void* osrfHashSet( osrfHash* hash, void* item, const char* key, ... );
+
+/**
+  Removes an item from the hash.
+  if 'freeItem' is defined it is used and NULL is returned,
+  else the freed item is returned
+  */
+void* osrfHashRemove( osrfHash* hash, const char* key, ... );
+
+void* osrfHashGet( osrfHash* hash, const char* key, ... );
+
+
+/**
+  @return A list of strings representing the keys of the hash. 
+  caller is responsible for freeing the returned string array 
+  with osrfStringArrayFree();
+  */
+osrfStringArray* osrfHashKeys( osrfHash* hash );
+
+osrfStringArray* osrfHashKeysInc( osrfHash* hash );
+
+/**
+  Frees a hash
+  */
+void osrfHashFree( osrfHash* hash );
+
+/**
+  @return The number of items in the hash
+  */
+unsigned long osrfHashGetCount( osrfHash* hash );
+
+
+
+
+/**
+  Creates a new list iterator with the given list
+  */
+osrfHashIterator* osrfNewHashIterator( osrfHash* hash );
+
+int osrfHashIteratorHasNext( osrfHashIterator* itr );
+
+/**
+  Returns the next non-NULL item in the list, return NULL when
+  the end of the list has been reached
+  */
+void* osrfHashIteratorNext( osrfHashIterator* itr );
+
+/**
+  Deallocates the given list
+  */
+void osrfHashIteratorFree( osrfHashIterator* itr );
+
+void osrfHashIteratorReset( osrfHashIterator* itr );
+
+#endif
diff --git a/include/opensrf/osrf_list.h b/include/opensrf/osrf_list.h
new file mode 100644 (file)
index 0000000..f59669a
--- /dev/null
@@ -0,0 +1,155 @@
+#ifndef OSRF_LIST_H
+#define OSRF_LIST_H
+
+#include <opensrf/utils.h>
+
+#define OSRF_LIST_DEFAULT_SIZE 48 /* most opensrf lists are small... */
+#define OSRF_LIST_INC_SIZE 256
+#define OSRF_LIST_MAX_SIZE 10240
+
+
+#define OSRF_LIST_GET_INDEX(l, i) (!l || i >= l->size) ? NULL: l->arrlist[i]
+
+/**
+  Items are stored as void*'s so it's up to the user to
+  manage the data wisely.  Also, if the 'freeItem' callback is defined for the list,
+  then, it will be used on any item that needs to be freed, so don't mix data
+  types in the list if you want magic freeing */
+
+struct __osrfListStruct {
+       unsigned int size;                      /* how many items in the list including NULL items between non-NULL items */    
+       void (*freeItem) (void* item);  /* callback for freeing stored items */
+       void** arrlist;
+       int arrsize; /* how big is the currently allocated array */
+};
+typedef struct __osrfListStruct osrfList;
+
+
+struct __osrfListIteratorStruct {
+       const osrfList* list;
+       unsigned int current;
+};
+typedef struct __osrfListIteratorStruct osrfListIterator;
+
+osrfList* osrfNewListSize( unsigned int size );
+
+
+/**
+  Creates a new list iterator with the given list
+  */
+osrfListIterator* osrfNewListIterator( const osrfList* list );
+
+/**
+  Returns the next non-NULL item in the list, return NULL when
+  the end of the list has been reached
+  */
+void* osrfListIteratorNext( osrfListIterator* itr );
+
+/**
+  Deallocates the given list
+  */
+void osrfListIteratorFree( osrfListIterator* itr );
+
+void osrfListIteratorReset( osrfListIterator* itr );
+
+
+/**
+  Allocates a new list
+  @param compress If true, the list will compress empty slots on delete.  If item positionality
+  is not important, then using this feature is reccomended to keep the list from growing indefinitely.
+  if item positionality is not important.
+  @return The allocated list
+  */
+osrfList* osrfNewList();
+
+/**
+  Pushes an item onto the end of the list.  This always finds the highest index
+  in the list and pushes the new item into the list after it.
+  @param list The list
+  @param item The item to push
+  @return 0 on success, -1 on failure
+  */
+int osrfListPush( osrfList* list, void* item );
+
+
+/**
+ * Removes the last item in the list
+ * See osrfListRemove for details on how the removed item is handled
+ * @return The item, unless 'freeItem' exists, then returns NULL
+ */
+void* osrfListPop( osrfList* list );
+
+/**
+  Puts the given item into the list at the specified position.  If there
+  is already an item at the given position and the list has it's 
+  "freeItem" function defined, then it will be used to free said item.
+  If no 'freeItem' callback is defined, then the displaced item will
+  be returned;
+  @param list The list
+  @param item The item to put into the list
+  @param position The position to place the item in
+  @return NULL in successfully inserting the new item and freeing
+  any displaced items.  Returns the displaced item if no "freeItem"
+  callback is defined.
+       */
+void* osrfListSet( osrfList* list, void* item, unsigned int position );
+
+/**
+  Returns the item at the given position
+  @param list The list
+  @param postiont the position
+  */
+void* osrfListGetIndex( const osrfList* list, unsigned int  position );
+
+/**
+  Frees the list and all list items (if the list has a "freeItem" function defined )
+  @param list The list
+  */
+void osrfListFree( osrfList* list );
+
+/**
+  Removes the list item at the given index
+  @param list The list
+  @param position The position of the item to remove
+  @return A pointer to the item removed if "freeItem" is not defined
+  for this list, returns NULL if it is.
+  */
+void* osrfListRemove( osrfList* list, unsigned int position );
+
+/**
+  Finds the list item whose void* is the same as the one passed in
+  @param list The list
+  @param addr The pointer connected to the list item we're to find
+  @return the index of the item, or -1 if the item was not found
+  */
+int osrfListFind( const osrfList* list, void* addr );
+
+
+void __osrfListSetSize( osrfList* list );
+
+
+/**
+  @return The number of non-null items in the list
+  */
+unsigned int osrfListGetCount( const osrfList* list );
+
+/**
+ * May be used as a default memory freeing call
+ * Just calls free() on list items
+ */
+void osrfListVanillaFree( void* item );
+
+/**
+ * Tells the list to just call 'free()' on each item when
+ * an item or the whole list is destroyed
+ */
+void osrfListSetDefaultFree( osrfList* list );
+
+/**
+ * Inserts the new item at the first free (null) slot
+ * in the array.  Item is shoved onto the end of the
+ * list if there are no null slots */
+int osrfListPushFirst( osrfList* list, void* item );
+
+
+#endif
diff --git a/include/opensrf/osrf_message.h b/include/opensrf/osrf_message.h
new file mode 100644 (file)
index 0000000..12e14c6
--- /dev/null
@@ -0,0 +1,121 @@
+#include <opensrf/string_array.h>
+#include <opensrf/utils.h>
+#include <opensrf/log.h>
+#include <objson/object.h>
+#include <objson/json_parser.h>
+
+
+/* libxml stuff for the config reader */
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/tree.h>
+
+
+
+#ifndef osrf_message_h
+#define osrf_message_h
+
+#define OSRF_XML_NAMESPACE "http://open-ils.org/xml/namespaces/oils_v1"
+
+#define OSRF_STATUS_CONTINUE                                           100
+
+#define OSRF_STATUS_OK                                                         200
+#define OSRF_STATUS_ACCEPTED                                           202
+#define OSRF_STATUS_COMPLETE                                           205
+
+#define OSRF_STATUS_REDIRECTED                                 307
+
+#define OSRF_STATUS_BADREQUEST                                 400
+#define OSRF_STATUS_UNAUTHORIZED                                       401
+#define OSRF_STATUS_FORBIDDEN                                          403
+#define OSRF_STATUS_NOTFOUND                                           404
+#define OSRF_STATUS_NOTALLOWED                                 405
+#define OSRF_STATUS_TIMEOUT                                            408
+#define OSRF_STATUS_EXPFAILED                                          417
+
+#define OSRF_STATUS_INTERNALSERVERERROR                500
+#define OSRF_STATUS_NOTIMPLEMENTED                             501
+#define OSRF_STATUS_VERSIONNOTSUPPORTED                505
+
+
+enum M_TYPE { CONNECT, REQUEST, RESULT, STATUS, DISCONNECT };
+
+#define OSRF_MAX_PARAMS                                                                128;
+
+struct osrf_message_struct {
+
+       enum M_TYPE m_type;
+       int thread_trace;
+       int protocol;
+
+       /* if we're a STATUS message */
+       char* status_name;
+
+       /* if we're a STATUS or RESULT */
+       char* status_text;
+       int status_code;
+
+       int is_exception;
+
+       /* if we're a RESULT */
+       jsonObject* _result_content;
+
+       /* unparsed json string */
+       char* result_string;
+
+       /* if we're a REQUEST */
+       char* method_name;
+
+       jsonObject* _params;
+
+       /* in case anyone wants to make a list of us.  
+               we won't touch this variable */
+       struct osrf_message_struct* next;
+
+       char* full_param_string;
+
+};
+typedef struct osrf_message_struct osrf_message;
+typedef struct osrf_message_struct osrfMessage;
+
+
+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 osrfMessageFree( osrfMessage* );
+void osrf_message_free( osrf_message* );
+char* osrf_message_to_xml( osrf_message* );
+char* osrf_message_serialize(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);
+
+
+
+/** Pushes any message retreived from the xml into the 'msgs' array.
+  * it is assumed that 'msgs' has beenn pre-allocated.
+  * Returns the number of message that are in the buffer.
+  */
+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 );
+
+
+jsonObject* osrfMessageGetResult( osrfMessage* msg );
+
+/**
+  Returns the message as a jsonObject
+  @return The jsonObject which must be freed by the caller.
+  */
+jsonObject* osrfMessageToJSON( osrfMessage* msg );
+
+char* osrfMessageSerializeBatch( osrfMessage* msgs [], int count );
+
+
+#endif
diff --git a/include/opensrf/osrf_prefork.h b/include/opensrf/osrf_prefork.h
new file mode 100644 (file)
index 0000000..543343c
--- /dev/null
@@ -0,0 +1,96 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+
+#include <opensrf/utils.h>
+#include <opensrf/transport_message.h>
+#include <opensrf/transport_client.h>
+#include <opensrf/osrf_stack.h>
+#include <opensrf/osrf_settings.h>
+#include <opensrf/osrfConfig.h>
+
+//#define READ_BUFSIZE 4096
+#define READ_BUFSIZE 1024
+#define MAX_BUFSIZE 10485760 /* 10M enough? ;) */
+#define ABS_MAX_CHILDREN 256 
+
+/* we receive data.  we find the next child in
+       line that is available.  pass the data down that childs pipe and go
+       back to listening for more data.
+       when we receive SIGCHLD, we check for any dead children and clean up
+       their respective prefork_child objects, close pipes, etc.
+
+       we build a select fd_set with all the child pipes (going to the parent) 
+       when a child is done processing a request, it writes a small chunk of 
+       data to the parent to alert the parent that the child is again available 
+       */
+
+struct prefork_simple_struct {
+       int max_requests;
+       int min_children;
+       int max_children;
+       int fd;
+       int data_to_child;
+       int data_to_parent;
+       int current_num_children;
+       int keepalive; /* keepalive time for stateful sessions */
+       char* appname;
+       struct prefork_child_struct* first_child;
+       transport_client* connection;
+};
+typedef struct prefork_simple_struct prefork_simple;
+
+struct prefork_child_struct {
+       pid_t pid;
+       int read_data_fd;
+       int write_data_fd;
+       int read_status_fd;
+       int write_status_fd;
+       int min_children;
+       int available;
+       int max_requests;
+       char* appname;
+       int keepalive;
+       struct prefork_child_struct* next;
+};
+
+typedef struct prefork_child_struct prefork_child;
+
+int osrf_prefork_run(char* appname);
+
+prefork_simple*  prefork_simple_init( transport_client* client, 
+       int max_requests, int min_children, int max_children );
+
+prefork_child*  launch_child( prefork_simple* forker );
+void prefork_launch_children( prefork_simple* forker );
+
+void prefork_run(prefork_simple* forker);
+
+void add_prefork_child( prefork_simple* forker, prefork_child* child );
+prefork_child* find_prefork_child( prefork_simple* forker, pid_t pid );
+void del_prefork_child( prefork_simple* forker, pid_t pid );
+
+void check_children( prefork_simple* forker, int forever );
+
+void prefork_child_process_request(prefork_child*, char* data);
+int prefork_child_init_hook(prefork_child*);
+
+prefork_child* prefork_child_init( 
+               int max_requests, int read_data_fd, int write_data_fd, 
+               int read_status_fd, int write_status_fd );
+
+/* listens on the 'data_to_child' fd and wait for incoming data */
+void prefork_child_wait( prefork_child* child );
+
+int prefork_free( prefork_simple* );
+int prefork_child_free( prefork_child* );
+
+
+void osrf_prefork_register_routers( char* appname );
+
+void osrf_prefork_child_exit( prefork_child* );
diff --git a/include/opensrf/osrf_settings.h b/include/opensrf/osrf_settings.h
new file mode 100644 (file)
index 0000000..9aa0d47
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef OSRF_SETTINGS_H
+#define OSRF_SETTINGS_H
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdarg.h>
+
+#include <opensrf/log.h>
+#include <opensrf/utils.h>
+#include <opensrf/osrf_app_session.h>
+
+#include <objson/object.h>
+#include <objson/json_parser.h>
+
+typedef struct { 
+       char* hostname; 
+       jsonObject* config; 
+} osrf_host_config;
+
+
+osrf_host_config* osrf_settings_new_host_config(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);
+
+#endif
+
diff --git a/include/opensrf/osrf_stack.h b/include/opensrf/osrf_stack.h
new file mode 100644 (file)
index 0000000..da2db3d
--- /dev/null
@@ -0,0 +1,19 @@
+#include <opensrf/transport_client.h>
+#include <opensrf/osrf_message.h>
+#include <opensrf/osrf_app_session.h>
+
+#ifndef OSRF_STACK_H
+#define OSRF_STACK_H
+
+/* the max number of oilsMessage blobs present in any one root packet */
+#define OSRF_MAX_MSGS_PER_PACKET 256
+// -----------------------------------------------------------------------------
+
+int osrf_stack_process( transport_client* client, int timeout, int* msg_received );
+osrfAppSession*  osrf_stack_transport_handler( transport_message* msg, char* my_service );
+int osrf_stack_message_handler( osrf_app_session* session, osrf_message* msg );
+int osrf_stack_application_handler( osrf_app_session* session, osrf_message* msg );
+
+
+
+#endif
diff --git a/include/opensrf/osrf_system.h b/include/opensrf/osrf_system.h
new file mode 100644 (file)
index 0000000..540e8d1
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef OSRF_SYSTEM_H
+#define OSRF_SYSTEM_H
+
+#include <opensrf/transport_client.h>
+#include <opensrf/utils.h>
+#include <opensrf/log.h>
+#include <opensrf/osrf_settings.h>
+#include <opensrf/osrfConfig.h>
+#include <opensrf/osrf_cache.h>
+
+
+
+/** Connects to jabber.  Returns 1 on success, 0 on failure 
+       contextnode is the location in the config file where we collect config info
+*/
+
+
+int osrf_system_bootstrap_client( char* config_file, char* contextnode );
+
+/* bootstraps a client adding the given resource string to the host/pid, etc. resource string */
+/**
+  Sets up the global connection.
+  @param configFile The OpenSRF bootstrap config file
+  @param contextNode The location in the config file where we'll find the necessary info
+  @param resource The login resource.  If NULL a default will be created
+  @return 1 on successs, 0 on failure.
+  */
+int osrfSystemBootstrapClientResc( char* configFile, char* contextNode, char* resource );
+int osrf_system_bootstrap_client_resc( char* config_file, char* contextnode, char* resource );
+
+/**
+  Bootstrap the server.
+  @param hostname The name of this host.  This is the name that will be used to 
+       load the settings.
+  @param configfile The OpenSRF bootstrap config file
+  @param contextnode The config context
+  @return 0 on success, -1 on error
+  */
+int osrfSystemBootstrap( char* hostName, char* configfile, char* contextNode );
+
+transport_client* osrfSystemGetTransportClient( void );
+transport_client* osrf_system_get_transport_client( void );
+
+/* disconnects and destroys the current client connection */
+int osrf_system_disconnect_client();
+int osrf_system_shutdown( void ); 
+
+
+/* this will clear the global transport client pointer without
+ * actually destroying the socket.  this is useful for allowing
+ * children to have their own socket, even though their parent
+ * already created a socket
+ */
+void osrfSystemIgnoreTransportClient();
+
+
+#endif
diff --git a/include/opensrf/osrf_transgroup.h b/include/opensrf/osrf_transgroup.h
new file mode 100644 (file)
index 0000000..9788d93
--- /dev/null
@@ -0,0 +1,122 @@
+#include <opensrf/transport_client.h>
+#include <opensrf/transport_message.h>
+#include <opensrf/osrf_list.h>
+#include <opensrf/osrf_hash.h>
+#include <opensrf/osrfConfig.h>
+#include <opensrf/utils.h>
+#include <time.h>
+
+/**
+  Maintains a set of transport clients 
+  */
+
+struct __osrfTransportGroupStruct {
+       osrfHash* nodes;                                                /* our hash of nodes keyed by domain */
+       osrfHashIterator* itr;                          /* points to the next node in the list */
+};
+typedef struct __osrfTransportGroupStruct osrfTransportGroup;
+
+
+struct __osrfTransportGroupNode {
+       transport_client* connection;           /* our connection to the network */
+       char* domain;                                                   /* the domain we're connected to */
+       char* username;                                         /* username used to connect to the group of servers */
+       char* password;                                         /* password used to connect to the group of servers */
+       char* resource;                                         /* the login resource */
+       int port;                                                               /* port used to connect to the group of servers */
+
+       int active;                                                             /* true if we're able to send data on this connection */
+       time_t lastsent;                                                /* the last time we sent a message */
+};
+typedef struct __osrfTransportGroupNode osrfTransportGroupNode;
+
+
+/**
+  Creates a new group node
+  @param domain The domain we're connecting to
+  @param port The port to connect on
+  @param username The login name
+  @param password The login password
+  @param resource The login resource
+  @return A new transport group node
+  */
+osrfTransportGroupNode* osrfNewTransportGroupNode( 
+               char* domain, int port, char* username, char* password, char* resource );
+
+
+/**
+  Allocates and initializes a new transport group.
+  The first node in the array is the default node for client connections.
+  @param nodes The nodes in the group.
+  */
+osrfTransportGroup* osrfNewTransportGroup( osrfTransportGroupNode* nodes[], int count );
+
+/**
+  Attempts to connect all of the nodes in this group.
+  @param grp The transport group
+  @return The number of nodes successfully connected
+  */
+int osrfTransportGroupConnectAll( osrfTransportGroup* grp );
+
+void osrfTransportGroupDisconnectAll( osrfTransportGroup* grp );
+
+
+/**
+  Sends a transport message by going to the next domain in the set.
+  if we have a connection for the recipient domain, then we consider it to be
+  a 'local' message.  Local messages have their recipient domains re-written to
+  match the domain of the next server in the set and they are sent directly to 
+  that server.  If we do not have a connection for the recipient domain, it is 
+  considered a 'remote' message and the message is sent directly (unchanged)
+  to the next connection in the set.
+
+  @param grp The transport group
+  @param msg The message to send 
+  @return 0 on normal successful send.  
+  Returns -1 if the message cannot be sent.  
+  */
+int osrfTransportGroupSend( osrfTransportGroup* grp, transport_message* msg );
+
+/**
+  Sends the message to the exact recipient.  No failover is attempted.
+  @return 0 on success, -1 on error.
+  */
+int osrfTransportGroupSendMatch( osrfTransportGroup* grp, transport_message* msg );
+
+
+int _osrfTGServerSend( osrfTransportGroup* grp, char* domain, transport_message* msg );
+int _osrfTGClientSend( osrfTransportGroup* grp, char* domain, transport_message* msg );
+
+/**
+  Waits on all connections for inbound data.
+  @param grp The transport group
+  @param timeout How long to wait for data.  0 means check for data
+  but don't wait, a negative number means to wait indefinitely
+  @return The received message or NULL if the timeout occurred before a 
+  message was received 
+ */
+transport_message* osrfTransportGroupRecvAll( osrfTransportGroup* grp, int timeout );
+
+/**
+  Waits for data from a single domain
+  @param grp The transport group
+  @param domain The domain to wait for data on
+  @param timeout see osrfTransportGroupRecvAll
+  */
+transport_message* osrfTransportGroupRecv( osrfTransportGroup* grp, char* domain, int timeout );
+
+/**
+  Tells the group that a message to the given domain failed
+  domain did not make it through;
+  @param grp The transport group
+  @param comain The failed domain
+  */
+void osrfTransportGroupSetInactive( osrfTransportGroup* grp, char* domain );
+
+
+/**
+  Finds a node in our list of nodes 
+  */
+osrfTransportGroupNode* __osrfTransportGroupFindNode( osrfTransportGroup* grp, char* domain );
+
+
diff --git a/include/opensrf/sha.h b/include/opensrf/sha.h
new file mode 100644 (file)
index 0000000..6c3d2d4
--- /dev/null
@@ -0,0 +1,41 @@
+// sha.h
+// Jabber client library
+//
+// Original Code Copyright (C) 1999-2001 Dave Smith (dave@jabber.org)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// 
+// This library 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
+// Lesser General Public License for more details.
+// 
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// Contributor(s): Julian Missig
+//
+// This Original Code has been modified by IBM Corporation. Modifications 
+// made by IBM described herein are Copyright (c) International Business 
+// Machines Corporation, 2002.
+//
+// Date             Modified by     Description of modification
+// 01/20/2002       IBM Corp.       Updated to libjudo 1.1.1
+// 2002-03-05       IBM Corp.       Updated to libjudo 1.1.5
+// 2002-07-09       IBM Corp.       Added Roster::getSession()
+//
+// =====================================================================================
+
+
+//#ifdef WIN32
+     char* shahash(const char* str);
+//#else
+//extern "C" {
+//     char* shahash(const char* str);
+//}
+//#endif
+
diff --git a/include/opensrf/socket_bundle.h b/include/opensrf/socket_bundle.h
new file mode 100644 (file)
index 0000000..f290cdc
--- /dev/null
@@ -0,0 +1,111 @@
+#include <opensrf/utils.h>
+#include <opensrf/log.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <errno.h>
+
+
+//---------------------------------------------------------------
+// Unix headers
+//---------------------------------------------------------------
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+
+#include <signal.h>
+
+#ifndef SOCKET_BUNDLE_H
+#define SOCKET_BUNDLE_H
+
+
+#define SERVER_SOCKET                  1
+#define CLIENT_SOCKET                  2
+
+#define INET 10 
+#define UNIX 11 
+
+
+/* models a single socket connection */
+struct socket_node_struct {
+       int endpoint;           /* SERVER_SOCKET or CLIENT_SOCKET */
+       int addr_type;          /* INET or UNIX */
+       int sock_fd;
+       int parent_id;          /* if we're a new client for a server socket, 
+                                                               this points to the server socket we spawned from */
+       struct socket_node_struct* next;
+};
+typedef struct socket_node_struct socket_node;
+
+
+/* Maintains the socket set */
+struct socket_manager_struct {
+       /* callback for passing up any received data.  sock_fd is the socket
+               that read the data.  parent_id (if > 0) is the socket id of the 
+               server that this socket spawned from (i.e. it's a new client connection) */
+       void (*data_received) 
+               (void* blob, struct socket_manager_struct*, 
+                int sock_fd, char* data, int parent_id);
+
+       void (*on_socket_closed) (void* blob, int sock_fd);
+
+       socket_node* socket;
+       void* blob;
+};
+typedef struct socket_manager_struct socket_manager;
+
+void socket_manager_free(socket_manager* mgr);
+
+/* creates a new server socket node and adds it to the socket set.
+       returns socket id on success.  -1 on failure.
+       socket_type is one of INET or UNIX  */
+int socket_open_tcp_server(socket_manager*, int port, char* listen_ip );
+
+int socket_open_unix_server(socket_manager* mgr, char* path);
+
+int socket_open_udp_server( socket_manager* mgr, int port, char* listen_ip );
+
+/* creates a client TCP socket and adds it to the socket set.
+       returns 0 on success.  -1 on failure.  */
+int socket_open_tcp_client(socket_manager*, int port, char* dest_addr);
+
+/* creates a client UNIX socket and adds it to the socket set.
+       returns 0 on success.  -1 on failure.  */
+int socket_open_unix_client(socket_manager*, char* sock_path);
+
+int socket_open_udp_client( socket_manager* mgr, int port, char* dest_addr);
+
+/* sends the given data to the given socket. returns 0 on success, -1 otherwise */
+int socket_send(int sock_fd, const char* data);
+
+/* waits at most usecs microseconds for the socket buffer to
+ * be available */
+int socket_send_timeout( int sock_fd, const char* data, int usecs );
+
+/* disconnects the node with the given sock_fd and removes
+       it from the socket set */
+void socket_disconnect(socket_manager*, int sock_fd);
+
+/* XXX This only works if 'sock_fd' is a client socket... */
+int socket_wait(socket_manager* mgr, int timeout, int sock_fd);
+
+/* waits on all sockets for incoming data.  
+       timeout == -1   | block indefinitely
+       timeout == 0    | don't block, just read any available data off all sockets
+       timeout == x    | block for at most x seconds */
+int socket_wait_all(socket_manager* mgr, int timeout);
+
+/* utility function for displaying the currently attached sockets */
+void _socket_print_list(socket_manager* mgr);
+
+int socket_connected(int sock_fd);
+
+#endif
diff --git a/include/opensrf/string_array.h b/include/opensrf/string_array.h
new file mode 100644 (file)
index 0000000..77b3de0
--- /dev/null
@@ -0,0 +1,41 @@
+#include <stdio.h>
+
+#include <opensrf/utils.h>
+#include <opensrf/log.h>
+
+#define STRING_ARRAY_MAX_SIZE 1024
+
+#ifndef STRING_ARRAY_H
+#define STRING_ARRAY_H
+
+struct string_array_struct {
+               char** array;   
+               int size;
+               int arr_size;
+               int total_string_size;
+};
+typedef struct string_array_struct string_array;
+typedef struct string_array_struct osrfStringArray;
+
+osrfStringArray* init_string_array(int size);
+osrfStringArray* osrfNewStringArray(int size);
+
+void string_array_add(osrfStringArray*, char* string);
+void osrfStringArrayAdd(osrfStringArray*, char* string);
+
+char* string_array_get_string(osrfStringArray* arr, int index);
+char* osrfStringArrayGetString(osrfStringArray* arr, int index);
+
+/* returns true if this array contains the given string */
+int osrfStringArrayContains( osrfStringArray* arr, char* string );
+
+
+void string_array_destroy(osrfStringArray*);
+void osrfStringArrayFree(osrfStringArray*);
+
+/* total size of all included strings */
+int string_array_get_total_size(osrfStringArray* arr);
+
+void osrfStringArrayRemove( osrfStringArray* arr, char* str);
+
+#endif
diff --git a/include/opensrf/transport_client.h b/include/opensrf/transport_client.h
new file mode 100644 (file)
index 0000000..e61f840
--- /dev/null
@@ -0,0 +1,92 @@
+#include <opensrf/transport_session.h>
+#include <opensrf/utils.h>
+#include <opensrf/log.h>
+
+#include <time.h>
+
+#ifndef TRANSPORT_CLIENT_H
+#define TRANSPORT_CLIENT_H
+
+#define MESSAGE_LIST_HEAD 1
+#define MESSAGE_LIST_ITEM 2
+
+
+// ---------------------------------------------------------------------------
+// Represents a node in a linked list.  The node holds a pointer to the next
+// node (which is null unless set), a pointer to a transport_message, and
+// and a type variable (which is not really curently necessary).
+// ---------------------------------------------------------------------------
+struct message_list_struct {
+       struct message_list_struct* next;
+       transport_message* message;
+       int type;
+};
+
+typedef struct message_list_struct transport_message_list;
+typedef struct message_list_struct transport_message_node;
+
+// ---------------------------------------------------------------------------
+// Our client struct.  We manage a list of messages and a controlling session
+// ---------------------------------------------------------------------------
+struct transport_client_struct {
+       transport_message_list* m_list;
+       transport_session* session;
+       int error;
+};
+typedef struct transport_client_struct transport_client;
+
+// ---------------------------------------------------------------------------
+// Allocates and initializes and transport_client.  This does no connecting
+// The user must call client_free(client) when finished with the allocated
+// object.
+// if port > 0 => connect via TCP
+// else if unix_path != NULL => connect via UNIX socket
+// ---------------------------------------------------------------------------
+transport_client* client_init( const char* server, int port, const char* unix_path, int component );
+
+
+// ---------------------------------------------------------------------------
+// Connects to the Jabber server with the provided information. Returns 1 on
+// success, 0 otherwise.
+// ---------------------------------------------------------------------------
+int client_connect( transport_client* client, 
+               char* username, char* password, char* resource, 
+               int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type );
+
+
+int client_disconnect( transport_client* client );
+
+// ---------------------------------------------------------------------------
+// De-allocates memory associated with a transport_client object.  Users
+// must use this method when finished with a client object.
+// ---------------------------------------------------------------------------
+int client_free( transport_client* client );
+
+// ---------------------------------------------------------------------------
+//  Sends the given message.  The message must at least have the recipient
+// field set.
+// ---------------------------------------------------------------------------
+int client_send_message( transport_client* client, transport_message* msg );
+
+// ---------------------------------------------------------------------------
+// Returns 1 if this client is currently connected to the server, 0 otherwise
+// ---------------------------------------------------------------------------
+int client_connected( transport_client* client );
+
+// ---------------------------------------------------------------------------
+// This is the message handler required by transport_session.  This handler
+// takes all incoming messages and puts them into the back of a linked list
+// of messages.  
+// ---------------------------------------------------------------------------
+void client_message_handler( void* client, transport_message* msg );
+
+// ---------------------------------------------------------------------------
+// If there are any message in the message list, the 'oldest' message is
+// returned.  If not, this function will wait at most 'timeout' seconds 
+// for a message to arrive.  Specifying -1 means that this function will not
+// return unless a message arrives.
+// ---------------------------------------------------------------------------
+transport_message* client_recv( transport_client* client, int timeout );
+
+
+#endif
diff --git a/include/opensrf/transport_message.h b/include/opensrf/transport_message.h
new file mode 100644 (file)
index 0000000..ddd8f39
--- /dev/null
@@ -0,0 +1,99 @@
+#include <string.h>
+#include <libxml/globals.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/debugXML.h>
+#include <libxml/xmlmemory.h>
+
+#include <opensrf/utils.h>
+#include <opensrf/xml_utils.h>
+#include <opensrf/log.h>
+
+#ifndef TRANSPORT_MESSAGE_H
+#define TRANSPORT_MESSAGE_H
+
+
+
+// ---------------------------------------------------------------------------------
+// Jabber message object.
+// ---------------------------------------------------------------------------------
+struct transport_message_struct {
+       char* body;
+       char* subject;
+       char* thread;
+       char* recipient;
+       char* sender;
+       char* router_from;
+       char* router_to;
+       char* router_class;
+       char* router_command;
+   char* osrf_xid;
+       int is_error;
+       char* error_type;
+       int error_code;
+       int broadcast;
+       char* msg_xml; /* the entire message as XML complete with entity encoding */
+};
+typedef struct transport_message_struct transport_message;
+
+// ---------------------------------------------------------------------------------
+// Allocates and returns a transport_message.  All chars are safely re-allocated
+// within this method.
+// Returns NULL on error
+// ---------------------------------------------------------------------------------
+transport_message* message_init( char* body, char* subject, 
+               char* thread, char* recipient, char* sender );
+
+transport_message* new_message_from_xml( const char* msg_xml );
+
+
+void message_set_router_info( transport_message* msg, char* router_from,
+               char* router_to, char* router_class, char* router_command, int broadcast_enabled );
+
+void message_set_osrf_xid( transport_message* msg, char* osrf_xid );
+
+// ---------------------------------------------------------------------------------
+// Formats the Jabber message as XML for encoding. 
+// Returns NULL on error
+// ---------------------------------------------------------------------------------
+char* message_to_xml( const transport_message* msg );
+
+
+// ---------------------------------------------------------------------------------
+// Call this to create the encoded XML for sending on the wire.
+// This is a seperate function so that encoding will not necessarily have
+// to happen on all messages (i.e. typically only occurs outbound messages).
+// ---------------------------------------------------------------------------------
+int message_prepare_xml( transport_message* msg );
+
+// ---------------------------------------------------------------------------------
+// Deallocates the memory used by the transport_message
+// Returns 0 on error
+// ---------------------------------------------------------------------------------
+int message_free( transport_message* msg );
+
+// ---------------------------------------------------------------------------------
+// Prepares the shared XML document
+// ---------------------------------------------------------------------------------
+//int message_init_xml();
+
+// ---------------------------------------------------------------------------------
+// Determines the username of a Jabber ID.  This expects a pre-allocated char 
+// array for the return value.
+// ---------------------------------------------------------------------------------
+void jid_get_username( const char* jid, char buf[], int size );
+
+// ---------------------------------------------------------------------------------
+// Determines the resource of a Jabber ID.  This expects a pre-allocated char 
+// array for the return value.
+// ---------------------------------------------------------------------------------
+void jid_get_resource( const char* jid, char buf[], int size );
+
+/** Puts the domain portion of the given jid into the pre-allocated buffer */
+void jid_get_domain( const char* jid, char buf[], int size );
+
+void set_msg_error( transport_message*, char* error_type, int error_code);
+
+
+#endif
diff --git a/include/opensrf/transport_session.h b/include/opensrf/transport_session.h
new file mode 100644 (file)
index 0000000..5aa8597
--- /dev/null
@@ -0,0 +1,234 @@
+// ---------------------------------------------------------------------------------
+// Manages the Jabber session.  Data is taken from the TCP object and pushed into
+// a SAX push parser as it arrives.  When key Jabber documetn elements are met, 
+// logic ensues.
+// ---------------------------------------------------------------------------------
+#include <opensrf/transport_message.h>
+
+#include <opensrf/utils.h>
+#include <opensrf/log.h>
+#include <opensrf/socket_bundle.h>
+
+#include "sha.h"
+
+#include <string.h>
+#include <libxml/globals.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h> /* only for xmlNewInputFromFile() */
+#include <libxml/tree.h>
+#include <libxml/debugXML.h>
+#include <libxml/xmlmemory.h>
+
+#ifndef TRANSPORT_SESSION_H
+#define TRANSPORT_SESSION_H
+
+#define CONNECTING_1 1 /* just starting the connection to Jabber */
+#define CONNECTING_2 2 /* First <stream> packet sent and <stream> packet received from server */
+
+/* Note. these are growing buffers, so all that's necessary is a sane starting point */
+#define JABBER_BODY_BUFSIZE            4096
+#define JABBER_SUBJECT_BUFSIZE 64      
+#define JABBER_THREAD_BUFSIZE          64      
+#define JABBER_JID_BUFSIZE                     64      
+#define JABBER_STATUS_BUFSIZE          16 
+
+// ---------------------------------------------------------------------------------
+// Takes data from the socket handler and pushes it directly into the push parser
+// ---------------------------------------------------------------------------------
+//void grab_incoming( void * session, char* data );
+void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent);
+
+// ---------------------------------------------------------------------------------
+// Callback for handling the startElement event.  Much of the jabber logic occurs
+// in this and the characterHandler callbacks.
+// Here we check for the various top level jabber elements: body, iq, etc.
+// ---------------------------------------------------------------------------------
+void startElementHandler( 
+               void *session, const xmlChar *name, const xmlChar **atts);
+
+// ---------------------------------------------------------------------------------
+// Callback for handling the endElement event.  Updates the Jabber state machine
+// to let us know the element is over.
+// ---------------------------------------------------------------------------------
+void endElementHandler( void *session, const xmlChar *name);
+
+// ---------------------------------------------------------------------------------
+// This is where we extract XML text content.  In particular, this is useful for
+// extracting Jabber message bodies.
+// ---------------------------------------------------------------------------------
+void characterHandler(
+               void *session, const xmlChar *ch, int len);
+
+void  parseWarningHandler( void *session, const char* msg, ... );
+void  parseErrorHandler( void *session, const char* msg, ... );
+
+// ---------------------------------------------------------------------------------
+// Tells the SAX parser which functions will be used as event callbacks
+// ---------------------------------------------------------------------------------
+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 */
+};
+
+// ---------------------------------------------------------------------------------
+// Our SAX handler pointer.
+// ---------------------------------------------------------------------------------
+static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
+
+// ---------------------------------------------------------------------------------
+// Jabber state machine.  This is how we know where we are in the Jabber
+// conversation.
+// ---------------------------------------------------------------------------------
+struct jabber_state_machine_struct {
+       int connected;
+       int connecting;
+       int in_message;
+       int in_message_body;
+       int in_thread;
+       int in_subject;
+       int in_error;
+       int in_message_error;
+       int in_iq;
+       int in_presence;
+       int in_status;
+};
+typedef struct jabber_state_machine_struct jabber_machine;
+
+
+enum TRANSPORT_AUTH_TYPE { AUTH_PLAIN, AUTH_DIGEST };
+
+// ---------------------------------------------------------------------------------
+// Transport session.  This maintains all the various parts of a session
+// ---------------------------------------------------------------------------------
+struct transport_session_struct {
+
+       /* our socket connection */
+       //transport_socket* sock_obj;
+       socket_manager* sock_mgr;
+
+       /* our Jabber state machine */
+       jabber_machine* state_machine;
+       /* our SAX push parser context */
+       xmlParserCtxtPtr parser_ctxt;
+
+       /* our text buffers for holding text data */
+       growing_buffer* body_buffer;
+       growing_buffer* subject_buffer;
+       growing_buffer* thread_buffer;
+       growing_buffer* from_buffer;
+       growing_buffer* recipient_buffer;
+       growing_buffer* status_buffer;
+       growing_buffer* message_error_type;
+       growing_buffer* session_id;
+       int message_error_code;
+
+       /* for OILS extenstions */
+       growing_buffer* router_to_buffer;
+       growing_buffer* router_from_buffer;
+       growing_buffer* router_class_buffer;
+       growing_buffer* router_command_buffer;
+       growing_buffer* osrf_xid_buffer;
+       int router_broadcast;
+
+       /* this can be anything.  It will show up in the 
+               callbacks for your convenience. Otherwise, it's
+               left untouched.  */
+       void* user_data;
+
+       char* server;
+       char* unix_path;
+       int     port;
+       int sock_id;
+
+       int component; /* true if we're a component */
+
+       /* the Jabber message callback */
+       void (*message_callback) ( void* user_data, transport_message* msg );
+       //void (iq_callback) ( void* user_data, transport_iq_message* iq );
+};
+typedef struct transport_session_struct transport_session;
+
+
+// ------------------------------------------------------------------
+// Allocates and initializes the necessary transport session
+// data structures.
+// If port > 0, then this session uses  TCP connection.  Otherwise,
+// if unix_path != NULL, it uses a UNIX domain socket.
+// ------------------------------------------------------------------
+transport_session* init_transport( const char* server, int port, 
+       const char* unix_path, void* user_data, int component );
+
+// ------------------------------------------------------------------
+// Returns the value of the given XML attribute
+// The xmlChar** construct is commonly returned from SAX event
+// handlers.  Pass that in with the name of the attribute you want
+// to retrieve.
+// ------------------------------------------------------------------
+char* get_xml_attr( const xmlChar** atts, char* attr_name );
+
+// ------------------------------------------------------------------
+// Waits  at most 'timeout' seconds  for data to arrive from the 
+// TCP handler. A timeout of -1 means to wait indefinitely.
+// ------------------------------------------------------------------
+int session_wait( transport_session* session, int timeout );
+
+// ---------------------------------------------------------------------------------
+// Sends the given Jabber message
+// ---------------------------------------------------------------------------------
+int session_send_msg( transport_session* session, transport_message* msg );
+
+// ---------------------------------------------------------------------------------
+// Returns 1 if this session is connected to the jabber server. 0 otherwise
+// ---------------------------------------------------------------------------------
+int session_connected( transport_session* );
+
+// ------------------------------------------------------------------
+// Deallocates session memory
+// ------------------------------------------------------------------
+int session_free( transport_session* session );
+
+// ------------------------------------------------------------------
+// Connects to the Jabber server.  Waits at most connect_timeout
+// seconds before failing
+// ------------------------------------------------------------------
+int session_connect( transport_session* session, 
+               const char* username, const char* password, 
+               const char* resource, int connect_timeout, 
+               enum TRANSPORT_AUTH_TYPE auth_type );
+
+int session_disconnect( transport_session* session );
+
+int reset_session_buffers( transport_session* session );
+
+#endif
diff --git a/include/opensrf/utils.h b/include/opensrf/utils.h
new file mode 100644 (file)
index 0000000..7bbade0
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+Copyright (C) 2005  Georgia Public Library Service 
+Bill Erickson <highfalutin@gmail.com>
+Mike Rylander <mrylander@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.
+*/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+//#include <sys/timeb.h>
+
+#include "md5.h"
+
+#define OSRF_MALLOC(ptr, size) \
+       do {\
+               ptr = (void*) malloc( size ); \
+               if( ptr == NULL ) { \
+                       perror("OSRF_MALLOC(): Out of Memory" );\
+                       exit(99); \
+               } \
+               memset( ptr, 0, size );\
+       } while(0)
+
+
+#define OSRF_BUFFER_ADD(gb, data) \
+       do {\
+               int __tl; \
+               if(gb && data) {\
+                       __tl = strlen(data) + gb->n_used;\
+                       if( __tl < gb->size ) {\
+                               strcat(gb->buf, data);\
+                               gb->n_used = __tl; \
+                       } else { buffer_add(gb, data); }\
+               }\
+       } while(0)
+
+#define OSRF_BUFFER_ADD_CHAR(gb, c)\
+       do {\
+               if(gb) {\
+                       if(gb->n_used < gb->size - 1)\
+                               gb->buf[gb->n_used++] = c;\
+                       else\
+                               buffer_add_char(gb, c);\
+               }\
+       }while(0)
+
+       
+
+
+/* turns a va_list into a string */
+#define VA_LIST_TO_STRING(x) \
+       unsigned long __len = 0;\
+       va_list args; \
+       va_list a_copy;\
+       va_copy(a_copy, args); \
+       va_start(args, x); \
+       __len = vsnprintf(NULL, 0, x, args); \
+       va_end(args); \
+       __len += 2; \
+       char _b[__len]; \
+       bzero(_b, __len); \
+       va_start(a_copy, x); \
+       vsnprintf(_b, __len - 1, x, a_copy); \
+       va_end(a_copy); \
+       char* VA_BUF = _b; \
+
+/* turns a long into a string */
+#define LONG_TO_STRING(l) \
+       unsigned int __len = snprintf(NULL, 0, "%ld", l) + 2;\
+       char __b[__len]; \
+       bzero(__b, __len); \
+       snprintf(__b, __len - 1, "%ld", l); \
+       char* LONGSTR = __b;
+
+#define DOUBLE_TO_STRING(l) \
+       unsigned int __len = snprintf(NULL, 0, "%f", l) + 2; \
+       char __b[__len]; \
+       bzero(__b, __len); \
+       snprintf(__b, __len - 1, "%f", l); \
+       char* DOUBLESTR = __b;
+
+#define LONG_DOUBLE_TO_STRING(l) \
+       unsigned int __len = snprintf(NULL, 0, "%Lf", l) + 2; \
+       char __b[__len]; \
+       bzero(__b, __len); \
+       snprintf(__b, __len - 1, "%Lf", l); \
+       char* LONGDOUBLESTR = __b;
+
+
+#define INT_TO_STRING(l) \
+       unsigned int __len = snprintf(NULL, 0, "%d", l) + 2; \
+       char __b[__len]; \
+       bzero(__b, __len); \
+       snprintf(__b, __len - 1, "%d", l); \
+       char* INTSTR = __b;
+
+
+/*
+#define MD5SUM(s) \
+       struct md5_ctx ctx; \
+       unsigned char digest[16];\
+       MD5_start (&ctx);\
+       int i;\
+       for ( i=0 ; i != strlen(text) ; i++ ) MD5_feed (&ctx, text[i]);\
+       MD5_stop (&ctx, digest);\
+       char buf[16];\
+       memset(buf,0,16);\
+       char final[256];\
+       memset(final,0,256);\
+       for ( i=0 ; i<16 ; i++ ) {\
+               sprintf(buf, "%02x", digest[i]);\
+               strcat( final, buf );\
+       }\
+       char* MD5STR = final;
+       */
+
+
+       
+
+
+#define BUFFER_MAX_SIZE 10485760 
+
+/* these are evil and should be condemned 
+       ! Only use these if you are done with argv[].
+       call init_proc_title() first, then call
+       set_proc_title. 
+       the title is only allowed to be as big as the
+       initial process name of the process (full size of argv[]).
+       truncation may occurr.
+ */
+int init_proc_title( int argc, char* argv[] );
+int set_proc_title( char* format, ... );
+
+
+int daemonize();
+
+void* safe_malloc(int size);
+
+// ---------------------------------------------------------------------------------
+// Generic growing buffer. Add data all you want
+// ---------------------------------------------------------------------------------
+struct growing_buffer_struct {
+       char *buf;
+       int n_used;
+       int size;
+};
+typedef struct growing_buffer_struct growing_buffer;
+
+growing_buffer* buffer_init( int initial_num_bytes);
+
+// XXX This isn't defined in utils.c!! removing for now...
+//int buffer_addchar(growing_buffer* gb, char c);
+
+int buffer_add(growing_buffer* gb, char* c);
+int buffer_fadd(growing_buffer* gb, const char* format, ... );
+int buffer_reset( growing_buffer* gb);
+char* buffer_data( growing_buffer* gb);
+char* buffer_release( growing_buffer* gb );
+int buffer_free( growing_buffer* gb );
+int buffer_add_char(growing_buffer* gb, char c);
+
+/* returns the size needed to fill in the vsnprintf buffer.  
+       * ! this calls va_end on the va_list argument*
+       */
+long va_list_size(const char* format, va_list);
+
+/* turns a va list into a string, caller must free the 
+       allocated char */
+char* va_list_to_string(const char* format, ...);
+
+
+/* string escape utility method.  escapes unicode embeded characters.
+       escapes the usual \n, \t, etc. 
+       for example, if you provide a string like so:
+
+       hello,
+               you
+
+       you would get back:
+       hello,\n\tyou
+ */
+char* uescape( const char* string, int size, int full_escape );
+
+/* utility methods */
+int set_fl( int fd, int flags );
+int clr_fl( int fd, int flags );
+
+
+
+// Utility method
+double get_timestamp_millis();
+
+
+/* returns true if the whole string is a number */
+int stringisnum(char* s);
+
+/* reads a file and returns the string version of the file
+       user is responsible for freeing the returned char*
+       */
+char* file_to_string(const char* filename);
+
+
+
+/** 
+  Calculates the md5 of the text provided.
+  The returned string must be freed by the caller.
+  */
+char* md5sum( char* text, ... );
+
+
+/**
+  Checks the validity of the file descriptor
+  returns -1 if the file descriptor is invalid
+  returns 0 if the descriptor is OK
+  */
+int osrfUtilsCheckFileDescriptor( int fd );
+
+#endif
diff --git a/include/opensrf/xml_utils.h b/include/opensrf/xml_utils.h
new file mode 100644 (file)
index 0000000..2b3a030
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _XML_UTILS_H
+#define _XML_UTILS_H
+
+#include <objson/object.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+jsonObject* xmlDocToJSON(xmlDocPtr doc);
+
+/* helper function */
+jsonObject* _xmlToJSON(xmlNodePtr node, jsonObject*);
+
+/* debug function, prints each node and content */
+void recurse_doc( xmlNodePtr node );
+
+
+/* turns an XML doc into a char*.  
+       User is responsible for freeing the returned char*
+       if(full), then we return the whole doc (xml declaration, etc.)
+       else we return the doc from the root node down
+       */
+char* xmlDocToString(xmlDocPtr doc, int full);
+
+
+/* Takes an xmlChar** from a SAX callback and returns the value
+       for the attribute with name 'name'
+       */
+char* xmlSaxAttr( const xmlChar** atts, char* name ); 
+
+/**
+  Sets the xml attributes from atts to the given dom node 
+ */
+int xmlAddAttrs( xmlNodePtr node, const xmlChar** atts );
+
+
+#endif
index 4ff75c2..0cf5657 100644 (file)
@@ -9,62 +9,12 @@ export PERLDIR                        = $(LIBDIR)/perl5
 export INCLUDEDIR              = $(PREFIX)/include
 
 export LDLIBS                  += 
-export LDFLAGS                 += -Wl,-rpath=$(LIBDIR) -L $(TMPDIR) -L . -L /opt/lib
-export CFLAGS                  += -pipe -g -Wall -O2 -fPIC -I$(LIBXML2_HEADERS) -I$(APACHE2_HEADERS) \
-                                                               -I$(LIBXML2_HEADERS)/libxml  -I$(TMP) \
-                                                               -I$(APR_HEADERS) -I$(TMPDIR)
-
-LIBOPENSRF                             = libopensrf.so
-
-OPENSRF_TARGETS =      libtransport/transport_session.o \
-                       libtransport/transport_client.o \
-                       libtransport/transport_message.o \
-                       libstack/osrf_app_session.o \
-                       libstack/osrf_stack.o \
-                       libstack/osrfConfig.o \
-                       libstack/osrf_settings.o \
-                       libstack/osrf_message.o \
-                       libstack/osrf_prefork.o \
-                       libstack/osrf_system.o \
-                       libstack/osrf_application.o \
-                       libstack/osrf_cache.o \
-                       libstack/xml_utils.o \
-                       libstack/osrf_transgroup.o \
-                       libstack/osrf_list.o \
-                       libstack/osrf_hash.o \
-                       utils/socket_bundle.o \
-                       utils/string_array.o \
-                       utils/utils.o \
-                       utils/log.o \
-                       utils/md5.o \
-                       utils/sha.o 
-
-OPENSRF_HEADERS =      libtransport/transport_session.h \
-                       libtransport/transport_client.h \
-                       libtransport/transport_message.h \
-                       libstack/osrf_app_session.h \
-                       libstack/osrf_stack.h \
-                       libstack/osrfConfig.h \
-                       libstack/osrf_settings.h \
-                       libstack/osrf_message.h \
-                       libstack/osrf_prefork.h \
-                       libstack/osrf_system.h \
-                       libstack/osrf_application.h \
-                       libstack/osrf_cache.h \
-                       libstack/xml_utils.h \
-                       libstack/osrf_transgroup.h \
-                       libstack/osrf_list.h \
-                       libstack/osrf_hash.h \
-                       utils/socket_bundle.h \
-                       utils/string_array.h \
-                       utils/utils.h \
-                       utils/log.h \
-                       utils/md5.h \
-                       utils/sha.h 
+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) 
 
 all:   prep \
-       libopensrf.so \
-       libstack/opensrf \
+       opensrf \
        router \
        srfsh \
        jserver \
@@ -79,56 +29,35 @@ install:    install-prep \
                perl-install \
                objson-install
 
-prep:
-       mkdir -p $(TMPDIR)
-
-libopensrf.so: objson/libobjson.so
-       @echo utils
-       make -C utils
-       @echo transport
-       make -C libtransport
-       @echo stack
-       make -C libstack
-       @echo $@
-       $(CC) -shared -W1 $(LDFLAGS) -lxml2 -lmemcache -lobjson $(OPENSRF_TARGETS) -o $(TMPDIR)/$(LIBOPENSRF)
-       @echo apps
-       make -C  c-apps
 
+# --------------------------------------------------------------------------------
+# BINARIES and LIBS
+# --------------------------------------------------------------------------------
 
-opensrf-install:       objson-install
-       @echo $@
-       cp $(TMPDIR)/$(LIBOPENSRF) $(LIBDIR)/$(LIBOPENSRF)
-       cp $(OPENSRF_HEADERS) $(INCLUDEDIR)/opensrf/
-       cp libstack/opensrf $(BINDIR)/opensrf-c
-       make -C c-apps install
-
+prep:
+       mkdir -p $(TMPDIR)
 
 objson/libobjson.so:   prep
        @echo $@
        make -C objson
 
-# --------------------------------------------------------------------------------
-# BINARIES
-# --------------------------------------------------------------------------------
-libstack/opensrf.o:    libstack/opensrf.c libopensrf.so
-libstack/opensrf:      libstack/opensrf.o
-       @echo $@
-       $(CC) $(CFLAGS) $(LDFLAGS) -lxml2 -lopensrf -lobjson libstack/opensrf.o -o $@
-       
+opensrf:       objson/libobjson.so
+       make -C libopensrf
+       make -C c-apps
 
-router: libopensrf.so
+router: opensrf
        @echo $@
        make -C router 
 
-srfsh: libopensrf.so
+srfsh: opensrf
        @echo $@
        make -C srfsh
 
-gateway:       libopensrf.so
+gateway:       opensrf
        @echo $@
        make -C gateway
 
-jserver:       libopensrf.so
+jserver:       opensrf
        @echo $@
        make -C jserver
 
@@ -136,22 +65,32 @@ jserver:   libopensrf.so
 # --------------------------------------------------------------------------------
 # INSTALL
 # --------------------------------------------------------------------------------
+
+# make sure the install directories exist
 install-prep:  
        @echo $@
        mkdir -p $(LIBDIR)
        mkdir -p $(BINDIR)
        mkdir -p $(PERLDIR)
        mkdir -p $(INCLUDEDIR)
-       mkdir -p $(INCLUDEDIR)/$(OPENSRF)
        mkdir -p $(ETCDIR)
 
 objson-install:        install-prep 
        @echo $@
        make -C objson install
 
-libopensrf-install:    install-prep
+# installs libopensrf.so, opensrf-c, headers, example configs, and osrf_ctl.sh
+opensrf-install:       objson-install
        @echo $@
-       cp $(TMPDIR)/$(LIBOPENSRF) $(LIBDIR)
+       cp $(TMPDIR)/libopensrf.so $(LIBDIR)/libopensrf.so
+       cp -r ../include/opensrf $(INCLUDEDIR)
+       cp libopensrf/opensrf $(BINDIR)/opensrf-c
+       make -C c-apps install
+       cp ../examples/bootstrap.conf.example $(ETCDIR)
+       cp ../bin/osrf_ctl.sh $(BINDIR)
+       cp ../examples/opensrf.xml.example $(ETCDIR)
+       cp ../examples/opensrf_core.xml.example $(ETCDIR)
+       cp ../examples/srfsh.xml.example $(ETCDIR)
 
 gateway-install:       install-prep opensrf-install    
        @echo $@
@@ -172,28 +111,19 @@ jserver-install:  install-prep
 perl-install:  install-prep
        @echo $@
        cp -r perlmods/* $(PERLDIR)/
-       cp ../examples/bootstrap.conf.example $(ETCDIR)
-       cp ../bin/osrf_ctl.sh $(BINDIR)
-       cp ../examples/opensrf.xml.example $(ETCDIR)
-       cp ../examples/opensrf_core.xml.example $(ETCDIR)
-       cp ../examples/srfsh.xml.example $(ETCDIR)
 
 
 # --------------------------------------------------------------------------------
 # CLEAN        
 # --------------------------------------------------------------------------------
 clean:
-       @echo $@
-       make -C libtransport clean
-       make -C libstack clean
+       make -C libopensrf clean
        make -C router clean
        make -C gateway clean
        make -C jserver clean
-       make -C utils clean
        make -C objson clean
        make -C srfsh clean
        make -C c-apps clean
-       echo "Removing directory [$(TMPDIR)]"
        /bin/rm -rf $(TMPDIR) *.o
 
 
diff --git a/src/libopensrf/basic_client.c b/src/libopensrf/basic_client.c
new file mode 100644 (file)
index 0000000..5cf8f76
--- /dev/null
@@ -0,0 +1,81 @@
+#include <opensrf/transport_client.h>
+#include "signal.h"
+
+pid_t pid;
+void sig_int( int sig ) {
+       fprintf(stderr, "Killing child %d\n", pid );
+       kill( pid, SIGKILL );
+}
+
+/* connects and registers with the router */
+int main( int argc, char** argv ) {
+
+       if( argc < 5 ) {
+               osrfLogError( OSRF_LOG_MARK, "Usage: %s <username> <host> <resource> <recipient> \n", argv[0] );
+               return 99;
+       }
+
+       transport_message* send;
+       transport_client* client = client_init( argv[2], 5222, 0 );
+
+       // try to connect, allow 15 second connect timeout 
+       if( client_connect( client, argv[1], "jkjkasdf", argv[3], 15, AUTH_DIGEST ) ) 
+               osrfLogInfo(OSRF_LOG_MARK, "Connected...\n");
+        else { 
+               osrfLogError( OSRF_LOG_MARK, "NOT Connected...\n" ); 
+               return -1;
+        }
+       
+       if( (pid=fork()) ) { /* parent */
+
+               signal(SIGINT, sig_int);
+               fprintf(stderr, "Listener: %ld\n", (long) getpid() );   
+               char buf[300];
+               memset(buf, 0, 300);
+               printf("=> ");
+
+               while( fgets( buf, 299, stdin) ) {
+
+                       // remove newline
+                       buf[strlen(buf)-1] = '\0';
+
+                       if( strcmp(buf, "exit")==0) { 
+                               client_free( client );  
+                               break; 
+                       }
+
+                       send = message_init( buf, "", "123454321", argv[4], NULL );
+                       client_send_message( client, send );
+                       message_free( send );
+                       printf("\n=> ");
+                       memset(buf, 0, 300);
+               }
+               fprintf(stderr, "Killing child %d\n", pid );
+               kill( pid, SIGKILL );
+               return 0;
+
+       } else {
+
+               fprintf(stderr, "Sender: %ld\n", (long) getpid() );     
+
+               transport_message* recv;
+               while( (recv=client_recv( client, -1)) ) {
+                       if( recv->is_error )
+                               fprintf( stderr, "\nReceived Error\t: ------------------\nFrom:\t\t"
+                                       "%s\nRouterFrom:\t%s\nBody:\t\t%s\nType %s\nCode %d\n=> ", 
+                                       recv->sender, recv->router_from, recv->body, recv->error_type, recv->error_code );
+                       else
+                               fprintf( stderr, "\nReceived\t: ------------------\nFrom:\t\t"
+                                       "%s\nRouterFrom:\t%s\nBody:\t\t%s\n=> ", recv->sender, recv->router_from, recv->body );
+
+                       message_free( recv );
+               }
+
+       }
+       return 0;
+
+}
+
+
+
+
diff --git a/src/libopensrf/component.c b/src/libopensrf/component.c
new file mode 100644 (file)
index 0000000..c63dd44
--- /dev/null
@@ -0,0 +1,52 @@
+#include <opensrf/transport_client.h>
+#include "signal.h"
+
+
+/*
+void print_stuff(void* blah, char* data) {
+       fprintf(stderr, "Received from socket: %s\n", data);
+}
+*/
+
+/* connects and registers with the router */
+int main( int argc, char** argv ) {
+
+
+
+       if( argc < 5 ) {
+               osrfLogError(OSRF_LOG_MARK,  "Usage: %s <server> <port> <name> <secret>", argv[0] );
+               return -1;
+       }
+
+       int port = atoi(argv[2]);
+       transport_client* client = client_init( argv[1], port, 1 );
+
+       // try to connect, allow 15 second connect timeout 
+       if( client_connect( client, argv[3], argv[4], "", 15, 1 ) ) 
+               osrfLogInfo(OSRF_LOG_MARK, "Connected...\n");
+        else  {
+               osrfLogError(OSRF_LOG_MARK,  "NOT Connected...\n" ); 
+               return -1;
+        }
+       
+       transport_message* recv;
+       while( (recv=client_recv( client, -1)) ) {
+               if( recv->is_error )
+                       fprintf( stderr, "\nReceived Error\t: ------------------\nFrom:\t\t"
+                               "%s\nRouterFrom:\t%s\nBody:\t\t%s\nType %s\nCode %d\n=> ", 
+                               recv->sender, recv->router_from, recv->body, recv->error_type, recv->error_code );
+               else
+                       fprintf( stderr, "\nReceived\t: ------------------\nFrom:\t\t"
+                               "%s\nRouterFrom:\t%s\nBody:\t\t%s\n=> ", recv->sender, recv->router_from, recv->body );
+               transport_message* send = message_init( "Hello...", "", "123454321", recv->sender, argv[3] );
+               client_send_message( client, send );
+               message_free( recv );
+               message_free( send );
+       }
+       return 0;
+
+}
+
+
+
+
diff --git a/src/libopensrf/log.c b/src/libopensrf/log.c
new file mode 100644 (file)
index 0000000..35d2756
--- /dev/null
@@ -0,0 +1,252 @@
+#include <opensrf/log.h>
+
+static int _osrfLogType                                = -1;
+static int _osrfLogFacility                    = LOG_LOCAL0;
+static int _osrfLogActFacility         = LOG_LOCAL1;
+static char* _osrfLogFile                      = NULL;
+static char* _osrfLogAppname           = NULL;
+static int _osrfLogLevel                       = OSRF_LOG_INFO;
+static int _osrfLogActivityEnabled     = 1;
+static int _osrfLogIsClient         = 0;
+
+static char* _osrfLogXid            = NULL; /* current xid */
+static char* _osrfLogXidPfx         = NULL; /* xid prefix string */
+
+static void osrfLogSetType( int logtype );
+static void _osrfLogDetail( int level, const char* filename, int line, char* msg );
+static void _osrfLogToFile( char* msg, ... );
+static void _osrfLogSetXid(char* xid);
+
+#define OSRF_LOG_GO(f,li,m,l)  \
+        if(!m) return;         \
+        VA_LIST_TO_STRING(m);  \
+        _osrfLogDetail( l, f, li, VA_BUF );
+
+void osrfLogCleanup() {
+       free(_osrfLogAppname);
+       free(_osrfLogFile);
+}
+
+
+void osrfLogInit( int type, const char* appname, int maxlevel ) {
+       osrfLogSetType(type);
+       if(appname) osrfLogSetAppname(appname);
+       osrfLogSetLevel(maxlevel);
+       if( type == OSRF_LOG_TYPE_SYSLOG ) 
+               openlog(_osrfLogAppname, 0, _osrfLogFacility );
+}
+
+static void _osrfLogSetXid(char* xid) {
+   if(xid) {
+      if(_osrfLogXid) free(_osrfLogXid);
+      _osrfLogXid = strdup(xid);
+   }
+}
+
+void osrfLogClearXid() { _osrfLogSetXid(""); }
+void osrfLogSetXid(char* xid) {
+   if(!_osrfLogIsClient) _osrfLogSetXid(xid);
+}
+
+void osrfLogMkXid() {
+   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);
+      _osrfLogSetXid(buf);
+      _osrfLogXidInc++;
+   }
+}
+
+char* osrfLogGetXid() {
+   return _osrfLogXid;
+}
+
+void osrfLogSetIsClient(int is) {
+   _osrfLogIsClient = 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());
+   _osrfLogXidPfx = buff;
+}
+
+/** Sets the type of logging to perform.  See log types */
+static void osrfLogSetType( int logtype ) { 
+       if( logtype != OSRF_LOG_TYPE_FILE &&
+                       logtype != OSRF_LOG_TYPE_SYSLOG ) {
+               fprintf(stderr, "Unrecognized log type.  Logging to stderr\n");
+               return;
+       }
+       _osrfLogType = logtype;
+}
+
+void osrfLogSetFile( const char* logfile ) {
+       if(!logfile) return;
+       if(_osrfLogFile) free(_osrfLogFile);
+       _osrfLogFile = strdup(logfile);
+}
+
+void osrfLogSetActivityEnabled( int enabled ) {
+       _osrfLogActivityEnabled = enabled;
+}
+
+void osrfLogSetAppname( const char* appname ) {
+       if(!appname) return;
+       if(_osrfLogAppname) free(_osrfLogAppname);
+       _osrfLogAppname = strdup(appname);
+
+       /* if syslogging, re-open the log with the appname */
+       if( _osrfLogType == OSRF_LOG_TYPE_SYSLOG) {
+               closelog();
+               openlog(_osrfLogAppname, 0, _osrfLogFacility);
+       }
+}
+
+void osrfLogSetSyslogFacility( int facility ) {
+       _osrfLogFacility = facility;
+}
+void osrfLogSetSyslogActFacility( int facility ) {
+       _osrfLogActFacility = facility;
+}
+
+/** Sets the global log level.  Any log statements with a higher level
+ * than "level" will not be logged */
+void osrfLogSetLevel( int loglevel ) {
+       _osrfLogLevel = loglevel;
+}
+
+/** Gets the current global log level. **/
+int osrfLogGetLevel( void ) {
+       return _osrfLogLevel;
+}
+
+void osrfLogError( const char* file, int line, const char* msg, ... ) 
+       { OSRF_LOG_GO(file, line, msg, OSRF_LOG_ERROR); }
+void osrfLogWarning( const char* file, int line, const char* msg, ... ) 
+       { OSRF_LOG_GO(file, line, msg, OSRF_LOG_WARNING); }
+void osrfLogInfo( const char* file, int line, const char* msg, ... ) 
+       { OSRF_LOG_GO(file, line, msg, OSRF_LOG_INFO); }
+void osrfLogDebug( const char* file, int line, const char* msg, ... ) 
+       { OSRF_LOG_GO(file, line, msg, OSRF_LOG_DEBUG); }
+void osrfLogInternal( const char* file, int line, const char* msg, ... ) 
+       { OSRF_LOG_GO(file, line, msg, OSRF_LOG_INTERNAL); }
+void osrfLogActivity( const char* file, int line, const char* msg, ... ) { 
+       OSRF_LOG_GO(file, line, msg, OSRF_LOG_ACTIVITY); 
+       _osrfLogDetail( OSRF_LOG_INFO, file, line, VA_BUF ); /* also log at info level */
+}
+
+/** Actually does the logging */
+static void _osrfLogDetail( int level, const char* filename, int line, char* msg ) {
+
+       if( level == OSRF_LOG_ACTIVITY && ! _osrfLogActivityEnabled ) return;
+       if( level > _osrfLogLevel ) return;
+       if(!msg) return;
+       if(!filename) filename = "";
+
+       char* l = "INFO";               /* level name */
+       int lvl = LOG_INFO;     /* syslog level */
+       int fac = _osrfLogFacility;
+
+       switch( level ) {
+               case OSRF_LOG_ERROR:            
+                       l = "ERR "; 
+                       lvl = LOG_ERR;
+                       break;
+
+               case OSRF_LOG_WARNING:  
+                       l = "WARN"; 
+                       lvl = LOG_WARNING;
+                       break;
+
+               case OSRF_LOG_INFO:             
+                       l = "INFO"; 
+                       lvl = LOG_INFO;
+                       break;
+
+               case OSRF_LOG_DEBUG:    
+                       l = "DEBG"; 
+                       lvl = LOG_DEBUG;
+                       break;
+
+               case OSRF_LOG_INTERNAL: 
+                       l = "INT "; 
+                       lvl = LOG_DEBUG;
+                       break;
+
+               case OSRF_LOG_ACTIVITY: 
+                       l = "ACT"; 
+                       lvl = LOG_INFO;
+                       fac = _osrfLogActFacility;
+                       break;
+       }
+
+   char* xid = (_osrfLogXid) ? _osrfLogXid : "";
+
+       if(_osrfLogType == OSRF_LOG_TYPE_SYSLOG ) {
+               char buf[1536];  
+               memset(buf, 0x0, 1536);
+               /* give syslog some breathing room, and be cute about it */
+               strncat(buf, msg, 1535);
+               buf[1532] = '.';
+               buf[1533] = '.';
+               buf[1534] = '.';
+               buf[1535] = '\0';
+               syslog( fac | lvl, "[%s:%ld:%s:%d:%s] %s", l, (long) getpid(), filename, line, xid, buf );
+       }
+
+       else if( _osrfLogType == OSRF_LOG_TYPE_FILE )
+               _osrfLogToFile("[%s:%ld:%s:%d:%s] %s", l, (long) getpid(), filename, line, xid, msg );
+
+}
+
+
+static void _osrfLogToFile( char* msg, ... ) {
+
+       if(!msg) return;
+       if(!_osrfLogFile) return;
+       VA_LIST_TO_STRING(msg);
+
+       if(!_osrfLogAppname) _osrfLogAppname = strdup("osrf");
+       int l = strlen(VA_BUF) + strlen(_osrfLogAppname) + 36;
+       char buf[l];
+       bzero(buf,l);
+
+       char datebuf[36];
+       bzero(datebuf,36);
+       time_t t = time(NULL);
+       struct tm* tms = localtime(&t);
+       strftime(datebuf, 36, "%Y-%m-%d %H:%M:%S", tms);
+
+       FILE* file = fopen(_osrfLogFile, "a");
+       if(!file) {
+               fprintf(stderr, "Unable to fopen file %s for writing\n", _osrfLogFile);
+               return;
+       }
+
+       fprintf(file, "%s %s %s\n", _osrfLogAppname, datebuf, VA_BUF );
+       if( fclose(file) != 0 ) 
+               osrfLogWarning(OSRF_LOG_MARK, "Error closing log file: %s", strerror(errno));
+
+}
+
+
+int osrfLogFacilityToInt( char* facility ) {
+       if(!facility) return LOG_LOCAL0;
+       if(strlen(facility) < 6) return LOG_LOCAL0;
+       switch( facility[5] ) {
+               case '0': return LOG_LOCAL0;
+               case '1': return LOG_LOCAL1;
+               case '2': return LOG_LOCAL2;
+               case '3': return LOG_LOCAL3;
+               case '4': return LOG_LOCAL4;
+               case '5': return LOG_LOCAL5;
+               case '6': return LOG_LOCAL6;
+               case '7': return LOG_LOCAL7;
+       }
+       return LOG_LOCAL0;
+}
+
+
diff --git a/src/libopensrf/md5.c b/src/libopensrf/md5.c
new file mode 100644 (file)
index 0000000..c8d7688
--- /dev/null
@@ -0,0 +1,367 @@
+/* --- The data --- */
+
+const char data[] =
+"/* --- The MD5 routines --- */\n\n/* MD5 routines, after Ron R"
+"ivest */\n/* Written by David Madore <david.madore@ens.fr>, w"
+"ith code taken in\n * part from Colin Plumb. */\n/* Public dom"
+"ain (1999/11/24) */\n\n/* Note: these routines do not depend o"
+"n endianness. */\n\n/* === The header === */\n\n/* Put this in m"
+"d5.h if you don't like having everything in one big\n * file."
+" */\n\n#ifndef _DMADORE_MD5_H\n#define _DMADORE_MD5_H\n\nstruct m"
+"d5_ctx {\n  /* The four chaining variables */\n  unsigned long"
+" buf[4];\n  /* Count number of message bits */\n  unsigned lon"
+"g bits[2];\n  /* Data being fed in */\n  unsigned long in[16];"
+"\n  /* Our position within the 512 bits (always between 0 and"
+" 63) */\n  int b;\n};\n\nvoid MD5_transform (unsigned long buf[4"
+"], const unsigned long in[16]);\nvoid MD5_start (struct md5_c"
+"tx *context);\nvoid MD5_feed (struct md5_ctx *context, unsign"
+"ed char inb);\nvoid MD5_stop (struct md5_ctx *context, unsign"
+"ed char digest[16]);\n\n#endif /* not defined _DMADORE_MD5_H *"
+"/\n\n/* === The implementation === */\n\n#define F1(x, y, z) (z "
+"^ (x & (y ^ z)))\n#define F2(x, y, z) F1(z, x, y)\n#define F3("
+"x, y, z) (x ^ y ^ z)\n#define F4(x, y, z) (y ^ (x | ~z))\n\n#de"
+"fine MD5STEP(f, w, x, y, z, data, s) \\\n\t{ w += f (x, y, z) +"
+" data;  w = w<<s | (w&0xffffffffUL)>>(32-s); \\\n\t  w += x; }\n"
+"\nvoid\nMD5_transform (unsigned long buf[4], const unsigned lo"
+"ng in[16])\n{\n  register unsigned long a, b, c, d;\n\n  a = buf"
+"[0];  b = buf[1];  c = buf[2];  d = buf[3];\n  MD5STEP(F1, a,"
+" b, c, d, in[0] + 0xd76aa478UL, 7);\n  MD5STEP(F1, d, a, b, c"
+", in[1] + 0xe8c7b756UL, 12);\n  MD5STEP(F1, c, d, a, b, in[2]"
+" + 0x242070dbUL, 17);\n  MD5STEP(F1, b, c, d, a, in[3] + 0xc1"
+"bdceeeUL, 22);\n  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU"
+"L, 7);\n  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aUL, 12);\n"
+"  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613UL, 17);\n  MD5ST"
+"EP(F1, b, c, d, a, in[7] + 0xfd469501UL, 22);\n  MD5STEP(F1, "
+"a, b, c, d, in[8] + 0x698098d8UL, 7);\n  MD5STEP(F1, d, a, b,"
+" c, in[9] + 0x8b44f7afUL, 12);\n  MD5STEP(F1, c, d, a, b, in["
+"10] + 0xffff5bb1UL, 17);\n  MD5STEP(F1, b, c, d, a, in[11] + "
+"0x895cd7beUL, 22);\n  MD5STEP(F1, a, b, c, d, in[12] + 0x6b90"
+"1122UL, 7);\n  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193UL,"
+" 12);\n  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eUL, 17);\n"
+"  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821UL, 22);\n  MD5S"
+"TEP(F2, a, b, c, d, in[1] + 0xf61e2562UL, 5);\n  MD5STEP(F2, "
+"d, a, b, c, in[6] + 0xc040b340UL, 9);\n  MD5STEP(F2, c, d, a,"
+" b, in[11] + 0x265e5a51UL, 14);\n  MD5STEP(F2, b, c, d, a, in"
+"[0] + 0xe9b6c7aaUL, 20);\n  MD5STEP(F2, a, b, c, d, in[5] + 0"
+"xd62f105dUL, 5);\n  MD5STEP(F2, d, a, b, c, in[10] + 0x024414"
+"53UL, 9);\n  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681UL, 1"
+"4);\n  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8UL, 20);\n  M"
+"D5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6UL, 5);\n  MD5STEP(F"
+"2, d, a, b, c, in[14] + 0xc33707d6UL, 9);\n  MD5STEP(F2, c, d"
+", a, b, in[3] + 0xf4d50d87UL, 14);\n  MD5STEP(F2, b, c, d, a,"
+" in[8] + 0x455a14edUL, 20);\n  MD5STEP(F2, a, b, c, d, in[13]"
+" + 0xa9e3e905UL, 5);\n  MD5STEP(F2, d, a, b, c, in[2] + 0xfce"
+"fa3f8UL, 9);\n  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9UL,"
+" 14);\n  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aUL, 20);\n"
+"  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942UL, 4);\n  MD5STE"
+"P(F3, d, a, b, c, in[8] + 0x8771f681UL, 11);\n  MD5STEP(F3, c"
+", d, a, b, in[11] + 0x6d9d6122UL, 16);\n  MD5STEP(F3, b, c, d"
+", a, in[14] + 0xfde5380cUL, 23);\n  MD5STEP(F3, a, b, c, d, i"
+"n[1] + 0xa4beea44UL, 4);\n  MD5STEP(F3, d, a, b, c, in[4] + 0"
+"x4bdecfa9UL, 11);\n  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b"
+"60UL, 16);\n  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70UL, "
+"23);\n  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6UL, 4);\n  "
+"MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faUL, 11);\n  MD5STEP"
+"(F3, c, d, a, b, in[3] + 0xd4ef3085UL, 16);\n  MD5STEP(F3, b,"
+" c, d, a, in[6] + 0x04881d05UL, 23);\n  MD5STEP(F3, a, b, c, "
+"d, in[9] + 0xd9d4d039UL, 4);\n  MD5STEP(F3, d, a, b, c, in[12"
+"] + 0xe6db99e5UL, 11);\n  MD5STEP(F3, c, d, a, b, in[15] + 0x"
+"1fa27cf8UL, 16);\n  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac566"
+"5UL, 23);\n  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244UL, 6)"
+";\n  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97UL, 10);\n  MD5"
+"STEP(F4, c, d, a, b, in[14] + 0xab9423a7UL, 15);\n  MD5STEP(F"
+"4, b, c, d, a, in[5] + 0xfc93a039UL, 21);\n  MD5STEP(F4, a, b"
+", c, d, in[12] + 0x655b59c3UL, 6);\n  MD5STEP(F4, d, a, b, c,"
+" in[3] + 0x8f0ccc92UL, 10);\n  MD5STEP(F4, c, d, a, b, in[10]"
+" + 0xffeff47dUL, 15);\n  MD5STEP(F4, b, c, d, a, in[1] + 0x85"
+"845dd1UL, 21);\n  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU"
+"L, 6);\n  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0UL, 10);"
+"\n  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314UL, 15);\n  MD5S"
+"TEP(F4, b, c, d, a, in[13] + 0x4e0811a1UL, 21);\n  MD5STEP(F4"
+", a, b, c, d, in[4] + 0xf7537e82UL, 6);\n  MD5STEP(F4, d, a, "
+"b, c, in[11] + 0xbd3af235UL, 10);\n  MD5STEP(F4, c, d, a, b, "
+"in[2] + 0x2ad7d2bbUL, 15);\n  MD5STEP(F4, b, c, d, a, in[9] +"
+" 0xeb86d391UL, 21);\n  buf[0] += a;  buf[1] += b;  buf[2] += "
+"c;  buf[3] += d;\n}\n\n#undef F1\n#undef F2\n#undef F3\n#undef F4\n"
+"#undef MD5STEP\n\nvoid\nMD5_start (struct md5_ctx *ctx)\n{\n  int"
+" i;\n\n  ctx->buf[0] = 0x67452301UL;\n  ctx->buf[1] = 0xefcdab8"
+"9UL;\n  ctx->buf[2] = 0x98badcfeUL;\n  ctx->buf[3] = 0x1032547"
+"6UL;\n  ctx->bits[0] = 0;\n  ctx->bits[1] = 0;\n  for ( i=0 ; i"
+"<16 ; i++ )\n    ctx->in[i] = 0;\n  ctx->b = 0;\n}\n\nvoid\nMD5_fe"
+"ed (struct md5_ctx *ctx, unsigned char inb)\n{\n  int i;\n  uns"
+"igned long temp;\n\n  ctx->in[ctx->b/4] |= ((unsigned long)inb"
+") << ((ctx->b%4)*8);\n  if ( ++ctx->b >= 64 )\n    {\n      MD5"
+"_transform (ctx->buf, ctx->in);\n      ctx->b = 0;\n      for "
+"( i=0 ; i<16 ; i++ )\n\tctx->in[i] = 0;\n    }\n  temp = ctx->bi"
+"ts[0];\n  ctx->bits[0] += 8;\n  if ( (temp&0xffffffffUL) > (ct"
+"x->bits[0]&0xffffffffUL) )\n    ctx->bits[1]++;\n}\n\nvoid\nMD5_s"
+"top (struct md5_ctx *ctx, unsigned char digest[16])\n{\n  int "
+"i;\n  unsigned long bits[2];\n\n  for ( i=0 ; i<2 ; i++ )\n    b"
+"its[i] = ctx->bits[i];\n  MD5_feed (ctx, 0x80);\n  for ( ; ctx"
+"->b!=56 ; )\n    MD5_feed (ctx, 0);\n  for ( i=0 ; i<2 ; i++ )"
+"\n    {\n      MD5_feed (ctx, bits[i]&0xff);\n      MD5_feed (c"
+"tx, (bits[i]>>8)&0xff);\n      MD5_feed (ctx, (bits[i]>>16)&0"
+"xff);\n      MD5_feed (ctx, (bits[i]>>24)&0xff);\n    }\n  for "
+"( i=0 ; i<4 ; i++ )\n    {\n      digest[4*i] = ctx->buf[i]&0x"
+"ff;\n      digest[4*i+1] = (ctx->buf[i]>>8)&0xff;\n      diges"
+"t[4*i+2] = (ctx->buf[i]>>16)&0xff;\n      digest[4*i+3] = (ct"
+"x->buf[i]>>24)&0xff;\n    }\n}\n\f\n/* --- The core of the progra"
+"m --- */\n\n#include <stdio.h>\n#include <string.h>\n\n#define LA"
+"RGE_ENOUGH 16384\n\nchar buffer[LARGE_ENOUGH];\n\nint\nmain (int "
+"argc, char *argv[])\n{\n  unsigned int i;\n\n  buffer[0] = 0;\n  "
+"strcat (buffer, \"/* --- The data --- */\\n\\n\");\n  strcat (buf"
+"fer, \"const char data[] =\");\n  for ( i=0 ; data[i] ; i++ )\n "
+"   {\n      if ( i%60 == 0 )\n\tstrcat (buffer, \"\\n\\\"\");\n      "
+"switch ( data[i] )\n\t{\n\tcase '\\\\':\n\tcase '\"':\n\t  strcat (buff"
+"er, \"\\\\\");\n\t  buffer[strlen(buffer)+1] = 0;\n\t  buffer[strlen"
+"(buffer)] = data[i];\n\t  break;\n\tcase '\\n':\n\t  strcat (buffer"
+", \"\\\\n\");\n\t  break;\n\tcase '\\t':\n\t  strcat (buffer, \"\\\\t\");\n\t"
+"  break;\n\tcase '\\f':\n\t  strcat (buffer, \"\\\\f\");\n\t  break;\n\td"
+"efault:\n\t  buffer[strlen(buffer)+1] = 0;\n\t  buffer[strlen(bu"
+"ffer)] = data[i];\n\t}\n      if ( i%60 == 59 || !data[i+1] )\n\t"
+"strcat (buffer, \"\\\"\");\n    }\n  strcat (buffer, \";\\n\\f\\n\");\n "
+" strcat (buffer, data);\n  if ( argc >= 2 && strcmp (argv[1],"
+" \"xyzzy\") == 0 )\n    printf (\"%s\", buffer);\n  else\n    {\n   "
+"   struct md5_ctx ctx;\n      unsigned char digest[16];\n\n    "
+"  MD5_start (&ctx);\n      for ( i=0 ; buffer[i] ; i++ )\n\tMD5"
+"_feed (&ctx, buffer[i]);\n      MD5_stop (&ctx, digest);\n    "
+"  for ( i=0 ; i<16 ; i++ )\n\tprintf (\"%02x\", digest[i]);\n    "
+"  printf (\"\\n\");\n    }\n  return 0;\n}\n";
+
+
+//#include "md5.h"
+#include "opensrf/md5.h"
+
+
+/* --- The MD5 routines --- */
+
+/* MD5 routines, after Ron Rivest */
+/* Written by David Madore <david.madore@ens.fr>, with code taken in
+ * part from Colin Plumb. */
+/* Public domain (1999/11/24) */
+
+/* Note: these routines do not depend on endianness. */
+
+/* === The header === */
+
+/* Put this in md5.h if you don't like having everything in one big
+ * file. */
+
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, data, s) \
+       { w += f (x, y, z) + data;  w = w<<s | (w&0xffffffffUL)>>(32-s); \
+         w += x; }
+
+void
+MD5_transform (unsigned long buf[4], const unsigned long in[16])
+{
+  register unsigned long a, b, c, d;
+
+  a = buf[0];  b = buf[1];  c = buf[2];  d = buf[3];
+  MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478UL, 7);
+  MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756UL, 12);
+  MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbUL, 17);
+  MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeUL, 22);
+  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafUL, 7);
+  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aUL, 12);
+  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613UL, 17);
+  MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501UL, 22);
+  MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8UL, 7);
+  MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afUL, 12);
+  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1UL, 17);
+  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beUL, 22);
+  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122UL, 7);
+  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193UL, 12);
+  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eUL, 17);
+  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821UL, 22);
+  MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562UL, 5);
+  MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340UL, 9);
+  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51UL, 14);
+  MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaUL, 20);
+  MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dUL, 5);
+  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453UL, 9);
+  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681UL, 14);
+  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8UL, 20);
+  MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6UL, 5);
+  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6UL, 9);
+  MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87UL, 14);
+  MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edUL, 20);
+  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905UL, 5);
+  MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8UL, 9);
+  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9UL, 14);
+  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aUL, 20);
+  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942UL, 4);
+  MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681UL, 11);
+  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122UL, 16);
+  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cUL, 23);
+  MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44UL, 4);
+  MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9UL, 11);
+  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60UL, 16);
+  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70UL, 23);
+  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6UL, 4);
+  MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faUL, 11);
+  MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085UL, 16);
+  MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05UL, 23);
+  MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039UL, 4);
+  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5UL, 11);
+  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8UL, 16);
+  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665UL, 23);
+  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244UL, 6);
+  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97UL, 10);
+  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7UL, 15);
+  MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039UL, 21);
+  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3UL, 6);
+  MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92UL, 10);
+  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dUL, 15);
+  MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1UL, 21);
+  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fUL, 6);
+  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0UL, 10);
+  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314UL, 15);
+  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1UL, 21);
+  MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82UL, 6);
+  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235UL, 10);
+  MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbUL, 15);
+  MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391UL, 21);
+  buf[0] += a;  buf[1] += b;  buf[2] += c;  buf[3] += d;
+}
+
+#undef F1
+#undef F2
+#undef F3
+#undef F4
+#undef MD5STEP
+
+void
+MD5_start (struct md5_ctx *ctx)
+{
+  int i;
+
+  ctx->buf[0] = 0x67452301UL;
+  ctx->buf[1] = 0xefcdab89UL;
+  ctx->buf[2] = 0x98badcfeUL;
+  ctx->buf[3] = 0x10325476UL;
+  ctx->bits[0] = 0;
+  ctx->bits[1] = 0;
+  for ( i=0 ; i<16 ; i++ )
+    ctx->in[i] = 0;
+  ctx->b = 0;
+}
+
+void
+MD5_feed (struct md5_ctx *ctx, unsigned char inb)
+{
+  int i;
+  unsigned long temp;
+
+  ctx->in[ctx->b/4] |= ((unsigned long)inb) << ((ctx->b%4)*8);
+  if ( ++ctx->b >= 64 )
+    {
+      MD5_transform (ctx->buf, ctx->in);
+      ctx->b = 0;
+      for ( i=0 ; i<16 ; i++ )
+       ctx->in[i] = 0;
+    }
+  temp = ctx->bits[0];
+  ctx->bits[0] += 8;
+  if ( (temp&0xffffffffUL) > (ctx->bits[0]&0xffffffffUL) )
+    ctx->bits[1]++;
+}
+
+void
+MD5_stop (struct md5_ctx *ctx, unsigned char digest[16])
+{
+  int i;
+  unsigned long bits[2];
+
+  for ( i=0 ; i<2 ; i++ )
+    bits[i] = ctx->bits[i];
+  MD5_feed (ctx, 0x80);
+  for ( ; ctx->b!=56 ; )
+    MD5_feed (ctx, 0);
+  for ( i=0 ; i<2 ; i++ )
+    {
+      MD5_feed (ctx, bits[i]&0xff);
+      MD5_feed (ctx, (bits[i]>>8)&0xff);
+      MD5_feed (ctx, (bits[i]>>16)&0xff);
+      MD5_feed (ctx, (bits[i]>>24)&0xff);
+    }
+  for ( i=0 ; i<4 ; i++ )
+    {
+      digest[4*i] = ctx->buf[i]&0xff;
+      digest[4*i+1] = (ctx->buf[i]>>8)&0xff;
+      digest[4*i+2] = (ctx->buf[i]>>16)&0xff;
+      digest[4*i+3] = (ctx->buf[i]>>24)&0xff;
+    }
+}
+\f
+/* --- The core of the program --- */
+
+#include <stdio.h>
+#include <string.h>
+
+#define LARGE_ENOUGH 16384
+
+char buffer[LARGE_ENOUGH];
+
+/*
+int
+main (int argc, char *argv[])
+{
+  unsigned int i;
+
+  buffer[0] = 0;
+  strcat (buffer, \n\n");
+  strcat (buffer, "const char data[] =");
+  for ( i=0 ; data[i] ; i++ )
+    {
+      if ( i%60 == 0 )
+       strcat (buffer, "\n\"");
+      switch ( data[i] )
+       {
+       case '\\':
+       case '"':
+         strcat (buffer, "\\");
+         buffer[strlen(buffer)+1] = 0;
+         buffer[strlen(buffer)] = data[i];
+         break;
+       case '\n':
+         strcat (buffer, "\\n");
+         break;
+       case '\t':
+         strcat (buffer, "\\t");
+         break;
+       case '\f':
+         strcat (buffer, "\\f");
+         break;
+       default:
+         buffer[strlen(buffer)+1] = 0;
+         buffer[strlen(buffer)] = data[i];
+       }
+      if ( i%60 == 59 || !data[i+1] )
+       strcat (buffer, "\"");
+    }
+  strcat (buffer, ";\n\f\n");
+  strcat (buffer, data);
+  if ( argc >= 2 && strcmp (argv[1], "xyzzy") == 0 )
+    printf ("%s", buffer);
+  else
+    {
+      struct md5_ctx ctx;
+      unsigned char digest[16];
+
+      MD5_start (&ctx);
+      for ( i=0 ; buffer[i] ; i++ )
+       MD5_feed (&ctx, buffer[i]);
+      MD5_stop (&ctx, digest);
+      for ( i=0 ; i<16 ; i++ )
+       printf ("%02x", digest[i]);
+      printf ("\n");
+    }
+  return 0;
+}
+*/
diff --git a/src/libopensrf/opensrf.c b/src/libopensrf/opensrf.c
new file mode 100644 (file)
index 0000000..5532f73
--- /dev/null
@@ -0,0 +1,42 @@
+#include <opensrf/osrf_system.h>
+//#include <opensrf/osrf_hash.h>
+//#include <opensrf/osrf_list.h>
+
+int main( int argc, char* argv[] ) {
+
+       if( argc < 4 ) {
+               fprintf(stderr, "Usage: %s <host> <bootstrap_config> <config_context>\n", argv[0]);
+               return 1;
+       }
+
+       fprintf(stderr, "Loading OpenSRF host %s with bootstrap config %s "
+                       "and config context %s\n", argv[1], argv[2], argv[3] );
+
+       /* these must be strdup'ed because init_proc_title / set_proc_title 
+               are evil and overwrite the argv memory */
+       char* host              = strdup( argv[1] );
+       char* config    = strdup( argv[2] );
+       char* context   = strdup( argv[3] );
+
+       init_proc_title( argc, argv );
+       set_proc_title( "OpenSRF System-C" );
+
+       int ret = osrfSystemBootstrap( host, config, context );
+
+       if (ret != 0) {
+               osrfLogError(
+                       OSRF_LOG_MARK,
+                       "Server Loop returned an error condition, exiting with %d",
+                       ret
+               );
+       }
+
+
+       free(host);
+       free(config);
+       free(context);
+
+       return ret;
+}
+
+
diff --git a/src/libopensrf/osrfConfig.c b/src/libopensrf/osrfConfig.c
new file mode 100644 (file)
index 0000000..7fd9dbd
--- /dev/null
@@ -0,0 +1,135 @@
+/* defines the currently used bootstrap config file */
+#include <opensrf/osrfConfig.h>
+
+static osrfConfig* osrfConfigDefault = NULL;
+
+
+void osrfConfigSetDefaultConfig(osrfConfig* cfg) {
+       if(cfg) {
+               if( osrfConfigDefault )
+                       osrfConfigFree( osrfConfigDefault );
+               osrfConfigDefault = cfg;
+       }
+}
+
+void osrfConfigFree(osrfConfig* cfg) {
+       if(cfg) {
+               jsonObjectFree(cfg->config);
+               free(cfg->configContext);
+               free(cfg);
+       }       
+}
+
+
+int osrfConfigHasDefaultConfig() {
+       return ( osrfConfigDefault != NULL );
+}
+
+
+void osrfConfigCleanup() { 
+       osrfConfigFree(osrfConfigDefault);
+       osrfConfigDefault = NULL;
+}
+
+
+void osrfConfigReplaceConfig(osrfConfig* cfg, const jsonObject* obj) {
+       if(!cfg || !obj) return;
+       jsonObjectFree(cfg->config);
+       cfg->config = jsonObjectClone(obj);     
+}
+
+osrfConfig* osrfConfigInit(char* configFile, char* configContext) {
+       if(!configFile) return NULL;
+
+       // Load XML from the configuration file
+       
+       xmlDocPtr doc = xmlParseFile(configFile);
+       if(!doc) {
+               fprintf( stderr, "osrfConfigInit: Unable to parse XML config file %s\n", configFile);
+               osrfLogWarning( OSRF_LOG_MARK, "Unable to parse XML config file %s", configFile);
+               return NULL;
+       }
+
+       // Translate it into a jsonObject
+       
+       jsonObject* json_config = xmlDocToJSON(doc);
+       xmlFreeDoc(doc);
+
+       if(!json_config ) {
+               fprintf( stderr, "osrfConfigInit: xmlDocToJSON failed for config %s\n", configFile);
+               osrfLogWarning( OSRF_LOG_MARK, "xmlDocToJSON failed for config %s", configFile);
+               return NULL;
+       }       
+
+       // Build an osrfConfig and return it by pointer
+       
+       osrfConfig* cfg = safe_malloc(sizeof(osrfConfig));
+
+       if(configContext) cfg->configContext = strdup(configContext);
+       else cfg->configContext = NULL;
+
+       cfg->config = json_config;
+       
+       return cfg;
+}
+
+char* osrfConfigGetValue(osrfConfig* cfg, char* path, ...) {
+
+       if(!path) return NULL;
+       if(!cfg) cfg = osrfConfigDefault;
+       if(!cfg) { osrfLogWarning( OSRF_LOG_MARK, "No Config object in osrfConfigGetValue()"); return NULL; }
+
+       VA_LIST_TO_STRING(path);
+
+       jsonObject* obj;
+       char* val = NULL;
+
+       if(cfg->configContext) {
+               obj = jsonObjectFindPath( cfg->config, "//%s%s", cfg->configContext, VA_BUF);
+               if(obj) val = jsonObjectToSimpleString(jsonObjectGetIndex(obj, 0));
+
+       } else {
+               obj = jsonObjectFindPath( cfg->config, VA_BUF);
+               if(obj) val = jsonObjectToSimpleString(obj);
+       }
+
+       jsonObjectFree(obj);
+       return val;
+}
+
+
+int osrfConfigGetValueList(osrfConfig* cfg, osrfStringArray* arr, char* path, ...) {
+
+       if(!arr || !path) return 0;
+       if(!cfg) cfg = osrfConfigDefault;
+       if(!cfg) { osrfLogWarning( OSRF_LOG_MARK, "No Config object!"); return -1;}
+
+       VA_LIST_TO_STRING(path);
+
+       jsonObject* obj;
+       if(cfg->configContext) {
+               obj = jsonObjectFindPath( cfg->config, "//%s%s", cfg->configContext, VA_BUF);
+       } else {
+               obj = jsonObjectFindPath( cfg->config, VA_BUF);
+       }
+
+       int count = 0;
+
+       if(obj && obj->type == JSON_ARRAY ) {
+
+               int i;
+               for( i = 0; i < obj->size; i++ ) {
+
+                       char* val = jsonObjectToSimpleString(jsonObjectGetIndex(obj, i));
+                       if(val) {
+                               count++;
+                               osrfStringArrayAdd(arr, val);
+                               free(val);
+                       }
+               }
+       }
+
+       jsonObjectFree(obj);
+       return count;
+}
+
diff --git a/src/libopensrf/osrf_app_session.c b/src/libopensrf/osrf_app_session.c
new file mode 100644 (file)
index 0000000..d89c445
--- /dev/null
@@ -0,0 +1,660 @@
+#include <opensrf/osrf_app_session.h>
+#include <time.h>
+
+/* the global app_session cache */
+osrfHash* osrfAppSessionCache = NULL;
+
+
+// --------------------------------------------------------------------------
+// --------------------------------------------------------------------------
+// Request API
+// --------------------------------------------------------------------------
+
+/** Allocation and initializes a new app_request object */
+osrf_app_request* _osrf_app_request_init( 
+               osrf_app_session* session, osrf_message* msg ) {
+
+       osrf_app_request* req = 
+               (osrf_app_request*) safe_malloc(sizeof(osrf_app_request));
+
+       req->session            = session;
+       req->request_id = msg->thread_trace;
+       req->complete           = 0;
+       req->payload            = msg;
+       req->result                     = NULL;
+
+       return req;
+
+}
+
+
+void osrfAppSessionCleanup() {
+       osrfHashFree(osrfAppSessionCache);      
+}
+
+
+
+/** Frees memory used by an app_request object */
+void _osrf_app_request_free( void * req ){
+       if( req == NULL ) return;
+       osrfAppRequest* r = (osrfAppRequest*) req;
+       if( r->payload ) osrf_message_free( r->payload );
+       free( r );
+}
+
+/** Pushes the given message onto the list of 'responses' to this request */
+void _osrf_app_request_push_queue( osrf_app_request* req, osrf_message* result ){
+       if(req == NULL || result == NULL) return;
+       osrfLogDebug( OSRF_LOG_MARK,  "App Session pushing request [%d] onto request queue", result->thread_trace );
+       if(req->result == NULL) {
+               req->result = result;
+
+       } else {
+               
+               osrf_message* ptr = req->result;
+               osrf_message* ptr2 = req->result->next;
+               while( ptr2 ) {
+                       ptr = ptr2;
+                       ptr2 = ptr2->next;
+               }
+               ptr->next = result;
+       }
+}
+
+/** Removes this app_request from our session request set */
+void osrf_app_session_request_finish( 
+               osrf_app_session* session, int req_id ){
+
+       if(session == NULL) return;
+       osrf_app_request* req = OSRF_LIST_GET_INDEX( session->request_queue, req_id );
+       if(req == NULL) return;
+       osrfListRemove( req->session->request_queue, req->request_id );
+}
+
+
+void osrf_app_session_request_reset_timeout( osrf_app_session* session, int req_id ) {
+       if(session == NULL) return;
+       osrfLogDebug( OSRF_LOG_MARK, "Resetting request timeout %d", req_id );
+       osrf_app_request* req = OSRF_LIST_GET_INDEX( session->request_queue, req_id );
+       if(req == NULL) return;
+       req->reset_timeout = 1;
+}
+
+/** Checks the receive queue for messages.  If any are found, the first
+  * is popped off and returned.  Otherwise, this method will wait at most timeout 
+  * seconds for a message to appear in the receive queue.  Once it arrives it is returned.
+  * If no messages arrive in the timeout provided, null is returned.
+  */
+osrf_message* _osrf_app_request_recv( osrf_app_request* req, int timeout ) {
+
+       if(req == NULL) return NULL;
+
+       if( req->result != NULL ) {
+               /* pop off the first message in the list */
+               osrf_message* tmp_msg = req->result;
+               req->result = req->result->next;
+               return tmp_msg;
+       }
+
+       time_t start = time(NULL);      
+       time_t remaining = (time_t) timeout;
+
+       while( remaining >= 0 ) {
+               /* tell the session to wait for stuff */
+               osrfLogDebug( OSRF_LOG_MARK,  "In app_request receive with remaining time [%d]", (int) remaining );
+
+               osrf_app_session_queue_wait( req->session, 0, NULL );
+
+               if( req->result != NULL ) { /* if we received anything */
+                       /* pop off the first message in the list */
+                       osrfLogDebug( OSRF_LOG_MARK,  "app_request_recv received a message, returning it");
+                       osrf_message* ret_msg = req->result;
+                       osrf_message* tmp_msg = ret_msg->next;
+                       req->result = tmp_msg;
+                       return ret_msg;
+               }
+
+               if( req->complete )
+                       return NULL;
+
+               osrf_app_session_queue_wait( req->session, (int) remaining, NULL );
+
+               if( req->result != NULL ) { /* if we received anything */
+                       /* pop off the first message in the list */
+                       osrfLogDebug( OSRF_LOG_MARK,  "app_request_recv received a message, returning it");
+                       osrf_message* ret_msg = req->result;
+                       osrf_message* tmp_msg = ret_msg->next;
+                       req->result = tmp_msg;
+                       return ret_msg;
+               }
+               if( req->complete )
+                       return NULL;
+
+               if(req->reset_timeout) {
+                       remaining = (time_t) timeout;
+                       req->reset_timeout = 0;
+                       osrfLogDebug( OSRF_LOG_MARK, "Recevied a timeout reset");
+               } else {
+                       remaining -= (int) (time(NULL) - start);
+               }
+       }
+
+       osrfLogInfo( OSRF_LOG_MARK, "Returning NULL from app_request_recv after timeout");
+       return NULL;
+}
+
+/** Resend this requests original request message */
+int _osrf_app_request_resend( osrf_app_request* req ) {
+       if(req == NULL) return 0;
+       if(!req->complete) {
+               osrfLogDebug( OSRF_LOG_MARK,  "Resending request [%d]", req->request_id );
+               return _osrf_app_session_send( req->session, req->payload );
+       }
+       return 1;
+}
+
+
+
+// --------------------------------------------------------------------------
+// --------------------------------------------------------------------------
+// Session API
+// --------------------------------------------------------------------------
+
+/** 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;
+}
+
+
+/** adds a session to the global session cache */
+void _osrf_app_session_push_session( osrf_app_session* session ) {
+       if(!session) return;
+       if( osrfAppSessionCache == NULL ) osrfAppSessionCache = osrfNewHash();
+       if( osrfHashGet( osrfAppSessionCache, session->session_id ) ) return;
+       osrfHashSet( osrfAppSessionCache, session, session->session_id );
+}
+
+/** Allocates a initializes a new app_session */
+
+osrf_app_session* osrfAppSessionClientInit( char* remote_service ) {
+       return osrf_app_client_session_init( remote_service );
+}
+
+osrf_app_session* osrf_app_client_session_init( char* remote_service ) {
+
+       osrf_app_session* session = safe_malloc(sizeof(osrf_app_session));      
+
+       session->transport_handle = osrf_system_get_transport_client();
+       if( session->transport_handle == NULL ) {
+               osrfLogWarning( OSRF_LOG_MARK, "No transport client for service 'client'");
+               free( session );
+               return NULL;
+       }
+
+       char target_buf[512];
+       target_buf[ 0 ] = '\0';
+
+       osrfStringArray* arr = osrfNewStringArray(8);
+       osrfConfigGetValueList(NULL, arr, "/domains/domain");
+       char* domain = osrfStringArrayGetString(arr, 0);
+       char* router_name = osrfConfigGetValue(NULL, "/router_name");
+       
+       int len = snprintf( target_buf, 512, "%s@%s/%s",  router_name, domain, remote_service );
+       osrfStringArrayFree(arr);
+       //free(domain);
+       free(router_name);
+
+       if( len >= sizeof( target_buf ) ) {
+               osrfLogWarning( OSRF_LOG_MARK, "Buffer overflow for remote_id");
+               free( session );
+               return NULL;
+       }
+
+       session->request_queue = osrfNewList();
+       session->request_queue->freeItem = &_osrf_app_request_free;
+       session->remote_id = strdup(target_buf);
+       session->orig_remote_id = strdup(session->remote_id);
+       session->remote_service = strdup(remote_service);
+
+       #ifdef ASSUME_STATELESS
+       session->stateless = 1;
+       osrfLogDebug( OSRF_LOG_MARK, "%s session is stateless", remote_service );
+       #else
+       session->stateless = 0;
+       osrfLogDebug( OSRF_LOG_MARK, "%s session is NOT stateless", remote_service );
+       #endif
+
+       /* 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());
+       session->session_id = strdup(id);
+       osrfLogDebug( OSRF_LOG_MARK,  "Building a new client session with id [%s] [%s]", 
+                       session->remote_service, session->session_id );
+
+       session->thread_trace = 0;
+       session->state = OSRF_SESSION_DISCONNECTED;
+       session->type = OSRF_SESSION_CLIENT;
+       //session->next = NULL;
+       _osrf_app_session_push_session( session );
+       return session;
+}
+
+osrf_app_session* osrf_app_server_session_init( 
+               char* session_id, char* our_app, char* remote_id ) {
+
+       osrfLogDebug( OSRF_LOG_MARK, "Initing server session with session id %s, service %s,"
+                       " and remote_id %s", session_id, our_app, remote_id );
+
+       osrf_app_session* session = osrf_app_session_find_session( session_id );
+       if(session) return session;
+
+       session = safe_malloc(sizeof(osrf_app_session));        
+
+       session->transport_handle = osrf_system_get_transport_client();
+       if( session->transport_handle == NULL ) {
+               osrfLogWarning( OSRF_LOG_MARK, "No transport client for service '%s'", our_app );
+               return NULL;
+       }
+
+       int stateless = 0;
+       char* statel = osrf_settings_host_value("/apps/%s/stateless", our_app );
+       if(statel) stateless = atoi(statel);
+       free(statel);
+
+
+       session->request_queue = osrfNewList();
+       session->request_queue->freeItem = &_osrf_app_request_free;
+       session->remote_id = strdup(remote_id);
+       session->orig_remote_id = strdup(remote_id);
+       session->session_id = strdup(session_id);
+       session->remote_service = strdup(our_app);
+       session->stateless = stateless;
+
+       #ifdef ASSUME_STATELESS
+       session->stateless = 1;
+       #endif
+
+       session->thread_trace = 0;
+       session->state = OSRF_SESSION_DISCONNECTED;
+       session->type = OSRF_SESSION_SERVER;
+
+       _osrf_app_session_push_session( session );
+       return session;
+
+}
+
+
+
+/** frees memory held by a session */
+void _osrf_app_session_free( osrf_app_session* session ){
+       if(session==NULL)
+               return;
+
+       if( session->userDataFree && session->userData ) 
+               session->userDataFree(session->userData);
+       
+       free(session->remote_id);
+       free(session->orig_remote_id);
+       free(session->session_id);
+       free(session->remote_service);
+       osrfListFree(session->request_queue);
+       free(session);
+}
+
+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 );
+}
+
+int osrf_app_session_make_req( 
+               osrf_app_session* session, jsonObject* params, 
+               char* method_name, int protocol, string_array* param_strings ) {
+       if(session == NULL) return -1;
+
+   osrfLogMkXid();
+
+       osrf_message* req_msg = osrf_message_init( REQUEST, ++(session->thread_trace), protocol );
+       osrf_message_set_method(req_msg, method_name);
+       if(params) {
+               osrf_message_set_params(req_msg, params);
+
+       } else {
+
+               if(param_strings) {
+                       int i;
+                       for(i = 0; i!= param_strings->size ; i++ ) {
+                               osrf_message_add_param(req_msg,
+                                       string_array_get_string(param_strings,i));
+                       }
+               }
+       }
+
+       osrf_app_request* req = _osrf_app_request_init( session, req_msg );
+       if(_osrf_app_session_send( session, req_msg ) ) {
+               osrfLogWarning( OSRF_LOG_MARK,  "Error sending request message [%d]", session->thread_trace );
+               return -1;
+       }
+
+       osrfLogDebug( OSRF_LOG_MARK,  "Pushing [%d] onto requeust 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;
+}
+
+void osrf_app_session_set_complete( osrf_app_session* session, int request_id ) {
+       if(session == NULL)
+               return;
+
+       osrf_app_request* req = OSRF_LIST_GET_INDEX( session->request_queue, request_id );
+       if(req) req->complete = 1;
+}
+
+int osrf_app_session_request_complete( osrf_app_session* session, int request_id ) {
+       if(session == NULL)
+               return 0;
+       osrf_app_request* req = OSRF_LIST_GET_INDEX( session->request_queue, request_id );
+       if(req)
+               return req->complete;
+       return 0;
+}
+
+
+/** Resets the remote connection id to that of the original*/
+void osrf_app_session_reset_remote( osrf_app_session* session ){
+       if( session==NULL )
+               return;
+
+       free(session->remote_id);
+       osrfLogDebug( OSRF_LOG_MARK,  "App Session [%s] [%s] resetting remote id to %s",
+                       session->remote_service, session->session_id, session->orig_remote_id );
+
+       session->remote_id = strdup(session->orig_remote_id);
+}
+
+void osrf_app_session_set_remote( osrf_app_session* session, char* remote_id ) {
+       if(session == NULL)
+               return;
+       if( session->remote_id )
+               free(session->remote_id );
+       session->remote_id = strdup( remote_id );
+}
+
+/** pushes the given message into the result list of the app_request
+  with the given request_id */
+int osrf_app_session_push_queue( 
+               osrf_app_session* session, osrf_message* msg ){
+       if(session == NULL || msg == NULL) return 0;
+
+       osrf_app_request* req = OSRF_LIST_GET_INDEX( session->request_queue, msg->thread_trace );
+       if(req == NULL) return 0;
+       _osrf_app_request_push_queue( req, msg );
+
+       return 0;
+}
+
+int osrfAppSessionConnect( osrf_app_session* session ) { 
+       return osrf_app_session_connect(session);
+}
+
+
+/** Attempts to connect to the remote service */
+int osrf_app_session_connect(osrf_app_session* session){
+       
+       if(session == NULL)
+               return 0;
+
+       if(session->state == OSRF_SESSION_CONNECTED) {
+               return 1;
+       }
+
+       int timeout = 5; /* XXX CONFIG VALUE */
+
+       osrfLogDebug( OSRF_LOG_MARK,  "AppSession connecting to %s", session->remote_id );
+
+       /* defaulting to protocol 1 for now */
+       osrf_message* con_msg = osrf_message_init( CONNECT, session->thread_trace, 1 );
+       osrf_app_session_reset_remote( session );
+       session->state = OSRF_SESSION_CONNECTING;
+       int ret = _osrf_app_session_send( session, con_msg );
+       osrf_message_free(con_msg);
+       if(ret) return 0;
+
+       time_t start = time(NULL);      
+       time_t remaining = (time_t) timeout;
+
+       while( session->state != OSRF_SESSION_CONNECTED && remaining >= 0 ) {
+               osrf_app_session_queue_wait( session, remaining, NULL );
+               remaining -= (int) (time(NULL) - start);
+       }
+
+       if(session->state == OSRF_SESSION_CONNECTED)
+               osrfLogDebug( OSRF_LOG_MARK, " * Connected Successfully to %s", session->remote_service );
+
+       if(session->state != OSRF_SESSION_CONNECTED)
+               return 0;
+
+       return 1;
+}
+
+
+
+/** Disconnects from the remote service */
+int osrf_app_session_disconnect( osrf_app_session* session){
+       if(session == NULL)
+               return 1;
+
+       if(session->state == OSRF_SESSION_DISCONNECTED)
+               return 1;
+
+       if(session->stateless && session->state != OSRF_SESSION_CONNECTED) {
+               osrfLogDebug( OSRF_LOG_MARK,  
+                               "Exiting disconnect on stateless session %s", 
+                               session->session_id);
+               return 1;
+       }
+
+       osrfLogDebug(OSRF_LOG_MARK,  "AppSession disconnecting from %s", session->remote_id );
+
+       osrf_message* dis_msg = osrf_message_init( DISCONNECT, session->thread_trace, 1 );
+       _osrf_app_session_send( session, dis_msg );
+       session->state = OSRF_SESSION_DISCONNECTED;
+
+       osrf_message_free( dis_msg );
+       osrf_app_session_reset_remote( session );
+       return 1;
+}
+
+int osrf_app_session_request_resend( osrf_app_session* session, int req_id ) {
+       osrf_app_request* req = OSRF_LIST_GET_INDEX( session->request_queue, req_id );
+       return _osrf_app_request_resend( req );
+}
+
+
+int osrfAppSessionSendBatch( osrfAppSession* session, osrf_message* msgs[], int size ) {
+
+       if( !(session && msgs && size > 0) ) return 0;
+       int retval = 0;
+
+       osrfMessage* msg = msgs[0];
+
+       if(msg) {
+
+               osrf_app_session_queue_wait( session, 0, NULL );
+
+               if(session->state != OSRF_SESSION_CONNECTED)  {
+
+                       if(session->stateless) { /* stateless session always send to the root listener */
+                               osrf_app_session_reset_remote(session);
+
+                       } else { 
+
+                               /* do an auto-connect if necessary */
+                               if( ! session->stateless &&
+                                       (msg->m_type != CONNECT) && 
+                                       (msg->m_type != DISCONNECT) &&
+                                       (session->state != OSRF_SESSION_CONNECTED) ) {
+
+                                       if(!osrf_app_session_connect( session )) 
+                                               return 0;
+                               }
+                       }
+               }
+       }
+
+       char* string = osrfMessageSerializeBatch(msgs, size);
+
+       if( string ) {
+
+               transport_message* t_msg = message_init( 
+                               string, "", session->session_id, session->remote_id, NULL );
+      message_set_osrf_xid( t_msg, osrfLogGetXid() );
+
+               retval = client_send_message( session->transport_handle, t_msg );
+
+               if( retval ) osrfLogError(OSRF_LOG_MARK, "client_send_message failed");
+
+               osrfLogInfo(OSRF_LOG_MARK, "[%s] sent %d bytes of data to %s",
+                       session->remote_service, strlen(string), t_msg->recipient );
+
+               osrfLogDebug(OSRF_LOG_MARK, "Sent: %s", string );
+
+               free(string);
+               message_free( t_msg );
+       }
+
+       return retval; 
+}
+
+
+
+int _osrf_app_session_send( osrf_app_session* session, osrf_message* msg ){
+       if( !(session && msg) ) return 0;
+       osrfMessage* a[1];
+       a[0] = msg;
+       return osrfAppSessionSendBatch( session, a, 1 );
+}
+
+
+
+
+/**  Waits up to 'timeout' seconds for some data to arrive.
+  * Any data that arrives will be processed according to its
+  * payload and message type.  This method will return after
+  * any data has arrived.
+  */
+int osrf_app_session_queue_wait( osrf_app_session* session, int timeout, int* recvd ){
+       if(session == NULL) return 0;
+       int ret_val = 0;
+       osrfLogDebug(OSRF_LOG_MARK,  "AppSession in queue_wait with timeout %d", timeout );
+       ret_val = osrf_stack_entry_point(session->transport_handle, timeout, recvd);
+       return ret_val;
+}
+
+/** Disconnects (if client) and removes the given session from the global session cache 
+  * ! This free's all attached app_requests ! 
+  */
+void osrfAppSessionFree( osrfAppSession* ses ) {
+       osrf_app_session_destroy( ses );
+}
+
+
+void osrf_app_session_destroy( osrf_app_session* session ){
+       if(session == NULL) return;
+
+       osrfLogDebug(OSRF_LOG_MARK,  "AppSession [%s] [%s] destroying self and deleting requests", 
+                       session->remote_service, session->session_id );
+       if(session->type == OSRF_SESSION_CLIENT 
+                       && session->state != OSRF_SESSION_DISCONNECTED ) { /* disconnect if we're a client */
+               osrf_message* dis_msg = osrf_message_init( DISCONNECT, session->thread_trace, 1 );
+               _osrf_app_session_send( session, dis_msg ); 
+               osrf_message_free(dis_msg);
+       }
+
+       osrfHashRemove( osrfAppSessionCache, session->session_id );
+       _osrf_app_session_free( session );
+}
+
+osrf_message* osrfAppSessionRequestRecv(
+               osrf_app_session* session, int req_id, int timeout ) {
+       return osrf_app_session_request_recv( session, req_id, timeout );
+}
+osrf_message* osrf_app_session_request_recv( 
+               osrf_app_session* session, int req_id, int timeout ) {
+       if(req_id < 0 || session == NULL)
+               return NULL;
+       osrf_app_request* req = OSRF_LIST_GET_INDEX( session->request_queue, req_id );
+       return _osrf_app_request_recv( req, timeout );
+}
+
+
+
+int osrfAppRequestRespond( osrfAppSession* ses, int requestId, jsonObject* data ) {
+       if(!ses || ! data ) return -1;
+
+       osrf_message* msg = osrf_message_init( RESULT, requestId, 1 );
+       osrf_message_set_status_info( msg, NULL, "OK", OSRF_STATUS_OK );
+       char* json = jsonObjectToJSON( data );
+
+       osrf_message_set_result_content( msg, json );
+       _osrf_app_session_send( ses, msg ); 
+
+       free(json);
+       osrf_message_free( msg );
+
+       return 0;
+}
+
+
+int osrfAppRequestRespondComplete( 
+               osrfAppSession* ses, int requestId, jsonObject* data ) {
+
+       osrf_message* payload = osrf_message_init( RESULT, requestId, 1 );
+       osrf_message_set_status_info( payload, NULL, "OK", OSRF_STATUS_OK );
+
+       osrf_message* status = osrf_message_init( STATUS, requestId, 1);
+       osrf_message_set_status_info( status, "osrfConnectStatus", "Request Complete", OSRF_STATUS_COMPLETE );
+       
+       if (data) {
+               char* json = jsonObjectToJSON( data );
+               osrf_message_set_result_content( payload, json );
+               free(json);
+
+               osrfMessage* ms[2];
+               ms[0] = payload;
+               ms[1] = status;
+
+               osrfAppSessionSendBatch( ses, ms, 2 );
+
+               osrf_message_free( payload );
+       } else {
+               osrfAppSessionSendBatch( ses, &status, 1 );
+       }
+
+       osrf_message_free( status );
+
+       return 0;
+}
+
+int osrfAppSessionStatus( osrfAppSession* ses, int type, char* name, int reqId, char* message ) {
+
+       if(ses) {
+               osrf_message* msg = osrf_message_init( STATUS, reqId, 1);
+               osrf_message_set_status_info( msg, name, message, type );
+               _osrf_app_session_send( ses, msg ); 
+               osrf_message_free( msg );
+               return 0;
+       }
+       return -1;
+}
+
+
+
+
+
+
diff --git a/src/libopensrf/osrf_application.c b/src/libopensrf/osrf_application.c
new file mode 100644 (file)
index 0000000..1f12b86
--- /dev/null
@@ -0,0 +1,477 @@
+#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;
+
+       if(!__osrfAppHash) __osrfAppHash = osrfNewHash();
+
+       osrfLogInfo( OSRF_LOG_MARK, "Registering application %s with file %s", appName, soFile );
+
+       osrfApplication* app = safe_malloc(sizeof(osrfApplication));
+       app->handle = dlopen (soFile, RTLD_NOW);
+   app->onExit = NULL;
+
+       if(!app->handle) {
+               osrfLogWarning( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, dlerror() );
+               dlerror(); /* clear the error */
+               free(app);
+               return -1;
+       }
+
+       app->methods = osrfNewHash();
+       osrfHashSet( __osrfAppHash, app, appName );
+
+       /* see if we can run the initialize method */
+       int (*init) (void);
+       *(void **) (&init) = dlsym(app->handle, "osrfAppInitialize");
+
+       if( (error = dlerror()) != NULL ) {
+               osrfLogWarning( OSRF_LOG_MARK, 
+                       "! Unable to locate method symbol [osrfAppInitialize] for app %s: %s", appName, error );
+
+       } else {
+
+               /* run the method */
+               int ret;
+               if( (ret = (*init)()) ) {
+                       osrfLogWarning( OSRF_LOG_MARK, "Application %s returned non-zero value from "
+                                       "'osrfAppInitialize', not registering...", appName );
+                       //free(app->name); /* need a method to remove an application from the list */
+                       //free(app);
+                       return ret;
+               }
+       }
+
+       __osrfAppRegisterSysMethods(appName);
+
+       osrfLogInfo( OSRF_LOG_MARK, "Application %s registered successfully", appName );
+
+       osrfLogSetAppname(appName);
+
+   osrfAppSetOnExit(app, appName);
+
+       return 0;
+}
+
+
+void osrfAppSetOnExit(osrfApplication* app, char* appName) {
+   if(!(app && appName)) return;
+
+       /* see if we can run the initialize method */
+   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;
+   }
+
+   osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
+   app->onExit = (*onExit);
+   //if( (ret = (*onExit)()) ) {
+}
+
+
+int osrfAppRunChildInit(char* appname) {
+       osrfApplication* app = _osrfAppFindApplication(appname);
+       if(!app) return -1;
+
+       char* error;
+       int ret;
+       int (*childInit) (void);
+
+       *(void**) (&childInit) = dlsym(app->handle, "osrfAppChildInit");
+
+       if( (error = dlerror()) != NULL ) {
+               osrfLogInfo( OSRF_LOG_MARK, "No child init defined for app %s : %s", appname, error);
+               return 0;
+       }
+
+       if( (ret = (*childInit)()) ) {
+               osrfLogError(OSRF_LOG_MARK, "App %s child init failed", appname);
+               return -1;
+       }
+
+       osrfLogInfo(OSRF_LOG_MARK, "%s child init succeeded", appname);
+       return 0;
+}
+
+
+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();
+      }
+   }
+}
+
+
+int osrfAppRegisterMethod( char* appName, char* methodName, 
+               char* symbolName, char* notes, int argc, int options ) {
+
+       return osrfAppRegisterExtendedMethod(
+                       appName,
+                       methodName,
+                       symbolName,
+                       notes,
+                       argc,
+                       options,
+                       NULL
+       );
+
+}
+
+int osrfAppRegisterExtendedMethod( char* appName, char* methodName, 
+               char* symbolName, char* notes, int argc, int options, void * user_data ) {
+
+       if( !appName || ! methodName  ) return -1;
+
+       osrfApplication* app = _osrfAppFindApplication(appName);
+       if(!app) {
+               osrfLogWarning( OSRF_LOG_MARK, "Unable to locate application %s", appName );
+               return -1;
+       }
+
+       osrfLogDebug( OSRF_LOG_MARK, "Registering method %s for app %s", methodName, appName );
+
+       osrfMethod* method = _osrfAppBuildMethod(
+               methodName, symbolName, notes, argc, options, user_data );              
+       method->options = options;
+
+       /* plug the method into the list of methods */
+       osrfHashSet( app->methods, method, method->name );
+
+       if( options & OSRF_METHOD_STREAMING ) { /* build the atomic counterpart */
+               int newops = options | OSRF_METHOD_ATOMIC;
+               osrfMethod* atomicMethod = _osrfAppBuildMethod(
+                       methodName, symbolName, notes, argc, newops, NULL );            
+               osrfHashSet( app->methods, atomicMethod, atomicMethod->name );
+               atomicMethod->userData = method->userData;
+       }
+
+       return 0;
+}
+
+
+
+osrfMethod* _osrfAppBuildMethod( char* methodName, 
+       char* symbolName, char* notes, int argc, int options, void* user_data ) {
+
+       osrfMethod* method                                      = safe_malloc(sizeof(osrfMethod));
+
+       if(methodName) method->name             = strdup(methodName);
+       if(symbolName) method->symbol           = strdup(symbolName);
+       if(notes) method->notes                         = strdup(notes);
+       if(user_data) method->userData  = user_data;
+
+       method->argc                                                    = argc;
+       method->options                                         = options;
+
+       if(options & OSRF_METHOD_ATOMIC) { /* add ".atomic" to the end of the name */
+               char mb[strlen(method->name) + 8];
+               sprintf(mb, "%s.atomic", method->name);
+               free(method->name);
+               method->name = strdup(mb);
+               method->options |= OSRF_METHOD_STREAMING;
+       }
+
+       return method;
+}
+
+
+int __osrfAppRegisterSysMethods( char* app ) {
+
+       osrfAppRegisterMethod( 
+                       app, OSRF_SYSMETHOD_INTROSPECT, NULL, 
+                       "Return a list of methods whose names have the same initial "
+                       "substring as that of the provided method name PARAMS( methodNameSubstring )", 
+                       1, OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
+
+       osrfAppRegisterMethod( 
+                       app, OSRF_SYSMETHOD_INTROSPECT_ALL, NULL, 
+                       "Returns a complete list of methods. PARAMS()", 0, 
+                       OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
+
+       osrfAppRegisterMethod( 
+                       app, OSRF_SYSMETHOD_ECHO, NULL, 
+                       "Echos all data sent to the server back to the client. PARAMS([a, b, ...])", 0, 
+                       OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
+
+       return 0;
+}
+
+osrfApplication* _osrfAppFindApplication( char* name ) {
+       if(!name) return NULL;
+       return (osrfApplication*) osrfHashGet(__osrfAppHash, name);
+}
+
+osrfMethod* __osrfAppFindMethod( osrfApplication* app, char* methodName ) {
+       if(!app || ! methodName) return NULL;
+       return (osrfMethod*) osrfHashGet( app->methods, methodName );
+}
+
+osrfMethod* _osrfAppFindMethod( char* appName, char* methodName ) {
+       if(!appName || ! methodName) return NULL;
+       return __osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
+}
+
+
+int osrfAppRunMethod( char* appName, char* methodName, 
+               osrfAppSession* ses, int reqId, jsonObject* params ) {
+
+       if( !(appName && methodName && ses) ) return -1;
+
+       char* error;
+       osrfApplication* app;
+       osrfMethod* method;
+       osrfMethodContext context;
+
+       context.session = ses;
+       context.params = params;
+       context.request = reqId;
+       context.responses = NULL;
+
+       /* this is the method we're gonna run */
+       int (*meth) (osrfMethodContext*);       
+
+       if( !(app = _osrfAppFindApplication(appName)) )
+               return osrfAppRequestRespondException( ses, 
+                               reqId, "Application not found: %s", appName );
+       
+       if( !(method = __osrfAppFindMethod( app, methodName )) ) 
+               return osrfAppRequestRespondException( ses, reqId, 
+                               "Method [%s] not found for service %s", methodName, appName );
+
+       context.method = method;
+
+       #ifdef OSRF_STRICT_PARAMS
+       if( method->argc > 0 ) {
+               if(!params || params->type != JSON_ARRAY || params->size < method->argc )
+                       return osrfAppRequestRespondException( ses, reqId, 
+                               "Not enough params for method %s / service %s", methodName, appName );
+       }
+       #endif
+
+       int retcode = 0;
+
+       if( method->options & OSRF_METHOD_SYSTEM ) {
+               retcode = __osrfAppRunSystemMethod(&context);
+
+       } else {
+
+               /* open and now run the method */
+               *(void **) (&meth) = dlsym(app->handle, method->symbol);
+
+               if( (error = dlerror()) != NULL ) {
+                       return osrfAppRequestRespondException( ses, reqId, 
+                               "Unable to execute method [%s]  for service %s", methodName, appName );
+               }
+
+               retcode = (*meth) (&context);
+       }
+
+       if(retcode < 0) 
+               return osrfAppRequestRespondException( 
+                               ses, reqId, "An unknown server error occurred" );
+
+       return __osrfAppPostProcess( &context, retcode );
+
+}
+
+
+int osrfAppRespond( osrfMethodContext* ctx, jsonObject* data ) {
+       return _osrfAppRespond( ctx, data, 0 );
+}
+
+int osrfAppRespondComplete( osrfMethodContext* context, jsonObject* data ) {
+       return _osrfAppRespond( context, data, 1 );
+}
+
+int _osrfAppRespond( osrfMethodContext* ctx, jsonObject* data, int complete ) {
+       if(!(ctx && ctx->method)) return -1;
+
+       if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
+               osrfLogDebug( OSRF_LOG_MARK,   
+                       "Adding responses to stash for atomic method %s", ctx->method->name );
+
+               if( ctx->responses == NULL )                                                                                            
+                       ctx->responses = jsonParseString("[]");                                                 
+
+               if ( data != NULL )
+                       jsonObjectPush( ctx->responses, jsonObjectClone(data) );        
+       }
+
+
+       if(     !(ctx->method->options & OSRF_METHOD_ATOMIC) && 
+                       !(ctx->method->options & OSRF_METHOD_CACHABLE) ) {
+
+               if(complete) 
+                       osrfAppRequestRespondComplete( ctx->session, ctx->request, data );
+               else
+                       osrfAppRequestRespond( ctx->session, ctx->request, data );
+               return 0;
+       }
+
+       return 0;
+}
+
+
+
+
+int __osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
+       if(!(ctx && ctx->method)) return -1;
+
+       osrfLogDebug( OSRF_LOG_MARK,  "Postprocessing method %s with retcode %d",
+                       ctx->method->name, retcode );
+
+       if(ctx->responses) { /* we have cached responses to return (no responses have been sent) */
+
+               osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
+               jsonObjectFree(ctx->responses);
+               ctx->responses = NULL;
+
+       } else {
+
+               if( retcode > 0 ) 
+                       osrfAppSessionStatus( ctx->session, OSRF_STATUS_COMPLETE,  
+                                       "osrfConnectStatus", ctx->request, "Request Complete" );
+       }
+
+       return 0;
+}
+
+int osrfAppRequestRespondException( osrfAppSession* ses, int request, char* msg, ... ) {
+       if(!ses) return -1;
+       if(!msg) msg = "";
+       VA_LIST_TO_STRING(msg);
+       osrfLogWarning( OSRF_LOG_MARK,  "Returning method exception with message: %s", VA_BUF );
+       osrfAppSessionStatus( ses, OSRF_STATUS_NOTFOUND, "osrfMethodException", request,  VA_BUF );
+       return 0;
+}
+
+
+static void __osrfAppSetIntrospectMethod( osrfMethodContext* ctx, osrfMethod* method, jsonObject* resp ) {
+       if(!(ctx && resp)) return;
+
+       jsonObjectSetKey(resp, "api_name",      jsonNewObject(method->name));
+       jsonObjectSetKey(resp, "method",                jsonNewObject(method->symbol));
+       jsonObjectSetKey(resp, "service",       jsonNewObject(ctx->session->remote_service));
+       jsonObjectSetKey(resp, "notes",         jsonNewObject(method->notes));
+       jsonObjectSetKey(resp, "argc",          jsonNewNumberObject(method->argc));
+
+       jsonObjectSetKey(resp, "sysmethod", 
+                       jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
+       jsonObjectSetKey(resp, "atomic",                
+                       jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
+       jsonObjectSetKey(resp, "cachable",      
+                       jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
+
+       jsonObjectSetClass(resp, "method");
+}
+
+
+
+int __osrfAppRunSystemMethod(osrfMethodContext* ctx) {
+       OSRF_METHOD_VERIFY_CONTEXT(ctx);
+
+       if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) || 
+                       !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
+
+               return osrfAppIntrospectAll(ctx);
+       }
+
+
+       if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
+                       !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
+
+               return osrfAppIntrospect(ctx);
+       }
+
+       if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO ) ||
+                       !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO_ATOMIC )) {
+
+               return osrfAppEcho(ctx);
+       }
+
+
+       osrfAppRequestRespondException( ctx->session, 
+                       ctx->request, "System method implementation not found");
+
+       return 0;
+}
+
+
+int osrfAppIntrospect( osrfMethodContext* ctx ) {
+
+       jsonObject* resp = NULL;
+       char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
+       osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
+       int len = 0;
+
+       if(!methodSubstring) return 1; /* respond with no methods */
+
+       if(app) {
+
+               osrfHashIterator* itr = osrfNewHashIterator(app->methods);
+               osrfMethod* method;
+
+               while( (method = osrfHashIteratorNext(itr)) ) {
+                       if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
+                               if( !strncmp( method->name, methodSubstring, len) ) {
+                                       resp = jsonNewObject(NULL);
+                                       __osrfAppSetIntrospectMethod( ctx, method, resp );
+                                       osrfAppRespond(ctx, resp);
+                                       jsonObjectFree(resp);
+                               }
+                       }
+               }
+               osrfHashIteratorFree(itr);
+               return 1;
+       }
+
+       return -1;
+
+}
+
+
+int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
+       jsonObject* resp = NULL;
+       osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
+
+       if(app) {
+               osrfHashIterator* itr = osrfNewHashIterator(app->methods);
+               osrfMethod* method;
+               while( (method = osrfHashIteratorNext(itr)) ) {
+                       resp = jsonNewObject(NULL);
+                       __osrfAppSetIntrospectMethod( ctx, method, resp );
+                       osrfAppRespond(ctx, resp);
+                       jsonObjectFree(resp);
+               }
+               osrfHashIteratorFree(itr);
+               return 1;
+       }
+
+       return -1;
+}
+
+int osrfAppEcho( osrfMethodContext* ctx ) {
+       OSRF_METHOD_VERIFY_CONTEXT(ctx);
+       int i;
+       for( i = 0; i < ctx->params->size; i++ ) {
+               jsonObject* str = jsonObjectGetIndex(ctx->params,i);
+               osrfAppRespond(ctx, str);
+       }
+       return 1;
+}
+
diff --git a/src/libopensrf/osrf_big_hash.c b/src/libopensrf/osrf_big_hash.c
new file mode 100644 (file)
index 0000000..3e69f2f
--- /dev/null
@@ -0,0 +1,173 @@
+#include <opensrf/osrf_big_hash.h>
+
+osrfBigHash* osrfNewBigHash() {
+       osrfBigHash* hash = safe_malloc(sizeof(osrfBigHash));
+       hash->hash = (Pvoid_t) NULL;
+       hash->freeItem = NULL;
+       return hash;
+}
+
+void* osrfBigHashSet( osrfBigHash* hash, void* item, const char* key, ... ) {
+       if(!(hash && item && key )) return NULL;
+
+       Word_t* value;
+       VA_LIST_TO_STRING(key);
+       uint8_t idx[strlen(VA_BUF) + 1];
+       strcpy( idx, VA_BUF );
+
+       void* olditem = osrfBigHashRemove( hash, VA_BUF );
+
+       JSLI(value, hash->hash, idx);
+       if(value) *value = (Word_t) item;
+       return olditem;
+       
+}
+
+void* osrfBigHashRemove( osrfBigHash* hash, const char* key, ... ) {
+       if(!(hash && key )) return NULL;
+
+       VA_LIST_TO_STRING(key);
+
+       Word_t* value;
+       uint8_t idx[strlen(VA_BUF) + 1];
+       strcpy( idx, VA_BUF );
+       void* item = NULL;
+       int retcode;
+
+       JSLG( value, hash->hash,  idx);
+
+       if( value ) {
+               item = (void*) *value;
+               if(item) {
+                       if( hash->freeItem ) {
+                               hash->freeItem( (char*) idx, item ); 
+                               item = NULL;
+                       }
+               }
+       }
+
+
+       JSLD( retcode, hash->hash, idx );
+
+       return item;
+}
+
+
+void* osrfBigHashGet( osrfBigHash* hash, const char* key, ... ) {
+       if(!(hash && key )) return NULL;
+
+       VA_LIST_TO_STRING(key);
+
+       Word_t* value;
+       uint8_t idx[strlen(VA_BUF) + 1];
+       strcpy( idx, VA_BUF );
+
+       JSLG( value, hash->hash, idx );
+       if(value) return (void*) *value;
+       return NULL;
+}
+
+
+osrfStringArray* osrfBigHashKeys( osrfBigHash* hash ) {
+       if(!hash) return NULL;
+
+       Word_t* value;
+       uint8_t idx[OSRF_HASH_MAXKEY];
+       strcpy(idx, "");
+       char* key;
+       osrfStringArray* strings = osrfNewStringArray(8);
+
+       JSLF( value, hash->hash, idx );
+
+       while( value ) {
+               key = (char*) idx;
+               osrfStringArrayAdd( strings, key );
+               JSLN( value, hash->hash, idx );
+       }
+
+       return strings;
+}
+
+
+unsigned long osrfBigHashGetCount( osrfBigHash* hash ) {
+       if(!hash) return -1;
+
+       Word_t* value;
+       unsigned long count = 0;
+       uint8_t idx[OSRF_HASH_MAXKEY];
+
+       strcpy( (char*) idx, "");
+       JSLF(value, hash->hash, idx);
+
+       while(value) {
+               count++;
+               JSLN( value, hash->hash, idx );
+       }
+
+       return count;
+}
+
+void osrfBigHashFree( osrfBigHash* hash ) {
+       if(!hash) return;
+
+       int i;
+       osrfStringArray* keys = osrfBigHashKeys( hash );
+
+       for( i = 0; i != keys->size; i++ )  {
+               char* key = (char*) osrfStringArrayGetString( keys, i );
+               osrfBigHashRemove( hash, key );
+       }
+
+       osrfStringArrayFree(keys);
+       free(hash);
+}
+
+
+
+osrfBigHashIterator* osrfNewBigHashIterator( osrfBigHash* hash ) {
+       if(!hash) return NULL;
+       osrfBigHashIterator* itr = safe_malloc(sizeof(osrfBigHashIterator));
+       itr->hash = hash;
+       itr->current = NULL;
+       return itr;
+}
+
+void* osrfBigHashIteratorNext( osrfBigHashIterator* itr ) {
+       if(!(itr && itr->hash)) return NULL;
+
+       Word_t* value;
+       uint8_t idx[OSRF_HASH_MAXKEY];
+
+       if( itr->current == NULL ) { /* get the first item in the list */
+               strcpy(idx, "");
+               JSLF( value, itr->hash->hash, idx );
+
+       } else {
+               strcpy(idx, itr->current);
+               JSLN( value, itr->hash->hash, idx );
+       }
+
+       if(value) {
+               free(itr->current);
+               itr->current = strdup((char*) idx);
+               return (void*) *value;
+       }
+
+       return NULL;
+
+}
+
+void osrfBigHashIteratorFree( osrfBigHashIterator* itr ) {
+       if(!itr) return;
+       free(itr->current);
+       free(itr);
+}
+
+void osrfBigHashIteratorReset( osrfBigHashIterator* itr ) {
+       if(!itr) return;
+       free(itr->current);
+       itr->current = NULL;
+}
+
+
+
diff --git a/src/libopensrf/osrf_big_list.c b/src/libopensrf/osrf_big_list.c
new file mode 100644 (file)
index 0000000..26ff7b6
--- /dev/null
@@ -0,0 +1,174 @@
+#include <opensrf/osrf_big_list.h>
+
+
+osrfBigList* osrfNewBigList() {
+       osrfBigList* list = safe_malloc(sizeof(osrfBigList));
+       list->list = (Pvoid_t) NULL;
+       list->size = 0;
+       list->freeItem = NULL;
+       return list;
+}
+
+
+int osrfBigListPush( osrfBigList* list, void* item ) {
+       if(!(list && item)) return -1;
+       Word_t* value;
+       unsigned long index = -1;
+       JLL(value, list->list, index );
+       osrfBigListSet( list, item, index+1 );
+       return 0;
+}
+
+
+void* osrfBigListSet( osrfBigList* list, void* item, unsigned long position ) {
+       if(!list || position < 0) return NULL;
+
+       Word_t* value;
+       void* olditem = osrfBigListRemove( list, position );
+
+       JLI( value, list->list, position ); 
+       *value = (Word_t) item;
+       __osrfBigListSetSize( list );
+
+       return olditem;
+}
+
+
+void* osrfBigListGetIndex( osrfBigList* list, unsigned long position ) {
+       if(!list) return NULL;
+
+       Word_t* value;
+       JLG( value, list->list, position );
+       if(value) return (void*) *value;
+       return NULL;
+}
+
+void osrfBigListFree( osrfBigList* list ) {
+       if(!list) return;
+
+       Word_t* value;
+       unsigned long index = -1;
+       JLL(value, list->list, index );
+       int retcode;
+
+       while (value != NULL) {
+               if(list->freeItem) 
+                       list->freeItem( (void*) *value );
+               JLD(retcode, list->list, index);
+               JLP(value, list->list, index);
+       }               
+
+       free(list);
+}
+
+void* osrfBigListRemove( osrfBigList* list, int position ) {
+       if(!list) return NULL;
+
+       int retcode;
+       Word_t* value;
+       JLG( value, list->list, position );
+       void* olditem = NULL;
+
+       if( value ) {
+
+               olditem = (void*) *value;
+               if( olditem ) {
+                       JLD(retcode, list->list, position );
+                       if(retcode == 1) {
+                               if(list->freeItem) {
+                                       list->freeItem( olditem );
+                                       olditem = NULL;
+                               }
+                               __osrfBigListSetSize( list );
+                       }
+               }
+       }
+
+       return olditem;
+}
+
+
+int osrfBigListFind( osrfBigList* list, void* addr ) {
+       if(!(list && addr)) return -1;
+
+       Word_t* value;
+       unsigned long index = -1;
+       JLL(value, list->list, index );
+
+       while (value != NULL) {
+               if( (void*) *value == addr )
+                       return index;
+               JLP(value, list->list, index);
+       }
+
+       return -1;
+}
+
+
+
+void __osrfBigListSetSize( osrfBigList* list ) {
+       if(!list) return;
+
+       Word_t* value;
+       unsigned long index = -1;
+       JLL(value, list->list, index );
+       list->size = index + 1;
+}
+
+
+unsigned long osrfBigListGetCount( osrfBigList* list ) {
+       if(!list) return -1;
+       unsigned long retcode = -1;
+       JLC( retcode, list->list, 0, -1 );
+       return retcode;
+}
+
+
+void* osrfBigListPop( osrfBigList* list ) {
+       if(!list) return NULL;
+       return osrfBigListRemove( list, list->size - 1 );
+}
+
+
+osrfBigBigListIterator* osrfNewBigListIterator( osrfBigList* list ) {
+       if(!list) return NULL;
+       osrfBigBigListIterator* itr = safe_malloc(sizeof(osrfBigBigListIterator));
+       itr->list = list;
+       itr->current = 0;
+       return itr;
+}
+
+void* osrfBigBigListIteratorNext( osrfBigBigListIterator* itr ) {
+       if(!(itr && itr->list)) return NULL;
+
+       Word_t* value;
+       if(itr->current >= itr->list->size) return NULL;
+       JLF( value, itr->list->list, itr->current );
+       if(value) {
+               itr->current++;
+               return (void*) *value;
+       }
+       return NULL;
+}
+
+void osrfBigBigListIteratorFree( osrfBigBigListIterator* itr ) {
+       if(!itr) return;
+       free(itr);
+}
+
+
+
+void osrfBigBigListIteratorReset( osrfBigBigListIterator* itr ) {
+       if(!itr) return;
+       itr->current = 0;
+}
+
+
+void osrfBigListVanillaFree( void* item ) {
+       free(item);
+}
+
+void osrfBigListSetDefaultFree( osrfBigList* list ) {
+       if(!list) return;
+       list->freeItem = osrfBigListVanillaFree;
+}
diff --git a/src/libopensrf/osrf_cache.c b/src/libopensrf/osrf_cache.c
new file mode 100644 (file)
index 0000000..940af11
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+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.
+*/
+
+#include <opensrf/osrf_cache.h>
+
+struct memcache* __osrfCache = NULL;
+time_t __osrfCacheMaxSeconds = -1;
+
+int osrfCacheInit( char* serverStrings[], int size, time_t maxCacheSeconds ) {
+       if( !(serverStrings && size > 0) ) return -1;
+
+       int i;
+       __osrfCache = mc_new();
+       __osrfCacheMaxSeconds = maxCacheSeconds;
+
+       for( i = 0; i < size && serverStrings[i]; i++ ) 
+               mc_server_add4( __osrfCache, serverStrings[i] );
+
+       return 0;
+}
+
+int osrfCachePutObject( char* key, const jsonObject* obj, time_t seconds ) {
+       if( !(key && obj) ) return -1;
+       char* s = jsonObjectToJSON( obj );
+       osrfLogInternal( OSRF_LOG_MARK, "osrfCachePut(): Putting object: %s", s);
+       if( seconds < 0 ) seconds = __osrfCacheMaxSeconds;
+
+       mc_set(__osrfCache, key, strlen(key), s, strlen(s), seconds, 0);
+       free(s);
+       return 0;
+}
+
+int osrfCachePutString( char* key, const char* value, time_t seconds ) {
+       if( !(key && value) ) return -1;
+       if( seconds < 0 ) seconds = __osrfCacheMaxSeconds;
+       osrfLogInternal( OSRF_LOG_MARK, "osrfCachePutString(): Putting string: %s", value);
+       mc_set(__osrfCache, key, strlen(key), value, strlen(value), seconds, 0);
+       return 0;
+}
+
+jsonObject* osrfCacheGetObject( char* key, ... ) {
+       jsonObject* obj = NULL;
+       if( key ) {
+               VA_LIST_TO_STRING(key);
+               char* data = (char*) mc_aget( __osrfCache, VA_BUF, strlen(VA_BUF) );
+               if( data ) {
+                       osrfLogInternal( OSRF_LOG_MARK, "osrfCacheGetObject(): Returning object: %s", data);
+                       obj = jsonParseString( data );
+                       return obj;
+               }
+               osrfLogWarning(OSRF_LOG_MARK, "No cache data exists with key %s", VA_BUF);
+       }
+       return NULL;
+}
+
+char* osrfCacheGetString( char* key, ... ) {
+       if( key ) {
+               VA_LIST_TO_STRING(key);
+               char* data = (char*) mc_aget(__osrfCache, VA_BUF, strlen(VA_BUF) );
+               osrfLogInternal( OSRF_LOG_MARK, "osrfCacheGetObject(): Returning object: %s", data);
+               if(!data) osrfLogWarning(OSRF_LOG_MARK, "No cache data exists with key %s", VA_BUF);
+               return data;
+       }
+       return NULL;
+}
+
+
+int osrfCacheRemove( char* key, ... ) {
+       if( key ) {
+               VA_LIST_TO_STRING(key);
+               return mc_delete(__osrfCache, VA_BUF, strlen(VA_BUF), 0 );
+       }
+       return -1;
+}
+
+
+int osrfCacheSetExpire( time_t seconds, char* key, ... ) {
+       if( key ) {
+               VA_LIST_TO_STRING(key);
+               jsonObject* o = osrfCacheGetObject( VA_BUF );
+               //osrfCacheRemove(VA_BUF);
+               return osrfCachePutObject( VA_BUF, o, seconds );
+       }
+       return -1;
+}
+
+
diff --git a/src/libopensrf/osrf_hash.c b/src/libopensrf/osrf_hash.c
new file mode 100644 (file)
index 0000000..e00f09c
--- /dev/null
@@ -0,0 +1,242 @@
+#include <opensrf/osrf_hash.h>
+
+osrfHash* osrfNewHash() {
+       osrfHash* hash;
+       OSRF_MALLOC(hash, sizeof(osrfHash));
+       hash->hash              = osrfNewList();
+       hash->keys              = osrfNewStringArray(64);
+       return hash;
+}
+
+
+/* algorithm proposed by Donald E. Knuth 
+ * in The Art Of Computer Programming Volume 3 (more or less..)*/
+/*
+static unsigned int osrfHashMakeKey(char* str) {
+       if(!str) return 0;
+       unsigned int len = strlen(str);
+       unsigned int h = len;
+       unsigned int i = 0;
+       for(i = 0; i < len; str++, i++)
+               h = ((h << 5) ^ (h >> 27)) ^ (*str);
+       return (h & (OSRF_HASH_LIST_SIZE-1));
+}
+*/
+
+
+/* macro version of the above function */
+#define OSRF_HASH_MAKE_KEY(str,num) \
+   do {\
+      char* __k = str;\
+      unsigned int __len = strlen(__k); \
+      unsigned int __h = __len;\
+      unsigned int __i = 0;\
+      for(__i = 0; __i < __len; __k++, __i++)\
+         __h = ((__h << 5) ^ (__h >> 27)) ^ (*__k);\
+      num = (__h & (OSRF_HASH_LIST_SIZE-1));\
+   } while(0)
+
+
+
+/* returns the index of the item and points l to the sublist the item
+ * lives in if the item and points n to the hashnode the item 
+ * lives in if the item is found.  Otherwise -1 is returned */
+static unsigned int osrfHashFindItem( osrfHash* hash, char* key, osrfList** l, osrfHashNode** n ) {
+       if(!(hash && key)) return -1;
+
+
+       unsigned int i = 0;
+       OSRF_HASH_MAKE_KEY(key,i);
+
+       osrfList* list = OSRF_LIST_GET_INDEX( hash->hash, i );
+       if( !list ) { return -1; }
+
+       int k;
+       osrfHashNode* node = NULL;
+       for( k = 0; k < list->size; k++ ) {
+               node = OSRF_LIST_GET_INDEX(list, k);
+               if( node && node->key && !strcmp(node->key, key) )
+                       break;
+               node = NULL;
+       }
+
+       if(!node) return -1;
+
+       if(l) *l = list;
+       if(n) *n = node;
+       return k;
+}
+
+osrfHashNode* osrfNewHashNode(char* key, void* item) {
+       if(!(key && item)) return NULL;
+       osrfHashNode* n;
+       OSRF_MALLOC(n, sizeof(osrfHashNode));
+       n->key = strdup(key);
+       n->item = item;
+       return n;
+}
+
+void* osrfHashNodeFree(osrfHash* hash, osrfHashNode* node) {
+       if(!(node && hash)) return NULL;
+       void* item = NULL;
+       if( hash->freeItem )
+               hash->freeItem( node->key, node->item );
+       else item = node->item;
+       free(node->key);
+       free(node);
+       return item;
+}
+
+void* osrfHashSet( osrfHash* hash, void* item, const char* key, ... ) {
+       if(!(hash && item && key )) return NULL;
+
+       VA_LIST_TO_STRING(key);
+       void* olditem = osrfHashRemove( hash, VA_BUF );
+
+       unsigned int bucketkey = 0;
+       OSRF_HASH_MAKE_KEY(VA_BUF,bucketkey);
+       
+       osrfList* bucket;
+       if( !(bucket = OSRF_LIST_GET_INDEX(hash->hash, bucketkey)) ) {
+               bucket = osrfNewList();
+               osrfListSet( hash->hash, bucket, bucketkey );
+       }
+
+       osrfHashNode* node = osrfNewHashNode(VA_BUF, item);
+       osrfListPushFirst( bucket, node );
+
+       if(!osrfStringArrayContains(hash->keys, VA_BUF))
+               osrfStringArrayAdd( hash->keys, VA_BUF );
+
+       hash->size++;
+       return olditem;
+}
+
+void* osrfHashRemove( osrfHash* hash, const char* key, ... ) {
+       if(!(hash && key )) return NULL;
+
+       VA_LIST_TO_STRING(key);
+
+       osrfList* list = NULL;
+       osrfHashNode* node;
+       int index = osrfHashFindItem( hash, (char*) VA_BUF, &list, &node );
+       if( index == -1 ) return NULL;
+
+       osrfListRemove( list, index );
+       hash->size--;
+
+       void* item = osrfHashNodeFree(hash, node);
+       osrfStringArrayRemove(hash->keys, VA_BUF);
+       return item;
+}
+
+
+void* osrfHashGet( osrfHash* hash, const char* key, ... ) {
+       if(!(hash && key )) return NULL;
+       VA_LIST_TO_STRING(key);
+
+       osrfHashNode* node = NULL;
+       int index = osrfHashFindItem( hash, (char*) VA_BUF, NULL, &node );
+       if( index == -1 ) return NULL;
+       return node->item;
+}
+
+
+osrfStringArray* osrfHashKeysInc( osrfHash* hash ) {
+       if(!hash) return NULL;
+       return hash->keys;
+}
+
+osrfStringArray* osrfHashKeys( osrfHash* hash ) {
+       if(!hash) return NULL;
+       
+       int i, k;
+       osrfList* list;
+       osrfHashNode* node;
+       osrfStringArray* strings = osrfNewStringArray(8);
+
+       for( i = 0; i != hash->hash->size; i++ ) {
+               list = OSRF_LIST_GET_INDEX( hash->hash, i );
+               if(list) {
+                       for( k = 0; k != list->size; k++ ) {
+                               node = OSRF_LIST_GET_INDEX( list, k );  
+                               if( node ) osrfStringArrayAdd( strings, node->key );
+                       }
+               }
+       }
+
+       return strings;
+}
+
+
+unsigned long osrfHashGetCount( osrfHash* hash ) {
+       if(!hash) return -1;
+       return hash->size;
+}
+
+void osrfHashFree( osrfHash* hash ) {
+       if(!hash) return;
+
+       int i, j;
+       osrfList* list;
+       osrfHashNode* node;
+
+       for( i = 0; i != hash->hash->size; i++ ) {
+               if( ( list = OSRF_LIST_GET_INDEX( hash->hash, i )) ) {
+                       for( j = 0; j != list->size; j++ ) {
+                               if( (node = OSRF_LIST_GET_INDEX( list, j )) ) {
+                                       OSRF_HASH_NODE_FREE(hash, node);
+                               }
+                       }
+                       osrfListFree(list);
+               }
+       }
+
+       osrfListFree(hash->hash);
+       osrfStringArrayFree(hash->keys);
+       free(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;
+       itr->current = NULL;
+       itr->keys = osrfHashKeysInc(hash);
+       return itr;
+}
+
+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++));
+       char* val = osrfHashGet( itr->hash, itr->current );
+       return val;
+}
+
+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;
+}
+
+
+int osrfHashIteratorHasNext( osrfHashIterator* itr ) {
+       return ( itr->currentIdx < itr->keys->size ) ? 1 : 0;
+}
diff --git a/src/libopensrf/osrf_list.c b/src/libopensrf/osrf_list.c
new file mode 100644 (file)
index 0000000..2d37b02
--- /dev/null
@@ -0,0 +1,154 @@
+#include <opensrf/osrf_list.h>
+
+osrfList* osrfNewList() {
+       osrfList* list;
+       OSRF_MALLOC(list, sizeof(osrfList));
+       list->arrsize   = OSRF_LIST_DEFAULT_SIZE;
+       OSRF_MALLOC(list->arrlist, list->arrsize * sizeof(void*));
+       return list;
+}
+
+osrfList* osrfNewListSize( unsigned int size ) {
+       osrfList* list;
+       OSRF_MALLOC(list, sizeof(osrfList));
+    if( size <= 0 ) size = 16;
+       list->arrsize   = size;
+       OSRF_MALLOC( list->arrlist, list->arrsize * sizeof(void*) );
+       return list;
+}
+
+
+int osrfListPush( osrfList* list, void* item ) {
+       if(!(list)) return -1;
+       osrfListSet( list, item, list->size );
+       return 0;
+}
+
+int osrfListPushFirst( osrfList* list, void* item ) {
+       if(!(list && item)) return -1;
+       int i;
+       for( i = 0; i < list->size; i++ ) 
+               if(!list->arrlist[i]) break;
+       osrfListSet( list, item, i );
+       return list->size;
+}
+
+void* osrfListSet( osrfList* list, void* item, unsigned int position ) {
+       if(!list || position < 0) return NULL;
+
+       int i;
+       int newsize = list->arrsize;
+       void** newarr;
+
+       while( position >= newsize ) 
+               newsize += OSRF_LIST_INC_SIZE;
+
+       if( newsize > list->arrsize ) { /* expand the list if necessary */
+               OSRF_MALLOC(newarr, newsize * sizeof(void*));
+               for( i = 0; i < list->arrsize; i++ ) 
+                       newarr[i] = list->arrlist[i];
+               free(list->arrlist);
+               list->arrlist = newarr;
+               list->arrsize = newsize;
+       }
+
+       void* olditem = osrfListRemove( list, position );
+       list->arrlist[position] = item;
+       if( list->size <= position ) list->size = position + 1;
+       return olditem;
+}
+
+
+void* osrfListGetIndex( const osrfList* list, unsigned int position ) {
+       if(!list || position >= list->size || position < 0) return NULL;
+       return list->arrlist[position];
+}
+
+void osrfListFree( osrfList* list ) {
+       if(!list) return;
+
+       if( list->freeItem ) {
+               int i; void* val;
+               for( i = 0; i < list->size; i++ ) {
+                       if( (val = list->arrlist[i]) ) 
+                               list->freeItem(val);
+               }
+       }
+
+       free(list->arrlist);
+       free(list);
+}
+
+void* osrfListRemove( osrfList* list, unsigned int position ) {
+       if(!list || position >= list->size || position < 0) return NULL;
+
+       void* olditem = list->arrlist[position];
+       list->arrlist[position] = NULL;
+       if( list->freeItem ) {
+               list->freeItem(olditem);
+               olditem = NULL;
+       }
+
+       if( position == list->size - 1 ) list->size--;
+       return olditem;
+}
+
+
+int osrfListFind( const osrfList* list, void* addr ) {
+       if(!(list && addr)) return -1;
+       int index;
+       for( index = 0; index < list->size; index++ ) {
+               if( list->arrlist[index] == addr ) 
+                       return index;
+       }
+       return -1;
+}
+
+
+unsigned int osrfListGetCount( const osrfList* list ) {
+       if(!list) return -1;
+       return list->size;
+}
+
+
+void* osrfListPop( osrfList* list ) {
+       if(!list) return NULL;
+       return osrfListRemove( list, list->size - 1 );
+}
+
+
+osrfListIterator* osrfNewListIterator( const osrfList* list ) {
+       if(!list) return NULL;
+       osrfListIterator* itr;
+       OSRF_MALLOC(itr, sizeof(osrfListIterator));
+       itr->list = list;
+       itr->current = 0;
+       return itr;
+}
+
+void* osrfListIteratorNext( osrfListIterator* itr ) {
+       if(!(itr && itr->list)) return NULL;
+       if(itr->current >= itr->list->size) return NULL;
+       return itr->list->arrlist[itr->current++];
+}
+
+void osrfListIteratorFree( osrfListIterator* itr ) {
+       if(!itr) return;
+       free(itr);
+}
+
+
+void osrfListIteratorReset( osrfListIterator* itr ) {
+       if(!itr) return;
+       itr->current = 0;
+}
+
+
+void osrfListVanillaFree( void* item ) {
+       free(item);
+}
+
+void osrfListSetDefaultFree( osrfList* list ) {
+       if(!list) return;
+       list->freeItem = osrfListVanillaFree;
+}
diff --git a/src/libopensrf/osrf_message.c b/src/libopensrf/osrf_message.c
new file mode 100644 (file)
index 0000000..313216a
--- /dev/null
@@ -0,0 +1,330 @@
+#include <opensrf/osrf_message.h>
+
+osrf_message* osrf_message_init( enum M_TYPE type, int thread_trace, int protocol ) {
+
+       osrf_message* msg                       = (osrf_message*) safe_malloc(sizeof(osrf_message));
+       msg->m_type                                     = type;
+       msg->thread_trace                       = thread_trace;
+       msg->protocol                           = protocol;
+       msg->next                                       = NULL;
+       msg->is_exception                       = 0;
+       msg->_params                            = NULL;
+       msg->_result_content            = NULL;
+
+       return msg;
+}
+
+
+void osrf_message_set_method( osrf_message* msg, 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 ) {
+       if(!msg|| !o) return;
+       if(!msg->_params)
+               msg->_params = jsonParseString("[]");
+       char* j = jsonObjectToJSON(o);
+       jsonObjectPush(msg->_params, jsonParseString(j));
+       free(j);
+}
+
+void osrf_message_set_params( osrf_message* msg, 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;
+               return;
+       }
+
+       if(msg->_params) jsonObjectFree(msg->_params);
+       msg->_params = jsonObjectClone(o);
+}
+
+
+/* only works if parse_json_params is false */
+void osrf_message_add_param( osrf_message* msg, 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 ) {
+       if(!msg) return;
+
+       if( status_name != NULL ) 
+               msg->status_name = strdup( status_name );
+
+       if( status_text != NULL )
+               msg->status_text = strdup( status_text );
+
+       msg->status_code = status_code;
+}
+
+
+void osrf_message_set_result_content( osrf_message* msg, 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);
+}
+
+
+
+void osrfMessageFree( osrfMessage* msg ) {
+       osrf_message_free( msg );
+}
+
+void osrf_message_free( osrf_message* msg ) {
+       if( msg == NULL )
+               return;
+
+       if( msg->status_name != NULL )
+               free(msg->status_name);
+
+       if( msg->status_text != NULL )
+               free(msg->status_text);
+
+       if( msg->_result_content != NULL )
+               jsonObjectFree( msg->_result_content );
+
+       if( msg->result_string != NULL )
+               free( msg->result_string);
+
+       if( msg->method_name != NULL )
+               free(msg->method_name);
+
+       if( msg->_params != NULL )
+               jsonObjectFree(msg->_params);
+
+       free(msg);
+}
+
+
+char* osrfMessageSerializeBatch( osrfMessage* msgs [], int count ) {
+       if( !msgs ) return NULL;
+
+       char* j;
+       int i = 0;
+       osrfMessage* msg = NULL;
+       jsonObject* wrapper = jsonNewObject(NULL);
+
+       while( ((msg = msgs[i]) && (i++ < count)) ) 
+               jsonObjectPush(wrapper, osrfMessageToJSON( msg ));
+
+       j = jsonObjectToJSON(wrapper);
+       jsonObjectFree(wrapper);
+
+       return j;       
+}
+
+
+char* osrf_message_serialize(osrf_message* msg) {
+
+       if( msg == NULL ) return NULL;
+       char* j = NULL;
+
+       jsonObject* json = osrfMessageToJSON( msg );
+
+       if(json) {
+               jsonObject* wrapper = jsonNewObject(NULL);
+               jsonObjectPush(wrapper, json);
+               j = jsonObjectToJSON(wrapper);
+               jsonObjectFree(wrapper);
+       }
+
+       return j;
+}
+
+
+jsonObject* osrfMessageToJSON( osrfMessage* msg ) {
+
+       jsonObject* json = jsonNewObject(NULL);
+       jsonObjectSetClass(json, "osrfMessage");
+       jsonObject* payload;
+       char sc[64]; memset(sc,0,64);
+
+       char* str;
+
+       INT_TO_STRING(msg->thread_trace);
+       jsonObjectSetKey(json, "threadTrace", jsonNewObject(INTSTR));
+
+       switch(msg->m_type) {
+               
+               case CONNECT: 
+                       jsonObjectSetKey(json, "type", jsonNewObject("CONNECT"));
+                       break;
+
+               case DISCONNECT: 
+                       jsonObjectSetKey(json, "type", jsonNewObject("DISCONNECT"));
+                       break;
+
+               case STATUS:
+                       jsonObjectSetKey(json, "type", jsonNewObject("STATUS"));
+                       payload = jsonNewObject(NULL);
+                       jsonObjectSetClass(payload, msg->status_name);
+                       jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
+         sprintf(sc,"%d",msg->status_code);
+                       jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
+                       jsonObjectSetKey(json, "payload", payload);
+                       break;
+
+               case REQUEST:
+                       jsonObjectSetKey(json, "type", jsonNewObject("REQUEST"));
+                       payload = jsonNewObject(NULL);
+                       jsonObjectSetClass(payload, "osrfMethod");
+                       jsonObjectSetKey(payload, "method", jsonNewObject(msg->method_name));
+                       str = jsonObjectToJSON(msg->_params);
+                       jsonObjectSetKey(payload, "params", jsonParseString(str));
+                       free(str);
+                       jsonObjectSetKey(json, "payload", payload);
+
+                       break;
+
+               case RESULT:
+                       jsonObjectSetKey(json, "type", jsonNewObject("RESULT"));
+                       payload = jsonNewObject(NULL);
+                       jsonObjectSetClass(payload,"osrfResult");
+                       jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
+         sprintf(sc,"%d",msg->status_code);
+                       jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
+                       str = jsonObjectToJSON(msg->_result_content);
+                       jsonObjectSetKey(payload, "content", jsonParseString(str));
+                       free(str);
+                       jsonObjectSetKey(json, "payload", payload);
+                       break;
+       }
+
+       return json;
+}
+
+
+int osrf_message_deserialize(char* string, osrf_message* msgs[], int count) {
+
+       if(!string || !msgs || count <= 0) return 0;
+       int numparsed = 0;
+
+       jsonObject* json = jsonParseString(string);
+
+       if(!json) {
+               osrfLogWarning( OSRF_LOG_MARK, 
+                       "osrf_message_deserialize() unable to parse data: \n%s\n", string);
+               return 0;
+       }
+
+       int x;
+
+       for( x = 0; x < json->size && x < count; x++ ) {
+
+               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");
+
+                       char* t;
+                       if( ( t = jsonObjectGetString(tmp)) ) {
+
+                               if(!strcmp(t, "CONNECT"))               new_msg->m_type = CONNECT;
+                               if(!strcmp(t, "DISCONNECT"))    new_msg->m_type = DISCONNECT;
+                               if(!strcmp(t, "STATUS"))                new_msg->m_type = STATUS;
+                               if(!strcmp(t, "REQUEST"))               new_msg->m_type = REQUEST;
+                               if(!strcmp(t, "RESULT"))                new_msg->m_type = RESULT;
+                       }
+
+                       tmp = jsonObjectGetKey(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));
+                                       */
+                       }
+
+
+                       tmp = jsonObjectGetKey(message, "protocol");
+
+                       if(tmp) {
+                               char* proto = jsonObjectToSimpleString(tmp);
+                               if(proto) {
+                                       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");
+                       if(tmp) {
+                               if(tmp->classname)
+                                       new_msg->status_name = strdup(tmp->classname);
+
+                               jsonObject* tmp0 = jsonObjectGetKey(tmp,"method");
+                               if(jsonObjectGetString(tmp0))
+                                       new_msg->method_name = strdup(jsonObjectGetString(tmp0));
+
+                               tmp0 = jsonObjectGetKey(tmp,"params");
+                               if(tmp0) {
+                                       char* s = jsonObjectToJSON(tmp0);
+                                       new_msg->_params = jsonParseString(s);
+                                       if(new_msg->_params && new_msg->_params->type == JSON_NULL) 
+                                               new_msg->_params->type = JSON_ARRAY;
+                                       free(s);
+                               }
+
+                               tmp0 = jsonObjectGetKey(tmp,"status");
+                               if(jsonObjectGetString(tmp0))
+                                       new_msg->status_text = strdup(jsonObjectGetString(tmp0));
+
+                               tmp0 = jsonObjectGetKey(tmp,"statusCode");
+                               if(tmp0) {
+                                       if(jsonObjectGetString(tmp0))
+                                               new_msg->status_code = atoi(jsonObjectGetString(tmp0));
+                                       if(tmp0->type == JSON_NUMBER)
+                                               new_msg->status_code = (int) jsonObjectGetNumber(tmp0);
+                               }
+
+                               tmp0 = jsonObjectGetKey(tmp,"content");
+                               if(tmp0) {
+                                       char* s = jsonObjectToJSON(tmp0);
+                                       new_msg->_result_content = jsonParseString(s);
+                                       free(s);
+                               }
+
+                       }
+                       msgs[numparsed++] = new_msg;
+               }
+       }
+
+       jsonObjectFree(json);
+       return numparsed;
+}
+
+
+
+jsonObject* osrfMessageGetResult( osrfMessage* msg ) {
+       if(msg) return msg->_result_content;
+       return NULL;
+}
+
diff --git a/src/libopensrf/osrf_prefork.c b/src/libopensrf/osrf_prefork.c
new file mode 100644 (file)
index 0000000..01a97cc
--- /dev/null
@@ -0,0 +1,765 @@
+#include <opensrf/osrf_prefork.h>
+#include <opensrf/osrf_app_session.h>
+#include <opensrf/osrf_application.h>
+#include <signal.h>
+
+/* true if we just deleted a child.  This will allow us to make sure we're
+       not trying to use freed memory */
+int child_dead;
+
+int main();
+void sigchld_handler( int sig );
+
+int osrf_prefork_run(char* appname) {
+
+       if(!appname) {
+               osrfLogError( OSRF_LOG_MARK, "osrf_prefork_run requires an appname to run!");
+               return -1;
+       }
+
+       set_proc_title( "OpenSRF Listener [%s]", appname );
+
+       int maxr = 1000; 
+       int maxc = 10;
+       int minc = 3;
+
+       osrfLogInfo( OSRF_LOG_MARK, "Loading config in osrf_forker for app %s", appname);
+
+       jsonObject* max_req = osrf_settings_host_value_object("/apps/%s/unix_config/max_requests", appname);
+       jsonObject* min_children = osrf_settings_host_value_object("/apps/%s/unix_config/min_children", appname);
+       jsonObject* max_children = osrf_settings_host_value_object("/apps/%s/unix_config/max_children", appname);
+
+       char* keepalive = osrf_settings_host_value("/apps/%s/keepalive", appname);
+       time_t kalive;
+       if( keepalive ) {
+               kalive = atoi(keepalive);
+               free(keepalive);
+       } else {
+               kalive = 5; /* give it a default */
+       }
+
+       osrfLogInfo(OSRF_LOG_MARK, "keepalive setting = %d seconds", kalive);
+
+
+       
+       if(!max_req) osrfLogWarning( OSRF_LOG_MARK, "Max requests not defined, assuming 1000");
+       else maxr = (int) jsonObjectGetNumber(max_req);
+
+       if(!min_children) osrfLogWarning( OSRF_LOG_MARK, "Min children not defined, assuming 3");
+       else minc = (int) jsonObjectGetNumber(min_children);
+
+       if(!max_children) osrfLogWarning( OSRF_LOG_MARK, "Max children not defined, assuming 10");
+       else maxc = (int) jsonObjectGetNumber(max_children);
+
+       jsonObjectFree(max_req);
+       jsonObjectFree(min_children);
+       jsonObjectFree(max_children);
+       /* --------------------------------------------------- */
+
+       char* resc = va_list_to_string("%s_listener", appname);
+
+       if(!osrf_system_bootstrap_client_resc( NULL, NULL, resc )) {
+               osrfLogError( OSRF_LOG_MARK, "Unable to bootstrap client for osrf_prefork_run()");
+               free(resc);
+               return -1;
+       }
+
+       free(resc);
+
+       prefork_simple* forker = prefork_simple_init(
+               osrfSystemGetTransportClient(), maxr, minc, maxc);
+
+       forker->appname = strdup(appname);
+       forker->keepalive       = kalive;
+
+       if(forker == NULL) {
+               osrfLogError( OSRF_LOG_MARK, "osrf_prefork_run() failed to create prefork_simple object");
+               return -1;
+       }
+
+       prefork_launch_children(forker);
+
+       osrf_prefork_register_routers(appname);
+       
+       osrfLogInfo( OSRF_LOG_MARK, "Launching osrf_forker for app %s", appname);
+       prefork_run(forker);
+       
+       osrfLogWarning( OSRF_LOG_MARK, "prefork_run() retuned - how??");
+       prefork_free(forker);
+       return 0;
+
+}
+
+void osrf_prefork_register_routers( char* appname ) {
+
+       osrfStringArray* arr = osrfNewStringArray(4);
+
+       int c = osrfConfigGetValueList( NULL, arr, "/routers/router" );
+       char* routerName = osrfConfigGetValue( NULL, "/router_name" );
+       transport_client* client = osrfSystemGetTransportClient();
+
+       osrfLogInfo( OSRF_LOG_MARK, "router name is %s and we have %d routers to connect to", routerName, c );
+
+       while( c ) {
+               char* domain = osrfStringArrayGetString(arr, --c);
+               if(domain) {
+
+                       char* jid = va_list_to_string( "%s@%s/router", routerName, domain );
+                       osrfLogInfo( OSRF_LOG_MARK, "Registering with router %s", jid );
+
+                       transport_message* msg = message_init("registering", NULL, NULL, jid, NULL );
+                       message_set_router_info( msg, NULL, NULL, appname, "register", 0 );
+
+                       client_send_message( client, msg );
+                       message_free( msg );
+                       free(jid);
+               }
+       }
+
+       free(routerName);
+       osrfStringArrayFree(arr);
+}
+
+int prefork_child_init_hook(prefork_child* child) {
+
+       if(!child) return -1;
+       osrfLogDebug( OSRF_LOG_MARK, "Child init hook for child %d", child->pid);
+       char* resc = va_list_to_string("%s_drone",child->appname);
+
+   /* if we're a source-client, tell the logger now that we're a new process*/
+   char* isclient = osrfConfigGetValue(NULL, "/client");
+   if( isclient && !strcasecmp(isclient,"true") )
+      osrfLogSetIsClient(1);
+   free(isclient);
+
+
+       /* we want to remove traces of our parents socket connection 
+        * so we can have our own */
+       osrfSystemIgnoreTransportClient();
+
+       if(!osrf_system_bootstrap_client_resc( NULL, NULL, resc)) {
+               osrfLogError( OSRF_LOG_MARK, "Unable to bootstrap client for osrf_prefork_run()");
+               free(resc);
+               return -1;
+       }
+
+       free(resc);
+
+       if( ! osrfAppRunChildInit(child->appname) ) {
+               osrfLogDebug(OSRF_LOG_MARK, "Prefork child_init succeeded\n");
+       } else {
+               osrfLogError(OSRF_LOG_MARK, "Prefork child_init failed\n");
+               return -1;
+       }
+
+       set_proc_title( "OpenSRF Drone [%s]", child->appname );
+       return 0;
+}
+
+void prefork_child_process_request(prefork_child* child, char* data) {
+       if( !child ) return;
+
+       transport_client* client = osrfSystemGetTransportClient();
+
+       if(!client_connected(client)) {
+               osrfSystemIgnoreTransportClient();
+               osrfLogWarning(OSRF_LOG_MARK, "Reconnecting child to opensrf after disconnect...");
+               if(!osrf_system_bootstrap_client(NULL, NULL)) {
+                       osrfLogError( OSRF_LOG_MARK, 
+                               "Unable to bootstrap client in prefork_child_process_request()");
+                       sleep(1);
+         osrf_prefork_child_exit(child);
+               }
+       }
+
+       /* construct the message from the xml */
+       transport_message* msg = new_message_from_xml( data );
+
+       osrfAppSession* session = osrf_stack_transport_handler(msg, child->appname);
+       if(!session) return;
+
+       if( session->stateless && session->state != OSRF_SESSION_CONNECTED ) {
+               osrfAppSessionFree( session );
+               return;
+       }
+
+       osrfLogDebug( OSRF_LOG_MARK, "Entering keepalive loop for session %s", session->session_id );
+       int keepalive = child->keepalive;
+       int retval;
+       int recvd;
+       time_t start;
+       time_t end;
+
+       while(1) {
+
+               osrfLogDebug(OSRF_LOG_MARK, 
+                               "osrf_prefork calling queue_wait [%d] in keepalive loop", keepalive);
+               start           = time(NULL);
+               retval  = osrf_app_session_queue_wait(session, keepalive, &recvd);
+               end             = time(NULL);
+
+               osrfLogDebug(OSRF_LOG_MARK, "Data received == %d", recvd);
+
+               if(retval) {
+                       osrfLogError(OSRF_LOG_MARK, "queue-wait returned non-success %d", retval);
+                       break;
+               }
+
+               /* see if the client disconnected from us */
+               if(session->state != OSRF_SESSION_CONNECTED) break;
+
+               /* 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);
+                       osrfAppSessionStatus( 
+                                       session, 
+                                       OSRF_STATUS_TIMEOUT, 
+                                       "osrfConnectStatus", 
+                                       0, "Disconnected on timeout" );
+
+                       break;
+               }
+       }
+
+       osrfLogDebug( OSRF_LOG_MARK, "Exiting keepalive loop for session %s", session->session_id );
+       osrfAppSessionFree( session );
+       return;
+}
+
+
+prefork_simple*  prefork_simple_init( transport_client* client, 
+               int max_requests, int min_children, int max_children ) {
+
+       if( min_children > max_children ) {
+               osrfLogError( OSRF_LOG_MARK,  "min_children (%d) is greater "
+                               "than max_children (%d)", min_children, max_children );
+               return NULL;
+       }
+
+       if( max_children > ABS_MAX_CHILDREN ) {
+               osrfLogError( OSRF_LOG_MARK,  "max_children (%d) is greater than ABS_MAX_CHILDREN (%d)",
+                               max_children, ABS_MAX_CHILDREN );
+               return NULL;
+       }
+
+       osrfLogInfo(OSRF_LOG_MARK, "Prefork launching child with max_request=%d,"
+               "min_children=%d, max_children=%d", max_requests, min_children, max_children );
+
+       /* flesh out the struct */
+       prefork_simple* prefork = (prefork_simple*) safe_malloc(sizeof(prefork_simple));        
+       prefork->max_requests = max_requests;
+       prefork->min_children = min_children;
+       prefork->max_children = max_children;
+       prefork->first_child = NULL;
+       prefork->connection = client;
+
+       return prefork;
+}
+
+prefork_child*  launch_child( prefork_simple* forker ) {
+
+       pid_t pid;
+       int data_fd[2];
+       int status_fd[2];
+
+       /* Set up the data pipes and add the child struct to the parent */
+       if( pipe(data_fd) < 0 ) { /* build the data pipe*/
+               osrfLogError( OSRF_LOG_MARK,  "Pipe making error" );
+               return NULL;
+       }
+
+       if( pipe(status_fd) < 0 ) {/* build the status pipe */
+               osrfLogError( OSRF_LOG_MARK,  "Pipe making error" );
+               return NULL;
+       }
+
+       osrfLogInternal( OSRF_LOG_MARK,  "Pipes: %d %d %d %d", data_fd[0], data_fd[1], status_fd[0], status_fd[1] );
+       prefork_child* child = prefork_child_init( forker->max_requests, data_fd[0], 
+                       data_fd[1], status_fd[0], status_fd[1] );
+
+       child->appname = strdup(forker->appname);
+       child->keepalive = forker->keepalive;
+
+
+       add_prefork_child( forker, child );
+
+       if( (pid=fork()) < 0 ) {
+               osrfLogError( OSRF_LOG_MARK,  "Forking Error" );
+               return NULL;
+       }
+
+       if( pid > 0 ) {  /* parent */
+
+               signal(SIGCHLD, sigchld_handler);
+               (forker->current_num_children)++;
+               child->pid = pid;
+
+               osrfLogDebug( OSRF_LOG_MARK,  "Parent launched %d", pid );
+               /* *no* child pipe FD's can be closed or the parent will re-use fd's that
+                       the children are currently using */
+               return child;
+       }
+
+       else { /* child */
+
+               osrfLogInternal( OSRF_LOG_MARK, "I am  new child with read_data_fd = %d and write_status_fd = %d",
+                       child->read_data_fd, child->write_status_fd );
+
+               child->pid = getpid();
+               close( child->write_data_fd );
+               close( child->read_status_fd );
+
+               /* do the initing */
+               if( prefork_child_init_hook(child) == -1 ) {
+                       osrfLogError(OSRF_LOG_MARK, 
+                               "Forker child going away because we could not connect to OpenSRF...");
+         osrf_prefork_child_exit(child);
+               }
+
+               prefork_child_wait( child );
+      osrf_prefork_child_exit(child); /* just to be sure */
+        }
+       return NULL;
+}
+
+void osrf_prefork_child_exit(prefork_child* child) {
+   osrfAppRunExitCode();
+   exit(0);
+}
+
+void prefork_launch_children( prefork_simple* forker ) {
+       if(!forker) return;
+       int c = 0;
+       while( c++ < forker->min_children )
+               launch_child( forker );
+}
+
+
+void sigchld_handler( int sig ) {
+       signal(SIGCHLD, sigchld_handler);
+       child_dead = 1;
+}
+
+
+void reap_children( prefork_simple* forker ) {
+
+       pid_t child_pid;
+       int status;
+
+       while( (child_pid=waitpid(-1,&status,WNOHANG)) > 0) 
+               del_prefork_child( forker, child_pid ); 
+
+       /* replenish */
+       while( forker->current_num_children < forker->min_children ) 
+               launch_child( forker );
+
+       child_dead = 0;
+}
+
+void prefork_run(prefork_simple* forker) {
+
+       if( forker->first_child == NULL )
+               return;
+
+       transport_message* cur_msg = NULL;
+
+
+       while(1) {
+
+               if( forker->first_child == NULL ) {/* no more children */
+                       osrfLogWarning( OSRF_LOG_MARK, "No more children..." );
+                       return;
+               }
+
+               osrfLogDebug( OSRF_LOG_MARK, "Forker going into wait for data...");
+               cur_msg = client_recv( forker->connection, -1 );
+
+               //fprintf(stderr, "Got Data %f\n", get_timestamp_millis() );
+
+               if( cur_msg == NULL ) continue;
+
+               int honored = 0;        /* true if we've serviced the request */
+               int no_recheck = 0;
+
+               while( ! honored ) {
+
+                       if(!no_recheck) check_children( forker, 0 ); 
+                       no_recheck = 0;
+
+                       osrfLogDebug( OSRF_LOG_MARK,  "Server received inbound data" );
+                       int k;
+                       prefork_child* cur_child = forker->first_child;
+
+                       /* Look for an available child */
+                       for( k = 0; k < forker->current_num_children; k++ ) {
+
+                               osrfLogInternal( OSRF_LOG_MARK, "Searching for available child. cur_child->pid = %d", cur_child->pid );
+                               osrfLogInternal( OSRF_LOG_MARK, "Current num children %d and loop %d", forker->current_num_children, k);
+                       
+                               if( cur_child->available ) {
+                                       osrfLogDebug( OSRF_LOG_MARK,  "forker sending data to %d", cur_child->pid );
+
+                                       message_prepare_xml( cur_msg );
+                                       char* data = cur_msg->msg_xml;
+                                       if( ! data || strlen(data) < 1 ) break;
+
+                                       cur_child->available = 0;
+                                       osrfLogInternal( OSRF_LOG_MARK,  "Writing to child fd %d", cur_child->write_data_fd );
+
+                                       int written = 0;
+                                       //fprintf(stderr, "Writing Data %f\n", get_timestamp_millis() );
+                                       if( (written = write( cur_child->write_data_fd, data, strlen(data) + 1 )) < 0 ) {
+                                               osrfLogWarning( OSRF_LOG_MARK, "Write returned error %d", errno);
+                                               cur_child = cur_child->next;
+                                               continue;
+                                       }
+
+                                       //fprintf(stderr, "Wrote %d bytes to child\n", written);
+
+                                       forker->first_child = cur_child->next;
+                                       honored = 1;
+                                       break;
+                               } else 
+                                       cur_child = cur_child->next;
+                       } 
+
+                       /* if none available, add a new child if we can */
+                       if( ! honored ) {
+                               osrfLogDebug( OSRF_LOG_MARK, "Not enough children, attempting to add...");
+
+                               if( forker->current_num_children < forker->max_children ) {
+                                       osrfLogDebug( OSRF_LOG_MARK,  "Launching new child with current_num = %d",
+                                                       forker->current_num_children );
+
+                                       prefork_child* new_child = launch_child( forker );
+                    if( new_child ) {
+
+                                           message_prepare_xml( cur_msg );
+                                           char* data = cur_msg->msg_xml;
+
+                        if( data ) {
+                            int len = strlen(data);
+
+                            if( len > 0 ) {
+                                                   new_child->available = 0;
+                                                   osrfLogDebug( OSRF_LOG_MARK,  "Writing to new child fd %d : pid %d", 
+                                                               new_child->write_data_fd, new_child->pid );
+        
+                                                   if( write( new_child->write_data_fd, data, len + 1 ) >= 0 ) {
+                                                       forker->first_child = new_child->next;
+                                                       honored = 1;
+                                }
+                            }
+                        }
+                    }
+
+                               }
+                       }
+
+                       if( !honored ) {
+                               osrfLogWarning( OSRF_LOG_MARK,  "No children available, waiting...");
+
+                               check_children( forker, 1 );  /* non-poll version */
+                               /* tell the loop no to call check_children again, since we're calling it now */
+                               no_recheck = 1;
+                       }
+
+                       if( child_dead )
+                               reap_children(forker);
+
+
+                       //fprintf(stderr, "Parent done with request %f\n", get_timestamp_millis() );
+
+               } // honored?
+
+               message_free( cur_msg );
+
+       } /* top level listen loop */
+
+}
+
+
+/** XXX Add a flag which tells select() to wait forever on children
+ * in the best case, this will be faster than calling usleep(x), and
+ * in the worst case it won't be slower and will do less logging...
+ */
+
+void check_children( prefork_simple* forker, int forever ) {
+
+       //check_begin:
+
+       int select_ret;
+       fd_set read_set;
+       FD_ZERO(&read_set);
+       int max_fd = 0;
+       int n;
+
+
+       if( child_dead )
+               reap_children(forker);
+
+       prefork_child* cur_child = forker->first_child;
+
+       int i;
+       for( i = 0; i!= forker->current_num_children; i++ ) {
+
+               if( cur_child->read_status_fd > max_fd )
+                       max_fd = cur_child->read_status_fd;
+               FD_SET( cur_child->read_status_fd, &read_set );
+               cur_child = cur_child->next;
+       }
+
+       FD_CLR(0,&read_set);/* just to be sure */
+
+       if( forever ) {
+               osrfLogWarning(OSRF_LOG_MARK, "We have no children available - waiting for one to show up...");
+
+               if( (select_ret=select( max_fd + 1 , &read_set, NULL, NULL, NULL)) == -1 ) {
+                       osrfLogWarning( OSRF_LOG_MARK,  "Select returned error %d on check_children", errno );
+               }
+               osrfLogInfo(OSRF_LOG_MARK, "select() completed after waiting on children to become available");
+
+       } else {
+
+               struct timeval tv;
+               tv.tv_sec       = 0;
+               tv.tv_usec      = 0;
+       
+               if( (select_ret=select( max_fd + 1 , &read_set, NULL, NULL, &tv)) == -1 ) {
+                       osrfLogWarning( OSRF_LOG_MARK,  "Select returned error %d on check_children", errno );
+               }
+       }
+
+       if( select_ret == 0 )
+               return;
+
+       /* see if one of a child has told us it's done */
+       cur_child = forker->first_child;
+       int j;
+       int num_handled = 0;
+       for( j = 0; j!= forker->current_num_children && num_handled < select_ret ; j++ ) {
+
+               if( FD_ISSET( cur_child->read_status_fd, &read_set ) ) {
+                       //printf( "Server received status from a child %d\n", cur_child->pid );
+                       osrfLogDebug( OSRF_LOG_MARK,  "Server received status from a child %d", cur_child->pid );
+
+                       num_handled++;
+
+                       /* now suck off the data */
+                       char buf[64];
+                       memset( buf, 0, 64);
+                       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);
+                       }
+
+                       osrfLogDebug( OSRF_LOG_MARK,  "Read %d bytes from status buffer: %s", n, buf );
+                       cur_child->available = 1;
+               }
+               cur_child = cur_child->next;
+       } 
+
+}
+
+
+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 );
+
+       for( i = 0; i < child->max_requests; i++ ) {
+
+               n = -1;
+               int gotdata = 0;
+               clr_fl(child->read_data_fd, O_NONBLOCK );
+
+               while( (n=read(child->read_data_fd, buf, READ_BUFSIZE-1)) > 0 ) {
+                       osrfLogDebug(OSRF_LOG_MARK, "Prefork child read %d bytes of data", n);
+                       if(!gotdata)
+                               set_fl(child->read_data_fd, O_NONBLOCK );
+                       buffer_add( gbuf, buf );
+                       memset( buf, 0, READ_BUFSIZE );
+                       gotdata = 1;
+               }
+
+               if( errno == EAGAIN ) n = 0;
+
+      if( errno == EPIPE ) {
+         osrfLogDebug(OSRF_LOG_MARK, "C child attempted read on broken pipe, exiting...");
+         break;
+      }
+
+               if( n < 0 ) {
+                       osrfLogWarning( OSRF_LOG_MARK,  "Prefork child read returned error with errno %d", errno );
+                       break;
+
+               } else if( gotdata ) {
+                       osrfLogDebug(OSRF_LOG_MARK, "Prefork child got a request.. processing..");
+                       prefork_child_process_request(child, gbuf->buf);
+                       buffer_reset( gbuf );
+               }
+
+               if( i < child->max_requests - 1 ) 
+                       write( child->write_status_fd, "available" /*less than 64 bytes*/, 9 );
+       }
+
+       buffer_free(gbuf);
+
+       osrfLogDebug( OSRF_LOG_MARK, "Child with max-requests=%d, num-served=%d exiting...[%ld]", 
+                       child->max_requests, i, (long) getpid() );
+
+   osrf_prefork_child_exit(child); /* just to be sure */
+}
+
+
+void add_prefork_child( prefork_simple* forker, prefork_child* child ) {
+       
+       if( forker->first_child == NULL ) {
+               forker->first_child = child;
+               child->next = child;
+               return;
+       }
+
+       /* we put the child in as the last because, regardless, 
+               we have to do the DLL splice dance, and this is the
+          simplest way */
+
+       prefork_child* start_child = forker->first_child;
+       while(1) {
+               if( forker->first_child->next == start_child ) 
+                       break;
+               forker->first_child = forker->first_child->next;
+       }
+
+       /* here we know that forker->first_child is the last element 
+               in the list and start_child is the first.  Insert the
+               new child between them*/
+
+       forker->first_child->next = child;
+       child->next = start_child;
+       return;
+}
+
+prefork_child* find_prefork_child( prefork_simple* forker, pid_t pid ) {
+
+       if( forker->first_child == NULL ) { return NULL; }
+       prefork_child* start_child = forker->first_child;
+       do {
+               if( forker->first_child->pid == pid ) 
+                       return forker->first_child;
+       } while( (forker->first_child = forker->first_child->next) != start_child );
+
+       return NULL;
+}
+
+
+void del_prefork_child( prefork_simple* forker, pid_t pid ) { 
+
+       if( forker->first_child == NULL ) { return; }
+
+       (forker->current_num_children)--;
+       osrfLogDebug( OSRF_LOG_MARK, "Deleting Child: %d", pid );
+
+       prefork_child* start_child = forker->first_child; /* starting point */
+       prefork_child* cur_child        = start_child; /* current pointer */
+       prefork_child* prev_child       = start_child; /* the trailing pointer */
+
+       /* special case where there is only one in the list */
+       if( start_child == start_child->next ) {
+               if( start_child->pid == pid ) {
+                       forker->first_child = NULL;
+
+                       close( start_child->read_data_fd );
+                       close( start_child->write_data_fd );
+                       close( start_child->read_status_fd );
+                       close( start_child->write_status_fd );
+
+                       prefork_child_free( start_child );
+               }
+               return;
+       }
+
+
+       /* special case where the first item in the list needs to be removed */
+       if( start_child->pid == pid ) { 
+
+               /* find the last one so we can remove the start_child */
+               do { 
+                       prev_child = cur_child;
+                       cur_child = cur_child->next;
+               }while( cur_child != start_child );
+
+               /* now cur_child == start_child */
+               prev_child->next = cur_child->next;
+               forker->first_child = prev_child;
+
+               close( cur_child->read_data_fd );
+               close( cur_child->write_data_fd );
+               close( cur_child->read_status_fd );
+               close( cur_child->write_status_fd );
+
+               prefork_child_free( cur_child );
+               return;
+       } 
+
+       do {
+               prev_child = cur_child;
+               cur_child = cur_child->next;
+
+               if( cur_child->pid == pid ) {
+                       prev_child->next = cur_child->next;
+
+                       close( cur_child->read_data_fd );
+                       close( cur_child->write_data_fd );
+                       close( cur_child->read_status_fd );
+                       close( cur_child->write_status_fd );
+
+                       prefork_child_free( cur_child );
+                       return;
+               }
+
+       } while(cur_child != start_child);
+}
+
+
+
+
+prefork_child* prefork_child_init( 
+       int max_requests, int read_data_fd, int write_data_fd, 
+       int read_status_fd, int write_status_fd ) {
+
+       prefork_child* child = (prefork_child*) safe_malloc(sizeof(prefork_child));
+       child->max_requests             = max_requests;
+       child->read_data_fd             = read_data_fd;
+       child->write_data_fd            = write_data_fd;
+       child->read_status_fd   = read_status_fd;
+       child->write_status_fd  = write_status_fd;
+       child->available                        = 1;
+
+       return child;
+}
+
+
+int prefork_free( prefork_simple* prefork ) {
+       
+       while( prefork->first_child != NULL ) {
+               osrfLogInfo( OSRF_LOG_MARK,  "Killing children and sleeping 1 to reap..." );
+               kill( 0,        SIGKILL );
+               sleep(1);
+       }
+
+       client_free(prefork->connection);
+       free(prefork->appname);
+       free( prefork );
+       return 1;
+}
+
+int prefork_child_free( prefork_child* child ) { 
+       free(child->appname);
+       close(child->read_data_fd);
+       close(child->write_status_fd);
+       free( child ); 
+       return 1;
+}
+
diff --git a/src/libopensrf/osrf_settings.c b/src/libopensrf/osrf_settings.c
new file mode 100644 (file)
index 0000000..a732b08
--- /dev/null
@@ -0,0 +1,88 @@
+#include <opensrf/osrf_settings.h> 
+
+osrf_host_config* config = NULL;
+
+char* osrf_settings_host_value(char* format, ...) {
+       VA_LIST_TO_STRING(format);
+
+       if( ! config ) {
+               const char * msg = "NULL config pointer";
+               fprintf( stderr, "osrf_settings_host_value: %s\n", msg );
+               osrfLogError( OSRF_LOG_MARK, msg );
+               exit( 99 );
+       }
+
+       jsonObject* o = jsonObjectFindPath(config->config, VA_BUF);
+       char* val = jsonObjectToSimpleString(o);
+       jsonObjectFree(o);
+       return val;
+}
+
+jsonObject* osrf_settings_host_value_object(char* format, ...) {
+       VA_LIST_TO_STRING(format);
+
+       if( ! config ) {
+               const char * msg = "config pointer is NULL";
+               fprintf( stderr, "osrf_settings_host_value_object: %s\n", msg );
+               osrfLogError( OSRF_LOG_MARK, msg );
+               exit( 99 );
+       }
+
+       return jsonObjectFindPath(config->config, VA_BUF);
+}
+
+
+int osrf_settings_retrieve(char* hostname) {
+
+       if(!config) {
+
+               osrf_app_session* session = osrf_app_client_session_init("opensrf.settings");
+               jsonObject* params = jsonNewObject(NULL);
+               jsonObjectPush(params, jsonNewObject(hostname));
+               int req_id = osrf_app_session_make_req( 
+                       session, params, "opensrf.settings.host_config.get", 1, NULL );
+               osrf_message* omsg = osrf_app_session_request_recv( session, req_id, 60 );
+               jsonObjectFree(params);
+
+               if(!omsg) {
+                       osrfLogError( OSRF_LOG_MARK, "No osrf_message received from host %s (timeout?)", hostname);
+               } else if(!omsg->_result_content) {
+                       osrf_message_free(omsg);
+                       osrfLogError(
+                               OSRF_LOG_MARK,
+                               "NULL or non-existant osrf_message result content received from host %s, "
+                               "broken message or no settings for host",
+                               hostname
+                       );
+               } else {
+                       config = osrf_settings_new_host_config(hostname);
+                       config->config = jsonObjectClone(omsg->_result_content);
+                       osrf_message_free(omsg);
+               }
+
+               osrf_app_session_request_finish( session, req_id );
+               osrf_app_session_destroy( session );
+
+               if(!config) {
+                       osrfLogError( OSRF_LOG_MARK, "Unable to load config for host %s", hostname);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+osrf_host_config* osrf_settings_new_host_config(char* hostname) {
+       if(!hostname) return NULL;
+       osrf_host_config* c = safe_malloc(sizeof(osrf_host_config));
+       c->hostname = strdup(hostname);
+       return c;
+}
+
+void osrf_settings_free_host_config(osrf_host_config* c) {
+       if(!c) c = config;
+       if(!c) return;
+       free(c->hostname);
+       jsonObjectFree(c->config);      
+       free(c);
+}
diff --git a/src/libopensrf/osrf_stack.c b/src/libopensrf/osrf_stack.c
new file mode 100644 (file)
index 0000000..231ccbc
--- /dev/null
@@ -0,0 +1,263 @@
+#include <opensrf/osrf_stack.h>
+#include <opensrf/osrf_application.h>
+
+osrf_message* _do_client( osrf_app_session*, osrf_message* );
+osrf_message* _do_server( osrf_app_session*, osrf_message* );
+
+/* tell osrf_app_session where the stack entry is */
+int (*osrf_stack_entry_point) (transport_client*, int, int*)  = &osrf_stack_process;
+
+int osrf_stack_process( transport_client* client, int timeout, int* msg_received ) {
+       if( !client ) return -1;
+       transport_message* msg = NULL;
+       if(msg_received) *msg_received = 0;
+
+       while( (msg = client_recv( client, timeout )) ) {
+               if(msg_received) *msg_received = 1;
+               osrfLogDebug( OSRF_LOG_MARK,  "Received message from transport code from %s", msg->sender );
+               osrf_stack_transport_handler( msg, NULL );
+               timeout = 0;
+       }
+
+       if( client->error ) {
+               osrfLogWarning(OSRF_LOG_MARK, "transport_client had trouble reading from the socket..");
+               return -1;
+       }
+
+       if( ! client_connected( client ) ) return -1;
+
+       return 0;
+}
+
+
+
+// -----------------------------------------------------------------------------
+// Entry point into the stack
+// -----------------------------------------------------------------------------
+osrfAppSession* osrf_stack_transport_handler( transport_message* msg, char* my_service ) { 
+
+       if(!msg) return NULL;
+
+   osrfLogSetXid(msg->osrf_xid);
+
+       osrfLogDebug( OSRF_LOG_MARK,  "Transport handler received new message \nfrom %s "
+                       "to %s with body \n\n%s\n", msg->sender, msg->recipient, msg->body );
+
+       if( msg->is_error && ! msg->thread ) {
+               osrfLogWarning( OSRF_LOG_MARK, "!! Received jabber layer error for %s ... exiting\n", msg->sender );
+               message_free( msg );
+               return NULL;
+       }
+
+       if(! msg->thread  && ! msg->is_error ) {
+               osrfLogWarning( OSRF_LOG_MARK, "Received a non-error message with no thread trace... dropping");
+               message_free( msg );
+               return NULL;
+       }
+
+       osrf_app_session* session = osrf_app_session_find_session( msg->thread );
+
+       if( !session && my_service ) 
+               session = osrf_app_server_session_init( msg->thread, my_service, msg->sender);
+
+       if( !session ) return NULL;
+
+       if(!msg->is_error)
+               osrfLogDebug( OSRF_LOG_MARK, "Session [%s] found or built", session->session_id );
+
+       osrf_app_session_set_remote( session, msg->sender );
+       osrf_message* arr[OSRF_MAX_MSGS_PER_PACKET];
+       memset(arr, 0, OSRF_MAX_MSGS_PER_PACKET );
+       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 );
+
+       double starttime = get_timestamp_millis();
+
+       int i;
+       for( i = 0; i != num_msgs; i++ ) {
+
+               /* if we've received a jabber layer error message (probably talking to 
+                       someone who no longer exists) and we're not talking to the original
+                       remote id for this server, consider it a redirect and pass it up */
+               if(msg->is_error) {
+                       osrfLogWarning( OSRF_LOG_MARK,  " !!! Received Jabber layer error message" ); 
+
+                       if(strcmp(session->remote_id,session->orig_remote_id)) {
+                               osrfLogWarning( OSRF_LOG_MARK,  "Treating jabber error as redirect for tt [%d] "
+                                       "and session [%s]", arr[i]->thread_trace, session->session_id );
+
+                               arr[i]->m_type = STATUS;
+                               arr[i]->status_code = OSRF_STATUS_REDIRECTED;
+
+                       } else {
+                               osrfLogWarning( OSRF_LOG_MARK, " * Jabber Error is for top level remote id [%s], no one "
+                                               "to send my message too!!!", session->remote_id );
+                       }
+               }
+
+               osrf_stack_message_handler( session, arr[i] );
+       }
+
+       double duration = get_timestamp_millis() - starttime;
+       osrfLogInfo(OSRF_LOG_MARK, "Message processing duration %f", duration);
+
+       message_free( msg );
+       osrfLogDebug( OSRF_LOG_MARK, "after msg delete");
+
+       return session;
+}
+
+int osrf_stack_message_handler( osrf_app_session* session, osrf_message* msg ) {
+       if(session == NULL || msg == NULL)
+               return 0;
+
+       osrf_message* ret_msg = NULL;
+
+       if( session->type ==  OSRF_SESSION_CLIENT )
+                ret_msg = _do_client( session, msg );
+       else
+               ret_msg= _do_server( session, msg );
+
+       if(ret_msg) {
+               osrfLogDebug( OSRF_LOG_MARK, "passing message %d / session %s to app handler", 
+                               msg->thread_trace, session->session_id );
+               osrf_stack_application_handler( session, ret_msg );
+       } else
+               osrf_message_free(msg);
+
+       return 1;
+
+} 
+
+/** If we return a message, that message should be passed up the stack, 
+  * if we return NULL, we're finished for now...
+  */
+osrf_message* _do_client( osrf_app_session* session, osrf_message* msg ) {
+       if(session == NULL || msg == NULL)
+               return NULL;
+
+       osrf_message* new_msg;
+
+       if( msg->m_type == STATUS ) {
+               
+               switch( msg->status_code ) {
+
+                       case OSRF_STATUS_OK:
+                               osrfLogDebug( OSRF_LOG_MARK, "We connected successfully");
+                               session->state = OSRF_SESSION_CONNECTED;
+                               osrfLogDebug( OSRF_LOG_MARK,  "State: %x => %s => %d", session, session->session_id, session->state );
+                               return NULL;
+
+                       case OSRF_STATUS_COMPLETE:
+                               osrf_app_session_set_complete( session, msg->thread_trace );
+                               return NULL;
+
+                       case OSRF_STATUS_CONTINUE:
+                               osrf_app_session_request_reset_timeout( session, msg->thread_trace );
+                               return NULL;
+
+                       case OSRF_STATUS_REDIRECTED:
+                               osrf_app_session_reset_remote( session );
+                               session->state = OSRF_SESSION_DISCONNECTED;
+                               osrf_app_session_request_resend( session, msg->thread_trace );
+                               return NULL;
+
+                       case OSRF_STATUS_EXPFAILED: 
+                               osrf_app_session_reset_remote( session );
+                               session->state = OSRF_SESSION_DISCONNECTED;
+                               return NULL;
+
+                       case OSRF_STATUS_TIMEOUT:
+                               osrf_app_session_reset_remote( session );
+                               session->state = OSRF_SESSION_DISCONNECTED;
+                               osrf_app_session_request_resend( session, msg->thread_trace );
+                               return NULL;
+
+
+                       default:
+                               new_msg = osrf_message_init( RESULT, msg->thread_trace, msg->protocol );
+                               osrf_message_set_status_info( new_msg, 
+                                               msg->status_name, msg->status_text, msg->status_code );
+                               osrfLogWarning( OSRF_LOG_MARK, "The stack doesn't know what to do with " 
+                                               "the provided message code: %d, name %s. Passing UP.", 
+                                               msg->status_code, msg->status_name );
+                               new_msg->is_exception = 1;
+                               osrf_app_session_set_complete( session, msg->thread_trace );
+                               osrf_message_free(msg);
+                               return new_msg;
+               }
+
+               return NULL;
+
+       } else if( msg->m_type == RESULT ) 
+               return msg;
+
+       return NULL;
+
+}
+
+
+/** If we return a message, that message should be passed up the stack, 
+  * if we return NULL, we're finished for now...
+  */
+osrf_message* _do_server( osrf_app_session* session, osrf_message* msg ) {
+
+       if(session == NULL || msg == NULL) return NULL;
+
+       osrfLogDebug( OSRF_LOG_MARK, "Server received message of type %d", msg->m_type );
+
+       switch( msg->m_type ) {
+
+               case STATUS:
+                               return NULL;
+
+               case DISCONNECT:
+                               /* session will be freed by the forker */
+                               osrfLogDebug(OSRF_LOG_MARK, "Client sent explicit disconnect");
+                               session->state = OSRF_SESSION_DISCONNECTED;
+                               return NULL;
+
+               case CONNECT:
+                               osrfAppSessionStatus( session, OSRF_STATUS_OK, 
+                                               "osrfConnectStatus", msg->thread_trace, "Connection Successful" );
+                               session->state = OSRF_SESSION_CONNECTED;
+                               return NULL;
+
+               case REQUEST:
+
+                               osrfLogDebug( OSRF_LOG_MARK, "server passing message %d to application handler "
+                                               "for session %s", msg->thread_trace, session->session_id );
+                               return msg;
+
+               default:
+                       osrfLogWarning( OSRF_LOG_MARK, "Server cannot handle message of type %d", msg->m_type );
+                       session->state = OSRF_SESSION_DISCONNECTED;
+                       return NULL;
+
+       }
+}
+
+
+
+int osrf_stack_application_handler( osrf_app_session* session, osrf_message* msg ) {
+       if(session == NULL || msg == NULL) return 0;
+
+       if(msg->m_type == RESULT && session->type == OSRF_SESSION_CLIENT) {
+               osrf_app_session_push_queue( session, msg ); 
+               return 1;
+       }
+
+       if(msg->m_type != REQUEST) return 1;
+
+       char* method = msg->method_name;
+       char* app       = session->remote_service;
+       jsonObject* params = msg->_params;
+
+       osrfAppRunMethod( app, method,  session, msg->thread_trace, params );
+       osrfMessageFree(msg);
+               
+       return 1;
+}
+
+
diff --git a/src/libopensrf/osrf_system.c b/src/libopensrf/osrf_system.c
new file mode 100644 (file)
index 0000000..c378b7b
--- /dev/null
@@ -0,0 +1,326 @@
+#include <opensrf/osrf_system.h>
+#include <opensrf/osrf_application.h>
+#include <opensrf/osrf_prefork.h>
+#include <signal.h>
+
+static int _osrfSystemInitCache( void );
+
+static transport_client* osrfGlobalTransportClient = NULL;
+
+transport_client* osrfSystemGetTransportClient( void ) {
+       return osrfGlobalTransportClient;
+}
+
+void osrfSystemIgnoreTransportClient() {
+       osrfGlobalTransportClient = NULL;
+}
+
+transport_client* osrf_system_get_transport_client( void ) {
+       return osrfGlobalTransportClient;
+}
+
+int osrf_system_bootstrap_client( char* config_file, char* contextnode ) {
+       return osrf_system_bootstrap_client_resc(config_file, contextnode, NULL);
+}
+
+int osrfSystemBootstrapClientResc( char* config_file, char* contextnode, char* resource ) {
+       return osrf_system_bootstrap_client_resc( config_file, contextnode, resource );
+}
+
+
+static int _osrfSystemInitCache( void ) {
+
+       jsonObject* cacheServers = osrf_settings_host_value_object("/cache/global/servers/server");
+       char* maxCache = osrf_settings_host_value("/cache/global/max_cache_time");
+
+       if( cacheServers && maxCache) {
+
+               if( cacheServers->type == JSON_ARRAY ) {
+                       int i;
+                       char* servers[cacheServers->size];
+                       for( i = 0; i != cacheServers->size; i++ ) {
+                               servers[i] = jsonObjectGetString( jsonObjectGetIndex(cacheServers, i) );
+                               osrfLogInfo( OSRF_LOG_MARK, "Adding cache server %s", servers[i]);
+                       }
+                       osrfCacheInit( servers, cacheServers->size, atoi(maxCache) );
+
+               } else {
+                       char* servers[] = { jsonObjectGetString(cacheServers) };                
+                       osrfLogInfo( OSRF_LOG_MARK, "Adding cache server %s", servers[0]);
+                       osrfCacheInit( servers, 1, atoi(maxCache) );
+               }
+
+       } else {
+               osrfLogError( OSRF_LOG_MARK,  "Missing config value for /cache/global/servers/server _or_ "
+                       "/cache/global/max_cache_time");
+       }
+
+       return 0;
+}
+
+
+int osrfSystemBootstrap( char* hostname, char* configfile, char* contextNode ) {
+       if( !(hostname && configfile && contextNode) ) return -1;
+
+       /* first we grab the settings */
+       if(!osrfSystemBootstrapClientResc(configfile, contextNode, "settings_grabber" )) {
+               osrfLogError( OSRF_LOG_MARK,
+                       "Unable to bootstrap for host %s from configuration file %s",
+                       hostname, configfile );
+               return -1;
+       }
+
+       int retcode = osrf_settings_retrieve(hostname);
+       osrf_system_disconnect_client();
+
+       if( retcode ) {
+               osrfLogError( OSRF_LOG_MARK,
+                       "Unable to retrieve settings for host %s from configuration file %s",
+                       hostname, configfile );
+               return -1;
+       }
+       
+       jsonObject* apps = osrf_settings_host_value_object("/activeapps/appname");
+       osrfStringArray* arr = osrfNewStringArray(8);
+       
+       _osrfSystemInitCache();
+
+       if(apps) {
+               int i = 0;
+
+               if(apps->type == JSON_STRING) {
+                       osrfStringArrayAdd(arr, jsonObjectGetString(apps));
+
+               } else {
+                       jsonObject* app;
+                       while( (app = jsonObjectGetIndex(apps, i++)) ) 
+                               osrfStringArrayAdd(arr, jsonObjectGetString(app));
+               }
+
+               char* appname = NULL;
+               i = 0;
+               while( (appname = osrfStringArrayGetString(arr, i++)) ) {
+
+                       char* lang = osrf_settings_host_value("/apps/%s/language", appname);
+
+                       if(lang && !strcasecmp(lang,"c"))  {
+
+                               char* libfile = osrf_settings_host_value("/apps/%s/implementation", appname);
+               
+                               if(! (appname && libfile) ) {
+                                       osrfLogWarning( OSRF_LOG_MARK, "Missing appname / libfile in settings config");
+                                       continue;
+                               }
+
+                               osrfLogInfo( OSRF_LOG_MARK, "Launching application %s with implementation %s", appname, libfile);
+               
+                               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);
+       
+                               } else {
+               
+                                       fprintf(stderr, " * Running application %s\n", appname);
+                                       if( osrfAppRegisterApplication( appname, libfile ) == 0 ) 
+                                               osrf_prefork_run(appname);
+       
+                                       osrfLogDebug( OSRF_LOG_MARK, "Server exiting for app %s and library %s\n", appname, libfile );
+                                       exit(0);
+                               }
+                       } // language == c
+               } 
+       }
+
+       /** daemonize me **/
+
+       /* background and let our children do their thing */
+       daemonize();
+    while(1) {
+        errno = 0;
+        pid_t pid = wait(NULL);
+        if(-1 == pid) {
+            if(errno == ECHILD)
+                osrfLogError(OSRF_LOG_MARK, "We have no more live services... exiting");
+            else
+                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);
+        }
+    }
+
+
+       return 0;
+}
+
+int osrf_system_bootstrap_client_resc( char* config_file, char* contextnode, char* resource ) {
+
+       int failure = 0;
+
+       if(osrfSystemGetTransportClient()) {
+               osrfLogInfo(OSRF_LOG_MARK, "Client is already bootstrapped");
+               return 1; /* we already have a client connection */
+       }
+
+       if( !( config_file && contextnode ) && ! osrfConfigHasDefaultConfig() ) {
+               osrfLogError( OSRF_LOG_MARK, "No Config File Specified\n" );
+               return -1;
+       }
+
+       if( config_file ) {
+               osrfConfig* cfg = osrfConfigInit( config_file, contextnode );
+               if(cfg)
+                       osrfConfigSetDefaultConfig(cfg);
+               else
+                       return 0;   /* Can't load configuration?  Bail out */
+       }
+
+
+       char* log_file          = osrfConfigGetValue( NULL, "/logfile");
+       char* log_level         = osrfConfigGetValue( NULL, "/loglevel" );
+       osrfStringArray* arr    = osrfNewStringArray(8);
+       osrfConfigGetValueList(NULL, arr, "/domains/domain");
+
+       char* username          = osrfConfigGetValue( NULL, "/username" );
+       char* password          = osrfConfigGetValue( NULL, "/passwd" );
+       char* port              = osrfConfigGetValue( NULL, "/port" );
+       char* unixpath          = osrfConfigGetValue( NULL, "/unixpath" );
+       char* facility          = osrfConfigGetValue( NULL, "/syslog" );
+       char* actlog            = osrfConfigGetValue( NULL, "/actlog" );
+
+       if(!log_file) {
+               fprintf(stderr, "No log file specified in configuration file %s\n",
+                          config_file);
+               free(log_level);
+               free(username);
+               free(password);
+               free(port);
+               free(unixpath);
+               free(facility);
+               free(actlog);
+               return -1;
+       }
+
+       /* if we're a source-client, tell the logger */
+       char* isclient = osrfConfigGetValue(NULL, "/client");
+       if( isclient && !strcasecmp(isclient,"true") )
+               osrfLogSetIsClient(1);
+       free(isclient);
+
+       int llevel = 0;
+       int iport = 0;
+       if(port) iport = atoi(port);
+       if(log_level) llevel = atoi(log_level);
+
+       if(!strcmp(log_file, "syslog")) {
+               osrfLogInit( OSRF_LOG_TYPE_SYSLOG, contextnode, llevel );
+               osrfLogSetSyslogFacility(osrfLogFacilityToInt(facility));
+               if(actlog) osrfLogSetSyslogActFacility(osrfLogFacilityToInt(actlog));
+
+       } else {
+               osrfLogInit( OSRF_LOG_TYPE_FILE, contextnode, llevel );
+               osrfLogSetFile( log_file );
+       }
+
+
+       /* Get a domain, if one is specified */
+       const char* domain = osrfStringArrayGetString( arr, 0 ); /* just the first for now */
+       if(!domain) {
+               fprintf(stderr, "No domain specified in configuration file %s\n", config_file);
+               osrfLogError( OSRF_LOG_MARK, "No domain specified in configuration file %s\n", config_file);
+               failure = 1;
+       }
+
+       if(!username) {
+               fprintf(stderr, "No username specified in configuration file %s\n", config_file);
+               osrfLogError( OSRF_LOG_MARK, "No username specified in configuration file %s\n", config_file);
+               failure = 1;
+       }
+
+       if(!password) {
+               fprintf(stderr, "No password specified in configuration file %s\n", config_file);
+               osrfLogError( OSRF_LOG_MARK, "No password specified in configuration file %s\n", config_file);
+               failure = 1;
+       }
+
+       if((iport <= 0) && !unixpath) {
+               fprintf(stderr, "No unixpath or valid port in configuration file %s\n", config_file);
+               osrfLogError( OSRF_LOG_MARK, "No unixpath or valid port in configuration file %s\n",
+                       config_file);
+               failure = 1;
+       }
+
+       if (failure) {
+               osrfStringArrayFree(arr);
+               free(log_level);
+               free(username);
+               free(password);
+               free(port);
+               free(unixpath);
+               free(facility);
+               free(actlog);
+               return 0;
+       }
+
+       osrfLogInfo( OSRF_LOG_MARK, "Bootstrapping system with domain %s, port %d, and unixpath %s",
+               domain, iport, unixpath ? unixpath : "(none)" );
+       transport_client* client = client_init( domain, iport, unixpath, 0 );
+
+       const char* host;
+       host = getenv("HOSTNAME");
+
+       char tbuf[32];
+       tbuf[0] = '\0';
+       snprintf(tbuf, 32, "%f", get_timestamp_millis());
+
+       if(!host) host = "";
+       if(!resource) resource = "";
+
+       int len = strlen(resource) + 256;
+       char buf[len];
+       buf[0] = '\0';
+       snprintf(buf, len - 1, "%s_%s_%s_%ld", resource, host, tbuf, (long) getpid() );
+
+       if(client_connect( client, username, password, buf, 10, AUTH_DIGEST )) {
+               /* child nodes will leak the parents client... but we can't free
+                       it without disconnecting the parents client :( */
+               osrfGlobalTransportClient = client;
+       }
+
+       osrfStringArrayFree(arr);
+       free(actlog);
+       free(facility);
+       free(log_level);
+       free(log_file);
+       free(username);
+       free(password);
+       free(port);     
+       free(unixpath);
+
+       if(osrfGlobalTransportClient)
+               return 1;
+
+       return 0;
+}
+
+int osrf_system_disconnect_client( void ) {
+       client_disconnect( osrfGlobalTransportClient );
+       client_free( osrfGlobalTransportClient );
+       osrfGlobalTransportClient = NULL;
+       return 0;
+}
+
+int osrf_system_shutdown( void ) {
+       osrfConfigCleanup();
+       osrf_system_disconnect_client();
+       osrf_settings_free_host_config(NULL);
+       osrfAppSessionCleanup();
+       osrfLogCleanup();
+       return 1;
+}
+
+
+
+
diff --git a/src/libopensrf/osrf_transgroup.c b/src/libopensrf/osrf_transgroup.c
new file mode 100644 (file)
index 0000000..72b203f
--- /dev/null
@@ -0,0 +1,244 @@
+#include <opensrf/osrf_transgroup.h>
+#include <sys/select.h>
+
+
+osrfTransportGroupNode* osrfNewTransportGroupNode( 
+               char* domain, int port, char* username, char* password, char* resource ) {
+
+       if(!(domain && port && username && password && resource)) return NULL;
+
+       osrfTransportGroupNode* node = safe_malloc(sizeof(osrfTransportGroupNode));
+       node->domain    = strdup(domain);
+       node->port              = port;
+       node->username = strdup(username);
+       node->password = strdup(password);
+       node->domain    = strdup(domain);
+       node->resource  = strdup(resource);
+       node->active    = 0;
+       node->lastsent  = 0;
+       node->connection = client_init( domain, port, NULL, 0 );
+
+       return node;
+}
+
+
+osrfTransportGroup* osrfNewTransportGroup( osrfTransportGroupNode* nodes[], int count ) {
+       if(!nodes || count < 1) return NULL;
+
+       osrfTransportGroup* grp = safe_malloc(sizeof(osrfTransportGroup));
+       grp->nodes                                      = osrfNewHash();
+       grp->itr                                                = osrfNewHashIterator(grp->nodes);
+
+       int i;
+       for( i = 0; i != count; i++ ) {
+               if(!(nodes[i] && nodes[i]->domain) ) return NULL;
+               osrfHashSet( grp->nodes, nodes[i], nodes[i]->domain );
+               osrfLogDebug( OSRF_LOG_MARK, "Adding domain %s to TransportGroup", nodes[i]->domain);
+       }
+
+       return grp;
+}
+
+
+/* connect all of the nodes to their servers */
+int osrfTransportGroupConnectAll( osrfTransportGroup* grp ) {
+       if(!grp) return -1;
+       int active = 0;
+
+       osrfTransportGroupNode* node;
+       osrfHashIteratorReset(grp->itr);
+
+       while( (node = osrfHashIteratorNext(grp->itr)) ) {
+               osrfLogInfo( OSRF_LOG_MARK, "TransportGroup attempting to connect to domain %s", 
+                                                        node->connection->session->server);
+
+               if(client_connect( node->connection, node->username, 
+                                       node->password, node->resource, 10, AUTH_DIGEST )) {
+                       node->active = 1;
+                       active++;
+                       osrfLogInfo( OSRF_LOG_MARK, "TransportGroup successfully connected to domain %s", 
+                                                        node->connection->session->server);
+               } else {
+                       osrfLogWarning( OSRF_LOG_MARK, "TransportGroup unable to connect to domain %s", 
+                                                        node->connection->session->server);
+               }
+       }
+
+       osrfHashIteratorReset(grp->itr);
+       return active;
+}
+
+void osrfTransportGroupDisconnectAll( osrfTransportGroup* grp ) {
+       if(!grp) return;
+
+       osrfTransportGroupNode* node;
+       osrfHashIteratorReset(grp->itr);
+
+       while( (node = osrfHashIteratorNext(grp->itr)) ) {
+               osrfLogInfo( OSRF_LOG_MARK, "TransportGroup disconnecting from domain %s", 
+                                                        node->connection->session->server);
+               client_disconnect(node->connection);
+               node->active = 0;
+       }
+
+       osrfHashIteratorReset(grp->itr);
+}
+
+
+int osrfTransportGroupSendMatch( osrfTransportGroup* grp, transport_message* msg ) {
+       if(!(grp && msg)) return -1;
+
+       char domain[256];
+       bzero(domain, 256);
+       jid_get_domain( msg->recipient, domain, 255 );
+
+       osrfTransportGroupNode* node = osrfHashGet(grp->nodes, domain);
+       if(node) {
+               if( (client_send_message( node->connection, msg )) == 0 )
+                       return 0;
+       }
+
+       osrfLogWarning( OSRF_LOG_MARK, "Error sending message to domain %s", domain );
+       return -1;
+}
+
+int osrfTransportGroupSend( osrfTransportGroup* grp, transport_message* msg ) {
+
+       if(!(grp && msg)) return -1;
+       int bufsize = 256;
+
+       char domain[bufsize];
+       bzero(domain, bufsize);
+       jid_get_domain( msg->recipient, domain, bufsize - 1 );
+
+       char msgrecip[bufsize];
+       bzero(msgrecip, bufsize);
+       jid_get_username(msg->recipient, msgrecip, bufsize - 1);
+
+       char msgres[bufsize];
+       bzero(msgres, bufsize);
+       jid_get_resource(msg->recipient, msgres, bufsize - 1);
+
+       char* firstdomain = NULL;
+       char newrcp[1024];
+
+       int updateRecip = 1;
+       /* if we don't host this domain, don't update the recipient but send it as is */
+       if(!osrfHashGet(grp->nodes, domain)) updateRecip = 0;
+
+       osrfTransportGroupNode* node;
+
+       do {
+
+               node = osrfHashIteratorNext(grp->itr);
+               if(!node) osrfHashIteratorReset(grp->itr);
+
+               node = osrfHashIteratorNext(grp->itr);
+               if(!node) return -1;
+
+               if(firstdomain == NULL) {
+                       firstdomain = node->domain;
+
+               } else {
+                       if(!strcmp(firstdomain, node->domain)) { /* we've made a full loop */
+                               osrfLogWarning( OSRF_LOG_MARK, "We've tried to send to all domains.. giving up");
+                               return -1;
+                       }
+               }
+
+               /* update the recipient domain if necessary */
+
+               if(updateRecip) {
+                       bzero(newrcp, 1024);
+                       sprintf(newrcp, "%s@%s/%s", msgrecip, node->domain, msgres);
+                       free(msg->recipient);
+                       msg->recipient = strdup(newrcp);
+               }
+
+               if( (client_send_message( node->connection, msg )) == 0 ) 
+                       return 0;
+
+       } while(1);
+
+       return -1;
+}
+
+static int __osrfTGWait( fd_set* fdset, int maxfd, int timeout ) {
+       if(!(fdset && maxfd)) return 0;
+
+       struct timeval tv;
+       tv.tv_sec = timeout;
+       tv.tv_usec = 0;
+       int retval = 0;
+
+       if( timeout < 0 ) {
+               if( (retval = select( maxfd + 1, fdset, NULL, NULL, NULL)) == -1 ) 
+                       return 0;
+
+       } else {
+               if( (retval = select( maxfd + 1, fdset, NULL, NULL, &tv)) == -1 ) 
+                       return 0;
+       }
+
+       return retval;
+}
+
+
+transport_message* osrfTransportGroupRecvAll( osrfTransportGroup* grp, int timeout ) {
+       if(!grp) return NULL;
+
+       int maxfd = 0;
+       fd_set fdset;
+       FD_ZERO( &fdset );
+
+       osrfTransportGroupNode* node;
+       osrfHashIterator* itr = osrfNewHashIterator(grp->nodes);
+
+       while( (node = osrfHashIteratorNext(itr)) ) {
+               if(node->active) {
+                       int fd = node->connection->session->sock_id;
+                       if( fd < maxfd ) maxfd = fd;
+                       FD_SET( fd, &fdset );
+               }
+       }
+       osrfHashIteratorReset(itr);
+
+       if( __osrfTGWait( &fdset, maxfd, timeout ) ) {
+               while( (node = osrfHashIteratorNext(itr)) ) {
+                       if(node->active) {
+                               int fd = node->connection->session->sock_id;
+                               if( FD_ISSET( fd, &fdset ) ) {
+                                       return client_recv( node->connection, 0 );
+                               }
+                       }
+               }
+       }
+
+       osrfHashIteratorFree(itr);
+       return NULL;
+}
+
+transport_message* osrfTransportGroupRecv( osrfTransportGroup* grp, char* domain, int timeout ) {
+       if(!(grp && domain)) return NULL;
+
+       osrfTransportGroupNode* node = osrfHashGet(grp->nodes, domain);
+       if(!node && node->connection && node->connection->session) return NULL;
+       int fd = node->connection->session->sock_id;
+
+       fd_set fdset;
+       FD_ZERO( &fdset );
+       FD_SET( fd, &fdset );
+
+       int active = __osrfTGWait( &fdset, fd, timeout );
+       if(active) return client_recv( node->connection, 0 );
+       
+       return NULL;
+}
+
+void osrfTransportGroupSetInactive( osrfTransportGroup* grp, char* domain ) {
+       if(!(grp && domain)) return;
+       osrfTransportGroupNode* node = osrfHashGet(grp->nodes, domain );
+       if(node) node->active = 0;
+}
+
+
diff --git a/src/libopensrf/sha.c b/src/libopensrf/sha.c
new file mode 100644 (file)
index 0000000..14791bd
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ *  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.
+ *
+ *  You should have received