Index: /pjproject/branches/archives/pjproject-0.2/INSTALL.txt
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/INSTALL.txt	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/INSTALL.txt	(revision 1753)
@@ -0,0 +1,120 @@
+Last Update: 2005/05/28 for pjproject-0.2.8
+
+
+
+BUILD INSTRUCTIONS
+================================================================================
+
+The main target of the build process is to build executable pjsip/bin/pjsua.exe.
+
+
+1. Build instructions for Microsoft Visual Studio .NET 2003
+   ----------------------------------------------
+   - Open solution file: build/pjproject.sln
+     There will be some dialogs appear asking about Source Safe, just ignore it.
+   - Build the solution. That should build every single projects in pjproject.
+
+
+2. Build instructions for Microsoft Visual Studio 6
+   ------------------------------------------------
+   Note: MSVC6 workspace is normally updated less often than MSVC7.1 projects. 
+         Especially during intermediate release. Normally the status will be
+         noted in the web page.
+
+   - Open workspace file: build/pjproject.dsw
+   - Perform batch build. That should build every single projects in pjproject.
+
+
+3. Build instructions for Linux i386, Mingw
+   ----------------------------------------
+   Note: mingw/Linux Makefiles are normally updated less often than MSVC7.1 projects.
+
+   Step-by-step instruction to build the whole thing:
+
+     $ tar xzvf pjproject-0.2.6.tar.gz 
+     $ cd pjproject-0.2.6
+     
+     $ export TARGET=mingw       <== for Mingw, or
+     $ export TARGET=linux-i386  <== for Linux
+   
+     $ make dep
+     $ make all
+
+   That should build all libraries and test applications (including pjsua).
+
+   There are some other make targets:
+
+     $ make clean      ==> clean files (except libraries, binaries, & dependency files).
+     $ make realclean  ==> clean everything
+     $ make distclean  ==> ditto.
+
+   Note: 
+     - pjsua for Mingw and Linux build doesn't have audio device at present.
+
+
+4. Additional flags (for debugging etc.)
+   -------------------------------------
+   The build system for mingw/Linux accepts can additional flags, for example
+   for debugging.
+
+   Example:
+
+    $ (on pjproject root directory)
+    $ make CFLAGS=-ggdb "LDFLAGS=-L/foo/lib -lfoo"
+
+
+
+REDUCING SIZE
+================================================================================
+
+There are some C macros that can be used to reduce the size. For Mingw and Linux build,
+these macros are activated if you specify MINSIZE=1 during make, e.g.:
+
+     $ .. (we're in pjproject directory) ..
+     $ make MINSIZE=1 all
+
+Then after building the projects, you can check the size of the libraries:
+
+     $ make size
+
+If you use Microsoft Visual Studio, then you'll have to set the C macros manually:
+
+     pjlib/src/pj/config.h:
+           - #define PJ_HAS_TCP                0   // ==> to exclude TCP
+           - #define PJ_HAS_THREADS            0   // ==> to exclude threads
+           - #define PJ_FUNCTIONS_ARE_INLINED  0   // ==> do not inline.
+     pjlib/src/pj/log.h:
+           - #define PJ_LOG_MAX_LEVEL          0   // ==> disable all tracing
+
+
+GENERATING DOCUMENTATION
+================================================================================
+
+You need to have doxygen to generate documentation.
+
+To generate doxygen documentation with GNU make, 
+
+    $ (on pjproject root directory)
+    $ make doc
+
+If GNU make is not available, generate documentation on each project:
+
+    $ cd pjsip
+    $ doxygen docs/doxygen.cfg
+
+    $ cd pjlib
+    $ doxygen docs/doxygen.cfg
+
+    etc..
+
+The HTML files will be put under docs/html directory.
+
+
+That's about it I guess, sorry couldn't write more. Feel free to drop me a note if
+you encounter any problems.
+
+
+Thanks,
+Benny Prijono
+<bennylp@bulukucing.org>
+
Index: /pjproject/branches/archives/pjproject-0.2/Makefile
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/Makefile	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/Makefile	(revision 1753)
@@ -0,0 +1,43 @@
+DIRS = pjlib pjsdp pjmedia pjsip
+
+MAKE_FLAGS := TARGET=$(TARGET)
+
+ifdef MINSIZE
+MAKE_FLAGS := $(MAKE_FLAGS) MINSIZE=1
+endif
+
+all clean dep depend distclean doc print realclean:
+	for dir in $(DIRS); do \
+	   if [ -d $$dir ]; then \
+		if make $(MAKE_FLAGS) -C $$dir/build $@; then \
+		    true; \
+		else \
+		    exit 1; \
+		fi; \
+	   fi \
+	done
+
+LIBS = pjlib/lib/libpj.a pjsdp/lib/libpjsdp.a pjmedia/lib/libpjmedia.a \
+       pjsip/lib/libpjsip_core.a pjsip/lib/libpjsip_ua.a
+BINS = pjsip/bin/pjsua$(EXE) 
+
+include pjlib/build/make-$(TARGET).inc
+
+size:
+	@echo 'TARGET=$(TARGET)'
+	@echo -n 'Date: '
+	@date
+	@echo
+	@for lib in $(LIBS); do \
+		echo "$$lib:"; \
+		ar tv $$lib | awk '{print $$3 "\t" $$8}' | sort -n; \
+		echo -n 'Total: '; \
+		ar tv $$lib | awk '{print " + " $$3}' | xargs expr 0; \
+		echo; \
+	done
+	@echo
+	@for bin in $(BINS); do \
+		echo "size $$bin:"; \
+		size $$bin; \
+	done
+
Index: /pjproject/branches/archives/pjproject-0.2/RELNOTES.txt
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/RELNOTES.txt	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/RELNOTES.txt	(revision 1753)
@@ -0,0 +1,147 @@
+Version 0.2.9 - 2005/06/19
+Core:
+  - Moved authentication stuff to core. 
+SIMPLE:
+  - Initial implementation of Event framework (SUBSCRIBE/NOTIFY)
+  - Initial implementation of Presence
+  - Tidying up here and there.
+ 
+Version 0.2.8.5 - 2005/06/05
+====================================
+Core:
+  - Tidying up sip_msg.h (no need to export clone/shallow_clone/print API
+    for headers).
+  - Endpoint now can respond with 501/Not Supported if incoming request is
+    not handled by any modules.
+  - Endpoint also supports Allow header now.
+  - Changed transport names to capital letters (thanks ...)
+  - Fixed bug with locking in select() ioqueue.
+  - Add status phrase for >= 700 status codes.
+  
+pjsua:
+  - Verify URL in arguments to prevent crash.
+  - Can read commands from config file.
+  - Now has buddy list and can send IM!
+
+SIMPLE:
+  - Instant Messaging support!
+ 
+MEDIA:
+  - CLOSING SEQUENCE IS NOT PROPER!!! SOMETIMES THREAD IS DEADLOCKED,
+    OR DSOUND IS NOT CLOSED PROPERLY!!!
+   
+Version 0.2.8 - 2005/05/28
+====================================
+- Simple STUN client support
+  SIP UDP port and media RTP/RTCP ports are now STUN aware. 
+- Major changed in I/O queue, now callback is used.
+  Callback is better because multiple libraries can register to single I/O queue.
+  It was not possible with previous implementation, because the function which does
+  polling needs to understand what to do when a key is signalled. The changes was
+  initially needed to support STUN, but then the STUN client implementation uses the
+  simpler select() (in stun_client.c).
+- Merge SDP library into PJMEDIA (no more PJSDP).
+  PJSDP only has couple of files (sdp.[hc]), not worth maintaining a library.
+- Fixed bug in select() I/O queue (not thread safe).
+
+
+Version 0.2.7 - 2005/05/14
+====================================
+PJLIB:
+- Major reorganization in pool, introducing pool factory and policy.
+  All libraries now can be completely agnostic about memory management 
+  choosen by application.
+- Fixed bug in GUID generation on mingw
+- Fixed bug in scanner if ASCII > 127 is fed into the input
+- More doxygen documentation
+
+PJMEDIA:
+- Renamed some functions/structs/etc.
+
+UA library:
+- Registration client completed (including authentication).
+- Fixed a crash condition when tsx has not received any response.
+
+PJSUA:
+- Use getopt.c
+
+
+Version 0.2.6 - 2005/04/17
+====================================
+All:
+- tidying up header files.
+
+Core library:
+- Removed PJSIP_HAS_DUMP macro (now automatically calculated based on log level)
+- Added pjsip_tx_data_invalidate_msg()
+
+UA library:
+- big modification in dialog API to better support injecting custom header
+  in outgoing message and to make it more flexible for future features (such
+  as caching the outgoing message):
+    - sending messages is now done in two steps: (1)create the msg transmit
+      buffer (pjsip_dlg_tx_data), (2)send the msg transmit buffer.
+    - dialog state won't change in step (1); it will change only
+      when the message is actually sent in step (2).
+      What won't change:
+	- the dialog state
+	- outgoing CSeq
+    - outgoing message transmit buffer (pjsip_dlg_tx_data) will be deleted 
+      when sent in step (2). Application MAY save request messages for 
+      future transmission, even after the request has been sent. To do so,
+      it must increment the reference counter and remember that each time
+      the request is sent, the reference counter will be decremented. Also
+      application CAN NOT re-send the message while the transaction that
+      sends the message has not terminated.
+- changed API names: pjsip_dlg_answer_invitation() --> pjsip_dlg_answer(), etc.
+- initial sip_reg.h for SIP registration.
+
+Auth library:
+- the digest authentication should work, however it has not been tested
+  with any SIP servers because we don't have REGISTER support yet.
+*Note*:
+  authentication in pjsua still uses hardcoded user/pass: hello/world.
+
+
+Version 0.2.5.2 - 2005/03/25
+====================================
+UA library:
+- Major modification in dialog callbacks, now high level callbacks such as
+  on_calling(), on_incoming(), on_provisional(), on_established(), and
+  on_disconnected() are provided instead of just one callback (on_event()).
+- Added pjsip_dlg_disconnect() which should handle all cases of disconnection
+  such as sending CANCEL, sending BYE, or sending final response to INVITE.
+- Added and updated doxygen comments.
+- Changed: pjsip_dialog_xxx --> pjsip_dlg_xxx
+
+New:
+- PJSIP Auth library, which supports digest scheme.
+- Only client functionality is present at the moment.
+
+PJSUA:
+- Remove callgen feature, as it makes the application complicated.
+  Will move it to different application, to make way for more sophisticated
+  call generator.
+- Support the new callback framework.
+- Support the new digest authentication (UAC only).
+
+SIP core:
+- Added PJSIP_EVENT_BEFORE_TX, triggered by transaction before sending
+  outgoing message (including retransmission). Application can use this event
+  (via dialog callback) to modify the message before transmission (such as
+  adding authorization headers).
+- Added general purpose function to print text body.
+- Move constant strings in parser to public/extern, just in case other
+  part of the library need to use them.
+
+PJMEDIA:
+- Protect against NULL in destroy session.
+
+Misc:
+- Rename build output directory x_Win32_x --> x_vc7_x or x_vc6_x, also
+  library naming includes _vc7_ or _vc6_ now.
+- Renamed pjsip_test_core --> pjsip_core_test.
+- Renamed pjaudio_tool --> pjmedia_audio_tool.
+- Renamed sdp_test --> pjsdp_test
+- PJLIB test: added second pool test after new/malloc test.
+- Renamed README.txt --> INSTALL.txt
Index: /pjproject/branches/archives/pjproject-0.2/build/pjproject.dsw
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/build/pjproject.dsw	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/build/pjproject.dsw	(revision 1753)
@@ -0,0 +1,233 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "pjlib"=..\pjlib\build\pjlib.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjlib/build", UIAAAAAA
+    ..\pjlib\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjlib_test"=..\pjlib\build\pjlib_test.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjlib/build", HICAAAAA
+    ..\pjlib\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pjlibpp"=..\pjlib\build\pjlibpp.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjlib/build", HICAAAAA
+    ..\pjlib\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjmedia"=..\pjmedia\build\pjmedia.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjmedia/build", MJCAAAAA
+    ..\pjmedia\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjmedia_audio_tool"=..\pjmedia\build\pjmedia_audio_tool.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjmedia/build", MJCAAAAA
+    ..\pjmedia\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjmedia
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pjmedia_test"=..\pjmedia\build\pjmedia_test.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjmedia/build", MJCAAAAA
+    ..\pjmedia\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjmedia
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pjsip_core"=..\pjsip\build\pjsip_core.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", LJCAAAAA
+    ..\pjsip\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjsip_core_test"=..\pjsip\build\pjsip_core_test.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", LJCAAAAA
+    ..\pjsip\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjsip_core
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pjsip_simple"=..\pjsip\build\pjsip_simple.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", LJCAAAAA
+    ..\pjsip\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjsip_ua"=..\pjsip\build\pjsip_ua.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", LJCAAAAA
+    ..\pjsip\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjsua"=..\pjsip\build\pjsua.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", LJCAAAAA
+    ..\pjsip\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjmedia
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjsip_ua
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjsip_core
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjsip_simple
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/build", KYBAAAAA
+    .
+    end source code control
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
Index: /pjproject/branches/archives/pjproject-0.2/build/pjproject.sln
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/build/pjproject.sln	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/build/pjproject.sln	(revision 1753)
@@ -0,0 +1,187 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib", "..\pjlib\build\pjlib.vcproj", "{72790D99-35BB-45AC-9A23-3BB60C901E63}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib_test", "..\pjlib\build\pjlib_test.vcproj", "{A684A4C0-00D6-4497-B144-FC6AED4FAD8A}"
+	ProjectSection(ProjectDependencies) = postProject
+		{72790D99-35BB-45AC-9A23-3BB60C901E63} = {72790D99-35BB-45AC-9A23-3BB60C901E63}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlibpp", "..\pjlib\build\pjlibpp.vcproj", "{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia_lib", "..\pjmedia\build\pjmedia.vcproj", "{EB8559B2-D738-4987-8591-4D217F8B0099}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia_test", "..\pjmedia\build\pjmedia_test.vcproj", "{692B42C4-6888-4BF8-9613-E48A2F138005}"
+	ProjectSection(ProjectDependencies) = postProject
+		{72790D99-35BB-45AC-9A23-3BB60C901E63} = {72790D99-35BB-45AC-9A23-3BB60C901E63}
+		{EB8559B2-D738-4987-8591-4D217F8B0099} = {EB8559B2-D738-4987-8591-4D217F8B0099}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_core_lib", "..\pjsip\build\pjsip_core.vcproj", "{58A72B82-7369-4B89-B511-7191A6B0D8C3}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_ua_lib", "..\pjsip\build\pjsip_ua.vcproj", "{DEE358A5-ADD3-4403-AD82-4967E63F17D1}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsua", "..\pjsip\build\pjsua.vcproj", "{B8500B7B-C6A8-46B9-84E6-90E200C1B35A}"
+	ProjectSection(ProjectDependencies) = postProject
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE} = {B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}
+		{58A72B82-7369-4B89-B511-7191A6B0D8C3} = {58A72B82-7369-4B89-B511-7191A6B0D8C3}
+		{72790D99-35BB-45AC-9A23-3BB60C901E63} = {72790D99-35BB-45AC-9A23-3BB60C901E63}
+		{DEE358A5-ADD3-4403-AD82-4967E63F17D1} = {DEE358A5-ADD3-4403-AD82-4967E63F17D1}
+		{EB8559B2-D738-4987-8591-4D217F8B0099} = {EB8559B2-D738-4987-8591-4D217F8B0099}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_core_test", "..\pjsip\build\pjsip_core_test.vcproj", "{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}"
+	ProjectSection(ProjectDependencies) = postProject
+		{58A72B82-7369-4B89-B511-7191A6B0D8C3} = {58A72B82-7369-4B89-B511-7191A6B0D8C3}
+		{72790D99-35BB-45AC-9A23-3BB60C901E63} = {72790D99-35BB-45AC-9A23-3BB60C901E63}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia_audio_tool", "..\pjmedia\build\pjmedia_audio_tool.vcproj", "{5FD061CF-A0E8-43DF-990C-B046AA1BF5EE}"
+	ProjectSection(ProjectDependencies) = postProject
+		{72790D99-35BB-45AC-9A23-3BB60C901E63} = {72790D99-35BB-45AC-9A23-3BB60C901E63}
+		{EB8559B2-D738-4987-8591-4D217F8B0099} = {EB8559B2-D738-4987-8591-4D217F8B0099}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_callgen", "..\pjsip\build\pjsip_callgen.vcproj", "{DCBEF2A3-D444-46FC-82E7-D939113EECA7}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_simple_lib", "..\pjsip\build\pjsip_simple.vcproj", "{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SourceCodeControl) = preSolution
+		SccNumberOfProjects = 13
+		SccLocalPath0 = .
+		CanCheckoutShared = false
+		SolutionUniqueID = {23953A1F-662B-4D0D-BC25-C13C88DE8618}
+		SccProjectUniqueName1 = ..\\pjlib\\build\\pjlib.vcproj
+		SccProjectName1 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath1 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection1 = pjlib\\build\\
+		SccProjectUniqueName2 = ..\\pjlib\\build\\pjlib_test.vcproj
+		SccProjectName2 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath2 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection2 = pjlib\\build\\
+		SccProjectUniqueName3 = ..\\pjlib\\build\\pjlibpp.vcproj
+		SccProjectName3 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath3 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection3 = pjlib\\build\\
+		SccProjectUniqueName4 = ..\\pjmedia\\build\\pjmedia.vcproj
+		SccProjectName4 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath4 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection4 = pjmedia\\build\\
+		SccProjectUniqueName5 = ..\\pjmedia\\build\\pjmedia_test.vcproj
+		SccProjectName5 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath5 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection5 = pjmedia\\build\\
+		SccProjectUniqueName6 = ..\\pjsip\\build\\pjsip_core.vcproj
+		SccProjectName6 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath6 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection6 = pjsip\\build\\
+		SccProjectUniqueName7 = ..\\pjsip\\build\\pjsip_ua.vcproj
+		SccProjectName7 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath7 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection7 = pjsip\\build\\
+		SccProjectUniqueName8 = ..\\pjsip\\build\\pjsua.vcproj
+		SccProjectName8 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath8 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection8 = pjsip\\build\\
+		SccProjectUniqueName9 = ..\\pjsip\\build\\pjsip_core_test.vcproj
+		SccProjectName9 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath9 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection9 = pjsip\\build\\
+		SccProjectUniqueName10 = ..\\pjmedia\\build\\pjmedia_audio_tool.vcproj
+		SccProjectName10 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath10 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection10 = pjmedia\\build\\
+		SccProjectUniqueName11 = ..\\pjsip\\build\\pjsip_callgen.vcproj
+		SccProjectName11 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath11 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection11 = pjsip\\build\\
+		SccProjectUniqueName12 = ..\\pjsip\\build\\pjsip_simple.vcproj
+		SccProjectName12 = \u0022$/pjproject/pjsip\u0022,\u0020QIAAAAAA
+		SccLocalPath12 = ..\\pjsip
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection12 = build\\
+	EndGlobalSection
+	GlobalSection(SolutionConfiguration) = preSolution
+		Debug = Debug
+		Release = Release
+	EndGlobalSection
+	GlobalSection(ProjectConfiguration) = postSolution
+		{72790D99-35BB-45AC-9A23-3BB60C901E63}.Debug.ActiveCfg = Debug|Win32
+		{72790D99-35BB-45AC-9A23-3BB60C901E63}.Debug.Build.0 = Debug|Win32
+		{72790D99-35BB-45AC-9A23-3BB60C901E63}.Release.ActiveCfg = Release|Win32
+		{72790D99-35BB-45AC-9A23-3BB60C901E63}.Release.Build.0 = Release|Win32
+		{A684A4C0-00D6-4497-B144-FC6AED4FAD8A}.Debug.ActiveCfg = Debug|Win32
+		{A684A4C0-00D6-4497-B144-FC6AED4FAD8A}.Debug.Build.0 = Debug|Win32
+		{A684A4C0-00D6-4497-B144-FC6AED4FAD8A}.Release.ActiveCfg = Release|Win32
+		{A684A4C0-00D6-4497-B144-FC6AED4FAD8A}.Release.Build.0 = Release|Win32
+		{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}.Debug.ActiveCfg = Debug|Win32
+		{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}.Debug.Build.0 = Debug|Win32
+		{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}.Release.ActiveCfg = Release|Win32
+		{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}.Release.Build.0 = Release|Win32
+		{EB8559B2-D738-4987-8591-4D217F8B0099}.Debug.ActiveCfg = Debug|Win32
+		{EB8559B2-D738-4987-8591-4D217F8B0099}.Debug.Build.0 = Debug|Win32
+		{EB8559B2-D738-4987-8591-4D217F8B0099}.Release.ActiveCfg = Release|Win32
+		{EB8559B2-D738-4987-8591-4D217F8B0099}.Release.Build.0 = Release|Win32
+		{692B42C4-6888-4BF8-9613-E48A2F138005}.Debug.ActiveCfg = Debug|Win32
+		{692B42C4-6888-4BF8-9613-E48A2F138005}.Debug.Build.0 = Debug|Win32
+		{692B42C4-6888-4BF8-9613-E48A2F138005}.Release.ActiveCfg = Release|Win32
+		{692B42C4-6888-4BF8-9613-E48A2F138005}.Release.Build.0 = Release|Win32
+		{58A72B82-7369-4B89-B511-7191A6B0D8C3}.Debug.ActiveCfg = Debug|Win32
+		{58A72B82-7369-4B89-B511-7191A6B0D8C3}.Debug.Build.0 = Debug|Win32
+		{58A72B82-7369-4B89-B511-7191A6B0D8C3}.Release.ActiveCfg = Release|Win32
+		{58A72B82-7369-4B89-B511-7191A6B0D8C3}.Release.Build.0 = Release|Win32
+		{DEE358A5-ADD3-4403-AD82-4967E63F17D1}.Debug.ActiveCfg = Debug|Win32
+		{DEE358A5-ADD3-4403-AD82-4967E63F17D1}.Debug.Build.0 = Debug|Win32
+		{DEE358A5-ADD3-4403-AD82-4967E63F17D1}.Release.ActiveCfg = Release|Win32
+		{DEE358A5-ADD3-4403-AD82-4967E63F17D1}.Release.Build.0 = Release|Win32
+		{B8500B7B-C6A8-46B9-84E6-90E200C1B35A}.Debug.ActiveCfg = Debug|Win32
+		{B8500B7B-C6A8-46B9-84E6-90E200C1B35A}.Debug.Build.0 = Debug|Win32
+		{B8500B7B-C6A8-46B9-84E6-90E200C1B35A}.Release.ActiveCfg = Release|Win32
+		{B8500B7B-C6A8-46B9-84E6-90E200C1B35A}.Release.Build.0 = Release|Win32
+		{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}.Debug.ActiveCfg = Debug|Win32
+		{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}.Debug.Build.0 = Debug|Win32
+		{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}.Release.ActiveCfg = Release|Win32
+		{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}.Release.Build.0 = Release|Win32
+		{5FD061CF-A0E8-43DF-990C-B046AA1BF5EE}.Debug.ActiveCfg = Debug|Win32
+		{5FD061CF-A0E8-43DF-990C-B046AA1BF5EE}.Debug.Build.0 = Debug|Win32
+		{5FD061CF-A0E8-43DF-990C-B046AA1BF5EE}.Release.ActiveCfg = Release|Win32
+		{5FD061CF-A0E8-43DF-990C-B046AA1BF5EE}.Release.Build.0 = Release|Win32
+		{DCBEF2A3-D444-46FC-82E7-D939113EECA7}.Debug.ActiveCfg = Debug|Win32
+		{DCBEF2A3-D444-46FC-82E7-D939113EECA7}.Debug.Build.0 = Debug|Win32
+		{DCBEF2A3-D444-46FC-82E7-D939113EECA7}.Release.ActiveCfg = Release|Win32
+		{DCBEF2A3-D444-46FC-82E7-D939113EECA7}.Release.Build.0 = Release|Win32
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}.Debug.ActiveCfg = Debug|Win32
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}.Debug.Build.0 = Debug|Win32
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}.Release.ActiveCfg = Release|Win32
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}.Release.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+	EndGlobalSection
+	GlobalSection(ExtensibilityAddIns) = postSolution
+	EndGlobalSection
+EndGlobal
Index: /pjproject/branches/archives/pjproject-0.2/docs/pjproject-0.2.8.html
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/docs/pjproject-0.2.8.html	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/docs/pjproject-0.2.8.html	(revision 1753)
@@ -0,0 +1,61 @@
+<HTML>
+<HEAD>
+<TITLE>pjproject version 0.2.8</TITLE>
+</HEAD>
+<BODY>
+<strong>Version 0.2.8 - 2005/05/28</strong>
+<hr>
+<H3>Status</H3><P>
+- Should not crash when everything is ok. :)<BR>
+- All targets are up to date.</P>
+
+<H3>What's new</H3><P>
+- STUN client support (pj/stun*).<BR>
+- Briefly tested with&nbsp;iptel.org service&nbsp;(with or without STUN).<BR>
+- Removed PJSDP project (merged into PJMEDIA).<BR>
+- Changed I/O queue to use callback for more flexibility.</P>
+
+<P>For more information, go to <STRONG><A href="http://www.bulukucing.org/pjsip.html">PJSIP web page</A></STRONG> or <STRONG><A href="http://www.bulukucing.org/pjproject/v0.2.8/">Browse the Source Files</A></STRONG>.</P>
+
+<H3>Download source tar ball</H3>
+<P>
+<STRONG><A href="http://www.bulukucing.org/pjproject/v0.2.8/pjproject-0.2.8.tar.gz">pjproject-0.2.8.tar.gz</A></STRONG> (339KB)</P>
+
+<H3>Download executables</H3>
+<P>
+Win2k/XP (needs Direct Sound/msvcrt.dll, won't work on NT because of Direct Sound):<BR>
+- <A href="http://www.bulukucing.org/pjproject/v0.2.8/pjsua_vc6.exe"><STRONG>pjsua_vc6.exe</STRONG></A> (124 KB) or <A href="http://www.bulukucing.org/pjproject/v0.2.8/pjsua_vc6.exe.gz"><STRONG>pjsua_vc6.exe.gz</STRONG></A> (62 KB)</P>
+
+<P>Win32 mingw (no audio):<BR>
+- <A href="http://www.bulukucing.org/pjproject/v0.2.8/pjsua_mingw.exe"><STRONG>pjsua_mingw.exe</STRONG></A> (209 KB) or <A href="http://www.bulukucing.org/pjproject/v0.2.8/pjsua_mingw.exe.gz"><STRONG>pjsua_mingw.exe.gz</STRONG></A> (92 KB)</P>
+
+<P>Linux (compiled on Fedora Linux 3, no audio):<BR>
+- <A href="http://www.bulukucing.org/pjproject/v0.2.8/pjsua.gz"><STRONG>pjsua.gz</STRONG></A> (74 KB)<BR>
+</P>
+
+<H3>Release Notes:</H3>
+<ul>
+<li>Simple <strong>STUN</strong> client support.<BR>
+  SIP UDP port and media RTP/RTCP ports are now STUN aware. <BR>
+</li>
+<li>Client <strong>registration</strong> tested<BR>
+  Including digest authentication, tested with iptel.org.
+</li>
+<li>Major changed in <strong>I/O queue</strong>, now callback is used.<BR>
+  Callback is better because multiple libraries can register to single I/O queue.
+  It was not possible with previous implementation, because the function which does
+  polling needs to understand what to do when a key is signalled. The changes was
+  initially needed to support STUN, but then I decided that the STUN client 
+  implementation uses the simpler select() (in stun_client.c). But the changes in
+  I/O queue stays there because it's better than previous implementation.
+</li>
+<li>Merge <strong>SDP</strong> library into PJMEDIA (no more PJSDP).<BR>
+  PJSDP only has couple of files (sdp.[hc]), not worth maintaining a library.
+</li>
+<li>Fixed bug in select() I/O queue (not thread safe).
+</li>
+</ul>
+
+
+</BODY>
+</HTML>
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/LGPL.TXT
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/LGPL.TXT	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/LGPL.TXT	(revision 1753)
@@ -0,0 +1,513 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/Makefile
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/Makefile	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/Makefile	(revision 1753)
@@ -0,0 +1,57 @@
+include make-$(TARGET).inc
+
+export PJLIB_SRCDIR = ../src/pj
+export PJLIB_SRCEXT = .c
+export PJLIB_SRCS = $(PJLIB_SOURCES) array.c config.c except.c fifobuf.c guid.c \
+	hash.c ioqueue_select.c list.c log.c log_stdout.c \
+	pool.c pool_caching.c pool_policy.c \
+	rbtree.c scanner.c sock.c string.c timer.c types.c md5.c stun.c stun_client.c \
+	xml.c
+
+export TEST_SRCDIR = ../src/test
+export TEST_SRCEXT = .cpp
+export TEST_SRCS = $(TEST_SOURCES) exception_test.cpp fifobuf_test.cpp \
+		ioqueue_test.cpp ioqueue_test_tcp.cpp \
+		ioqueue_test_udp.cpp list_test.cpp main.cpp \
+		os_test.cpp pool_test.cpp rbtree_test.cpp \
+		timer_test.cpp xml_test.cpp
+
+export TARGET CCOUT CC AR RANLIB MV RM RMDIR MKDIR OBJEXT LD LDOUT 
+
+all: pjlib 
+# test
+# If test is to be enabled, also remove several comments below
+
+doc:
+	cd .. && doxygen docs/doxygen.cfg
+
+print:
+	$(MAKE) -f make-rules APP=PJLIB app=pjlib print_lib
+#	$(MAKE) -f make-rules APP=TEST app=test print_bin
+	
+depend:
+	$(MAKE) -f make-rules APP=PJLIB app=pjlib depend
+#	$(MAKE) -f make-rules APP=TEST app=test depend
+#	echo '$(TEST_EXE): $(PJLIB_LIB)' >> .test.depend
+
+
+dep: depend
+
+pjlib:
+	$(MAKE) -f make-rules APP=PJLIB app=pjlib $(PJLIB_LIB)
+
+
+test: 
+	$(MAKE) -f make-rules APP=TEST app=test $(TEST_EXE)
+
+
+clean:
+	$(MAKE) -f make-rules APP=PJLIB app=pjlib clean
+	$(MAKE) -f make-rules APP=TEST app=test clean
+
+realclean:
+	$(MAKE) -f make-rules APP=PJLIB app=pjlib realclean
+	$(MAKE) -f make-rules APP=TEST app=test realclean
+
+distclean: realclean
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/make-linux-i386.inc
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/make-linux-i386.inc	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/make-linux-i386.inc	(revision 1753)
@@ -0,0 +1,40 @@
+#
+# make-linux-i386.inc: Linux i386 specific compilation switches.
+#
+
+#
+# PJLIB_OPTIMIZE variable.
+#
+ifdef MINSIZE
+PJLIB_OPTIMIZE := -DPJ_HAS_TCP=0 -DPJ_LOG_MAX_LEVEL=0 -DPJ_HAS_THREADS=0 \
+		  -DPJ_FUNCTIONS_ARE_INLINED=0
+else
+PJLIP_OPTIMIZE :=
+endif
+
+
+_CFLAGS := $(CFLAGS) -Wall -O2 -I../src -DLINUX -DPJ_GUID_TYPE=PJ_GUID_SIMPLE \
+	  $(PJLIB_OPTIMIZE)
+_CXXFLAGS := $(_CFLAGS) $(CXXFLAGS)
+CCOUT := -o
+CC := gcc -c
+AR := ar r
+MV := mv
+RM := rm -f
+RMDIR := rm -rf
+MKDIR := mkdir 
+OBJEXT := .o
+LD := gcc
+LDOUT := -o
+RANLIB := ranlib
+EXE :=
+
+export PJLIB_SOURCES := os_unix.c
+export PJLIB_CFLAGS := $(_CFLAGS)
+export PJLIB_LIB := ../lib/libpj.a
+
+export TEST_SOURCES: = 
+export TEST_CXXFLAGS := $(PJLIB_CFLAGS)
+export TEST_LDFLAGS := -L../lib -lpj -lpthread -lstdc++
+export TEST_EXE := ../bin/test
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/make-mingw.inc
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/make-mingw.inc	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/make-mingw.inc	(revision 1753)
@@ -0,0 +1,41 @@
+#
+# make-mingw.inc: Mingw specific compilation switches.
+#
+
+#
+# PJLIB_OPTIMIZE variable.
+#
+ifdef MINSIZE
+PJLIB_OPTIMIZE := -DPJ_HAS_TCP=0 -DPJ_LOG_MAX_LEVEL=0 -DPJ_HAS_THREADS=0 \
+		  -DPJ_FUNCTIONS_ARE_INLINED=0
+else
+PJLIP_OPTIMIZE :=
+endif
+
+
+_CFLAGS := $(CFLAGS) -Wall -O3 -I../src -DWIN32 -D_WIN32 \
+	   -DPJ_GUID_TYPE=PJ_GUID_COCREATEGUID $(PJLIB_OPTIMIZE)
+_CXXFLAGS := $(_CFLAGS) $(CXXFLAGS)
+_LDFLAGS := -L../lib -lpj -lwsock32 -lws2_32 -lstdc++ -lole32 $(LDFLAGS)
+CCOUT := -o
+CC := gcc -c
+AR := ar r
+MV := mv
+RM := rm -f
+RMDIR := rm -rf
+MKDIR := mkdir 
+OBJEXT := .o
+LD := gcc
+LDOUT := -o
+RANLIB := ranlib
+EXE := .exe
+
+export PJLIB_SOURCES := os_win32.c ioqueue_winnt.c
+export PJLIB_CFLAGS := $(_CFLAGS)
+export PJLIB_LIB := ../lib/libpj.a
+
+export TEST_SOURCES = 
+export TEST_CXXFLAGS = $(_CXXFLAGS)
+export TEST_LDFLAGS = $(_LDFLAGS)
+export TEST_EXE = ../bin/test.exe
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/make-rules
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/make-rules	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/make-rules	(revision 1753)
@@ -0,0 +1,119 @@
+LIBDIR = ../lib
+BINDIR = ../bin
+
+#
+# The full path of output lib file (e.g. ../lib/libapp.a).
+#
+LIB = $($(APP)_LIB)
+
+#
+# The full path of output executable file (e.g. ../bin/app.exe).
+#
+EXE = $($(APP)_EXE)
+
+#
+# Source directory
+#
+SRCDIR = $($(APP)_SRCDIR)
+
+#
+# SRCEXT is .c
+# SRCS is file.c
+# FULL_SRCS is ../src/app/file.c
+#
+SRCEXT = $($(APP)_SRCEXT)
+SRCS = $($(APP)_SRCS)
+FULL_SRCS = $(foreach file, $(SRCS), $(SRCDIR)/$(file))
+
+
+#
+# Output directory for object files (i.e. output/target)
+#
+OBJDIR = ./output/$(app)-$(TARGET)
+
+#
+# OBJS1 is ./output/target/file.c
+# OBJS is ./output/target/file.o
+#
+OBJS1 = $(foreach file, $(SRCS), $(OBJDIR)/$(file))
+OBJS = $(OBJS1:%$(SRCEXT)=%$(OBJEXT))
+OBJDIRS := $(sort $(foreach file, $(SRCS), $(dir $(OBJDIR)/$(file))))
+
+
+#
+# When generating dependency (gcc -MM), ideally we use only either
+# CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work.
+#
+DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS)
+
+print_common:
+	@echo "###"
+	@echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):"
+	@echo "###"
+	@echo APP=$(APP)
+	@echo SRCEXT=$(SRCEXT)
+	@echo OBJDIR=$(OBJDIR)
+	@echo OBJS=$(OBJS)
+	@echo SRCDIR=$(SRCDIR)
+	@echo FULL_SRCS=$(FULL_SRCS)
+	@echo $(APP)_CFLAGS=$($(APP)_CFLAGS)
+	@echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS)
+	@echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS)
+	@echo DEPFLAGS=$(DEPFLAGS)
+
+print_bin: print_common
+	@echo EXE=$(EXE)
+	@echo BINDIR=$(BINDIR)
+
+print_lib: print_common
+	@echo LIB=$(LIB)
+	@echo LIBDIR=$(LIBDIR)
+
+$(LIB): $(LIBDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP)
+	$(AR) $(LIB) $(OBJS)
+	$(RANLIB) $(LIB)
+
+$(EXE): $(BINDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP)
+	$(LD) $(LDOUT) $(EXE) $(OBJS) $($(APP)_LDFLAGS)
+
+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c
+	$(CC) $($(APP)_CFLAGS) $< $(CCOUT) $@
+
+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp
+	$(CC) $($(APP)_CXXFLAGS) $< $(CCOUT) $@
+
+#$(OBJDIR):
+#	$(MKDIR) $(OBJDIR)
+$(OBJDIRS):
+	$(MKDIR) $@
+
+$(LIBDIR):
+	$(MKDIR) $(LIBDIR)
+
+$(BINDIR):
+	$(MKDIR) $(BINDIR)
+
+clean:
+	$(RM) -r $(OBJDIR)/*
+	$(RMDIR) $(OBJDIR)
+
+realclean: clean
+	$(RM) $(LIB) $(EXE)
+	$(RM) .$(app).depend
+
+depend:
+	$(RM) .$(app).depend
+	for F in $(FULL_SRCS); do \
+	   echo -n $(OBJDIR)/ >> .$(app).depend; \
+	   if gcc -MM $(DEPFLAGS) $$F >> .$(app).depend; then \
+		true; \
+	   else \
+		echo 'err:' >> .$(app).depend; \
+		exit 1; \
+	   fi; \
+	done
+
+dep: depend
+
+-include .$(app).depend
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.dsp	(revision 1753)
@@ -0,0 +1,322 @@
+# Microsoft Developer Studio Project File - Name="pjlib" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pjlib - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjlib.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjlib.mak" CFG="pjlib - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjlib - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pjlib - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjlib/build", UIAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjlib - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\output\pjlib_vc6_Release"
+# PROP BASE Intermediate_Dir "output\pjlib_vc6_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "\.output\pjlib_vc6_Release"
+# PROP Intermediate_Dir ".\output\pjlib_vc6_Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /Zi /O2 /I "../src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjlib_vc6s.lib"
+
+!ELSEIF  "$(CFG)" == "pjlib - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\output\pjlib_vc6_Debug"
+# PROP BASE Intermediate_Dir ".\output\pjlib_vc6_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\pjlib_vc6_Debug"
+# PROP Intermediate_Dir ".\output\pjlib_vc6_Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../src" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjlib_vc6sd.lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjlib - Win32 Release"
+# Name "pjlib - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\pj\array.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\config.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\except.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\fifobuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\guid.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\hash.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\ioqueue_select.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\ioqueue_winnt.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\list.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\log.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\log_stdout.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\os_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool_caching.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool_dbg_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool_policy.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\rbtree.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\scanner.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\sock.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\string.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\stun.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\stun_client.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\timer.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\types.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\xml.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\pj\array.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\compat.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\except.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\fifobuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\fwd.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\guid.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\hash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\ioqueue.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\list.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\log.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\os.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\rbtree.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\scanner.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\sock.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\string.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\stun.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\timer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\xml.h
+# End Source File
+# End Group
+# Begin Group "Inline Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\src\pj\array_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\list_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\scanner_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\sock_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\string_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\timer_i.h
+# End Source File
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.dsw
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.dsw	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.dsw	(revision 1753)
@@ -0,0 +1,56 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "pjlib"=.\pjlib.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjlib_test"=.\pjlib_test.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pjlibpp"=.\pjlibpp.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.sln
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.sln	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.sln	(revision 1753)
@@ -0,0 +1,59 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib", "pjlib.vcproj", "{07A676B3-6E86-49E5-B20A-394A33B05B28}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib_test", "pjlib_test.vcproj", "{CF20BE3E-30FA-4A05-91F2-FF978F8705BF}"
+	ProjectSection(ProjectDependencies) = postProject
+		{07A676B3-6E86-49E5-B20A-394A33B05B28} = {07A676B3-6E86-49E5-B20A-394A33B05B28}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlibpp", "pjlibpp.vcproj", "{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SourceCodeControl) = preSolution
+		SccNumberOfProjects = 4
+		SccProjectUniqueName0 = pjlib.vcproj
+		SccLocalPath0 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection0 = pjlib\\build\\
+		SccProjectName1 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath1 = ..\\..
+		SccProvider1 = MSSCCI:Microsoft\u0020Visual\u0020SourceSafe
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection1 = pjlib\\build\\
+		SolutionUniqueID = {9AAE883A-59C1-4404-A119-28ED201D7215}
+		SccProjectUniqueName2 = pjlib_test.vcproj
+		SccLocalPath2 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection2 = pjlib\\build\\
+		SccProjectUniqueName3 = pjlibpp.vcproj
+		SccLocalPath3 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection3 = pjlib\\build\\
+	EndGlobalSection
+	GlobalSection(SolutionConfiguration) = preSolution
+		Debug = Debug
+		Release = Release
+	EndGlobalSection
+	GlobalSection(ProjectConfiguration) = postSolution
+		{07A676B3-6E86-49E5-B20A-394A33B05B28}.Debug.ActiveCfg = Debug|Win32
+		{07A676B3-6E86-49E5-B20A-394A33B05B28}.Debug.Build.0 = Debug|Win32
+		{07A676B3-6E86-49E5-B20A-394A33B05B28}.Release.ActiveCfg = Release|Win32
+		{07A676B3-6E86-49E5-B20A-394A33B05B28}.Release.Build.0 = Release|Win32
+		{CF20BE3E-30FA-4A05-91F2-FF978F8705BF}.Debug.ActiveCfg = Debug|Win32
+		{CF20BE3E-30FA-4A05-91F2-FF978F8705BF}.Debug.Build.0 = Debug|Win32
+		{CF20BE3E-30FA-4A05-91F2-FF978F8705BF}.Release.ActiveCfg = Release|Win32
+		{CF20BE3E-30FA-4A05-91F2-FF978F8705BF}.Release.Build.0 = Release|Win32
+		{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}.Debug.ActiveCfg = Debug|Win32
+		{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}.Debug.Build.0 = Debug|Win32
+		{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}.Release.ActiveCfg = Release|Win32
+		{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}.Release.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+	EndGlobalSection
+	GlobalSection(ExtensibilityAddIns) = postSolution
+	EndGlobalSection
+EndGlobal
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib.vcproj	(revision 1753)
@@ -0,0 +1,609 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjlib"
+	ProjectGUID="{72790D99-35BB-45AC-9A23-3BB60C901E63}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjlib_vc7_Debug"
+			IntermediateDirectory=".\output\pjlib_vc7_Debug"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile=".\output\pjlib_vc7_Debug/pjlib.pch"
+				AssemblerListingLocation=".\output\pjlib_vc7_Debug/"
+				ObjectFile=".\output\pjlib_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjlib_vc7_Debug/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib_vc7sd.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjlib_vc7_Release"
+			IntermediateDirectory=".\output\pjlib_vc7_Release"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjlib_vc7_Release/pjlib.pch"
+				AssemblerListingLocation=".\output\pjlib_vc7_Release/"
+				ObjectFile=".\output\pjlib_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjlib_vc7_Release/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="3"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib_vc7s.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\pj\array.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\config.c">
+			</File>
+			<File
+				RelativePath="..\src\pj\except.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\fifobuf.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\guid.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\hash.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\ioqueue_select.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\ioqueue_winnt.c">
+			</File>
+			<File
+				RelativePath="..\src\pj\list.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\log.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\log_stdout.c">
+			</File>
+			<File
+				RelativePath="..\src\pj\md5.c">
+			</File>
+			<File
+				RelativePath="..\src\pj\os_win32.c">
+			</File>
+			<File
+				RelativePath="..\src\pj\pool.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\pool_caching.c">
+			</File>
+			<File
+				RelativePath="..\src\pj\pool_dbg_win32.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\pool_policy.c">
+			</File>
+			<File
+				RelativePath="..\src\pj\rbtree.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\scanner.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\sock.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\string.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\stun.c">
+			</File>
+			<File
+				RelativePath="..\src\pj\stun_client.c">
+			</File>
+			<File
+				RelativePath="..\src\pj\timer.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\types.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj\xml.c">
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\pj\array.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\compat.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\config.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\except.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\fifobuf.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\guid.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\hash.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\ioqueue.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\list.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\log.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\md5.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\os.h">
+			</File>
+			<File
+				RelativePath="..\src\pjlib.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\pool.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\rbtree.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\scanner.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\sock.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\string.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\stun.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\timer.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\types.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\xml.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Inline Files"
+			Filter="">
+			<File
+				RelativePath="..\src\pj\array_i.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\list_i.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\pool_i.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\scanner_i.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\sock_i.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\string_i.h">
+			</File>
+			<File
+				RelativePath="..\src\pj\timer_i.h">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib_test.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib_test.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib_test.dsp	(revision 1753)
@@ -0,0 +1,150 @@
+# Microsoft Developer Studio Project File - Name="pjlib_test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=pjlib_test - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjlib_test.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjlib_test.mak" CFG="pjlib_test - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjlib_test - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "pjlib_test - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjlib/build", UIAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjlib_test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\output\test_vc6_Release"
+# PROP BASE Intermediate_Dir ".\output\test_vc6_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\output\test_vc6_Release"
+# PROP Intermediate_Dir ".\output\test_vc6_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../src" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjlib_test_vc6.exe"
+
+!ELSEIF  "$(CFG)" == "pjlib_test - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\output\test_vc6_Debug"
+# PROP BASE Intermediate_Dir ".\output\test_vc6_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\test_vc6_Debug"
+# PROP Intermediate_Dir ".\output\test_vc6_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjlib_test_vc6d.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjlib_test - Win32 Release"
+# Name "pjlib_test - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\test\exception_test.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\fifobuf_test.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\ioqueue_test.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\ioqueue_test_tcp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\ioqueue_test_udp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\list_test.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\main.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\os_test.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\pool_test.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\rbtree_test.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\timer_test.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\xml_test.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\test\libpj_test.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib_test.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib_test.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlib_test.vcproj	(revision 1753)
@@ -0,0 +1,416 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjlib_test"
+	ProjectGUID="{A684A4C0-00D6-4497-B144-FC6AED4FAD8A}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjlib_test_vc7_Debug"
+			IntermediateDirectory=".\output\pjlib_test_vc7_Debug"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjlib_test_vc7_Debug/pjlib_test.pch"
+				AssemblerListingLocation=".\output\pjlib_test_vc7_Debug/"
+				ObjectFile=".\output\pjlib_test_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjlib_test_vc7_Debug/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib"
+				OutputFile="..\bin\pjlib_test_vc7d.exe"
+				LinkIncremental="2"
+				SuppressStartupBanner="TRUE"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile=".\output\pjlib_test_vc7_Debug/pjlib_test.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\output\pjlib_test_vc7_Debug/pjlib_test.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjlib_test_vc7_Release"
+			IntermediateDirectory=".\output\pjlib_test_vc7_Release"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjlib_test_vc7_Release/pjlib_test.pch"
+				AssemblerListingLocation=".\output\pjlib_test_vc7_Release/"
+				ObjectFile=".\output\pjlib_test_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjlib_test_vc7_Release/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="3"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib"
+				OutputFile="../bin/pjlib_test_vc7.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="TRUE"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile=".\output\pjlib_test_vc7_Release/pjlib_test.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\output\pjlib_test_vc7_Release/pjlib_test.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\test\exception_test.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\fifobuf_test.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\ioqueue_test.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\ioqueue_test_tcp.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\ioqueue_test_udp.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\list_test.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\main.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\os_test.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\pool_test.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\rbtree_test.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\timer_test.cpp">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\xml_test.cpp">
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\test\libpj_test.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
+		</Filter>
+		<Filter
+			Name="Test Files"
+			Filter="">
+			<File
+				RelativePath="..\src\test\pidf-diff.xml">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlibpp.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlibpp.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlibpp.dsp	(revision 1753)
@@ -0,0 +1,158 @@
+# Microsoft Developer Studio Project File - Name="pjlibpp" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pjlibpp - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjlibpp.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjlibpp.mak" CFG="pjlibpp - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjlibpp - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pjlibpp - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjlib/build", UIAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjlibpp - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\output\pjlibpp_vc6_Release"
+# PROP BASE Intermediate_Dir ".\output\pjlibpp_vc6_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\output\pjlibpp_vc6_Release"
+# PROP Intermediate_Dir ".\output\pjlibpp_vc6_Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjlibp_vc6s.lib"
+
+!ELSEIF  "$(CFG)" == "pjlibpp - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\output\pjlibpp_vc6_Debug"
+# PROP BASE Intermediate_Dir ".\output\pjlibpp_vc6_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\pjlibpp_vc6_Debug"
+# PROP Intermediate_Dir ".\output\pjlibpp_vc6_Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../src" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjlibp_vc6sd.lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjlibpp - Win32 Release"
+# Name "pjlibpp - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE="..\src\pj++\compiletest.cpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\pj++.cpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\proactor.cpp"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE="..\src\pj++\hash.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\ioqueue.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\list.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\os.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjlib++.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\pool.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\proactor.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\scanner.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\sock.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\string.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\timer.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\tree.hpp"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pj++\types.hpp"
+# End Source File
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlibpp.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlibpp.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/pjlibpp.vcproj	(revision 1753)
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjlibpp"
+	ProjectGUID="{488B9CA1-8F59-4E4E-8748-D6A712CF9F3C}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjlibpp_vc7_Release"
+			IntermediateDirectory=".\output\pjlibpp_vc7_Release"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjlibpp_vc7_Release/pjlibpp.pch"
+				AssemblerListingLocation=".\output\pjlibpp_vc7_Release/"
+				ObjectFile=".\output\pjlibpp_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjlibpp_vc7_Release/"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlibpp_vc7s.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjlibpp_vc7_Debug"
+			IntermediateDirectory=".\output\pjlibpp_vc7_Debug"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjlibpp_vc7_Debug/pjlibpp.pch"
+				AssemblerListingLocation=".\output\pjlibpp_vc7_Debug/"
+				ObjectFile=".\output\pjlibpp_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjlibpp_vc7_Debug/"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlibpp_vc7sd.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\pj++\compiletest.cpp">
+				<FileConfiguration
+					Name="Release|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						AssemblerOutput="4"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj++\pj++.cpp">
+				<FileConfiguration
+					Name="Release|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pj++\proactor.cpp">
+				<FileConfiguration
+					Name="Release|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\pj++\hash.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\ioqueue.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\list.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\os.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\pool.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\proactor.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\scanner.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\sock.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\string.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\timer.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\tree.hpp">
+			</File>
+			<File
+				RelativePath="..\src\pj++\types.hpp">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/build/ppcpjlib.vcp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/build/ppcpjlib.vcp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/build/ppcpjlib.vcp	(revision 1753)
@@ -0,0 +1,1759 @@
+# Microsoft eMbedded Visual Tools Project File - Name="ppcpjlib" - Package Owner=<4>
+# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (WCE ARMV4) Static Library" 0xa304
+# TARGTYPE "Win32 (WCE ARMV4I) Static Library" 0xa504
+# TARGTYPE "Win32 (WCE emulator) Static Library" 0xa604
+
+CFG=ppcpjlib - Win32 (WCE emulator) Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ppcpjlib.vcn".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ppcpjlib.vcn" CFG="ppcpjlib - Win32 (WCE emulator) Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ppcpjlib - Win32 (WCE emulator) Release" (based on "Win32 (WCE emulator) Static Library")
+!MESSAGE "ppcpjlib - Win32 (WCE emulator) Debug" (based on "Win32 (WCE emulator) Static Library")
+!MESSAGE "ppcpjlib - Win32 (WCE ARMV4I) Release" (based on "Win32 (WCE ARMV4I) Static Library")
+!MESSAGE "ppcpjlib - Win32 (WCE ARMV4I) Debug" (based on "Win32 (WCE ARMV4I) Static Library")
+!MESSAGE "ppcpjlib - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Static Library")
+!MESSAGE "ppcpjlib - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+# PROP ATL_Project 2
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "emulatorRel"
+# PROP BASE Intermediate_Dir "emulatorRel"
+# PROP BASE CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "emulatorRel"
+# PROP Intermediate_Dir "emulatorRel"
+# PROP CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Target_Dir ""
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "_i386_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_LIB" /YX /Gs8192 /GF /O2 /c
+# ADD CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "_i386_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_LIB" /YX /Gs8192 /GF /O2 /c
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "emulatorDbg"
+# PROP BASE Intermediate_Dir "emulatorDbg"
+# PROP BASE CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "emulatorDbg"
+# PROP Intermediate_Dir "emulatorDbg"
+# PROP CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Target_Dir ""
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "_i386_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_LIB" /YX /Gs8192 /GF /c
+# ADD CPP /nologo /W3 /Zi /Od /I "../src" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "_i386_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_LIB" /YX /Gs8192 /GF /c
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "ARMV4IRel"
+# PROP BASE Intermediate_Dir "ARMV4IRel"
+# PROP BASE CPU_ID "{DC70F430-E78B-494F-A9D5-62ADC56443B8}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "ARMV4IRel"
+# PROP Intermediate_Dir "ARMV4IRel"
+# PROP CPU_ID "{DC70F430-E78B-494F-A9D5-62ADC56443B8}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Target_Dir ""
+CPP=clarm.exe
+# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "ARM" /D "_ARM_" /D "$(CePlatform)" /D "ARMV4I" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_LIB" /YX /QRarch4T /QRinterwork-return /O2 /M$(CECrtMT) /c
+# ADD CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "ARM" /D "_ARM_" /D "$(CePlatform)" /D "ARMV4I" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_LIB" /YX /QRarch4T /QRinterwork-return /O2 /M$(CECrtMT) /c
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "ARMV4IDbg"
+# PROP BASE Intermediate_Dir "ARMV4IDbg"
+# PROP BASE CPU_ID "{DC70F430-E78B-494F-A9D5-62ADC56443B8}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "ARMV4IDbg"
+# PROP Intermediate_Dir "ARMV4IDbg"
+# PROP CPU_ID "{DC70F430-E78B-494F-A9D5-62ADC56443B8}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Target_Dir ""
+CPP=clarm.exe
+# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "ARM" /D "_ARM_" /D "$(CePlatform)" /D "ARMV4I" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /QRarch4T /QRinterwork-return /M$(CECrtMTDebug) /c
+# ADD CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "ARM" /D "_ARM_" /D "$(CePlatform)" /D "ARMV4I" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /QRarch4T /QRinterwork-return /M$(CECrtMTDebug) /c
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "ARMV4Rel"
+# PROP BASE Intermediate_Dir "ARMV4Rel"
+# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "ARMV4Rel"
+# PROP Intermediate_Dir "ARMV4Rel"
+# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Target_Dir ""
+CPP=clarm.exe
+# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "NDEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /O2 /M$(CECrtMT) /c
+# ADD CPP /nologo /W3 /I "../src" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "NDEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /O2 /M$(CECrtMT) /c
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "ARMV4Dbg"
+# PROP BASE Intermediate_Dir "ARMV4Dbg"
+# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "ARMV4Dbg"
+# PROP Intermediate_Dir "ARMV4Dbg"
+# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Target_Dir ""
+CPP=clarm.exe
+# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /M$(CECrtMTDebug) /c
+# ADD CPP /nologo /W3 /Zi /Od /I "../src" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /M$(CECrtMTDebug) /c
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ppcpjlib - Win32 (WCE emulator) Release"
+# Name "ppcpjlib - Win32 (WCE emulator) Debug"
+# Name "ppcpjlib - Win32 (WCE ARMV4I) Release"
+# Name "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+# Name "ppcpjlib - Win32 (WCE ARMV4) Release"
+# Name "ppcpjlib - Win32 (WCE ARMV4) Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\pj\array.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_ARRAY=\
+	"..\src\pj\array_i.h"\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_ARRAY=\
+	"..\src\pj\array_i.h"\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_ARRAY=\
+	"..\src\pj\array_i.h"\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\config.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_CONFI=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_CONFI=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_CONFI=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\except.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_EXCEP=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_EXCEP=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_EXCEP=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\fifobuf.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+DEP_CPP_FIFOB=\
+	"..\src\pj\fifobuf.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_FIFOB=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\fifobuf.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+DEP_CPP_FIFOB=\
+	"..\src\pj\fifobuf.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+DEP_CPP_FIFOB=\
+	"..\src\pj\fifobuf.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_FIFOB=\
+	"..\src\pj\fifobuf.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_FIFOB=\
+	"..\src\pj\fifobuf.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\guid.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_GUID_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\guid.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_GUID_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\guid.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_GUID_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\guid.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\hash.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_HASH_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\hash.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_HASH_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\hash.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_HASH_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\hash.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\ioqueue_select.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_IOQUE=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\ioqueue.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_IOQUE=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\ioqueue.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_IOQUE=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\ioqueue.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\ioqueue_winnt.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_IOQUEU=\
+	"..\src\pj\array.h"\
+	"..\src\pj\array_i.h"\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\ioqueue.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_IOQUEU=\
+	"..\src\pj\array.h"\
+	"..\src\pj\array_i.h"\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\ioqueue.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_IOQUEU=\
+	"..\src\pj\array.h"\
+	"..\src\pj\array_i.h"\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\ioqueue.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\list.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+DEP_CPP_LIST_=\
+	"..\src\pj\list_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_LIST_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+DEP_CPP_LIST_=\
+	"..\src\pj\list_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+DEP_CPP_LIST_=\
+	"..\src\pj\list_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_LIST_=\
+	"..\src\pj\list_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_LIST_=\
+	"..\src\pj\list_i.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\log.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_LOG_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_LOG_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_LOG_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\log_stdout.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_LOG_S=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_LOG_S=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_LOG_S=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\md5.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+DEP_CPP_MD5_C=\
+	"..\src\pj\md5.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_MD5_C=\
+	"..\src\pj\config.h"\
+	"..\src\pj\md5.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+DEP_CPP_MD5_C=\
+	"..\src\pj\md5.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+DEP_CPP_MD5_C=\
+	"..\src\pj\md5.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_MD5_C=\
+	"..\src\pj\md5.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_MD5_C=\
+	"..\src\pj\md5.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\os_win32.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_OS_WI=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\guid.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_OS_WI=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\guid.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_OS_WI=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\guid.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+DEP_CPP_POOL_=\
+	"..\src\pj\pool_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_POOL_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+DEP_CPP_POOL_=\
+	"..\src\pj\pool_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+DEP_CPP_POOL_=\
+	"..\src\pj\pool_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_POOL_=\
+	"..\src\pj\pool_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_POOL_=\
+	"..\src\pj\pool_i.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool_caching.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_POOL_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_POOL_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_POOL_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool_dbg_win32.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_POOL_D=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_POOL_D=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_POOL_D=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool_policy.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_POOL_P=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_POOL_P=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_POOL_P=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\rbtree.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_RBTRE=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\rbtree.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_RBTRE=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\rbtree.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_RBTRE=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\rbtree.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\scanner.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+DEP_CPP_SCANN=\
+	"..\src\pj\scanner_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_SCANN=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\scanner.h"\
+	"..\src\pj\scanner_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+DEP_CPP_SCANN=\
+	"..\src\pj\scanner_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+DEP_CPP_SCANN=\
+	"..\src\pj\scanner_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_SCANN=\
+	"..\src\pj\scanner_i.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_SCANN=\
+	"..\src\pj\scanner_i.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\sock.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_SOCK_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_SOCK_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_SOCK_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\string.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_STRIN=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_STRIN=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_STRIN=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\stun.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_STUN_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\stun.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_STUN_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\stun.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_STUN_=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\stun.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\stun_client.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_STUN_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\stun.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_STUN_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\stun.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_STUN_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\sock.h"\
+	"..\src\pj\sock_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\stun.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\timer.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_TIMER=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\timer.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_TIMER=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\timer.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_TIMER=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\os.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\timer.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\types.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_TYPES=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_TYPES=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_TYPES=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\xml.c
+
+!IF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE emulator) Debug"
+
+DEP_CPP_XML_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\scanner.h"\
+	"..\src\pj\scanner_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	"..\src\pj\xml.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Release"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4I) Debug"
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_XML_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\scanner.h"\
+	"..\src\pj\scanner_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	"..\src\pj\xml.h"\
+	
+
+!ELSEIF  "$(CFG)" == "ppcpjlib - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_XML_C=\
+	"..\src\pj\compat.h"\
+	"..\src\pj\config.h"\
+	"..\src\pj\except.h"\
+	"..\src\pj\list.h"\
+	"..\src\pj\list_i.h"\
+	"..\src\pj\log.h"\
+	"..\src\pj\pool.h"\
+	"..\src\pj\pool_i.h"\
+	"..\src\pj\scanner.h"\
+	"..\src\pj\scanner_i.h"\
+	"..\src\pj\string.h"\
+	"..\src\pj\string_i.h"\
+	"..\src\pj\types.h"\
+	"..\src\pj\xml.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\pj\array.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\array_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\compat.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\except.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\fifobuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\fwd.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\guid.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\hash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\ioqueue.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\list.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\list_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\log.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\os.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\pool_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\rbtree.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\scanner.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\scanner_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\sock.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\sock_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\string.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\string_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\stun.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\timer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\timer_i.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\xml.h
+# End Source File
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/docs/doxygen.cfg
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/docs/doxygen.cfg	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/docs/doxygen.cfg	(revision 1753)
@@ -0,0 +1,1042 @@
+# Doxyfile 1.3-rc3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           =  PJLIB
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = docs
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, 
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en 
+# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, 
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these class will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH        = "c:\project\pjproject"
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower case letters. If set to YES upper case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments  will behave just like the Qt-style comments (thus requiring an 
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consist of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  =  src/pj
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS          = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = *_i.h
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
+INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output dir.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non empty doxygen will try to run 
+# the html help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimised for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assigments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_XML           = NO
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed.
+
+PREDEFINED             = PJ_DECL(x)=x PJ_DEF(x)=x PJ_IDECL(x)=x \
+			 PJ_IDEF(x)=x PJ_INLINE(x)=x
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS         = NO
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermedate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
+
+# The CGI_NAME tag should be the name of the CGI script that 
+# starts the search engine (doxysearch) with the correct parameters. 
+# A script with this name will be generated by doxygen.
+
+#CGI_NAME               = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the 
+# cgi binaries are located. See the documentation of your http daemon for 
+# details.
+
+#CGI_URL                = 
+
+# The DOC_URL tag should be the absolute URL to the directory where the 
+# documentation is located. If left blank the absolute path to the 
+# documentation, with file:// prepended to it, will be used.
+
+#DOC_URL                = 
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the 
+# documentation is located. If left blank the directory on the local machine 
+# will be used.
+
+#DOC_ABSPATH            = 
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary 
+# is installed.
+
+#BIN_ABSPATH            = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to 
+# documentation generated for other projects. This allows doxysearch to search 
+# the documentation for these projects as well.
+
+#EXT_DOC_PATHS          = 
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/compiletest.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/compiletest.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/compiletest.cpp	(revision 1753)
@@ -0,0 +1,65 @@
+/* $Header: /pjproject/pjlib/src/pj++/compiletest.cpp 4     8/24/05 10:29a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjlib++.hpp>
+
+
+#if 0
+struct MyNode
+{
+    PJ_DECL_LIST_MEMBER(struct MyNode)
+    int data;
+};
+
+int test()
+{
+    typedef PJ_List<MyNode> MyList;
+    MyList list;
+    MyList::iterator it, end = list.end();
+
+    for (it=list.begin(); it!=end; ++it) {
+	MyNode *n = *it;
+    }
+
+    return 0;
+}
+
+int test_scan()
+{
+    PJ_Scanner scan;
+    PJ_String s;
+    PJ_CharSpec cs;
+
+    scan.get(&cs, &s);
+    return 0;
+}
+
+int test_scan_c()
+{
+    pj_scanner scan;
+    pj_str_t s;
+    pj_char_spec cs;
+
+    pj_scan_get(&scan, cs, &s);
+    return 0;
+}
+#endif
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/hash.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/hash.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/hash.hpp	(revision 1753)
@@ -0,0 +1,92 @@
+/* $Header: /pjproject/pjlib/src/pj++/hash.hpp 5     8/24/05 10:29a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_HASH_H__
+#define __PJPP_HASH_H__
+
+#include <pj++/types.hpp>
+#include <pj/hash.h>
+
+class PJ_Hash_Table
+{
+public:
+    class iterator
+    {
+    public:
+	iterator() {}
+	explicit iterator(pj_hash_table_t *h, pj_hash_iterator_t *i) : ht_(h), it_(i) {}
+	iterator(const iterator &rhs) : ht_(rhs.ht_), it_(rhs.it_) {}
+	void operator++() { it_ = pj_hash_next(ht_, it_); }
+	bool operator==(const iterator &rhs) { return ht_ == rhs.ht_ && it_ == rhs.it_; }
+	iterator & operator=(const iterator &rhs) { ht_=rhs.ht_; it_=rhs.it_; return *this; }
+    private:
+	pj_hash_table_t *ht_;
+	pj_hash_iterator_t it_val_;
+	pj_hash_iterator_t *it_;
+
+	friend class PJ_Hash_Table;
+    };
+
+    static PJ_Hash_Table *create(PJ_Pool *pool, unsigned size)
+    {
+	return (PJ_Hash_Table*) pj_hash_create(pool->pool_(), size);
+    }
+
+    static pj_uint32_t calc(pj_uint32_t initial_hval, const void *key, unsigned keylen)
+    {
+	return pj_hash_calc(initial_hval, key, keylen);
+    }
+
+    pj_hash_table_t *hash_table_()
+    {
+	return (pj_hash_table_t*)this;
+    }
+
+    void *get(const void *key, unsigned keylen)
+    {
+	return pj_hash_get(this->hash_table_(), key, keylen);
+    }
+
+    void set(PJ_Pool *pool, const void *key, unsigned keylen, void *value)
+    {
+	pj_hash_set(pool->pool_(), this->hash_table_(), key, keylen, value);
+    }
+
+    unsigned count()
+    {
+	return pj_hash_count(this->hash_table_());
+    }
+
+    iterator begin()
+    {
+	iterator it(this->hash_table_(), NULL);
+	it.it_ = pj_hash_first(this->hash_table_(), &it.it_val_);
+	return it;
+    }
+
+    iterator end()
+    {
+	return iterator(this->hash_table_(), NULL);
+    }
+};
+
+#endif	/* __PJPP_HASH_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/ioqueue.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/ioqueue.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/ioqueue.hpp	(revision 1753)
@@ -0,0 +1,193 @@
+/* $Header: /pjproject/pjlib/src/pj++/ioqueue.hpp 4     8/24/05 10:29a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_IOQUEUE_H__
+#define __PJPP_IOQUEUE_H__
+
+#include <pj++/sock.hpp>
+#include <pj++/pool.hpp>
+#include <pj++/types.hpp>
+#include <pj/ioqueue.h>
+
+class PJ_IOQueue;
+
+class PJ_IOQueue_Event_Handler
+{
+public:
+    virtual ~PJ_IOQueue_Event_Handler()
+    {
+    }
+
+    pj_ioqueue_key_t* get_key() const
+    {
+	return key_;
+    }
+
+protected:
+    //
+    // Override this to get notification from I/O Queue
+    //
+    virtual void on_read_complete(pj_ssize_t bytes_read)
+    {
+    }
+
+    virtual void on_write_complete(pj_ssize_t bytes_sent)
+    {
+    }
+
+    virtual void on_accept_complete(int status)
+    {
+    }
+
+    virtual void on_connect_complete(int status)
+    {
+    }
+
+protected:
+    PJ_IOQueue_Event_Handler()
+	: ioqueue_(NULL), key_(NULL)
+    {
+    }
+
+private:
+    PJ_IOQueue *ioqueue_;
+    pj_ioqueue_key_t *key_;
+
+    static void read_complete_cb(pj_ioqueue_key_t *key, pj_ssize_t bytes_read)
+    {
+	PJ_IOQueue_Event_Handler *handler = 
+	    (PJ_IOQueue_Event_Handler*)pj_ioqueue_get_user_data(key);
+	handler->on_read_complete(bytes_read);
+    }
+
+    static void write_complete_cb(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent);
+    static void accept_complete_cb(pj_ioqueue_key_t *key, int status);
+    static void connect_complete_cb(pj_ioqueue_key_t *key, int status);
+
+    friend class PJ_IOQueue;
+};
+
+
+class PJ_IOQueue
+{
+    typedef pj_ioqueue_t *B_;
+
+public:
+    typedef pj_ioqueue_key_t Key;
+
+    enum Operation
+    {
+	OP_NONE	     = PJ_IOQUEUE_OP_NONE,
+	OP_READ	     = PJ_IOQUEUE_OP_READ,
+	OP_RECV_FROM = PJ_IOQUEUE_OP_RECV_FROM,
+	OP_WRITE     = PJ_IOQUEUE_OP_WRITE,
+	OP_SEND_TO   = PJ_IOQUEUE_OP_SEND_TO,
+#if PJ_HAS_TCP
+	OP_ACCEPT    = PJ_IOQUEUE_OP_ACCEPT,
+	OP_CONNECT   = PJ_IOQUEUE_OP_CONNECT,
+#endif
+    };
+
+    enum Status
+    {
+	IS_PENDING = PJ_IOQUEUE_PENDING
+    };
+
+    static PJ_IOQueue *create(PJ_Pool *pool, pj_size_t max_fd)
+    {
+	return (PJ_IOQueue*) pj_ioqueue_create(pool->pool_(), max_fd);
+    }
+
+    operator B_()
+    {
+	return (pj_ioqueue_t*)(PJ_IOQueue*)this;
+    }
+
+    pj_ioqueue_t *ioq_()
+    {
+	return (B_)this;
+    }
+
+    void destroy()
+    {
+	pj_ioqueue_destroy(this->ioq_());
+    }
+
+    Key *register_handle(PJ_Pool *pool, pj_oshandle_t hnd, void *user_data)
+    {
+	return pj_ioqueue_register(pool->pool_(), this->ioq_(), hnd, user_data);
+    }
+
+    Key *register_socket(PJ_Pool *pool, pj_sock_t hnd, void *user_data)
+    {
+	return pj_ioqueue_register(pool->pool_(), this->ioq_(), (pj_oshandle_t)hnd, user_data);
+    }
+
+    pj_status_t unregister(Key *key)
+    {
+	return pj_ioqueue_unregister(this->ioq_(), key);
+    }
+
+    void *get_user_data(Key *key)
+    {
+	return pj_ioqueue_get_user_data(key);
+    }
+
+    int poll(Key **key, pj_ssize_t *bytes_status, Operation *op, const PJ_Time_Val *timeout)
+    {
+	return pj_ioqueue_poll(this->ioq_(), key, bytes_status, (pj_ioqueue_operation_e*)op, timeout);
+    }
+
+#if PJ_HAS_TCP
+    pj_status_t connect(Key *key, const pj_sockaddr_t *addr, int addrlen)
+    {
+	return pj_ioqueue_connect(this->ioq_(), key, addr, addrlen);
+    }
+
+    pj_status_t accept(Key *key, PJ_Socket *sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen)
+    {
+	return pj_ioqueue_accept(this->ioq_(), key, &sock->get_handle(), local, remote, addrlen);
+    }
+#endif
+
+    int read(Key *key, void *buf, pj_size_t len)
+    {
+	return pj_ioqueue_read(this->ioq_(), key, buf, len);
+    }
+
+    int recvfrom(Key *key, void *buf, pj_size_t len, pj_sockaddr_t *addr, int *addrlen)
+    {
+	return pj_ioqueue_recvfrom(this->ioq_(), key, buf, len, addr, addrlen);
+    }
+
+    int write(Key *key, const void *data, pj_size_t len)
+    {
+	return pj_ioqueue_write(this->ioq_(), key, data, len);
+    }
+
+    int sendto(Key *key, const void *data, pj_size_t len, const pj_sockaddr_t *addr, int addrlen)
+    {
+	return pj_ioqueue_sendto(this->ioq_(), key, data, len, addr, addrlen);
+    }
+};
+
+#endif	/* __PJPP_IOQUEUE_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/list.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/list.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/list.hpp	(revision 1753)
@@ -0,0 +1,203 @@
+/* $Header: /pjproject/pjlib/src/pj++/list.hpp 2     2/24/05 11:23a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_LIST_H__
+#define __PJPP_LIST_H__
+
+#include <pj/list.h>
+
+template <typename T>
+struct PJ_List_Node
+{
+    PJ_DECL_LIST_MEMBER(T)
+};
+
+
+template <class Node>
+class PJ_List
+{
+public:
+    PJ_List() { pj_list_init(&root_); if (0) compiletest(); }
+    ~PJ_List() {}
+
+    class const_iterator
+    {
+    public:
+	const_iterator() : node_(NULL) {}
+	const_iterator(const Node *nd) : node_((Node*)nd) {}
+	const Node * operator *() { return node_; }
+	const Node * operator -> () { return node_; }
+	const_iterator operator++() { return const_iterator(node_->next); }
+	bool operator==(const const_iterator &rhs) { return node_ == rhs.node_; }
+	bool operator!=(const const_iterator &rhs) { return node_ != rhs.node_; }
+
+    protected:
+	Node *node_;
+    };
+
+    class iterator : public const_iterator
+    {
+    public:
+	iterator() {}
+	iterator(Node *nd) : const_iterator(nd) {}
+	Node * operator *() { return node_; }
+	Node * operator -> () { return node_; }
+	iterator operator++() { return iterator(node_->next); }
+	bool operator==(const iterator &rhs) { return node_ == rhs.node_; }
+	bool operator!=(const iterator &rhs) { return node_ != rhs.node_; }
+    };
+
+    bool empty() const
+    {
+	return pj_list_empty(&root_);
+    }
+
+    iterator begin()
+    {
+	return iterator(root_.next);
+    }
+
+    const_iterator begin() const
+    {
+	return const_iterator(root_.next);
+    }
+
+    const_iterator end() const
+    {
+	return const_iterator((Node*)&root_);
+    }
+
+    iterator end()
+    {
+	return iterator((Node*)&root_);
+    }
+
+    void insert_before (iterator &pos, Node *node)
+    {
+	pj_list_insert_before( *pos, node );
+    }
+
+    void insert_after(iterator &pos, Node *node)
+    {
+	pj_list_insert_after(*pos, node);
+    }
+
+    void merge_first(Node *list2)
+    {
+	pj_list_merge_first(&root_, list2);
+    }
+
+    void merge_last(PJ_List *list)
+    {
+	pj_list_merge_last(&root_, &list->root_);
+    }
+
+    void insert_nodes_before(iterator &pos, PJ_List *list2)
+    {
+	pj_list_insert_nodes_before(*pos, &list2->root_);
+    }
+
+    void insert_nodes_after(iterator &pos, PJ_List *list2)
+    {
+	pj_list_insert_nodes_after(*pos, &list2->root_);
+    }
+
+    void erase(iterator &it)
+    {
+	pj_list_erase(*it);
+    }
+
+    Node *front()
+    {
+	return root_.next;
+    }
+
+    const Node *front() const
+    {
+	return root_.next;
+    }
+
+    void pop_front()
+    {
+	pj_list_erase(root_.next);
+    }
+
+    Node *back()
+    {
+	return root_.prev;
+    }
+
+    const Node *back() const
+    {
+	return root_.prev;
+    }
+
+    void pop_back()
+    {
+	pj_list_erase(root_.prev);
+    }
+
+    iterator find(Node *node)
+    {
+	Node *n = pj_list_find_node(&root_, node);
+	return n ? iterator(n) : end();
+    }
+
+    const_iterator find(Node *node) const
+    {
+	Node *n = pj_list_find_node(&root_, node);
+	return n ? const_iterator(n) : end();
+    }
+
+    void push_back(Node *node)
+    {
+	pj_list_insert_after(root_.prev, node);
+    }
+
+    void push_front(Node *node)
+    {
+	pj_list_insert_before(root_.next, node);
+    }
+
+    void clear()
+    {
+	root_.next = &root_;
+	root_.prev = &root_;
+    }
+
+private:
+    struct RootNode
+    {
+	PJ_DECL_LIST_MEMBER(Node)
+    } root_;
+
+    void compiletest()
+    {
+	// If you see error in this line, 
+	// it's because Node is not derived from PJ_List_Node.
+	Node *n = (Node*)0;
+	n = n->next; n = n->prev;
+    }
+};
+
+
+#endif	/* __PJPP_LIST_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/os.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/os.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/os.hpp	(revision 1753)
@@ -0,0 +1,363 @@
+/* $Header: /pjproject/pjlib/src/pj++/os.hpp 2     2/24/05 11:23a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_OS_H__
+#define __PJPP_OS_H__
+
+#include <pj/os.h>
+#include <pj++/types.hpp>
+#include <pj++/pool.hpp>
+
+class PJ_Thread
+{
+public:
+    enum Flags
+    {
+	FLAG_SUSPENDED = PJ_THREAD_SUSPENDED
+    };
+
+    static PJ_Thread *create( PJ_Pool *pool, const char *thread_name,
+			      pj_thread_proc *proc, void *arg,
+			      pj_size_t stack_size, void *stack_ptr, 
+			      unsigned flags)
+    {
+	return (PJ_Thread*) pj_thread_create( pool->pool_(), thread_name, proc, arg, stack_size, stack_ptr, flags);
+    }
+
+    static PJ_Thread *register_current_thread(const char *name, pj_thread_desc desc)
+    {
+	return (PJ_Thread*) pj_thread_register(name, desc);
+    }
+
+    static PJ_Thread *get_current_thread()
+    {
+	return (PJ_Thread*) pj_thread_this();
+    }
+
+    static pj_status_t sleep(unsigned msec)
+    {
+	return pj_thread_sleep(msec);
+    }
+
+    static pj_status_t usleep(unsigned usec)
+    {
+	return pj_thread_usleep(usec);
+    }
+
+    pj_thread_t *pj_thread_t_()
+    {
+	return (pj_thread_t*)this;
+    }
+
+    const char *get_name()
+    {
+	return pj_thread_get_name( this->pj_thread_t_() );
+    }
+
+    pj_status_t resume()
+    {
+	return pj_thread_resume( this->pj_thread_t_() );
+    }
+
+    pj_status_t join()
+    {
+	return pj_thread_join( this->pj_thread_t_() );
+    }
+
+    pj_status_t destroy()
+    {
+	return pj_thread_destroy( this->pj_thread_t_() );
+    }
+};
+
+
+class PJ_Thread_Local
+{
+public:
+    static PJ_Thread_Local *alloc()
+    {
+	long index = pj_thread_local_alloc();
+	return index < 0 ? NULL : (PJ_Thread_Local*)index;
+    }
+    void free()
+    {
+	pj_thread_local_free( this->tls_() );
+    }
+
+    long tls_() const
+    {
+	return (long)this;
+    }
+
+    void set(void *value)
+    {
+	pj_thread_local_set( this->tls_(), value );
+    }
+
+    void *get()
+    {
+	return pj_thread_local_get( this->tls_() );
+    }
+};
+
+
+class PJ_Atomic
+{
+public:
+    static PJ_Atomic *create(PJ_Pool *pool, long initial)
+    {
+	return (PJ_Atomic*) pj_atomic_create(pool->pool_(), initial);
+    }
+
+    pj_atomic_t *pj_atomic_t_()
+    {
+	return (pj_atomic_t*)this;
+    }
+
+    pj_status_t destroy()
+    {
+	return pj_atomic_destroy( this->pj_atomic_t_() );
+    }
+
+    long set(long val)
+    {
+	return pj_atomic_set( this->pj_atomic_t_(), val);
+    }
+
+    long get()
+    {
+	return pj_atomic_get( this->pj_atomic_t_() );
+    }
+
+    long inc()
+    {
+	return pj_atomic_inc( this->pj_atomic_t_() );
+    }
+
+    long dec()
+    {
+	return pj_atomic_dec( this->pj_atomic_t_() );
+    }
+};
+
+
+class PJ_Mutex
+{
+public:
+    enum Type
+    {
+	DEFAULT = PJ_MUTEX_DEFAULT,
+	SIMPLE = PJ_MUTEX_SIMPLE,
+	RECURSE = PJ_MUTEX_RECURSE,
+    };
+
+    static PJ_Mutex *create( PJ_Pool *pool, const char *name, Type type)
+    {
+	return (PJ_Mutex*) pj_mutex_create( pool->pool_(), name, type);
+    }
+
+    pj_mutex_t *pj_mutex_()
+    {
+	return (pj_mutex_t*)this;
+    }
+
+    pj_status_t destroy()
+    {
+	return pj_mutex_destroy( this->pj_mutex_() );
+    }
+
+    pj_status_t lock()
+    {
+	return pj_mutex_lock( this->pj_mutex_() );
+    }
+
+    pj_status_t unlock()
+    {
+	return pj_mutex_unlock( this->pj_mutex_() );
+    }
+
+    pj_status_t trylock()
+    {
+	return pj_mutex_trylock( this->pj_mutex_() );
+    }
+
+#if PJ_DEBUG
+    pj_status_t is_locked()
+    {
+	return pj_mutex_is_locked( this->pj_mutex_() );
+    }
+#endif
+};
+
+
+class PJ_Semaphore
+{
+public:
+    static PJ_Semaphore *create( PJ_Pool *pool, const char *name, unsigned initial, unsigned max)
+    {
+	return (PJ_Semaphore*) pj_sem_create( pool->pool_(), name, initial, max);
+    }
+
+    pj_sem_t *pj_sem_t_()
+    {
+	return (pj_sem_t*)this;
+    }
+
+    pj_status_t destroy()
+    {
+	return pj_sem_destroy(this->pj_sem_t_());
+    }
+
+    pj_status_t wait()
+    {
+	return pj_sem_wait(this->pj_sem_t_());
+    }
+
+    pj_status_t lock()
+    {
+	return wait();
+    }
+
+    pj_status_t trywait()
+    {
+	return pj_sem_trywait(this->pj_sem_t_());
+    }
+
+    pj_status_t trylock()
+    {
+	return trywait();
+    }
+
+    pj_status_t post()
+    {
+	return pj_sem_post(this->pj_sem_t_());
+    }
+
+    pj_status_t unlock()
+    {
+	return post();
+    }
+};
+
+
+class PJ_Event
+{
+public:
+    static PJ_Event *create( PJ_Pool *pool, const char *name, bool manual_reset, bool initial)
+    {
+	return (PJ_Event*) pj_event_create(pool->pool_(), name, manual_reset, initial);
+    }
+
+    pj_event_t *pj_event_t_()
+    {
+	return (pj_event_t*)this;
+    }
+
+    pj_status_t destroy()
+    {
+	return pj_event_destroy(this->pj_event_t_());
+    }
+
+    pj_status_t wait()
+    {
+	return pj_event_wait(this->pj_event_t_());
+    }
+
+    pj_status_t trywait()
+    {
+	return pj_event_trywait(this->pj_event_t_());
+    }
+
+    pj_status_t set()
+    {
+	return pj_event_set(this->pj_event_t_());
+    }
+
+    pj_status_t pulse()
+    {
+	return pj_event_pulse(this->pj_event_t_());
+    }
+
+    pj_status_t reset()
+    {
+	return pj_event_reset(this->pj_event_t_());
+    }
+};
+
+class PJ_OS
+{
+public:
+    static pj_status_t gettimeofday( PJ_Time_Val *tv )
+    {
+	return pj_gettimeofday(tv);
+    }
+
+    static pj_status_t time_decode( const PJ_Time_Val *tv, pj_parsed_time *pt )
+    {
+	return pj_time_decode(tv, pt);
+    }
+
+    static pj_status_t time_encode(const pj_parsed_time *pt, PJ_Time_Val *tv)
+    {
+	return pj_time_encode(pt, tv);
+    }
+
+    static pj_status_t time_local_to_gmt( PJ_Time_Val *tv )
+    {
+	return pj_time_local_to_gmt( tv );
+    }
+
+    static pj_status_t time_gmt_to_local( PJ_Time_Val *tv) 
+    {
+	return pj_time_gmt_to_local( tv );
+    }
+};
+
+
+inline pj_status_t PJ_Time_Val::gettimeofday()
+{
+    return PJ_OS::gettimeofday(this);
+}
+
+inline pj_parsed_time PJ_Time_Val::decode()
+{
+    pj_parsed_time pt;
+    PJ_OS::time_decode(this, &pt);
+    return pt;
+}
+
+inline pj_status_t PJ_Time_Val::encode(const pj_parsed_time *pt)
+{
+    return PJ_OS::time_encode(pt, this);
+}
+
+inline pj_status_t PJ_Time_Val::to_gmt()
+{
+    return PJ_OS::time_local_to_gmt(this);
+}
+
+inline pj_status_t PJ_Time_Val::to_local()
+{
+    return PJ_OS::time_gmt_to_local(this);
+}
+
+#endif	/* __PJPP_OS_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/pj++.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/pj++.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/pj++.cpp	(revision 1753)
@@ -0,0 +1,36 @@
+/* $Header: /pjproject/pjlib/src/pj++/pj++.cpp 4     4/17/05 11:59a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj++/scanner.hpp>
+#include <pj++/timer.hpp>
+#include <pj/except.h>
+
+void PJ_Scanner::syntax_error_handler_throw_pj(pj_scanner *)
+{
+    PJ_THROW( PJ_Scanner::SYNTAX_ERROR );
+}
+
+void PJ_Timer_Entry::timer_heap_callback(pj_timer_heap_t *, pj_timer_entry *e)
+{
+    PJ_Timer_Entry *entry = static_cast<PJ_Timer_Entry*>(e);
+    entry->on_timeout();
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/pool.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/pool.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/pool.hpp	(revision 1753)
@@ -0,0 +1,105 @@
+/* $Header: /pjproject/pjlib/src/pj++/pool.hpp 4     8/24/05 10:29a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_POOL_H__
+#define __PJPP_POOL_H__
+
+#include <pj/pool.h>
+
+class PJ_Pool
+{
+public:
+    const char *getobjname() const
+    {
+	return pj_pool_getobjname(this->pool_());
+    }
+
+    pj_pool_t *pool_()
+    {
+	return (pj_pool_t*)this;
+    }
+
+    const pj_pool_t *pool_() const
+    {
+	return (const pj_pool_t*)this;
+    }
+
+    void release()
+    {
+	pj_pool_release(this->pool_());
+    }
+
+    void reset()
+    {
+	pj_pool_reset(this->pool_());
+    }
+
+    pj_size_t get_capacity()
+    {
+	pj_pool_get_capacity(this->pool_());
+    }
+
+    pj_size_t get_used_size()
+    {
+	pj_pool_get_used_size(this->pool_());
+    }
+
+    void *alloc(pj_size_t size)
+    {
+	return pj_pool_alloc(this->pool_(), size);
+    }
+
+    void *calloc(pj_size_t count, pj_size_t elem)
+    {
+	return pj_pool_calloc(this->pool_(), count, elem);
+    }
+};
+
+class PJ_Caching_Pool
+{
+public:
+    void init(pj_size_t max_capacity,
+	      const pj_pool_factory_policy *pol=&pj_pool_factory_default_policy)
+    {
+	pj_caching_pool_init(&cp_, pol, max_capacity);
+    }
+
+    void destroy()
+    {
+	pj_caching_pool_destroy(&cp_);
+    }
+
+    PJ_Pool *create_pool(const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback)
+    {
+	return (PJ_Pool*) (*cp_.factory.create_pool)(&cp_.factory, name, initial_size, increment_size, callback);
+    }
+
+    void release_pool( PJ_Pool *pool )
+    {
+	pj_pool_release(pool->pool_());
+    }
+
+private:
+    pj_caching_pool cp_;
+};
+
+#endif	/* __PJPP_POOL_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/proactor.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/proactor.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/proactor.cpp	(revision 1753)
@@ -0,0 +1,316 @@
+/* $Header: /pjproject/pjlib/src/pj++/proactor.cpp 4     8/24/05 10:29a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj++/proactor.hpp>
+#include <pj/string.h>	// memset
+
+static struct pj_ioqueue_callback ioqueue_cb =
+{
+    &PJ_Event_Handler::read_complete_cb,
+    &PJ_Event_Handler::write_complete_cb,
+    &PJ_Event_Handler::accept_complete_cb,
+    &PJ_Event_Handler::connect_complete_cb,
+};
+
+PJ_Event_Handler::PJ_Event_Handler()
+: proactor_(NULL), key_(NULL)
+{
+    pj_memset(&timer_, 0, sizeof(timer_));
+    timer_.user_data = this;
+    timer_.cb = &timer_callback;
+}
+
+PJ_Event_Handler::~PJ_Event_Handler()
+{
+}
+
+#if PJ_HAS_TCP
+bool PJ_Event_Handler::connect(const PJ_INET_Addr &addr)
+{
+    pj_assert(key_ != NULL && proactor_ != NULL);
+
+    if (key_ == NULL || proactor_ == NULL)
+	return false;
+
+    int status = pj_ioqueue_connect(proactor_->get_io_queue(), key_, 
+				    &addr, sizeof(PJ_INET_Addr));
+    if (status == 0) {
+	on_connect_complete(0);
+	return true;
+    } else if (status == PJ_IOQUEUE_PENDING) {
+	return true;
+    } else {
+	return false;
+    }
+}
+
+bool PJ_Event_Handler::accept(PJ_Socket *sock, PJ_INET_Addr *local, PJ_INET_Addr *remote)
+{
+    pj_assert(key_ != NULL && proactor_ != NULL);
+
+    if (key_ == NULL || proactor_ == NULL)
+	return false;
+
+    int status = pj_ioqueue_accept(proactor_->get_io_queue(), key_, 
+				   &sock->get_handle(), 
+				   local_addr, remote, 
+				   (remote? sizeof(*remote) : 0));
+    if (status == 0) {
+	on_accept_complete(0);
+	return true;
+    } else if (status == PJ_IOQUEUE_PENDING) {
+	return true;
+    } else {
+	return false;
+    }
+}
+
+#endif
+
+bool PJ_Event_Handler::read(void *buf, pj_size_t len)
+{
+    pj_assert(key_ != NULL && proactor_ != NULL);
+
+    if (key_ == NULL || proactor_ == NULL)
+	return false;
+
+    int bytes_status = pj_ioqueue_read(proactor_->get_io_queue(), 
+				       key_, buf, len);
+    if (bytes_status >= 0) {
+	on_read_complete(bytes_status);
+	return true;
+    } else if (bytes_status == PJ_IOQUEUE_PENDING) {
+	return true;
+    } else {
+	return false;
+    }
+}
+
+bool PJ_Event_Handler::recvfrom(void *buf, pj_size_t len, PJ_INET_Addr *addr)
+{
+    pj_assert(key_ != NULL && proactor_ != NULL);
+
+    if (key_ == NULL || proactor_ == NULL)
+	return false;
+
+
+    tmp_recvfrom_addr_len = sizeof(PJ_INET_Addr);
+
+    int bytes_status = pj_ioqueue_recvfrom(proactor_->get_io_queue(), 
+					   key_, buf, len,
+					   addr,
+					   (addr? &tmp_recvfrom_addr_len : NULL));
+    if (bytes_status >= 0) {
+	on_read_complete(bytes_status);
+	return true;
+    } else if (bytes_status == PJ_IOQUEUE_PENDING) {
+	return true;
+    } else {
+	return false;
+    }
+}
+
+bool PJ_Event_Handler::write(const void *data, pj_size_t len)
+{
+    pj_assert(key_ != NULL && proactor_ != NULL);
+
+    if (key_ == NULL || proactor_ == NULL)
+	return false;
+
+    int bytes_status = pj_ioqueue_write(proactor_->get_io_queue(), 
+					key_, data, len);
+    if (bytes_status >= 0) {
+	on_write_complete(bytes_status);
+	return true;
+    } else if (bytes_status == PJ_IOQUEUE_PENDING) {
+	return true;
+    } else {
+	return false;
+    }
+}
+
+bool PJ_Event_Handler::sendto(const void *data, pj_size_t len, const PJ_INET_Addr &addr)
+{
+    pj_assert(key_ != NULL && proactor_ != NULL);
+
+    if (key_ == NULL || proactor_ == NULL)
+	return false;
+
+    int bytes_status = pj_ioqueue_sendto(proactor_->get_io_queue(), 
+					 key_, data, len, 
+					 &addr, sizeof(PJ_INET_Addr));
+    if (bytes_status >= 0) {
+	on_write_complete(bytes_status);
+	return true;
+    } else if (bytes_status == PJ_IOQUEUE_PENDING) {
+	return true;
+    } else {
+	return false;
+    }
+}
+
+
+void PJ_Event_Handler::read_complete_cb(pj_ioqueue_key_t *key, pj_ssize_t bytes_read)
+{
+    PJ_Event_Handler *handler = 
+	(PJ_Event_Handler*) pj_ioqueue_get_user_data(key);
+
+    handler->on_read_complete(bytes_read);
+}
+
+void PJ_Event_Handler::write_complete_cb(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent)
+{
+    PJ_Event_Handler *handler = 
+	(PJ_Event_Handler*) pj_ioqueue_get_user_data(key);
+
+    handler->on_write_complete(bytes_sent);
+}
+
+void PJ_Event_Handler::accept_complete_cb(pj_ioqueue_key_t *key, int status)
+{
+#if PJ_HAS_TCP
+    PJ_Event_Handler *handler = 
+	(PJ_Event_Handler*) pj_ioqueue_get_user_data(key);
+
+    handler->on_accept_complete(status);
+#endif
+}
+
+void PJ_Event_Handler::connect_complete_cb(pj_ioqueue_key_t *key, int status)
+{
+#if PJ_HAS_TCP
+    PJ_Event_Handler *handler = 
+	(PJ_Event_Handler*) pj_ioqueue_get_user_data(key);
+
+    handler->on_connect_complete(status);
+#endif
+}
+
+void PJ_Event_Handler::timer_callback( pj_timer_heap_t *timer_heap,
+				       struct pj_timer_entry *entry)
+{
+    PJ_Event_Handler *handler = (PJ_Event_Handler*) entry->user_data;
+    handler->on_timeout(entry->id);
+}
+
+
+PJ_Proactor *PJ_Proactor::create(PJ_Pool *pool, pj_size_t max_fd, 
+				 pj_size_t timer_entry_count, unsigned timer_flags)
+{
+    PJ_Proactor *p = (PJ_Proactor*) pool->calloc(1, sizeof(PJ_Proactor));
+    if (!p) return NULL;
+
+    p->ioq_ = pj_ioqueue_create(pool->pool_(), max_fd);
+    if (!p->ioq_) return NULL;
+
+    p->th_ = pj_timer_heap_create(pool->pool_(), timer_entry_count, timer_flags);
+    if (!p->th_) return NULL;
+
+    return p;
+}
+
+void PJ_Proactor::destroy()
+{
+    pj_ioqueue_destroy(ioq_);
+}
+
+bool PJ_Proactor::register_handler(PJ_Pool *pool, PJ_Event_Handler *handler)
+{
+    pj_assert(handler->key_ == NULL && handler->proactor_ == NULL);
+
+    if (handler->key_ != NULL) 
+	return false;
+
+    handler->key_ = pj_ioqueue_register(pool->pool_(), ioq_, handler->get_handle(), 
+					handler, &ioqueue_cb);
+    if (handler->key_ != NULL) {
+	handler->proactor_ = this;
+	return true;
+    } else {
+	return false;
+    }
+}
+
+void PJ_Proactor::unregister_handler(PJ_Event_Handler *handler)
+{
+    if (handler->key_ == NULL) return;
+    pj_ioqueue_unregister(ioq_, handler->key_);
+    handler->key_ = NULL;
+    handler->proactor_ = NULL;
+}
+
+bool PJ_Proactor::schedule_timer( pj_timer_heap_t *timer, PJ_Event_Handler *handler,
+				  const PJ_Time_Val &delay, int id)
+{
+    handler->timer_.id = id;
+    return pj_timer_heap_schedule(timer, &handler->timer_, &delay) == 0;
+}
+
+bool PJ_Proactor::schedule_timer(PJ_Event_Handler *handler, const PJ_Time_Val &delay, 
+				 int id)
+{
+    return schedule_timer(th_, handler, delay, id);
+}
+
+bool PJ_Proactor::cancel_timer(PJ_Event_Handler *handler)
+{
+    return pj_timer_heap_cancel(th_, &handler->timer_) == 1;
+}
+
+bool PJ_Proactor::handle_events(PJ_Time_Val *max_timeout)
+{
+    pj_time_val timeout;
+
+    timeout.sec = timeout.msec = 0; /* timeout is 'out' var. */
+
+    if (pj_timer_heap_poll( th_, &timeout ) > 0)
+	return true;
+
+    if (timeout.sec < 0) timeout.sec = PJ_MAXLONG;
+
+    /* If caller specifies maximum time to wait, then compare the value with
+     * the timeout to wait from timer, and use the minimum value.
+     */
+    if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
+	timeout = *max_timeout;
+    }
+
+    /* Poll events in ioqueue. */
+    int result;
+
+    result = pj_ioqueue_poll(ioq_, &timeout);
+    if (result != 1)
+	return false;
+
+    return true;
+}
+
+pj_ioqueue_t *PJ_Proactor::get_io_queue()
+{
+    return ioq_;
+}
+
+pj_timer_heap_t *PJ_Proactor::get_timer_heap()
+{
+    return th_;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/proactor.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/proactor.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/proactor.hpp	(revision 1753)
@@ -0,0 +1,107 @@
+/* $Header: /pjproject/pjlib/src/pj++/proactor.hpp 3     8/24/05 10:29a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_EVENT_HANDLER_H__
+#define __PJPP_EVENT_HANDLER_H__
+
+#include <pj/ioqueue.h>
+#include <pj++/pool.hpp>
+#include <pj++/sock.hpp>
+#include <pj++/timer.hpp>
+
+class PJ_Proactor;
+
+
+class PJ_Event_Handler
+{
+    friend class PJ_Proactor;
+public:
+    PJ_Event_Handler();
+    virtual ~PJ_Event_Handler();
+
+    virtual pj_oshandle_t get_handle() = 0;
+
+    bool read(void *buf, pj_size_t len);
+    bool recvfrom(void *buf, pj_size_t len, PJ_INET_Addr *addr);
+    bool write(const void *data, pj_size_t len);
+    bool sendto(const void *data, pj_size_t len, const PJ_INET_Addr &addr);
+#if PJ_HAS_TCP
+    bool connect(const PJ_INET_Addr &addr);
+    bool accept(PJ_Socket *sock, PJ_INET_Addr *local=NULL, PJ_INET_Addr *remote=NULL);
+#endif
+
+protected:
+    //
+    // Overridables
+    //
+    virtual void on_timeout(int data) {}
+    virtual void on_read_complete(pj_ssize_t bytes_read) {}
+    virtual void on_write_complete(pj_ssize_t bytes_sent) {}
+#if PJ_HAS_TCP
+    virtual void on_connect_complete(int status) {}
+    virtual void on_accept_complete(int status) {}
+#endif
+
+private:
+    PJ_Proactor	     *proactor_;
+    pj_ioqueue_key_t *key_;
+    pj_timer_entry    timer_;
+    int		      tmp_recvfrom_addr_len;
+
+public:
+    // Internal IO Queue/timer callback.
+    static void timer_callback( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry);
+    static void read_complete_cb(pj_ioqueue_key_t *key, pj_ssize_t bytes_read);
+    static void write_complete_cb(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent);
+    static void accept_complete_cb(pj_ioqueue_key_t *key, int status);
+    static void connect_complete_cb(pj_ioqueue_key_t *key, int status);
+};
+
+class PJ_Proactor
+{
+public:
+    static PJ_Proactor *create(PJ_Pool *pool, pj_size_t max_fd, 
+			       pj_size_t timer_entry_count, unsigned timer_flags=0);
+
+    void destroy();
+
+    bool register_handler(PJ_Pool *pool, PJ_Event_Handler *handler);
+    void unregister_handler(PJ_Event_Handler *handler);
+
+    static bool schedule_timer( pj_timer_heap_t *timer, PJ_Event_Handler *handler,
+				const PJ_Time_Val &delay, int id=-1);
+    bool schedule_timer(PJ_Event_Handler *handler, const PJ_Time_Val &delay, int id=-1);
+    bool cancel_timer(PJ_Event_Handler *handler);
+
+    bool handle_events(PJ_Time_Val *timeout);
+
+    pj_ioqueue_t *get_io_queue();
+    pj_timer_heap_t *get_timer_heap();
+
+private:
+    pj_ioqueue_t *ioq_;
+    pj_timer_heap_t *th_;
+
+    PJ_Proactor() {}
+};
+
+#endif	/* __PJPP_EVENT_HANDLER_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/scanner.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/scanner.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/scanner.hpp	(revision 1753)
@@ -0,0 +1,192 @@
+/* $Header: /pjproject/pjlib/src/pj++/scanner.hpp 3     2/27/05 10:09p Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_SCANNER_H__
+#define __PJPP_SCANNER_H__
+
+#include <pj/scanner.h>
+#include <pj++/string.hpp>
+
+class PJ_CharSpec
+{
+public:
+    PJ_CharSpec() { pj_cs_init(cs__); }
+
+    void set(int c) { pj_cs_set(cs__, c); }
+    void add_range(int begin, int end) { pj_cs_add_range(cs__, begin, end); }
+    void add_alpha() { pj_cs_add_alpha(cs__); }
+    void add_num() { pj_cs_add_num(cs__); }
+    void add_str(const char *str) { pj_cs_add_str(cs__, str); }
+    void del_range(int begin, int end) { pj_cs_del_range(cs__, begin, end); }
+    void del_str(const char *str) { pj_cs_del_str(cs__, str); }
+    void invert() { pj_cs_invert(cs__); }
+    int  match(int c) { return pj_cs_match(cs__, c); }
+
+    pj_char_spec_element_t *cs_()
+    {
+	return cs__;
+    }
+
+    const pj_char_spec_element_t *cs_() const
+    {
+	return cs__;
+    }
+
+private:
+    pj_char_spec cs__;
+};
+
+class PJ_Scanner
+{
+public:
+    PJ_Scanner() {}
+
+    enum
+    {
+	SYNTAX_ERROR = 101
+    };
+    static void syntax_error_handler_throw_pj(pj_scanner *);
+
+    typedef pj_scan_state State;
+
+    void init(char *buf, int len, unsigned options=PJ_SCAN_AUTOSKIP_WS, 
+	      pj_syn_err_func_ptr callback = &syntax_error_handler_throw_pj)
+    {
+	pj_scan_init(&scanner_, buf, len, options, callback);
+    }
+
+    void fini()
+    {
+	pj_scan_fini(&scanner_);
+    }
+
+    int eof() const
+    {
+	return pj_scan_is_eof(&scanner_);
+    }
+
+    int peek_char() const
+    {
+	return *scanner_.current;
+    }
+
+    int peek(const PJ_CharSpec *cs, PJ_String *out)
+    {
+	return pj_scan_peek(&scanner_,  cs->cs_(), out);
+    }
+
+    int peek_n(pj_size_t len, PJ_String *out)
+    {
+	return pj_scan_peek_n(&scanner_, len, out);
+    }
+
+    int peek_until(const PJ_CharSpec *cs, PJ_String *out)
+    {
+	return pj_scan_peek_until(&scanner_, cs->cs_(), out);
+    }
+
+    void get(const PJ_CharSpec *cs, PJ_String *out)
+    {
+	pj_scan_get(&scanner_, cs->cs_(), out);
+    }
+
+    void get_n(unsigned N, PJ_String *out)
+    {
+	pj_scan_get_n(&scanner_, N, out);
+    }
+
+    int get_char()
+    {
+	return pj_scan_get_char(&scanner_);
+    }
+
+    void get_quote(int begin_quote, int end_quote, PJ_String *out)
+    {
+	pj_scan_get_quote(&scanner_, begin_quote, end_quote, out);
+    }
+
+    void get_newline()
+    {
+	pj_scan_get_newline(&scanner_);
+    }
+
+    void get_until(const PJ_CharSpec *cs, PJ_String *out)
+    {
+	pj_scan_get_until(&scanner_, cs->cs_(), out);
+    }
+
+    void get_until_ch(int until_ch, PJ_String *out)
+    {
+	pj_scan_get_until_ch(&scanner_, until_ch, out);
+    }
+
+    void get_until_chr(const char *spec, PJ_String *out)
+    {
+	pj_scan_get_until_chr(&scanner_, spec, out);
+    }
+
+    void advance_n(unsigned N, bool skip_ws=true)
+    {
+	pj_scan_advance_n(&scanner_, N, skip_ws);
+    }
+
+    int strcmp(const char *s, int len)
+    {
+	return pj_scan_strcmp(&scanner_, s, len);
+    }
+
+    int stricmp(const char *s, int len)
+    {
+	return pj_scan_stricmp(&scanner_, s, len);
+    }
+
+    void skip_ws()
+    {
+	pj_scan_skip_whitespace(&scanner_);
+    }
+
+    void save_state(State *state)
+    {
+	pj_scan_save_state(&scanner_, state);
+    }
+
+    void restore_state(State *state)
+    {
+	pj_scan_restore_state(&scanner_, state);
+    }
+
+    int get_pos_line() const
+    {
+	return scanner_.line;
+    }
+
+    int get_pos_col() const
+    {
+	return scanner_.col;
+    }
+
+
+private:
+    pj_scanner scanner_;
+};
+
+#endif	/* __PJPP_SCANNER_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/sock.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/sock.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/sock.hpp	(revision 1753)
@@ -0,0 +1,215 @@
+/* $Header: /pjproject/pjlib/src/pj++/sock.hpp 2     2/24/05 11:23a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_SOCK_H__
+#define __PJPP_SOCK_H__
+
+#include <pj/sock.h>
+
+class PJ_Addr
+{
+};
+
+class PJ_INET_Addr : public pj_sockaddr_in, public PJ_Addr
+{
+public:
+    pj_uint16_t get_port_number() const
+    {
+	return pj_sockaddr_get_port(this);
+    }
+
+    void set_port_number(pj_uint16_t port)
+    {
+	sin_family = PJ_AF_INET;
+	pj_sockaddr_set_port(this, port);
+    }
+
+    pj_uint32_t get_ip_address() const
+    {
+	return pj_sockaddr_get_addr(this);
+    }
+
+    const char *get_address() const
+    {
+	return pj_sockaddr_get_str_addr(this);
+    }
+
+    void set_ip_address(pj_uint32_t addr)
+    {
+	sin_family = PJ_AF_INET;
+	pj_sockaddr_set_addr(this, addr);
+    }
+
+    pj_status_t set_address(const pj_str_t *addr)
+    {
+	return pj_sockaddr_set_str_addr(this, addr);
+    }
+
+    pj_status_t set_address(const char *addr)
+    {
+	return pj_sockaddr_set_str_addr2(this, addr);
+    }
+
+    int cmp(const PJ_INET_Addr &rhs) const
+    {
+	return pj_sockaddr_cmp(this, &rhs);
+    }
+
+    bool operator==(const PJ_INET_Addr &rhs) const
+    {
+	return cmp(rhs) == 0;
+    }
+};
+
+class PJ_Socket
+{
+public:
+    PJ_Socket() {}
+    PJ_Socket(const PJ_Socket &rhs) : sock_(rhs.sock_) {}
+
+    void set_handle(pj_sock_t sock)
+    {
+	sock_ = sock;
+    }
+
+    pj_sock_t get_handle() const
+    {
+	return sock_;
+    }
+
+    pj_sock_t& get_handle()
+    {
+	return sock_;
+    }
+
+    bool socket(int af, int type, int proto, pj_uint32_t flag=0)
+    {
+	sock_ = pj_sock_socket(af, type, proto, flag);
+	return sock_ != -1;
+    }
+
+    bool bind(const PJ_INET_Addr &addr)
+    {
+	return pj_sock_bind(sock_, &addr, sizeof(PJ_INET_Addr)) == 0;
+    }
+
+    bool close()
+    {
+	return pj_sock_close(sock_) == 0;
+    }
+
+    bool getpeername(PJ_INET_Addr *addr)
+    {
+	int namelen;
+	return pj_sock_getpeername(sock_, addr, &namelen) == 0;
+    }
+
+    bool getsockname(PJ_INET_Addr *addr)
+    {
+	int namelen;
+	return pj_sock_getsockname(sock_, addr, &namelen) == 0;
+    }
+
+    bool getsockopt(int level, int optname, void *optval, int *optlen)
+    {
+	return pj_sock_getsockopt(sock_, level, optname, optval, optlen) == 0;
+    }
+
+    bool setsockopt(int level, int optname, const void *optval, int optlen)
+    {
+	return pj_sock_setsockopt(sock_, level, optname, optval, optlen) == 0;
+    }
+
+    bool ioctl(long cmd, pj_uint32_t *val)
+    {
+	return pj_sock_ioctl(sock_, cmd, val) == 0;
+    }
+
+    int recv(void *buf, int len, int flag = 0)
+    {
+	return pj_sock_recv(sock_, buf, len, flag);
+    }
+
+    int send(const void *buf, int len, int flag = 0)
+    {
+	return pj_sock_send(sock_, buf, len, flag);
+    }
+
+protected:
+    pj_sock_t sock_;
+};
+
+#if PJ_HAS_TCP
+class PJ_Sock_Stream : public PJ_Socket
+{
+public:
+    PJ_Sock_Stream() {}
+    PJ_Sock_Stream(const PJ_Sock_Stream &rhs) : PJ_Socket(rhs) {}
+    PJ_Sock_Stream &operator=(const PJ_Sock_Stream &rhs) { sock_ = rhs.sock_; return *this; }
+
+    bool listen(int backlog = 5)
+    {
+	return pj_sock_listen(sock_, backlog) == 0;
+    }
+
+    bool accept(PJ_Sock_Stream *new_sock, PJ_INET_Addr *addr, int *addrlen)
+    {
+	pj_sock_t s = pj_sock_accept(sock_, addr, addrlen);
+	if (s == -1)
+	    return false;
+	new_sock->set_handle(s);
+	return true;
+    }
+
+    bool connect(const PJ_INET_Addr &addr)
+    {
+	return pj_sock_connect(sock_, &addr, sizeof(PJ_INET_Addr)) == 0;
+    }
+
+    bool shutdown(int how)
+    {
+	return pj_sock_shutdown(sock_, how) == 0;
+    }
+
+};
+#endif
+
+class PJ_Sock_Dgram : public PJ_Socket
+{
+public:
+    PJ_Sock_Dgram() {}
+    PJ_Sock_Dgram(const PJ_Sock_Dgram &rhs) : PJ_Socket(rhs) {}
+    PJ_Sock_Dgram &operator=(const PJ_Sock_Dgram &rhs) { sock_ = rhs.sock_; return *this; }
+
+    int recvfrom(void *buf, int len, int flag, PJ_INET_Addr *fromaddr)
+    {
+	int addrlen;
+	return pj_sock_recvfrom(sock_, buf, len, flag, fromaddr, &addrlen);
+    }
+
+    int sendto(const void *buf, int len, int flag, const PJ_INET_Addr &addr)
+    {
+	return pj_sock_sendto(sock_, buf, len, flag, &addr, sizeof(PJ_INET_Addr));
+    }
+};
+
+#endif	/* __PJPP_SOCK_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/string.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/string.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/string.hpp	(revision 1753)
@@ -0,0 +1,268 @@
+/* $Header: /pjproject/pjlib/src/pj++/string.hpp 2     2/24/05 11:23a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_STRING_H__
+#define __PJPP_STRING_H__
+
+#include <pj/string.h>
+#include <pj++/pool.hpp>
+
+class PJ_String : public pj_str_t
+{
+public:
+    PJ_String() 
+    { 
+	pj_assert(sizeof(PJ_String) == sizeof(pj_str_t));
+	ptr=NULL; slen=0; 
+    }
+
+    explicit PJ_String(char *str) 
+    { 
+	set(str);
+    }
+
+    PJ_String(PJ_Pool *pool, const char *src)
+    {
+	set(pool, src);
+    }
+
+    explicit PJ_String(pj_str_t *s)
+    {
+	set(s);
+    }
+
+    PJ_String(PJ_Pool *pool, const pj_str_t *s)
+    {
+	set(pool, s);
+    }
+
+    explicit PJ_String(PJ_String &rhs)
+    {
+	set(rhs);
+    }
+
+    PJ_String(PJ_Pool *pool, const PJ_String &rhs)
+    {
+	set(pool, rhs);
+    }
+
+    PJ_String(char *str, pj_size_t len)
+    {
+	set(str, len);
+    }
+
+    PJ_String(char *begin, char *end)
+    {
+	pj_strset3(this, begin, end);
+    }
+
+    pj_size_t length() const
+    {
+	return pj_strlen(this);
+    }
+
+    pj_size_t size() const
+    {
+	return length();
+    }
+
+    const char *buf() const
+    {
+	return ptr;
+    }
+
+    void set(char *str)
+    {
+	pj_strset2(this, str);
+    }
+
+    void set(PJ_Pool *pool, const char *s)
+    {
+	pj_strdup2(pool->pool_(), this, s);
+    }
+
+    void set(pj_str_t *s)
+    {
+	pj_strassign(this, s);
+    }
+
+    void set(PJ_Pool *pool, const pj_str_t *s)
+    {
+	pj_strdup(pool->pool_(), this, s);
+    }
+
+    void set(char *str, pj_size_t len)
+    {
+	pj_strset(this, str, len);
+    }
+
+    void set(char *begin, char *end)
+    {
+	pj_strset3(this, begin, end);
+    }
+
+    void set(PJ_String &rhs)
+    {
+	pj_strassign(this, &rhs);
+    }
+
+    void set(PJ_Pool *pool, const PJ_String *s)
+    {
+	pj_strdup(pool->pool_(), this, s);
+    }
+
+    void set(PJ_Pool *pool, const PJ_String &s)
+    {
+	pj_strdup(pool->pool_(), this, &s);
+    }
+
+    void strcpy(const pj_str_t *s)
+    {
+	pj_strcpy(this, s);
+    }
+
+    void strcpy(const PJ_String &rhs)
+    {
+	pj_strcpy(this, &rhs);
+    }
+
+    void strcpy(const char *s)
+    {
+	pj_strcpy2(this, s);
+    }
+
+    int strcmp(const char *s) const
+    {
+	return pj_strcmp2(this, s);
+    }
+
+    int strcmp(const pj_str_t *s) const
+    {
+	return pj_strcmp(this, s);
+    }
+
+    int strcmp(const PJ_String &rhs) const
+    {
+	return pj_strcmp(this, &rhs);
+    }
+
+    int strncmp(const char *s, pj_size_t len) const
+    {
+	return pj_strncmp2(this, s, len);
+    }
+
+    int strncmp(const pj_str_t *s, pj_size_t len) const
+    {
+	return pj_strncmp(this, s, len);
+    }
+
+    int strncmp(const PJ_String &rhs, pj_size_t len) const
+    {
+	return pj_strncmp(this, &rhs, len);
+    }
+
+    int stricmp(const char *s) const
+    {
+	return pj_stricmp2(this, s);
+    }
+
+    int stricmp(const pj_str_t *s) const
+    {
+	return pj_stricmp(this, s);
+    }
+
+    int stricmp(const PJ_String &rhs) const
+    {
+	return stricmp(&rhs);
+    }
+
+    int strnicmp(const char *s, pj_size_t len) const
+    {
+	return pj_strnicmp2(this, s, len);
+    }
+
+    int strnicmp(const pj_str_t *s, pj_size_t len) const
+    {
+	return pj_strnicmp(this, s, len);
+    }
+
+    int strnicmp(const PJ_String &rhs, pj_size_t len) const
+    {
+	return strnicmp(&rhs, len);
+    }
+
+    bool operator==(const char *s) const
+    {
+	return strcmp(s) == 0;
+    }
+
+    bool operator==(const pj_str_t *s) const
+    {
+	return strcmp(s) == 0;
+    }
+
+    bool operator==(const PJ_String &rhs) const
+    {
+	return pj_strcmp(this, &rhs) == 0;
+    }
+
+    char *strchr(int chr)
+    {
+	return pj_strchr(this, chr);
+    }
+
+    char *find(int chr)
+    {
+	return strchr(chr);
+    }
+
+    void strcat(const PJ_String &rhs)
+    {
+	pj_strcat(this, &rhs);
+    }
+
+    void ltrim()
+    {
+	pj_strltrim(this);
+    }
+
+    void rtrim()
+    {
+	pj_strrtrim(this);
+    }
+
+    void trim()
+    {
+	pj_strtrim(this);
+    }
+
+    unsigned long toul() const
+    {
+	return pj_strtoul(this);
+    }
+
+private:
+    //PJ_String(const PJ_String &rhs) {}
+    void operator=(const PJ_String &rhs) { pj_assert(false); }
+};
+
+#endif	/* __PJPP_STRING_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/timer.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/timer.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/timer.hpp	(revision 1753)
@@ -0,0 +1,126 @@
+/* $Header: /pjproject/pjlib/src/pj++/timer.hpp 4     8/24/05 10:29a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_TIMER_H__
+#define __PJPP_TIMER_H__
+
+#include <pj/timer.h>
+#include <pj++/types.hpp>
+
+class PJ_Timer_Heap;
+
+class PJ_Timer_Entry : private pj_timer_entry
+{
+    friend class PJ_Timer_Heap;
+
+public:
+    static void timer_heap_callback(pj_timer_heap_t *, pj_timer_entry *);
+
+    PJ_Timer_Entry() { cb = &timer_heap_callback; }
+    PJ_Timer_Entry(int arg_id, void *arg_user_data)
+    {
+	cb = &timer_heap_callback; 
+	init(arg_id, arg_user_data);
+    }
+
+    virtual void on_timeout() = 0;
+
+    void init(int arg_id, void *arg_user_data)
+    {
+	id = arg_id;
+	user_data = arg_user_data;
+    }
+
+    int get_id() const
+    {
+	return id;
+    }
+
+    void set_id(int arg_id)
+    {
+	id = arg_id;
+    }
+
+    void set_user_data(void *arg_user_data)
+    {
+	user_data = arg_user_data;
+    }
+
+    void *get_user_data() const
+    {
+	return user_data;
+    }
+
+    const PJ_Time_Val &get_timeout() const
+    {
+	pj_assert(sizeof(PJ_Time_Val) == sizeof(pj_time_val));
+	return (PJ_Time_Val&)_timer_value;
+    }
+};
+
+class PJ_Timer_Heap
+{
+public:
+    PJ_Timer_Heap() {}
+
+    bool create(PJ_Pool *pool, pj_size_t initial_count, 
+		unsigned flag = PJ_TIMER_HEAP_SYNCHRONIZE)
+    {
+	ht_ = pj_timer_heap_create(pool->pool_(), initial_count, flag);
+	return ht_ != NULL;
+    }
+
+    pj_timer_heap_t *get_timer_heap()
+    {
+	return ht_;
+    }
+
+    bool schedule( PJ_Timer_Entry *ent, const PJ_Time_Val &delay)
+    {
+	return pj_timer_heap_schedule(ht_, ent, &delay) == 0;
+    }
+
+    bool cancel(PJ_Timer_Entry *ent)
+    {
+	return pj_timer_heap_cancel(ht_, ent) == 1;
+    }
+
+    pj_size_t count()
+    {
+	return pj_timer_heap_count(ht_);
+    }
+
+    void earliest_time(PJ_Time_Val *t)
+    {
+	pj_timer_heap_earliest_time(ht_, t);
+    }
+
+    int poll(PJ_Time_Val *next_delay = NULL)
+    {
+	return pj_timer_heap_poll(ht_, next_delay);
+    }
+
+private:
+    pj_timer_heap_t *ht_;
+};
+
+#endif	/* __PJPP_TIMER_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/tree.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/tree.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/tree.hpp	(revision 1753)
@@ -0,0 +1,128 @@
+/* $Header: /pjproject/pjlib/src/pj++/tree.hpp 2     2/24/05 11:23a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_TREE_H__
+#define __PJPP_TREE_H__
+
+#include <pj/rbtree.h>
+
+class PJ_Tree
+{
+public:
+    typedef pj_rbtree_comp Comp;
+    class iterator;
+    class reverse_iterator;
+
+    class Node : private pj_rbtree_node
+    {
+	friend class PJ_Tree;
+	friend class iterator;
+	friend class reverse_iterator;
+
+    public:
+	Node() {}
+	explicit Node(void *data) { user_data = data; }
+	void  set_user_data(void *data) { user_data = data; }
+	void *get_user_data() const { return user_data; }
+    };
+
+    class iterator
+    {
+    public:
+	iterator() {}
+	iterator(const iterator &rhs) : tr_(rhs.tr_), nd_(rhs.nd_) {}
+	iterator(pj_rbtree *tr, pj_rbtree_node *nd) : tr_(tr), nd_(nd) {}
+	Node *operator*() { return (Node*)nd_; }
+	bool operator==(const iterator &rhs) const { return tr_==rhs.tr_ && nd_==rhs.nd_; }
+	iterator &operator=(const iterator &rhs) { tr_=rhs.tr_; nd_=rhs.nd_; return *this; }
+	void operator++() { nd_=pj_rbtree_next(tr_, nd_); }
+	void operator--() { nd_=pj_rbtree_prev(tr_, nd_); }
+    protected:
+	pj_rbtree *tr_;
+	pj_rbtree_node *nd_;
+    };
+
+    class reverse_iterator : public iterator
+    {
+    public:
+	reverse_iterator() {}
+	reverse_iterator(const reverse_iterator &it) : iterator(it) {}
+	reverse_iterator(pj_rbtree *t, pj_rbtree_node *n) : iterator(t, n) {}
+	reverse_iterator &operator=(const reverse_iterator &rhs) { iterator::operator=(rhs); return *this; }
+	Node *operator*() { return (Node*)nd_; }
+	bool operator==(const reverse_iterator &rhs) const { return iterator::operator==(rhs); }
+	void operator++() { nd_=pj_rbtree_prev(tr_, nd_); }
+	void operator--() { nd_=pj_rbtree_next(tr_, nd_); }
+    };
+
+    explicit PJ_Tree(Comp *comp) { pj_rbtree_init(&t_, comp); }
+
+    iterator begin()
+    {
+	return iterator(&t_, pj_rbtree_first(&t_));
+    }
+
+    iterator end()
+    {
+	return iterator(&t_, NULL);
+    }
+
+    reverse_iterator rbegin()
+    {
+	return reverse_iterator(&t_, pj_rbtree_last(&t_));
+    }
+
+    reverse_iterator rend()
+    {
+	return reverse_iterator(&t_, NULL);
+    }
+
+    bool insert(Node *node)
+    {
+	return pj_rbtree_insert(&t_, node)==0 ? true : false;
+    }
+
+    Node *find(const void *key)
+    {
+	return (Node*)pj_rbtree_find(&t_, key);
+    }
+
+    Node *erase(Node *node)
+    {
+	return (Node*)pj_rbtree_erase(&t_, node);
+    }
+
+    unsigned max_height(Node *node=NULL)
+    {
+	return pj_rbtree_max_height(&t_, node);
+    }
+
+    unsigned min_height(Node *node=NULL)
+    {
+	return pj_rbtree_min_height(&t_, node);
+    }
+
+private:
+    pj_rbtree t_;
+};
+
+#endif	/* __PJPP_TREE_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/types.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/types.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj++/types.hpp	(revision 1753)
@@ -0,0 +1,80 @@
+/* $Header: /pjproject/pjlib/src/pj++/types.hpp 3     4/17/05 11:59a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJPP_TYPES_H__
+#define __PJPP_TYPES_H__
+
+#include <pj/types.h>
+
+class PJ_Pool;
+class PJ_Socket;
+
+
+class PJ_Time_Val : public pj_time_val
+{
+public:
+    PJ_Time_Val() {}
+    PJ_Time_Val(const PJ_Time_Val &rhs) { sec=rhs.sec; msec=rhs.msec; }
+    explicit PJ_Time_Val(const pj_time_val &tv) { sec = tv.sec; msec = tv.msec; }
+
+    long get_sec()  const    { return sec; }
+    long get_msec() const    { return msec; }
+    void set_sec (long s)    { sec = s; }
+    void set_msec(long ms)   { msec = ms; normalize(); }
+    long to_msec() const { return PJ_TIME_VAL_MSEC((*this)); }
+
+    bool operator == (const PJ_Time_Val &rhs) const { return PJ_TIME_VAL_EQ((*this), rhs);  }
+    bool operator >  (const PJ_Time_Val &rhs) const { return PJ_TIME_VAL_GT((*this), rhs);  }
+    bool operator >= (const PJ_Time_Val &rhs) const { return PJ_TIME_VAL_GTE((*this), rhs); }
+    bool operator <  (const PJ_Time_Val &rhs) const { return PJ_TIME_VAL_LT((*this), rhs);  }
+    bool operator <= (const PJ_Time_Val &rhs) const { return PJ_TIME_VAL_LTE((*this), rhs); }
+
+    PJ_Time_Val & operator = (const PJ_Time_Val &rhs) {
+	sec = rhs.sec;
+	msec = rhs.msec;
+	return *this;
+    }
+ 
+    PJ_Time_Val & operator += (const PJ_Time_Val &rhs) {
+	PJ_TIME_VAL_ADD((*this), rhs);
+	return *this;
+    }
+
+    PJ_Time_Val & operator -= (const PJ_Time_Val &rhs) {
+	PJ_TIME_VAL_SUB((*this), rhs);
+	return *this;
+    }
+
+    /* Must include os.hpp to use these, otherwise unresolved in linking */
+    pj_status_t	   gettimeofday();
+    pj_parsed_time decode();
+    pj_status_t    encode(const pj_parsed_time *pt);
+    pj_status_t    to_gmt();
+    pj_status_t    to_local();
+
+
+private:
+    void normalize() { pj_time_val_normalize(this); }
+
+};
+
+#endif	/* __PJPP_TYPES_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/array.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/array.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/array.c	(revision 1753)
@@ -0,0 +1,28 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/array.c,v 1.1 2005/12/02 20:02:28 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/config.h>
+
+#if !PJ_FUNCTIONS_ARE_INLINED
+#   include <pj/array_i.h>
+#endif
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/array.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/array.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/array.h	(revision 1753)
@@ -0,0 +1,104 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/array.h,v 1.1 2005/12/02 20:02:28 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_ARRAY_H__
+#define __PJ_ARRAY_H__
+
+/**
+ * @file array.h
+ * @brief PJLIB Array helper.
+ */
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_ARRAY Array helper.
+ * @ingroup PJ_DS
+ * @{
+ *
+ * This module provides helper to manipulate array of elements of any size.
+ * It provides most used array operations such as insert, erase, and search.
+ */
+
+/**
+ * Insert value to the array at the given position, and rearrange the
+ * remaining nodes after the position.
+ *
+ * @param array	    the array.
+ * @param elem_size the size of the individual element.
+ * @param count	    the current number of elements in the array.
+ * @param pos	    the position where the new element is put.
+ * @param value	    the value to copy to the new element.
+ */
+PJ_IDECL(void) pj_array_insert(void *array,
+			       unsigned elem_size,
+			       unsigned count,
+			       unsigned pos,
+			       const void *value);
+
+/**
+ * Erase a value from the array at given position, and rearrange the remaining
+ * elements post the erased element.
+ *
+ * @param array	    the array.
+ * @param elem_size the size of the individual element.
+ * @param count	    the current number of elements in the array.
+ * @param pos	    the index/position to delete.
+ */
+PJ_IDECL(void) pj_array_erase(void *array,
+			      unsigned elem_size,
+			      unsigned count,
+			      unsigned pos);
+
+/**
+ * Search the first value in the array according to matching function.
+ *
+ * @param array	    the array.
+ * @param elem_size the individual size of the element.
+ * @param count	    the number of elements.
+ * @param matching  the matching function, which MUST return PJ_OK if 
+ *		    the specified element match.
+ * @param result    the pointer to the value found.
+ *
+ * @return PJ_OK (zero) if value is found.
+ */
+PJ_IDECL(pj_status_t) pj_array_search(const void *array, 
+				      unsigned elem_size, 
+				      unsigned count, 
+				      pj_status_t (*matching)(const void *value),
+				      void **result);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#if PJ_FUNCTIONS_ARE_INLINED
+#   include <pj/array_i.h>
+#endif
+
+#endif	/* __PJ_ARRAY_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/array_i.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/array_i.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/array_i.h	(revision 1753)
@@ -0,0 +1,73 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/array_i.h,v 1.1 2005/12/02 20:02:28 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+
+#include <pj/string.h>
+
+PJ_IDEF(void) pj_array_insert(void *array,
+			      unsigned elem_size,
+			      unsigned count,
+			      unsigned pos,
+			      const void *value)
+{
+    if (count && pos < count-1) {
+	pj_memmove( (char*)array + (pos+1)*elem_size,
+		    (char*)array + pos*elem_size,
+		    (count-pos)*elem_size);
+    }
+    pj_memmove((char*)array + pos*elem_size, value, elem_size);
+}
+
+PJ_IDEF(void) pj_array_erase(void *array,
+			     unsigned elem_size,
+			     unsigned count,
+			     unsigned pos)
+{
+    pj_assert(count != 0);
+    if (pos < count-1) {
+	pj_memmove( (char*)array + pos*elem_size,
+		    (char*)array + (pos+1)*elem_size,
+		    (count-pos-1)*elem_size);
+    }
+}
+
+PJ_IDEF(pj_status_t) pj_array_find(const void *array, 
+				   unsigned elem_size, 
+				   unsigned count, 
+				   pj_status_t (*matching)(const void *value),
+				   void **result)
+{
+    unsigned i;
+    const char *char_array = array;
+    for (i=0; i<count; ++i) {
+	if ( (*matching)(char_array) == PJ_OK) {
+	    if (result) {
+		*result = (void*)char_array;
+	    }
+	    return PJ_OK;
+	}
+	char_array += elem_size;
+    }
+    return -1;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/compat.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/compat.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/compat.h	(revision 1753)
@@ -0,0 +1,106 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/compat.h,v 1.1 2005/12/02 20:02:28 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_COMPAT_H__
+#define __PJ_COMPAT_H__
+
+#include <pj/config.h>
+
+/*
+ * Compiler specific macros
+ */
+#if defined(_MSC_VER)
+  /*
+   * MSVC Compiler specific
+   */
+#  pragma warning(disable: 4127)	// conditional expression is constant
+#  pragma warning(disable: 4611)	// not wise to mix setjmp with C++
+#  pragma warning(disable: 4514)	// unreferenced inline function has been removed
+#  ifdef __cplusplus
+#    define PJ_INLINE_SPECIFIER	inline
+#  else
+#    define PJ_INLINE_SPECIFIER	static __inline
+#  endif
+#  define PJ_THREAD_FUNC	__stdcall
+#  define PJ_NORETURN		__declspec(noreturn)
+#  define PJ_ATTR_NORETURN	
+
+#  define PJ_HAS_INT64	1
+typedef __int64 pj_int64_t;
+typedef unsigned __int64 pj_uint64_t;
+
+#elif defined(__GNUC__)
+
+#  define PJ_INLINE_SPECIFIER	static inline
+#  define PJ_THREAD_FUNC	
+#  define PJ_NORETURN		
+#  define PJ_ATTR_NORETURN	__attribute__ ((noreturn))
+
+#  define PJ_HAS_INT64	0
+
+#else
+#  error Unknown compiler.
+#endif
+/* End of compiler specific macros */
+
+
+#define PJ_INLINE(type)	  PJ_INLINE_SPECIFIER type
+
+#ifdef __cplusplus
+#  define PJ_DECL(type)		    type
+#  define PJ_DECL_NO_RETURN(type)   type PJ_NORETURN
+#  define PJ_BEGIN_DECL		    extern "C" {
+#  define PJ_END_DECL		    }
+#else
+#  define PJ_DECL(type)		    type
+#  define PJ_DECL_NO_RETURN(type)   PJ_NORETURN type
+#  define PJ_BEGIN_DECL
+#  define PJ_END_DECL
+#endif
+
+#define PJ_DEF(type)	  type
+#define PJ_DECL_DATA
+
+
+#if PJ_FUNCTIONS_ARE_INLINED
+#  define PJ_IDECL(type)  PJ_INLINE(type)
+#  define PJ_IDEF(type)   PJ_INLINE(type)
+#else
+#  define PJ_IDECL(type)  PJ_DECL(type)
+#  define PJ_IDEF(type)   PJ_DEF(type)
+#endif
+
+
+#define PJ_UNUSED_ARG(arg)  (void)arg;
+
+PJ_BEGIN_DECL
+
+/**
+ * Dump configuration to log with verbosity equal to info(3).
+ */
+PJ_DECL(void) pj_dump_config(void);
+
+PJ_END_DECL
+
+#endif /* __PJ_COMPAT_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/config.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/config.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/config.c	(revision 1753)
@@ -0,0 +1,53 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/config.c,v 1.1 2005/12/02 20:02:28 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/compat.h>
+#include <pj/log.h>
+
+static const char *id = "";
+const char *PJ_VERSION = "0.2.8";
+
+PJ_DEF(void) pj_dump_config(void)
+{
+    PJ_LOG(3, (id, "PJLIB (c)2005 Benny Prijono"));
+    PJ_LOG(3, (id, "Dumping configurations:"));
+    PJ_LOG(3, (id, " PJ_VERSION               : %s", PJ_VERSION));
+    PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED));
+    PJ_LOG(3, (id, " PJ_POOL_DEBUG            : %d", PJ_POOL_DEBUG));
+    PJ_LOG(3, (id, " PJ_HAS_THREADS           : %d", PJ_HAS_THREADS));
+    PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL         : %d", PJ_LOG_MAX_LEVEL));
+#if defined(PJ_WIN32)
+    PJ_LOG(3, (id, " PJ_WIN32                 : %d", PJ_WIN32));
+#if defined(PJ_WIN32_WINNT)
+    PJ_LOG(3, (id, " PJ_WIN32_WINNT           : %d", PJ_WIN32_WINNT));
+#endif
+#endif
+#if defined(PJ_LINUX)
+    PJ_LOG(3, (id, " PJ_LINUX                 : %d", PJ_LINUX));
+#endif
+    PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER    : %d", PJ_HAS_HIGH_RES_TIMER));
+    PJ_LOG(3, (id, " PJ_IS_LITTLE_ENDIAN      : %d", PJ_IS_LITTLE_ENDIAN));
+    PJ_LOG(3, (id, " PJ_IS_BIG_ENDIAN         : %d", PJ_IS_BIG_ENDIAN));
+    PJ_LOG(3, (id, " PJ_IOQUEUE_USE_WIN32_IOCP: %d", PJ_IOQUEUE_USE_WIN32_IOCP));
+    PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES   : %d", PJ_IOQUEUE_MAX_HANDLES));
+    PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES   : %d", PJ_IOQUEUE_MAX_HANDLES));
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/config.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/config.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/config.h	(revision 1753)
@@ -0,0 +1,170 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/config.h,v 1.1 2005/12/02 20:02:28 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_CONFIG_H__
+#define __PJ_CONFIG_H__
+
+extern const char *PJ_VERSION;
+
+#ifndef PJ_HAS_SYS_TYPES_H   
+#  define PJ_HAS_SYS_TYPES_H	    0
+#endif
+#ifndef PJ_HAS_STDDEF_H	
+#  define PJ_HAS_STDDEF_H	    1
+#endif
+
+#ifndef PJ_HAS_ASSERT_H
+#  define PJ_HAS_ASSERT_H	    1
+#endif
+
+#ifndef PJ_FUNCTIONS_ARE_INLINED
+#  define PJ_FUNCTIONS_ARE_INLINED  0
+#endif
+
+#ifndef PJ_POOL_DEBUG
+#  define PJ_POOL_DEBUG		    0
+#endif
+
+#if PJ_HAS_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if PJ_HAS_STDDEF_H
+# include <stddef.h>
+#endif
+
+#if PJ_HAS_ASSERT_H
+# include <assert.h>
+#endif
+
+/*
+ * Threading.
+ */
+#ifndef PJ_HAS_THREADS
+#  define PJ_HAS_THREADS	    (1)
+#endif
+
+
+/*
+ * Win32 specific.
+ */
+#ifdef WIN32
+#  define PJ_WIN32		    1
+#  define PJ_WIN32_WINNT	    0x0400
+#endif
+
+#ifdef _WIN32_WCE
+#  undef  PJ_WIN32
+#  define PJ_WIN32		    2
+#  define PJ_WIN32_WINCE	1
+#  define PJ_GUID_TYPE	    1
+#endif
+
+/*
+ * Linux
+ */
+#ifdef LINUX
+#  define PJ_LINUX		    1
+#endif
+
+/*
+ * Default has high resolution timer (PJ_HAS_HIGH_RES_TIMER)
+ * (Otherwise pjtest will fail to compile).
+ */
+#ifndef PJ_HAS_HIGH_RES_TIMER
+#  define PJ_HAS_HIGH_RES_TIMER	    0
+#endif
+
+/*
+ * Default set to Pentium (PJ_HAS_PENTIUM) to get Pentium rdtsc working.
+ */
+#if !defined(PJ_HAS_PENTIUM)
+#  define PJ_HAS_PENTIUM	    0
+#endif
+
+/*
+ * Default also set to little endian.
+ */
+#if !defined(PJ_IS_LITTLE_ENDIAN)
+#  define PJ_IS_LITTLE_ENDIAN	1
+#  define PJ_IS_BIG_ENDIAN	0
+#elif defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN != 0
+#  define PJ_IS_BIG_ENDIAN	0
+#else
+#  define PJ_IS_BIG_ENDIAN	1
+#  define PJ_IS_LITTLE_ENDIAN	0
+#endif
+
+
+/*
+ * If not specified, TCP is by default disabled.
+ * Disabling TCP will reduce the footprint slightly.
+ */
+#ifndef PJ_HAS_TCP
+#  define PJ_HAS_TCP		0
+#endif
+
+/*
+ * I/O Queue uses IOCP on WinNT, and fallback to select() on others
+ */
+#if defined(PJ_WIN32) //&& defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+#  define PJ_IOQUEUE_USE_WIN32_IOCP (0)
+#  define PJ_IOQUEUE_USE_SELECT     (1)
+#else
+#  define PJ_IOQUEUE_USE_SELECT	    (1)
+#  define PJ_IOQUEUE_USE_WIN32_IOCP (0)
+#endif
+
+/*
+ * Max hostname.
+ */
+#ifndef PJ_MAX_HOSTNAME
+#  define PJ_MAX_HOSTNAME	    (80)
+#endif
+
+/**
+ * Constants for declaring the maximum handles that can be supported by
+ * a single IOQ framework. This constant might not be relevant to the 
+ * underlying I/O queue impelementation, but still, developers should be 
+ * aware of this constant, to make sure that the program will not break when
+ * the underlying implementation changes.
+ *
+ * For implementation based on select(), the value here will be used as the
+ * maximum number of socket handles passed to select() (i.e. FD_SETSIZE will 
+ * be set to this value).
+ */
+#ifndef PJ_IOQUEUE_MAX_HANDLES
+#  define PJ_IOQUEUE_MAX_HANDLES    (64)
+#endif
+
+
+/*
+ * TODO information
+ */
+#ifndef PJ_TODO
+#  define PJ_TODO(id)	    TODO___##id:
+#endif
+
+
+#endif	/* __PJ_CONFIG_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/except.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/except.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/except.c	(revision 1753)
@@ -0,0 +1,59 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/except.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/except.h>
+#include <pj/os.h>
+#include <assert.h>
+
+static int thread_local_id = -1;
+
+PJ_DEF(void) pj_throw_exception_(int exception_id)
+{
+    struct pj_exception_state_t *handler;
+
+    handler = pj_thread_local_get(thread_local_id);
+    assert(handler != NULL);
+    longjmp(handler->state, exception_id);
+}
+
+PJ_DEF(void) pj_push_exception_handler_(struct pj_exception_state_t *rec)
+{
+    struct pj_exception_state_t *parent_handler = NULL;
+
+    if (thread_local_id == -1) {
+	thread_local_id = pj_thread_local_alloc();
+	assert(thread_local_id != -1);
+    }
+    parent_handler = pj_thread_local_get(thread_local_id);
+    rec->prev = parent_handler;
+    pj_thread_local_set(thread_local_id, rec);
+}
+
+PJ_DEF(void) pj_pop_exception_handler_()
+{
+    struct pj_exception_state_t *handler;
+
+    handler = pj_thread_local_get(thread_local_id);
+    assert(handler != NULL);
+    pj_thread_local_set(thread_local_id, handler->prev);
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/except.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/except.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/except.h	(revision 1753)
@@ -0,0 +1,230 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/except.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_EXCEPTION_H__
+#define __PJ_EXCEPTION_H__
+
+/**
+ * @file except.h
+ * @brief Exception Handling in C.
+ */
+
+#include <setjmp.h>
+#include <pj/types.h>
+
+#ifdef __cplusplus
+#  if !defined(PJ_EXCEPT_USE_CPP)
+#     define PJ_EXCEPT_USE_CPP	0
+#  endif
+
+     PJ_BEGIN_DECL
+
+#endif
+
+
+/**
+ * @defgroup PJ_EXCEPT Exception Handling
+ * @ingroup PJ_MISC
+ * @{
+ *
+ * This module provides exception handling functionality similar to C++ in
+ * C language. The underlying mechanism use setjmp() and longjmp(), and since
+ * these constructs are ANSI standard, the mechanism here should be available
+ * on all platforms/compilers which are ANSI compliant (effectively almost all
+ * platforms/compilers, including embedded ones).
+ *
+ * The exception handling mechanism is completely thread safe, so the exception
+ * thrown by one thread will not interfere with other thread.
+ *
+ * CAVEATS:
+ *  - unlike C++ exception, the scheme here won't call destructors of local
+ *    objects if exception is thrown.
+ *  - You CAN NOT make nested exception in one single function without using
+ *    a nested PJ_USE_EXCEPTION.
+ *  - Exceptions will always be caught by the first handle (unlike C++ where
+ *    exception is only caught if the type matches.
+ *
+ * The exception handling constructs are similar to C++. The blocks will be
+ * constructed similar to the following sample:
+ *
+ * <pre>
+ * int main()
+ * {
+ *    PJ_USE_EXCEPTION;  // declare local exception stack.
+ *
+ *    ...
+ *    PJ_TRY {
+ *      ... 
+ *      // do something..
+ *      ...
+ *    }
+ *    PJ_CATCH( 1 ) {
+ *      ...
+ *      // handle exception 1
+ *    }
+ *    PJ_CATCH( 2 ) {
+ *      ...
+ *      // handle exception 2
+ *    }
+ *    PJ_DEFAULT {
+ *      ...
+ *      // handle other exceptions.
+ *    }
+ *    PJ_END;
+ * }
+ * </pre>
+ *
+ * Below is the keywords in the exception handling mechanism.
+ *
+ * \section PJ_EX_KEYWORDS Keywords
+ *
+ * \subsection PJ_THROW PJ_THROW(expression)
+ * Throw an exception. The expression thrown is an integer as the result of
+ * the \a expression. This keyword can be specified anywhere within the 
+ * program.
+ *
+ * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION
+ * Specify this in the variable definition section of the function block 
+ * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception 
+ * block. 
+ * Actually, this is just a macro to declare local variable which is used to
+ * push the exception state to the exception stack.
+ *
+ * \subsection PJ_TRY PJ_TRY
+ * The \a PJ_TRY keyword is typically followed by a block. If an exception is
+ * thrown in this block, then the execution will resume to the \a PJ_CATCH 
+ * handler.
+ *
+ * \subsection PJ_CATCH PJ_CATCH(expression)
+ * The \a PJ_CATCH is normally followed by a block. This block will be executed
+ * if the exception being thrown is equal to the expression specified in the
+ * \a PJ_CATCH.
+ *
+ * \subsection PJ_DEFAULT PJ_DEFAULT
+ * The \a PJ_DEFAULT keyword is normally followed by a block. This block will
+ * be executed if the exception being thrown doesn't match any of the \a
+ * PJ_CATCH specification. The \a PJ_DEFAULT block \b MUST be placed as the
+ * last block of the handlers.
+ *
+ * \subsection PJ_END PJ_END
+ * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks.
+ *
+ * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void)
+ * Get the last exception thrown. This macro is normally called inside the
+ * \a PJ_CATCH or \a PJ_DEFAULT block, altough it can be used anywhere where
+ * the \a PJ_USE_EXCEPTION definition is in scope.
+ *
+ * \section PJ_EX_SAMPLE Sample
+ * Here's a complete sample of how to use the exception.
+ *
+ * \verbatim
+   #define EX_NO_MEMORY	    1
+  
+   void randomly_throw_exception()
+   {
+	unsigned value = pj_rand();
+	if (value < 10)
+	    PJ_THROW(value);
+   }
+
+   void *my_malloc(size_t size)
+   {
+  	void *ptr = malloc(size);
+  	if (!ptr)
+  	    PJ_THROW(EX_NO_MEMORY);
+  	return ptr;
+   }
+  
+   int func()
+   {
+  	void *data;
+  	PJ_USE_EXCEPTION;
+  
+  	PJ_TRY {
+  	    data = my_malloc(200);
+	    randomly_throw_exception();
+  	}
+  	PJ_CATCH( EX_NO_MEMORY ) {
+  	    puts("Can't allocate memory");
+  	    return 0;
+  	}
+	PJ_DEFAULT {
+	    printf("Caught random exception %d\n", PJ_GET_EXCEPTION());
+	}
+  	PJ_END
+    }
+   \endverbatim
+ *
+ * @}
+ */
+
+#if PJ_EXCEPT_USE_CPP
+
+#define PJ_USE_EXCEPTION
+#define PJ_TRY		    try
+#define PJ_CATCH(id)	    
+#define PJ_DEFAULT	    
+#define PJ_GET_EXCEPTION    
+#define PJ_THROW(id)	    
+#define PJ_END		    
+
+#else	/* PJ_EXCEPT_USE_CPP */
+
+/*
+ * This structure (which should be invisible to user) manages the TRY handler
+ * stack.
+ */
+struct pj_exception_state_t
+{
+    struct pj_exception_state_t *prev;
+    jmp_buf state;
+};
+
+PJ_DECL_NO_RETURN(void) pj_throw_exception_(int id) PJ_ATTR_NORETURN;
+PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec);
+PJ_DECL(void) pj_pop_exception_handler_();
+
+#define PJ_USE_EXCEPTION    struct pj_exception_state_t pj_x_except__; int pj_x_code__
+
+#define PJ_TRY		    if (1) { \
+				pj_push_exception_handler_(&pj_x_except__); \
+				pj_x_code__ = setjmp(pj_x_except__.state); \
+				if (pj_x_code__ == 0)
+#define PJ_CATCH(id)	    else if (pj_x_code__ == (id))
+#define PJ_DEFAULT	    else
+#define PJ_END			pj_pop_exception_handler_(); \
+			    } else {}
+#define PJ_THROW(exception_id)	pj_throw_exception_(exception_id)
+#define PJ_GET_EXCEPTION()	(pj_x_code__)
+
+
+#endif	/* PJ_EXCEPT_USE_CPP */
+
+
+#if defined(__cplusplus)
+  PJ_END_DECL
+#endif
+
+#endif	/* __PJ_EXCEPTION_H__ */
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/fifobuf.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/fifobuf.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/fifobuf.c	(revision 1753)
@@ -0,0 +1,186 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/fifobuf.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#include "fifobuf.h"
+#include <pj/log.h>
+
+#define THIS_FILE   "fifobuf"
+
+#define SZ  sizeof(unsigned)
+
+PJ_DEF(void)
+pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size)
+{
+    PJ_LOG(6, (THIS_FILE, 
+	       "fifobuf_init fifobuf=%p buffer=%p, size=%d", 
+	       fifobuf, buffer, size));
+
+    fifobuf->first = buffer;
+    fifobuf->last = fifobuf->first + size;
+    fifobuf->ubegin = fifobuf->uend = fifobuf->first;
+    fifobuf->full = 0;
+}
+
+PJ_DEF(unsigned)
+pj_fifobuf_max_size (pj_fifobuf_t *fifobuf)
+{
+    unsigned s1, s2;
+
+    if (fifobuf->uend >= fifobuf->ubegin) {
+	s1 = fifobuf->last - fifobuf->uend;
+	s2 = fifobuf->ubegin - fifobuf->first;
+    } else {
+	s1 = s2 = fifobuf->ubegin - fifobuf->uend;
+    }
+    
+    return s1<s2 ? s2 : s1;
+}
+
+PJ_DEF(void*)
+pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size)
+{
+    unsigned available;
+    char *start;
+
+    if (fifobuf->full) {
+	PJ_LOG(6, (THIS_FILE, 
+		   "fifobuf_alloc fifobuf=%p, size=%d: full!", 
+		   fifobuf, size));
+	return NULL;
+    }
+
+    /* try to allocate from the end part of the fifo */
+    if (fifobuf->uend >= fifobuf->ubegin) {
+	available = fifobuf->last - fifobuf->uend;
+	if (available >= size+SZ) {
+	    char *ptr = fifobuf->uend;
+	    fifobuf->uend += (size+SZ);
+	    if (fifobuf->uend == fifobuf->last)
+		fifobuf->uend = fifobuf->first;
+	    if (fifobuf->uend == fifobuf->ubegin)
+		fifobuf->full = 1;
+	    *(unsigned*)ptr = size+SZ;
+	    ptr += SZ;
+
+	    PJ_LOG(6, (THIS_FILE, 
+		       "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", 
+		       fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
+	    return ptr;
+	}
+    }
+
+    /* try to allocate from the start part of the fifo */
+    start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first;
+    available = fifobuf->ubegin - start;
+    if (available >= size+SZ) {
+	char *ptr = start;
+	fifobuf->uend = start + size + SZ;
+	if (fifobuf->uend == fifobuf->ubegin)
+	    fifobuf->full = 1;
+	*(unsigned*)ptr = size+SZ;
+	ptr += SZ;
+
+	PJ_LOG(6, (THIS_FILE, 
+		   "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", 
+		   fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
+	return ptr;
+    }
+
+    PJ_LOG(6, (THIS_FILE, 
+	       "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p", 
+	       fifobuf, size, fifobuf->ubegin, fifobuf->uend));
+    return NULL;
+}
+
+PJ_DEF(pj_status_t)
+pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf)
+{
+    char *ptr = buf;
+    char *endptr;
+    unsigned sz;
+
+    ptr -= SZ;
+    sz = *(unsigned*)ptr;
+
+    endptr = fifobuf->uend;
+    if (endptr == fifobuf->first)
+	endptr = fifobuf->last;
+
+    if (ptr+sz != endptr) {
+	pj_assert(!"Invalid pointer to undo alloc");
+	return -1;
+    }
+
+    fifobuf->uend = ptr;
+    fifobuf->full = 0;
+
+    PJ_LOG(6, (THIS_FILE, 
+	       "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", 
+	       fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
+
+    return 0;
+}
+
+PJ_DEF(pj_status_t)
+pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf)
+{
+    char *ptr = buf;
+    char *end;
+    unsigned sz;
+
+    ptr -= SZ;
+    if (ptr < fifobuf->first || ptr >= fifobuf->last) {
+	pj_assert(!"Invalid pointer to free");
+	return -1;
+    }
+
+    if (ptr != fifobuf->ubegin && ptr != fifobuf->first) {
+	pj_assert(!"Invalid free() sequence!");
+	return -1;
+    }
+
+    end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last;
+    sz = *(unsigned*)ptr;
+    if (ptr+sz > end) {
+	pj_assert(!"Invalid size!");
+	return -1;
+    }
+
+    fifobuf->ubegin = ptr + sz;
+
+    /* Rollover */
+    if (fifobuf->ubegin == fifobuf->last)
+	fifobuf->ubegin = fifobuf->first;
+
+    /* Reset if fifobuf is empty */
+    if (fifobuf->ubegin == fifobuf->uend)
+	fifobuf->ubegin = fifobuf->uend = fifobuf->first;
+
+    fifobuf->full = 0;
+
+    PJ_LOG(6, (THIS_FILE, 
+	       "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", 
+	       fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
+
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/fifobuf.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/fifobuf.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/fifobuf.h	(revision 1753)
@@ -0,0 +1,47 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/fifobuf.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_FIFOBUF_H__
+#define __PJ_FIFOBUF_H__
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+typedef struct pj_fifobuf_t pj_fifobuf_t;
+struct pj_fifobuf_t
+{
+    char *first, *last;
+    char *ubegin, *uend;
+    int full;
+};
+
+PJ_DECL(void)	     pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size);
+PJ_DECL(unsigned)    pj_fifobuf_max_size (pj_fifobuf_t *fb);
+PJ_DECL(void*)	     pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size);
+PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf);
+PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf);
+
+PJ_END_DECL
+
+#endif	/* __PJ_FIFOBUF_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/fwd.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/fwd.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/fwd.h	(revision 1753)
@@ -0,0 +1,41 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/fwd.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_FWD_H__
+#define __PJ_FWD_H__
+
+typedef struct pj_pool_t pj_pool_t;
+typedef void pj_list_type;
+
+typedef struct pj_hash_table_t pj_hash_table_t;
+typedef struct pj_hash_iterator_t pj_hash_iterator_t;
+
+typedef struct pj_ioqueue_t pj_ioqueue_t;
+typedef struct pj_ioqueue_key_t pj_ioqueue_key_t;
+
+typedef struct pj_timer_heap_t pj_timer_heap_t;
+
+
+#endif	/* __PJ_FWD_H__ */
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/guid.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/guid.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/guid.c	(revision 1753)
@@ -0,0 +1,347 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/guid.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/guid.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <time.h>
+#include <stdlib.h>
+
+/*
+ * The algorithm presented here needs serious checking about it's correctness,
+ * especially the endian thingy.
+ */
+
+#define PJ_GUID_COCREATEGUID   1
+#define PJ_GUID_OWNGUID	    2
+#define PJ_GUID_SIMPLE	    3
+
+#ifndef PJ_GUID_TYPE
+# define PJ_GUID_TYPE	    PJ_GUID_SIMPLE
+#endif
+
+#if defined(PJ_WIN32_WINCE) && PJ_GUID_TYPE==PJ_GUID_COCREATEGUID
+#include <windows.h>
+#include <winsock2.h>
+#include <wincrypt.h>
+#endif
+
+#define MHZ_IN_HZ	    1000000.0
+#define SECOND_IN_MICRO	    1000000
+#define SECOND_IN_100_NANO  10000000
+
+static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+		     '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p)
+{
+    *p++ = hex[ (value & 0xF0) >> 4 ];
+    *p++ = hex[ (value & 0x0F) ];
+}
+
+#if PJ_GUID_TYPE != PJ_GUID_COCREATEGUID
+static int guid_initialized;
+static unsigned char mac_addr[6];
+static pj_uint16_t   clock_seq;
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+
+#if PJ_GUID_TYPE != PJ_GUID_COCREATEGUID
+#include <wincon.h>
+
+static void init_mac_address()
+{
+    /* Reference:
+       http://support.microsoft.com/support/kb/articles/q118/6/23.asp
+     */
+    typedef struct _ASTAT_
+    {
+	ADAPTER_STATUS adapt;
+	NAME_BUFFER    NameBuff [30];
+    } ASTAT, * PASTAT;
+    
+    ASTAT	Adapter;
+    NCB		Ncb;
+    UCHAR	uRetCode;
+    LANA_ENUM	lenum;
+    
+    memset( &Ncb, 0, sizeof(Ncb) );
+    Ncb.ncb_command = NCBENUM;
+    Ncb.ncb_buffer = (UCHAR *)&lenum;
+    Ncb.ncb_length = sizeof(lenum);
+    uRetCode = Netbios( &Ncb );
+    
+    if (uRetCode == 0 && lenum.length > 0) {
+	memset( &Ncb, 0, sizeof(Ncb) );
+	Ncb.ncb_command = NCBRESET;
+	Ncb.ncb_lana_num = lenum.lana[0];
+	
+	uRetCode = Netbios( &Ncb );
+	if ( uRetCode == 0 )
+	{
+	    memset( &Ncb, 0, sizeof (Ncb) );
+	    Ncb.ncb_command = NCBASTAT;
+	    Ncb.ncb_lana_num = lenum.lana[0];
+	    
+	    strcpy( (char*)Ncb.ncb_callname,  "*               " );
+	    Ncb.ncb_buffer = (unsigned char *) &Adapter;
+	    Ncb.ncb_length = sizeof(Adapter);
+	    
+	    uRetCode = Netbios( &Ncb );
+	    if ( uRetCode == 0 )
+	    {
+		PJ_LOG( 5, ("guid.c", "MAC address on LANA %d is: "
+				      "%02x-%02x-%02x-%02x-%02x-%02x",
+		    lenum.lana[0],
+		    Adapter.adapt.adapter_address[0],
+		    Adapter.adapt.adapter_address[1],
+		    Adapter.adapt.adapter_address[2],
+		    Adapter.adapt.adapter_address[3],
+		    Adapter.adapt.adapter_address[4],
+		    Adapter.adapt.adapter_address[5] ));
+		memcpy(mac_addr, Adapter.adapt.adapter_address, 6);
+	    }
+	}
+    } else {
+	uRetCode = 1;
+    }
+    
+    if (uRetCode != 0) {
+	unsigned my_mac_addr[6] = { 0x00, 0x02, 0xE3, 0x3B, 0x34, 0x20 };
+	memcpy(mac_addr, my_mac_addr, 6);
+
+	PJ_LOG( 3, ("guid.c", "Unable to get MAC address (retcode=0x%x). "
+			      "Using my MAC address, but this won't be unique..",
+		   uRetCode ));
+    }
+}
+#endif	/* PJ_GUID_TYPE == PJGUID_COCREATEGUID */
+
+#if PJ_GUID_TYPE==PJ_GUID_OWNGUID
+
+static pj_uint32_t get_processor_cycle()
+{
+    unsigned cycle;
+
+    __asm {
+	RDTSC
+	MOV DWORD PTR cycle, EAX
+    }
+
+    return cycle;
+}
+
+static pj_uint32_t get_100nsec_fraction()
+{
+    static pj_uint32_t cpu_mhz;
+    double cycle;
+
+    if (cpu_mhz == 0) {
+	HKEY hkey;
+	DWORD type, cbdata = sizeof(cpu_mhz);
+	DWORD err = ERROR_SUCCESS;
+
+	/* This doesn't seem to work on Win98 SE (presumably 98/95 as well */
+
+	err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
+			   "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
+			   0, KEY_READ, &hkey);
+
+	if (err == ERROR_SUCCESS) {
+	    err = RegQueryValueEx( hkey, "~MHz", NULL, &type, (unsigned char*)&cpu_mhz, &cbdata);
+	    RegCloseKey(hkey);
+	}
+
+	if (err != ERROR_SUCCESS) {
+	    cpu_mhz = 500;
+	    PJ_LOG(3, ("guid.c", "Unable to determine CPU speed. Using %u Mhz..", cpu_mhz));
+	}
+    }
+
+    cycle = get_processor_cycle();
+    return ((unsigned) (cycle * 10.0 / cpu_mhz)) % SECOND_IN_100_NANO;
+}
+
+/* Get the timestamp as a 60 bit value multiplexed by 4 bit version number.
+ * For UUID version 1, the timestamp is represented by Coordinated Universal 
+ * Time (UTC) as a count of 100-nanosecond intervals since 00:00:00.00, 
+ * 15 October 1582 (the date of Gregorian reform to the Christian calendar).
+ *
+ * Note about this implementation:
+ *  - we start the time from 1 Jan 1970 instead (easier).
+ */
+static void get_timestamp_and_version( unsigned char timestamp[8] )
+{
+    pj_uint64_t *t = (pj_uint64_t*) timestamp;
+
+    (*t) = time(NULL);
+    (*t) *= SECOND_IN_100_NANO;
+    (*t) += get_100nsec_fraction();
+
+    /* don't care about version.. */
+}
+#endif	/* PJ_GUID_TYPE==PJ_GUID_OWNGUID */
+
+
+#endif	/* _WIN32 */
+
+#ifdef LINUX
+static void init_mac_address()
+{
+    unsigned long *ulval1 = (unsigned long*) &mac_addr[0];
+    unsigned short *usval1 = (unsigned short*) &mac_addr[4];
+
+    *ulval1 = pj_rand();
+    *usval1 = (unsigned short) pj_rand();
+}
+
+#endif
+
+#if PJ_GUID_TYPE==PJ_GUID_OWNGUID
+unsigned PJ_GUID_LENGTH=32;
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+    unsigned char guid[16];
+    int i;
+    const unsigned char *src = guid;
+    char *dst = str->ptr;
+
+    if (guid_initialized == 0) {
+	init_mac_address();
+	clock_seq = (pj_uint16_t) pj_rand();
+	guid_initialized = 1;
+    }
+
+    get_timestamp_and_version( &guid[0] );
+    memcpy( &guid[8], &clock_seq, 2);
+    memcpy( &guid[10], mac_addr, 6);
+    ++clock_seq;
+
+    for (i=0; i<16; ++i) {
+	pj_val_to_hex_digit( *src, dst );
+	dst += 2;
+	++src;
+    }
+    str->slen = 32;
+
+    return str;
+}
+#endif
+
+#if PJ_GUID_TYPE==PJ_GUID_COCREATEGUID
+unsigned PJ_GUID_LENGTH=32;
+static void pj_guid_to_str( const GUID *guid, pj_str_t *str )
+{
+    unsigned i;
+    GUID guid_copy;
+    const unsigned char *src = (const unsigned char*)&guid_copy;
+    char *dst = str->ptr;
+
+    memcpy(&guid_copy, guid, sizeof(*guid));
+    guid_copy.Data1 = ntohl(guid_copy.Data1);
+    guid_copy.Data2 = ntohs(guid_copy.Data2);
+    guid_copy.Data3 = ntohs(guid_copy.Data3);
+
+    for (i=0; i<16; ++i) {
+	pj_val_to_hex_digit( *src, dst );
+	dst += 2;
+	++src;
+    }
+    str->slen = 32;
+}
+
+
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+    GUID guid;
+#if !defined(PJ_WIN32_WINCE)
+    CoCreateGuid(&guid);
+#else
+	char bits[16];
+	HCRYPTPROV hCryptProv = 0;
+
+	if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+		return NULL;
+	}
+
+    if (!CryptGenRandom(hCryptProv, 16, bits)) {
+		return NULL;
+    }
+
+	// set the variant
+    bits[8] &= 0x3f;
+    bits[8] |= 2 << 6;
+
+    // set the version
+    bits[7] &= 0x0f;
+    bits[7] |= 4 << 4;
+
+    if (hCryptProv != 0)
+       CryptReleaseContext(hCryptProv, 0);
+
+    pj_memcpy(&guid, bits, 16);
+#endif // WINCE
+
+    pj_guid_to_str( &guid, str );
+    return str;
+}
+#endif
+
+#if PJ_GUID_TYPE==PJ_GUID_SIMPLE
+unsigned PJ_GUID_LENGTH=20;
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+    static unsigned pid;
+    static char str_pid[5];
+    static char str_mac_addr[16];
+
+    if (guid_initialized == 0) {
+	pid = pj_getpid();
+	init_mac_address();
+	clock_seq = 0;
+
+	sprintf(str_pid, "%04x", pid);
+	sprintf(str_mac_addr, "%02x%02x%02x%02x%02x%02x",
+	    mac_addr[0], mac_addr[1], mac_addr[2],
+	    mac_addr[3], mac_addr[4], mac_addr[5]);
+
+	guid_initialized = 1;
+    }
+
+    strcpy(str->ptr, str_pid);
+    sprintf(str->ptr+4, "%04x", clock_seq++);
+    memcpy(str->ptr+8, str_mac_addr, 12);
+    str->slen = 20;
+
+    return str;
+}
+#endif
+
+PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str)
+{
+    str->ptr = pj_pool_alloc(pool, PJ_GUID_LENGTH);
+    pj_generate_unique_string(str);
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/guid.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/guid.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/guid.h	(revision 1753)
@@ -0,0 +1,84 @@
+/* $header: $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_GUID_H__
+#define __PJ_GUID_H__
+
+
+/**
+ * @file guid.h
+ * @brief GUID Globally Unique Identifier.
+ */
+#include <pj/types.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJ_DS Data Structure.
+ * @ingroup PJ
+ */
+/**
+ * @defgroup PJ_GUID Globally Unique Identifier
+ * @ingroup PJ_DS
+ * @{
+ */
+
+
+/**
+ * The length of globally unique identifier is dependent on the algorithm
+ * used internally. If real GUID generator is used, then the length will
+ * be 128bit or 32 bytes. If shadow GUID generator is used, then the length
+ * will be 20 bytes. Application should not assume which algorithm will
+ * be used by GUID generator.
+ */
+extern unsigned PJ_GUID_LENGTH;
+
+/**
+ * Create a globally unique string, which length is always PJ_GUID_LENGTH
+ * characters. Caller is responsible for preallocating the storage used
+ * in the string.
+ *
+ * @param str the string to store the result.
+ *
+ * @return the string.
+ */
+PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str);
+
+/**
+ * Generate a unique string.
+ *
+ * @param pool	    Pool to allocate memory from.
+ * @param str	    The string.
+ */
+PJ_DECL(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str);
+
+ /**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif/* __PJ_GUID_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/hash.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/hash.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/hash.c	(revision 1753)
@@ -0,0 +1,254 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/hash.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/hash.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+
+struct pj_hash_entry
+{
+    struct pj_hash_entry *next;
+    const void *key;
+    pj_uint32_t hash;
+    pj_uint32_t keylen;
+    void *value;
+};
+
+
+struct pj_hash_table_t
+{
+    pj_hash_entry     **table;
+    unsigned		count, rows;
+    pj_hash_iterator_t	iterator;
+};
+
+
+
+PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, unsigned keylen)
+{
+    if (keylen==PJ_HASH_KEY_STRING) {
+	const unsigned char *p = key;
+	for ( ; *p; ++p ) {
+	    hash = hash * PJ_HASH_MULTIPLIER + *p;
+	}
+	keylen = p - (const unsigned char*)key;
+    } else {
+	const unsigned char *p = key,
+			    *end = p + keylen;
+	for ( ; p!=end; ++p) {
+	    hash = hash * PJ_HASH_MULTIPLIER + *p;
+	}
+    }
+    return hash;
+}
+
+
+PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size)
+{
+    pj_hash_table_t *h;
+    unsigned table_size;
+    
+    h = pj_pool_alloc(pool, sizeof(pj_hash_table_t));
+    h->count = 0;
+
+    PJ_LOG( 5, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool)));
+
+    /* size must be 2^n - 1.
+       round-up the size to this rule, except when size is 2^n, then size
+       will be round-down to 2^n-1.
+     */
+    table_size = 8;
+    do {
+	table_size <<= 1;    
+    } while (table_size <= size);
+    table_size -= 1;
+    
+    h->rows = table_size;
+    h->table = pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*));
+    return h;
+}
+
+static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht, 
+				   const void *key, unsigned keylen,
+				   void *val)
+{
+    pj_uint32_t hash;
+    pj_hash_entry **p_entry, *entry;
+
+    hash=0;
+    if (keylen==PJ_HASH_KEY_STRING) {
+	const unsigned char *p = key;
+	for ( ; *p; ++p ) {
+	    hash = hash * PJ_HASH_MULTIPLIER + *p;
+	}
+	keylen = p - (const unsigned char*)key;
+    } else {
+	const unsigned char *p = key,
+			    *end = p + keylen;
+	for ( ; p!=end; ++p) {
+	    hash = hash * PJ_HASH_MULTIPLIER + *p;
+	}
+    }
+
+    /* scan the linked list */
+    for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry; 
+	 entry; 
+	 p_entry = &entry->next, entry = *p_entry)
+    {
+	if (entry->hash==hash && entry->keylen==keylen &&
+	    memcmp(entry->key, key, keylen)==0) 
+	{
+	    break;	
+	}
+    }
+
+    if (entry || val==NULL)
+	return p_entry;
+
+    /* create a new entry */
+    entry = pj_pool_alloc(pool, sizeof(pj_hash_entry));
+    PJ_LOG(5, ("hashtbl", "%p: New p_entry %p created, pool used=%u, cap=%u", ht, entry, 
+			  pj_pool_get_used_size(pool), pj_pool_get_capacity(pool)));
+    entry->next = NULL;
+    entry->hash = hash;
+    entry->key = key;
+    entry->keylen = keylen;
+    entry->value = val;
+    *p_entry = entry;
+    
+    ++ht->count;
+    
+    return p_entry;
+}
+
+PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht,
+			    const void *key, unsigned keylen )
+{
+    pj_hash_entry *entry;
+    entry = *find_entry( NULL, ht, key, keylen, NULL);
+    return entry ? entry->value : NULL;
+}
+
+PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
+			  const void *key, unsigned keylen,
+			  void *value )
+{
+    pj_hash_entry **p_entry;
+
+    p_entry = find_entry( pool, ht, key, keylen, value );
+    if (*p_entry) {
+	if (value == NULL) {
+	    /* delete entry */
+	    PJ_LOG(5, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
+	    *p_entry = (*p_entry)->next;
+	    --ht->count;
+	    
+	} else {
+	    /* overwrite */
+	    (*p_entry)->value = value;
+	    PJ_LOG(5, ("hashtbl", "%p: p_entry %p value set to %p", ht, *p_entry, value));
+	}
+    }
+}
+
+PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht )
+{
+    return ht->count;
+}
+
+PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
+					   pj_hash_iterator_t *it )
+{
+    it->index = 0;
+    it->entry = NULL;
+
+    for (; it->index < ht->rows; ++it->index) {
+	it->entry = ht->table[it->index];
+	if (it->entry) {
+	    break;
+	}
+    }
+
+    return it->entry ? it : NULL;
+}
+
+PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, 
+					  pj_hash_iterator_t *it )
+{
+    it->entry = it->entry->next;
+    if (it->entry) {
+	return it;
+    }
+
+    for (++it->index; it->index < ht->rows; ++it->index) {
+	it->entry = ht->table[it->index];
+	if (it->entry) {
+	    break;
+	}
+    }
+
+    return it->entry ? it : NULL;
+}
+
+PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it )
+{
+    PJ_UNUSED_ARG(ht)
+    return it->entry->value;
+}
+
+#if 0
+void pj_hash_dump_collision( pj_hash_table_t *ht )
+{
+    unsigned min=0xFFFFFFFF, max=0;
+    unsigned i;
+    char line[120];
+    int len, totlen = 0;
+
+    for (i=0; i<ht->rows; ++i) {
+	unsigned count = 0;    
+	pj_hash_entry *entry = ht->table[i];
+	while (entry) {
+	    ++count;
+	    entry = entry->next;
+	}
+	if (count < min)
+	    min = count;
+	if (count > max)
+	    max = count;
+	len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count);
+	if (len < 1)
+	    break;
+	totlen += len;
+
+	if ((i+1) % 10 == 0) {
+	    line[totlen] = '\0';
+	    PJ_LOG(4,(__FILE__, line));
+	}
+    }
+
+    PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max));
+}
+#endif
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/hash.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/hash.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/hash.h	(revision 1753)
@@ -0,0 +1,166 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/hash.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_HASH_H__
+#define __PJ_HASH_H__
+
+/**
+ * @file hash.h
+ * @brief Hash Table.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_HASH Hash Table
+ * @ingroup PJ_DS
+ * @{
+ * A hash table is a dictionary in which keys are mapped to array positions by
+ * hash functions. Having the keys of more than one item map to the same 
+ * position is called a collision. In this library, we will chain the nodes
+ * that have the same key in a list.
+ */
+
+/**
+ * If this constant is used as keylen, then the key is interpreted as
+ * NULL terminated string.
+ */
+#define PJ_HASH_KEY_STRING	((unsigned)-1)
+
+/**
+ * The hash multiplier used to calculate hash value.
+ */
+#define PJ_HASH_MULTIPLIER	33
+
+/**
+ * This is the function that is used by the hash table to calculate hash value
+ * of the specified key.
+ *
+ * @param hval	    the initial hash value, or zero.
+ * @param key	    the key to calculate.
+ * @param keylen    the length of the key, or PJ_HASH_KEY_STRING to treat 
+ *		    the key as null terminated string.
+ *
+ * @return the hash value.
+ */
+PJ_DECL(pj_uint32_t) pj_hash_calc(pj_uint32_t hval, 
+				  const void *key, unsigned keylen);
+
+
+/**
+ * Create a hash table with the specified 'bucket' size.
+ *
+ * @param pool	the pool from which the hash table will be allocated from.
+ * @param size	the bucket size, which will be round-up to the nearest 2^n+1
+ *
+ * @return the hash table.
+ */
+PJ_DECL(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size);
+
+
+/**
+ * Get the value associated with the specified key.
+ *
+ * @param ht	    the hash table.
+ * @param key	    the key to look for.
+ * @param keylen    the length of the key, or PJ_HASH_KEY_STRING to use the
+ *		    string length of the key.
+ *
+ * @return the value associated with the key, or NULL if the key is not found.
+ */
+PJ_DECL(void *) pj_hash_get( pj_hash_table_t *ht,
+			     const void *key, unsigned keylen );
+
+
+/**
+ * Associate/disassociate a value with the specified key.
+ *
+ * @param pool	    the pool to allocate the new entry if a new entry has to be
+ *		    created.
+ * @param ht	    the hash table.
+ * @param key	    the key.
+ * @param keylen    the length of the key, or PJ_HASH_KEY_STRING to use the 
+ *		    string length of the key.
+ * @param value	    value to be associated, or NULL to delete the entry with
+ *		    the specified key.
+ */
+PJ_DECL(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
+			   const void *key, unsigned keylen,
+			   void *value );
+
+/**
+ * Get the total number of entries in the hash table.
+ *
+ * @param ht	the hash table.
+ *
+ * @return the number of entries in the hash table.
+ */
+PJ_DECL(unsigned) pj_hash_count( pj_hash_table_t *ht );
+
+
+/**
+ * Get the iterator to the first element in the hash table. 
+ *
+ * @param ht	the hash table.
+ * @param it	the iterator for iterating hash elements.
+ *
+ * @return the iterator to the hash element, or NULL if no element presents.
+ */
+PJ_DECL(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
+					    pj_hash_iterator_t *it );
+
+
+/**
+ * Get the next element from the iterator.
+ *
+ * @param ht	the hash table.
+ * @param it	the hash iterator.
+ *
+ * @return the next iterator, or NULL if there's no more element.
+ */
+PJ_DECL(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, 
+					   pj_hash_iterator_t *it );
+
+/**
+ * Get the value associated with a hash iterator.
+ *
+ * @param ht	the hash table.
+ * @param it	the hash iterator.
+ *
+ * @return the value associated with the current element in iterator.
+ */
+PJ_DECL(void*) pj_hash_this( pj_hash_table_t *ht,
+			     pj_hash_iterator_t *it );
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/ioqueue.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/ioqueue.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/ioqueue.h	(revision 1753)
@@ -0,0 +1,353 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/ioqueue.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_IOQUEUE_H__
+#define __PJ_IOQUEUE_H__
+
+/**
+ * @file ioqueue.h
+ * @brief I/O Dispatching Mechanism
+ */
+
+//Can't include sock.h now (FD_SETSIZE declaration).
+//#include <pj/sock.h>
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_IO (Asynchronous) Input Output
+ * @brief (Asynchronous) Input Output
+ * @ingroup PJ_OS
+ */
+
+/**
+ * @defgroup PJ_IOQUEUE I/O Event Dispatching Queue
+ * @ingroup PJ_IO
+ * @{
+ *
+ * This file provides abstraction for various event dispatching mechanisms. 
+ * The interfaces for event dispatching vary alot, even in a single
+ * operating system. The abstraction here hopefully is suitable for most of
+ * the event dispatching available.
+ *
+ * Currently, the I/O Queue supports:
+ * - select(), as the common denominator, but the least efficient.
+ * - I/O Completion ports in Windows NT/2000/XP, which is the most efficient
+ *      way to dispatch events in Windows NT based OSes, and most importantly,
+ *      it doesn't have the limit on how many handles to monitor. And it works
+ *      with files (not only sockets) as well.
+ */
+
+/**
+ * @bug For any given socket, only one thread can issue one type of
+ *	operation.
+ */
+
+ /**
+  * This structure describes the callbacks to be called when I/O operation
+  * completes.
+  */
+typedef struct pj_ioqueue_callback
+{
+    /**
+     * This callback is called when #pj_ioqueue_read or #pj_ioqueue_recvfrom
+     * completes.
+     *
+     * @param key	    The key.
+     * @param bytes_read    The size of data that has just been read.
+     */
+    void (*on_read_complete)(pj_ioqueue_key_t *key, pj_ssize_t bytes_read);
+
+    /**
+     * This callback is called when #pj_ioqueue_write or #pj_ioqueue_sendto
+     * completes.
+     *
+     * @param key	    The key.
+     * @param bytes_read    The size of data that has just been read.
+     */
+    void (*on_write_complete)(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent);
+
+    /**
+     * This callback is called when #pj_ioqueue_accept completes.
+     *
+     * @param key	    The key.
+     * @param status	    Zero if the operation completes successfully.
+     */
+    void (*on_accept_complete)(pj_ioqueue_key_t *key, int status);
+
+    /**
+     * This callback is called when #pj_ioqueue_connect completes.
+     *
+     * @param key	    The key.
+     * @param status	    Zero if the operation completes successfully.
+     */
+    void (*on_connect_complete)(pj_ioqueue_key_t *key, int status);
+} pj_ioqueue_callback;
+
+
+/**
+ * Error value returned by I/O operations to indicate that the operation
+ * can't complete immediately and will complete later.
+ */
+#define PJ_IOQUEUE_PENDING	(-2)
+
+/**
+ * Types of I/O Queue operation.
+ */
+typedef enum pj_ioqueue_operation_e
+{
+    PJ_IOQUEUE_OP_NONE		= 0,	/**< No operation. */
+    PJ_IOQUEUE_OP_READ		= 1,	/**< read() operation. */
+    PJ_IOQUEUE_OP_RECV_FROM	= 2,	/**< recvfrom() operation. */
+    PJ_IOQUEUE_OP_WRITE		= 4,	/**< write() operation. */
+    PJ_IOQUEUE_OP_SEND_TO	= 8,	/**< sendto() operation. */
+#if PJ_HAS_TCP
+    PJ_IOQUEUE_OP_ACCEPT	= 16,	/**< accept() operation. */
+    PJ_IOQUEUE_OP_CONNECT	= 32,	/**< connect() operation. */
+#endif
+} pj_ioqueue_operation_e;
+
+
+/**
+ * Create a new I/O Queue framework.
+ *
+ * @param pool	    the pool to allocate the I/O queue structure. 
+ * @param max_fd    the maximum number of handles to be supported, which should
+ *		    not exceed PJ_IOQUEUE_MAX_HANDLES.
+ *
+ * @return the I/O queue structure, or NULL upon error.
+ */
+PJ_DECL(pj_ioqueue_t*) pj_ioqueue_create( pj_pool_t *pool, 
+					  pj_size_t max_fd );
+
+/**
+ * Destroy the I/O queue.
+ *
+ * @param ioque	    the I/O Queue
+ *
+ * @return PJ_OK (zero) if success.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque );
+
+
+/**
+ * Register a handle to the I/O queue framework. 
+ * For some types of handles, the handle attributes will be modified so
+ * that it fits in to the framework. For example, for socket, the socket will
+ * be modified to use NONBLOCKING I/O.
+ *
+ * @param pool	    to allocate the resource for the specified handle, 
+ *		    which must be valid until the handle/key is unregistered 
+ *		    from I/O Queue.
+ * @param ioque	    the I/O Queue.
+ * @param hnd	    the OS handle to be registered.
+ * @param user_data User data to be associated with the key, which can be
+ *		    retrieved later.
+ * @param cb	    Callback to be called when I/O operation completes. 
+ *
+ * @return the key to identify the handle in the AIO framework, or NULL
+ *	   upon error.
+ */
+PJ_DECL(pj_ioqueue_key_t*) pj_ioqueue_register(	pj_pool_t *pool,
+						pj_ioqueue_t *ioque,
+						pj_oshandle_t hnd,
+						void *user_data,
+						const pj_ioqueue_callback *cb);
+
+/**
+ * Unregister a handle from the I/O Queue framework.
+ *
+ * @param ioque the I/O Queue.
+ * @param key	the key that uniquely identifies the handle, which is returned
+ *		from the function pj_ioqueue_register().
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+					    pj_ioqueue_key_t *key );
+
+
+/**
+ * Get user data associated with the I/O Queue key.
+ *
+ * @param key	the key previously associated with the socket/handle with
+ *		pj_ioqueue_register().
+ *
+ * @return the user data.
+ */
+PJ_DECL(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key );
+
+
+#if PJ_HAS_TCP
+/**
+ * Instruct I/O Queue to wait for incoming connections on the specified 
+ * listening socket. This function will return
+ * immediately (i.e. non-blocking) regardless whether some data has been 
+ * transfered. If the function can't complete immediately, and the caller will
+ * be notified about the completion when it calls pj_ioqueue_poll().
+ *
+ * @param ioqueue   the I/O Queue
+ * @param key	    the key which registered to the server socket.
+ * @param sock	    variable to receive the socket for the incoming connection.
+ * @param local	    variable to receive local address.
+ * @param remote    variable to receive the remote address.
+ * @param addrlen   on input, contains the length of the buffer for the
+ *		    address, and on output, contains the actual length of the
+ *		    address.
+ * @return
+ *  - zero if there's a connection available immediately, which in this case
+ *    the callback should have been called before the function returns.
+ *  - PJ_IOQUEUE_PENDING if accept is queued, or 
+ *  - (-1) on error.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+					pj_ioqueue_key_t *key,
+					pj_sock_t *sock,
+					pj_sockaddr_t *local,
+					pj_sockaddr_t *remote,
+					int *addrlen);
+
+/**
+ * Initiate non-blocking socket connect. If the socket can NOT be connected
+ * immediately, the result will be reported during poll.
+ *
+ * @param ioqueue   the ioqueue
+ * @param key	    the key associated with TCP socket
+ * @param addr	    the remote address.
+ * @param addrlen   the remote address length.
+ *
+ * @return
+ *  - zero if socket is connected immediately, which in this case the callback
+ *    should have been called.
+ *  - PJ_IOQUEUE_PENDING if operation is queued, or 
+ *  - (-1) on error.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+					 pj_ioqueue_key_t *key,
+					 const pj_sockaddr_t *addr,
+					 int addrlen );
+
+#endif	/* PJ_HAS_TCP */
+
+/**
+ * Poll the I/O Queue for completed events.
+ *
+ * @param ioque		the I/O Queue.
+ * @param timeout	polling timeout, or NULL if the thread wishes to wait
+ *			indefinetely for the event.
+ *
+ * @return 
+ *  - zero if timed out (no event).
+ *  - (-1) if error occured during polling. Callback will NOT be called.
+ *  - (1)  if there's an event. Callback will be called.
+ */
+PJ_DECL(int) pj_ioqueue_poll( pj_ioqueue_t *ioque,
+			      const pj_time_val *timeout);
+
+/**
+ * Instruct the I/O Queue to read from the specified handle. This function
+ * returns immediately (i.e. non-blocking) regardless whether some data has 
+ * been transfered. If the operation can't complete immediately, caller will 
+ * be notified about the completion when it calls pj_ioqueue_poll().
+ *
+ * @param ioque	    the I/O Queue.
+ * @param key	    the key that uniquely identifies the handle.
+ * @param buffer    the buffer to hold the read data. The caller MUST make sure
+ *		    that this buffer remain valid until the framework completes
+ *		    reading the handle.
+ * @param buflen    the maximum size to be read.
+ *
+ * @return
+ *  - zero or positive number to indicate the number of bytes has been
+ *		    read, and in this case the operation was not queued.
+ *  - (-1) on error, which in this case operation was not queued.
+ *  - PJ_IOQUEUE_PENDING if the operation has been queued.
+ */
+PJ_DECL(int) pj_ioqueue_read( pj_ioqueue_t *ioque,
+			      pj_ioqueue_key_t *key,
+			      void *buffer,
+			      pj_size_t buflen);
+
+
+/**
+ * This function behaves similarly as pj_ioqueue_read(), except that it is
+ * normally called for socket, and the remote address will also be returned
+ * along with the data. Caller MUST make sure that both buffer and addr
+ * remain valid until the framework completes reading the data.
+ *
+ * @see ::pj_ioqueue_read
+ */
+PJ_DECL(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+				  pj_ioqueue_key_t *key,
+				  void *buffer,
+				  pj_size_t buflen,
+				  pj_sockaddr_t *addr,
+				  int *addrlen);
+
+/**
+ * Instruct the I/O Queue to write to the handle. This function will return
+ * immediately (i.e. non-blocking) regardless whether some data has been 
+ * transfered. If the function can't complete immediately, and the caller will
+ * be notified about the completion when it calls pj_ioqueue_poll().
+ *
+ * @param ioque	    the I/O Queue.
+ * @param key	    the key that identifies the handle.
+ * @param data	    the data to send. Caller MUST make sure that this buffer 
+ *		    remains valid until the write operation completes.
+ * @param datalen   the length of the data.
+ *
+ * @return
+ *  - zero or positive number to indicate the number of bytes has been
+ *	    written, and in this case the operation was not queued.
+ *  - (-1) on error, which in this case operation was not queued.
+ *  - PJ_IOQUEUE_PENDING if the operation has been queued.
+ */
+PJ_DECL(int) pj_ioqueue_write( pj_ioqueue_t *ioque,
+				pj_ioqueue_key_t *key,
+				const void *data,
+				pj_size_t datalen);
+
+/**
+ * This function behaves similarly as pj_ioqueue_write(), except that
+ * pj_sock_sendto() (or equivalent) will be called to send the data.
+ *
+ * @see pj_ioqueue_write
+ */
+PJ_DECL(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+				pj_ioqueue_key_t *key,
+				const void *data,
+				pj_size_t datalen,
+				const pj_sockaddr_t *addr,
+				int addrlen);
+
+
+/**
+ * !}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJ_IOQUEUE_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/ioqueue_select.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/ioqueue_select.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/ioqueue_select.c	(revision 1753)
@@ -0,0 +1,554 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/ioqueue_select.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#ifdef PJ_LINUX
+# include <errno.h>
+#endif
+
+/*
+ * This is the top level ifdef, which will enable/disable compilation of this file
+ * depending on whether PJ_IOQUEUE_USE_SELECT macro is set!
+ */
+#if defined(PJ_IOQUEUE_USE_SELECT) && PJ_IOQUEUE_USE_SELECT!=0
+
+
+#define FD_SETSIZE  PJ_IOQUEUE_MAX_HANDLES
+#define THIS_FILE   "ioq_select"
+
+#include <pj/sock.h>
+
+#define PJ_IOQUEUE_IS_READ_OP(op)   \
+	((op & PJ_IOQUEUE_OP_READ)  || (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op)  \
+	((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
+
+#if PJ_HAS_TCP
+#  define PJ_IOQUEUE_IS_ACCEPT_OP(op)	(op & PJ_IOQUEUE_OP_ACCEPT)
+#  define PJ_IOQUEUE_IS_CONNECT_OP(op)	(op & PJ_IOQUEUE_OP_CONNECT)
+#else
+#  define PJ_IOQUEUE_IS_ACCEPT_OP(op)	0
+#  define PJ_IOQUEUE_IS_CONNECT_OP(op)	0
+#endif
+
+struct pj_ioqueue_key_t
+{
+    PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+    pj_sock_t		    fd;
+    pj_ioqueue_operation_e  op;
+    void		   *user_data;
+    pj_ioqueue_callback	    cb;
+
+    void		   *rd_buf;
+    pj_size_t		    rd_buflen;
+    void		   *wr_buf;
+    pj_size_t		    wr_buflen;
+
+    pj_sockaddr_t	   *rmt_addr;
+    int			   *rmt_addrlen;
+
+    pj_sockaddr_t	   *local_addr;
+    int			   *local_addrlen;
+
+    pj_sock_t		   *accept_fd;
+};
+
+struct pj_ioqueue_t
+{
+    pj_mutex_t	     *mutex;
+    unsigned		max, count;
+    pj_ioqueue_key_t	hlist;
+    pj_fdset_t		rfdset;
+    pj_fdset_t		wfdset;
+#if PJ_HAS_TCP
+    pj_fdset_t		xfdset;
+#endif
+};
+
+PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd)
+{
+    pj_ioqueue_t *ioque;
+
+    if (max_fd > PJ_IOQUEUE_MAX_HANDLES) {
+	PJ_LOG(1,("ioqueue", "max_fd too large! Can't create ioqueue."));
+	return NULL;
+    }
+
+    ioque = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
+    ioque->max = max_fd;
+    ioque->count = 0;
+    PJ_FD_ZERO(&ioque->rfdset);
+    PJ_FD_ZERO(&ioque->wfdset);
+#if PJ_HAS_TCP
+    PJ_FD_ZERO(&ioque->xfdset);
+#endif
+    pj_list_init(&ioque->hlist);
+
+    ioque->mutex = pj_mutex_create(pool, "ioq%p", PJ_MUTEX_SIMPLE);
+    if (!ioque->mutex) {
+	PJ_LOG(1,("ioqueue", "Mutex creation failed!"));
+	return NULL;
+    }
+
+    PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioque));
+    return ioque;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+    pj_mutex_destroy(ioque->mutex);
+    return 0;
+}
+
+PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool,
+					       pj_ioqueue_t *ioque,
+					       pj_oshandle_t sock,
+					       void *user_data,
+					       const pj_ioqueue_callback *cb)
+{
+    pj_ioqueue_key_t *key = NULL;
+    pj_uint32_t value;
+    
+    pj_mutex_lock(ioque->mutex);
+
+    if (ioque->count >= ioque->max)
+	goto on_return;
+
+    /* Set socket to nonblocking. */
+    value = 1;
+    if (pj_sock_ioctl((pj_sock_t)sock, PJ_FIONBIO, &value)) {
+	PJ_PERROR(("ioqueue", "Error setting FIONBIO"));
+	goto on_return;
+    }
+
+    /* Create key. */
+    key = (pj_ioqueue_key_t*)pj_pool_calloc(pool, 1, sizeof(pj_ioqueue_key_t));
+    key->fd = (pj_sock_t)sock;
+    key->user_data = user_data;
+
+    /* Save callback. */
+    pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback));
+
+    /* Register */
+    pj_list_insert_before(&ioque->hlist, key);
+    ++ioque->count;
+
+on_return:
+    pj_mutex_unlock(ioque->mutex);
+    return key;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+					   pj_ioqueue_key_t *key)
+{
+    pj_mutex_lock(ioque->mutex);
+
+    pj_assert(ioque->count > 0);
+    --ioque->count;
+    pj_list_erase(key);
+    PJ_FD_CLR(key->fd, &ioque->rfdset);
+    PJ_FD_CLR(key->fd, &ioque->wfdset);
+#if PJ_HAS_TCP
+    PJ_FD_CLR(key->fd, &ioque->xfdset);
+#endif
+
+    pj_mutex_unlock(ioque->mutex);
+    return 0;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+    return key->user_data;
+}
+
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+    pj_fdset_t rfdset, wfdset, xfdset;
+    int rc;
+    pj_ioqueue_key_t *h;
+    
+    /* Copy ioqueue's fd_set to local variables. */
+    pj_mutex_lock(ioque->mutex);
+
+    rfdset = ioque->rfdset;
+    wfdset = ioque->wfdset;
+#if PJ_HAS_TCP
+    xfdset = ioque->xfdset;
+#else
+    PJ_FD_ZERO(&xfdset);
+#endif
+
+    /* Unlock ioqueue before select(). */
+    pj_mutex_unlock(ioque->mutex);
+
+    rc = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout);
+    
+    if (rc <= 0)
+	return rc;
+
+    /* Lock ioqueue again before scanning for signalled sockets. */
+    pj_mutex_lock(ioque->mutex);
+
+#if PJ_HAS_TCP
+    /* Scan for exception socket */
+    h = ioque->hlist.next;
+    for ( ; h!=&ioque->hlist; h = h->next) {
+	if ((h->op & PJ_IOQUEUE_OP_CONNECT) && PJ_FD_ISSET(h->fd, &xfdset))
+	    break;
+    }
+    if (h != &ioque->hlist) {
+	/* 'connect()' should be the only operation. */
+	pj_assert((h->op == PJ_IOQUEUE_OP_CONNECT));
+
+	/* Clear operation. */
+	h->op &= ~(PJ_IOQUEUE_OP_CONNECT);
+	PJ_FD_CLR(h->fd, &ioque->wfdset);
+	PJ_FD_CLR(h->fd, &ioque->xfdset);
+
+	/* Unlock I/O queue before calling callback. */
+	pj_mutex_unlock(ioque->mutex);
+
+	/* Call callback. */
+	(*h->cb.on_connect_complete)(h, -1);
+	return 1;
+    }
+#endif	/* PJ_HAS_TCP */
+
+    /* Scan for writable socket  */
+    h = ioque->hlist.next;
+    for ( ; h!=&ioque->hlist; h = h->next) {
+	if ((PJ_IOQUEUE_IS_WRITE_OP(h->op) || PJ_IOQUEUE_IS_CONNECT_OP(h->op)) && PJ_FD_ISSET(h->fd, &wfdset))
+	    break;
+    }
+    if (h != &ioque->hlist) {
+	pj_assert(PJ_IOQUEUE_IS_WRITE_OP(h->op) || PJ_IOQUEUE_IS_CONNECT_OP(h->op));
+
+#if PJ_HAS_TCP
+	if ((h->op & PJ_IOQUEUE_OP_CONNECT)) {
+	    /* Completion of connect() operation */
+	    pj_ssize_t bytes_transfered;
+
+#if defined(PJ_LINUX)
+	    /* from connect(2): 
+		* On Linux, use getsockopt to read the SO_ERROR option at
+		* level SOL_SOCKET to determine whether connect() completed
+		* successfully (if SO_ERROR is zero).
+		*/
+	    int value;
+	    socklen_t vallen = sizeof(value);
+	    int rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &value, &vallen);
+	    if (rc != 0) {
+		/* Argh!! What to do now??? 
+		    * Just indicate that the socket is connected. The
+		    * application will get error as soon as it tries to use
+		    * the socket to send/receive.
+		    */
+		PJ_PERROR(("ioqueue", "Unable to determine connect() status"));
+		bytes_transfered = 0;
+	    } else {
+		bytes_transfered = value;
+	    }
+#elif defined(PJ_WIN32)
+	    bytes_transfered = 0; /* success */
+#else
+#  error "Got to check this one!"
+#endif
+
+	    /* Clear operation. */
+	    h->op &= (~PJ_IOQUEUE_OP_CONNECT);
+	    PJ_FD_CLR(h->fd, &ioque->wfdset);
+	    PJ_FD_CLR(h->fd, &ioque->xfdset);
+
+	    /* Unlock mutex before calling callback. */
+	    pj_mutex_unlock(ioque->mutex);
+
+	    /* Call callback. */
+	    (*h->cb.on_connect_complete)(h, bytes_transfered);
+
+	    return 1;
+
+	} else 
+#endif /* PJ_HAS_TCP */
+	{
+	    /* Completion of write(), send(), or sendto() operation. */
+
+	    /* Clear operation. */
+	    h->op &= ~(PJ_IOQUEUE_OP_WRITE | PJ_IOQUEUE_OP_SEND_TO);
+	    PJ_FD_CLR(h->fd, &ioque->wfdset);
+
+	    /* Unlock mutex before calling callback. */
+	    pj_mutex_unlock(ioque->mutex);
+
+	    /* Call callback. */
+	    /* All data must have been sent? */
+	    (*h->cb.on_write_complete)(h, h->wr_buflen);
+
+	    return 1;
+	}
+
+	/* Unreached. */
+    }
+
+    /* Scan for readable socket. */
+    h = ioque->hlist.next;
+    for ( ; h!=&ioque->hlist; h = h->next) {
+	if ((PJ_IOQUEUE_IS_READ_OP(h->op) || PJ_IOQUEUE_IS_ACCEPT_OP(h->op)) && 
+	    PJ_FD_ISSET(h->fd, &rfdset))
+	    break;
+    }
+    if (h != &ioque->hlist) {
+	pj_assert(PJ_IOQUEUE_IS_READ_OP(h->op) || PJ_IOQUEUE_IS_ACCEPT_OP(h->op));
+#	if PJ_HAS_TCP
+	if ((h->op & PJ_IOQUEUE_OP_ACCEPT)) {
+	    /* accept() must be the only operation specified on server socket */
+	    pj_assert(h->op == PJ_IOQUEUE_OP_ACCEPT);
+
+	    *h->accept_fd = pj_sock_accept(h->fd, h->rmt_addr, h->rmt_addrlen);
+	    if (*h->accept_fd == PJ_INVALID_SOCKET) {
+		rc = -1;
+	    } else if (h->local_addr) {
+		rc = pj_sock_getsockname(*h->accept_fd, h->local_addr, h->local_addrlen);
+	    } else {
+		rc = 0;
+	    }
+
+	    h->op &= ~(PJ_IOQUEUE_OP_ACCEPT);
+	    PJ_FD_CLR(h->fd, &ioque->rfdset);
+
+	    /* Unlock mutex before calling callback. */
+	    pj_mutex_unlock(ioque->mutex);
+
+	    /* Call callback. */
+	    (*h->cb.on_accept_complete)(h, rc);
+
+	    return 1;
+
+	} else 
+#	endif
+	if ((h->op & PJ_IOQUEUE_OP_RECV_FROM)) {
+	    rc = pj_sock_recvfrom(h->fd, h->rd_buf, h->rd_buflen, 0,
+				    h->rmt_addr, h->rmt_addrlen);
+	} else {
+	    rc = pj_sock_recv(h->fd, h->rd_buf, h->rd_buflen, 0);
+	}
+	
+	if (rc < 0) {
+	    pj_status_t sock_err = -1;
+#	    if defined(_WIN32)
+	    /* On Win32, for UDP, WSAECONNRESET on the receive side 
+	     * indicates that previous sending has triggered ICMP Port 
+	     * Unreachable message.
+	     * But we wouldn't know at this point which one of previous 
+	     * key that has triggered the error, since UDP socket can
+	     * be shared!
+	     * So we'll just ignore it!
+	     */
+
+	    sock_err = pj_sock_getlasterror();
+	    if (sock_err == PJ_ECONNRESET) {
+		pj_mutex_unlock(ioque->mutex);
+		PJ_LOG(4,(THIS_FILE, "Received ICMP port unreachable on key=%p (ignored)!", h));
+		return 0;
+	    } 
+#	    endif
+
+	    PJ_LOG(4, (THIS_FILE, "socket recv error on key %p, rc=%d, err=%d", h, rc, sock_err));
+	}
+
+	h->op &= ~(PJ_IOQUEUE_OP_READ | PJ_IOQUEUE_OP_RECV_FROM);
+	PJ_FD_CLR(h->fd, &ioque->rfdset);
+
+	/* Unlock mutex before callback. */
+	pj_mutex_unlock(ioque->mutex);
+
+	/* Call callback. */
+	(*h->cb.on_read_complete)(h, rc);
+	return 1;
+    }
+
+    /* Shouldn't happen. */
+    /* For strange reason on WinXP select() can return 1 while there is no
+     * fd_set signaled. */
+    /* pj_assert(0); */
+
+    rc = 0;
+
+    pj_mutex_unlock(ioque->mutex);
+    return rc;
+}
+
+PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque,
+			     pj_ioqueue_key_t *key,
+			     void *buffer,
+			     pj_size_t buflen)
+{
+    pj_mutex_lock(ioque->mutex);
+
+    key->op |= PJ_IOQUEUE_OP_READ;
+    key->rd_buf = buffer;
+    key->rd_buflen = buflen;
+    PJ_FD_SET(key->fd, &ioque->rfdset);
+
+    pj_mutex_unlock(ioque->mutex);
+    return PJ_IOQUEUE_PENDING;
+}
+
+PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+				 pj_ioqueue_key_t *key,
+				 void *buffer,
+				 pj_size_t buflen,
+				 pj_sockaddr_t *addr,
+				 int *addrlen)
+{
+    pj_mutex_lock(ioque->mutex);
+
+    key->op |= PJ_IOQUEUE_OP_RECV_FROM;
+    key->rd_buf = buffer;
+    key->rd_buflen = buflen;
+    key->rmt_addr = addr;
+    key->rmt_addrlen = addrlen;
+    PJ_FD_SET(key->fd, &ioque->rfdset);
+
+    pj_mutex_unlock(ioque->mutex);
+    return PJ_IOQUEUE_PENDING;
+}
+
+PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque,
+			      pj_ioqueue_key_t *key,
+			      const void *data,
+			      pj_size_t datalen)
+{
+    if (pj_sock_send(key->fd, data, datalen, 0) != (pj_ssize_t)datalen)
+	return -1;
+
+    pj_mutex_lock(ioque->mutex);
+
+    key->op |= PJ_IOQUEUE_OP_WRITE;
+    key->wr_buf = NULL;
+    key->wr_buflen = datalen;
+    PJ_FD_SET(key->fd, &ioque->wfdset);
+
+    pj_mutex_unlock(ioque->mutex);
+
+    return PJ_IOQUEUE_PENDING;
+}
+
+PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+			       pj_ioqueue_key_t *key,
+			       const void *data,
+			       pj_size_t datalen,
+			       const pj_sockaddr_t *addr,
+			       int addrlen)
+{
+    if (pj_sock_sendto(key->fd, data, datalen, 0, addr, addrlen) != (pj_ssize_t)datalen)
+	return -1;
+
+    pj_mutex_lock(ioque->mutex);
+
+    key->op |= PJ_IOQUEUE_OP_SEND_TO;
+    key->wr_buf = NULL;
+    key->wr_buflen = datalen;
+    PJ_FD_SET(key->fd, &ioque->wfdset);
+
+    pj_mutex_unlock(ioque->mutex);
+    return PJ_IOQUEUE_PENDING;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+			       pj_ioqueue_key_t *key,
+			       pj_sock_t *new_sock,
+			       pj_sockaddr_t *local,
+			       pj_sockaddr_t *remote,
+			       int *addrlen)
+{
+    /* check parameters. All must be specified! */
+    pj_assert(ioqueue && key && new_sock && local && remote && addrlen);
+
+    /* Server socket must have no other operation! */
+    pj_assert(key->op == 0);
+    
+    pj_mutex_lock(ioqueue->mutex);
+
+    key->op = PJ_IOQUEUE_OP_ACCEPT;
+    key->accept_fd = new_sock;
+    key->rmt_addr = remote;
+    key->rmt_addrlen = addrlen;
+    key->local_addr = local;
+    key->local_addrlen = addrlen;   /* use same addr. as rmt_addrlen */
+
+    PJ_FD_SET(key->fd, &ioqueue->rfdset);
+
+    pj_mutex_unlock(ioqueue->mutex);
+    return PJ_IOQUEUE_PENDING;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+					pj_ioqueue_key_t *key,
+					const pj_sockaddr_t *addr,
+					int addrlen )
+{
+    int status;
+    
+    /* check parameters. All must be specified! */
+    pj_assert(ioqueue && key && addr && addrlen);
+
+    /* Connecting socket must have no other operation! */
+    pj_assert(key->op == 0);
+    
+    status = pj_sock_connect(key->fd, addr, addrlen);
+    if (status == 0) {
+	/* Connected! */
+	return 0;
+    } else {
+	pj_status_t oserr = pj_getlasterror();
+	if (oserr == PJ_EINPROGRESS || oserr == PJ_EWOULDBLOCK) {
+	    /* Pending! */
+	    pj_mutex_lock(ioqueue->mutex);
+	    key->op = PJ_IOQUEUE_OP_CONNECT;
+	    PJ_FD_SET(key->fd, &ioqueue->wfdset);
+	    PJ_FD_SET(key->fd, &ioqueue->xfdset);
+	    pj_mutex_unlock(ioqueue->mutex);
+	    return PJ_IOQUEUE_PENDING;
+	} else {
+	    /* Error! */
+	    return oserr;
+	}
+    }
+}
+#endif	/* PJ_HAS_TCP */
+
+#endif	/* #if PJ_IOQUEUE_USE_SELECT */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/ioqueue_winnt.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/ioqueue_winnt.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/ioqueue_winnt.c	(revision 1753)
@@ -0,0 +1,664 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/ioqueue_winnt.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/ioqueue.h>
+
+#if defined(PJ_IOQUEUE_USE_WIN32_IOCP) && PJ_IOQUEUE_USE_WIN32_IOCP!=0
+
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/array.h>
+#include <pj/log.h>
+#include <mswsock.h>
+
+#define ACCEPT_ADDR_LEN	    (sizeof(pj_sockaddr_in)+20)
+
+/*
+ * OVERLAP structure for send and receive.
+ */
+typedef struct ioqueue_overlapped
+{
+    WSAOVERLAPPED	   overlapped;
+    pj_ioqueue_operation_e operation;
+    WSABUF		   wsabuf;
+} ioqueue_overlapped;
+
+#if PJ_HAS_TCP
+/*
+ * OVERLAP structure for accept.
+ */
+typedef struct ioqueue_accept_rec
+{
+    WSAOVERLAPPED	    overlapped;
+    pj_ioqueue_operation_e  operation;
+    pj_sock_t		    newsock;
+    pj_sock_t		   *newsock_ptr;
+    int			   *addrlen;
+    void		   *remote;
+    void		   *local;
+    char		    accept_buf[2 * ACCEPT_ADDR_LEN];
+} ioqueue_accept_rec;
+#endif
+
+/*
+ * Structure for individual socket.
+ */
+struct pj_ioqueue_key_t
+{
+    HANDLE		hnd;
+    void	       *user_data;
+    ioqueue_overlapped	recv_overlapped;
+    ioqueue_overlapped	send_overlapped;
+#if PJ_HAS_TCP
+    int			connecting;
+    ioqueue_accept_rec	accept_overlapped;
+#endif
+    pj_ioqueue_callback	cb;
+};
+
+/*
+ * IO Queue structure.
+ */
+struct pj_ioqueue_t
+{
+    HANDLE	      iocp;
+    pj_mutex_t	     *mutex;
+    unsigned	      event_count;
+    HANDLE	      event_pool[MAXIMUM_WAIT_OBJECTS+1];
+#if PJ_HAS_TCP
+    unsigned	      connecting_count;
+    HANDLE	      connecting_handles[MAXIMUM_WAIT_OBJECTS+1];
+    pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1];
+#endif
+};
+
+
+#if PJ_HAS_TCP
+/*
+ * Process the socket when the overlapped accept() completed.
+ */
+static void ioqueue_on_accept_complete(ioqueue_accept_rec *accept_overlapped)
+{
+    struct sockaddr *local;
+    struct sockaddr *remote;
+    int locallen, remotelen;
+
+    /* Operation complete immediately. */
+    GetAcceptExSockaddrs( accept_overlapped->accept_buf,
+			  0, 
+			  ACCEPT_ADDR_LEN,
+			  ACCEPT_ADDR_LEN,
+			  &local,
+			  &locallen,
+			  &remote,
+			  &remotelen);
+    pj_memcpy(accept_overlapped->local, local, locallen);
+    pj_memcpy(accept_overlapped->remote, remote, locallen);
+    *accept_overlapped->addrlen = locallen;
+    *accept_overlapped->newsock_ptr = accept_overlapped->newsock;
+    accept_overlapped->operation = 0;
+    accept_overlapped->newsock = PJ_INVALID_SOCKET;
+}
+
+static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos)
+{
+    pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos];
+    HANDLE hEvent = ioqueue->connecting_handles[pos];
+    unsigned long optval;
+
+    /* Remove key from array of connecting handles. */
+    pj_array_erase(ioqueue->connecting_keys, sizeof(key),
+		   ioqueue->connecting_count, pos);
+    pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE),
+		   ioqueue->connecting_count, pos);
+    --ioqueue->connecting_count;
+
+    /* Disassociate the socket from the event. */
+    WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0);
+
+    /* Put event object to pool. */
+    if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) {
+	ioqueue->event_pool[ioqueue->event_count++] = hEvent;
+    } else {
+	/* Shouldn't happen. There should be no more pending connections
+	 * than max. 
+	 */
+	pj_assert(0);
+	CloseHandle(hEvent);
+    }
+
+    /* Set socket to blocking again. */
+    optval = 0;
+    if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) {
+	DWORD dwStatus;
+	dwStatus = WSAGetLastError();
+    }
+}
+
+/*
+ * Poll for the completion of non-blocking connect().
+ * If there's a completion, the function return the key of the completed
+ * socket, and 'result' argument contains the connect() result. If connect()
+ * succeeded, 'result' will have value zero, otherwise will have the error
+ * code.
+ */
+static pj_ioqueue_key_t *check_connecting( pj_ioqueue_t *ioqueue, 
+					   pj_ssize_t *connect_err )
+{
+    pj_ioqueue_key_t *key = NULL;
+
+    if (ioqueue->connecting_count) {
+	DWORD result;
+
+	pj_mutex_lock(ioqueue->mutex);
+	result = WaitForMultipleObjects(ioqueue->connecting_count,
+					ioqueue->connecting_handles,
+					FALSE, 0);
+	if (result >= WAIT_OBJECT_0 && 
+	    result < WAIT_OBJECT_0+ioqueue->connecting_count) 
+	{
+	    WSANETWORKEVENTS net_events;
+
+	    /* Got completed connect(). */
+	    unsigned pos = result - WAIT_OBJECT_0;
+	    key = ioqueue->connecting_keys[pos];
+
+	    /* See whether connect has succeeded. */
+	    WSAEnumNetworkEvents((pj_sock_t)key->hnd, 
+				 ioqueue->connecting_handles[pos], 
+				 &net_events);
+	    *connect_err = net_events.iErrorCode[FD_CONNECT_BIT];
+
+	    /* Erase socket from pending connect. */
+	    erase_connecting_socket(ioqueue, pos);
+	}
+	pj_mutex_unlock(ioqueue->mutex);
+    }
+    return key;
+}
+#endif
+
+
+PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create( pj_pool_t *pool, 
+					 pj_size_t max_fd )
+{
+    pj_ioqueue_t *ioq;
+
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(max_fd);
+
+    ioq = pj_pool_calloc(pool, 1, sizeof(*ioq));
+    ioq->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 2);
+    if (ioq->iocp == NULL) {
+	return NULL;
+    }
+    ioq->mutex = pj_mutex_create(pool, NULL, 0);
+    if (ioq->mutex == NULL) {
+	CloseHandle(ioq->iocp);
+	return NULL;
+    }
+
+    PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioq));
+    return ioq;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque )
+{
+    unsigned i;
+
+    /* Destroy events in the pool */
+    for (i=0; i<ioque->event_count; ++i) {
+	CloseHandle(ioque->event_pool[i]);
+    }
+    ioque->event_count = 0;
+
+    return CloseHandle(ioque->iocp) ? 0 : -1;
+}
+
+PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool,
+					       pj_ioqueue_t *ioque,
+					       pj_oshandle_t hnd,
+					       void *user_data,
+					       const pj_ioqueue_callback *cb)
+{
+    HANDLE hioq;
+    pj_ioqueue_key_t *rec;
+
+    rec = pj_pool_calloc(pool, 1, sizeof(pj_ioqueue_key_t));
+    rec->hnd = hnd;
+    rec->user_data = user_data;
+    pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback));
+#if PJ_HAS_TCP
+    rec->accept_overlapped.newsock = PJ_INVALID_SOCKET;
+#endif
+    hioq = CreateIoCompletionPort(hnd, ioque->iocp, (DWORD)rec, 2);
+    if (!hioq) {
+	return NULL;
+    }
+    return rec;
+}
+
+
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+					   pj_ioqueue_key_t *key )
+{
+#if PJ_HAS_TCP
+    if (key->connecting) {
+	unsigned pos;
+
+	/* Erase from connecting_handles */
+	pj_mutex_lock(ioque->mutex);
+	for (pos=0; pos < ioque->connecting_count; ++pos) {
+	    if (ioque->connecting_keys[pos] == key) {
+		erase_connecting_socket(ioque, pos);
+		closesocket(*key->accept_overlapped.newsock_ptr);
+		break;
+	    }
+	}
+	pj_mutex_unlock(ioque->mutex);
+	key->connecting = 0;
+    }
+#endif
+    return 0;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+    return key->user_data;
+}
+
+/*
+ * Poll for events.
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+    DWORD dwMsec, dwBytesTransfered, dwKey;
+    ioqueue_overlapped *ov;
+    pj_ioqueue_key_t *key;
+    pj_ssize_t size_status;
+    BOOL rc;
+
+    /* Check the connecting array. */
+#if PJ_HAS_TCP
+    key = check_connecting(ioque, &size_status);
+    if (key != NULL) {
+	key->cb.on_connect_complete(key, (int)size_status);
+	return 1;
+    }
+#endif
+
+    /* Calculate miliseconds timeout for GetQueuedCompletionStatus */
+    dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE;
+
+    /* Poll for completion status. */
+    rc = GetQueuedCompletionStatus(ioque->iocp, &dwBytesTransfered, &dwKey,
+				   (OVERLAPPED**)&ov, dwMsec);
+
+    /* The return value is:
+     * - nonzero if event was dequeued.
+     * - zero and ov==NULL if no event was dequeued.
+     * - zero and ov!=NULL if event for failed I/O was dequeued.
+     */
+    if (ov) {
+	/* Event was dequeued for either successfull or failed I/O */
+	key = (pj_ioqueue_key_t*)dwKey;
+	size_status = dwBytesTransfered;
+	switch (ov->operation) {
+	case PJ_IOQUEUE_OP_READ:
+	case PJ_IOQUEUE_OP_RECV_FROM:
+	    key->cb.on_read_complete(key, size_status);
+	    break;
+	case PJ_IOQUEUE_OP_WRITE:
+	case PJ_IOQUEUE_OP_SEND_TO:
+	    key->cb.on_write_complete(key, size_status);
+	    break;
+#if PJ_HAS_TCP
+	case PJ_IOQUEUE_OP_ACCEPT:
+	    /* special case for accept. */
+	    ioqueue_on_accept_complete((ioqueue_accept_rec*)ov);
+	    key->cb.on_accept_complete(key, 0);
+	    break;
+#endif
+	}
+	return 1;
+    }
+
+    if (GetLastError()==WAIT_TIMEOUT) {
+	/* Check the connecting array. */
+#if PJ_HAS_TCP
+	key = check_connecting(ioque, &size_status);
+	if (key != NULL) {
+	    key->cb.on_connect_complete(key, (int)size_status);
+	    return 1;
+	}
+#endif
+	return 0;
+    }
+    return -1;
+}
+
+/*
+ * Initiate overlapped ReadFile operation.
+ */
+PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque,
+			     pj_ioqueue_key_t *key,
+			     void *buffer,
+			     pj_size_t buflen)
+{
+    //BOOL rc;
+    int rc;
+    DWORD bytesRead;
+    DWORD dwFlags = 0;
+
+    PJ_UNUSED_ARG(ioque);
+
+    pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped));
+    key->recv_overlapped.operation = PJ_IOQUEUE_OP_READ;
+
+#if 0
+    rc = ReadFile(key->hnd, buffer, buflen, &bytesRead, 
+		  &key->recv_overlapped.overlapped);
+    if (rc == FALSE) {
+	DWORD dwStatus = GetLastError();
+	return dwStatus==ERROR_IO_PENDING ? PJ_IOQUEUE_PENDING : -1;
+    } else {
+	/*
+	 * This is workaround to a probable bug in Win2000 (probably NT as well).
+	 * Even if 'rc' is zero, which indicates operation has completed,
+	 * GetQueuedCompletionStatus still will return the key.
+	 * So as work around, we always return PJ_IOQUEUE_PENDING here.
+	 */
+	//return bytesRead;
+	return PJ_IOQUEUE_PENDING;
+    }
+#endif
+    key->recv_overlapped.wsabuf.buf = buffer;
+    key->recv_overlapped.wsabuf.len = buflen;
+    rc = WSARecv((SOCKET)key->hnd, &key->recv_overlapped.wsabuf, 1, &bytesRead, &dwFlags,
+		 &key->recv_overlapped.overlapped, NULL);
+    if (rc == SOCKET_ERROR) {
+	DWORD dwStatus = WSAGetLastError();
+	return dwStatus==WSA_IO_PENDING ? PJ_IOQUEUE_PENDING : -1;
+    } else {
+	// Must always return pending status.
+	// See comments on pj_ioqueue_read
+	//return bytesRead;
+	return PJ_IOQUEUE_PENDING;
+    }
+}
+
+/*
+ * Initiate overlapped RecvFrom operation.
+ */
+PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+				 pj_ioqueue_key_t *key,
+				 void *buffer,
+				 pj_size_t buflen,
+				 pj_sockaddr_t *addr,
+				 int *addrlen)
+{
+    BOOL rc;
+    DWORD bytesRead;
+    DWORD dwFlags;
+
+    PJ_UNUSED_ARG(ioque);
+
+    pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped));
+    key->recv_overlapped.operation = PJ_IOQUEUE_OP_RECV_FROM;
+    key->recv_overlapped.wsabuf.buf = buffer;
+    key->recv_overlapped.wsabuf.len = buflen;
+    dwFlags = 0;
+    rc = WSARecvFrom((SOCKET)key->hnd, &key->recv_overlapped.wsabuf, 1, 
+		     &bytesRead, &dwFlags, 
+		     addr, addrlen,
+		     &key->recv_overlapped.overlapped, NULL);
+    if (rc == SOCKET_ERROR) {
+	DWORD dwStatus = WSAGetLastError();
+	return dwStatus==WSA_IO_PENDING ? PJ_IOQUEUE_PENDING : -1;
+    } else {
+	// Must always return pending status.
+	// See comments on pj_ioqueue_read
+	//return bytesRead;
+	return PJ_IOQUEUE_PENDING;
+    }
+}
+
+/*
+ * Initiate overlapped WriteFile operation.
+ */
+PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque,
+			      pj_ioqueue_key_t *key,
+			      const void *data,
+			      pj_size_t datalen)
+{
+    //BOOL rc;
+    int rc;
+    DWORD bytesWritten;
+
+    PJ_UNUSED_ARG(ioque);
+
+    pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped));
+    key->send_overlapped.operation = PJ_IOQUEUE_OP_WRITE;
+    /*
+    rc = WriteFile(key->hnd, data, datalen, &bytesWritten, 
+		   &key->send_overlapped.overlapped);
+    
+    if (rc == FALSE) {
+	DWORD dwStatus = GetLastError();
+	return dwStatus==ERROR_IO_PENDING ? PJ_IOQUEUE_PENDING : -1;
+    } else {
+	// Must always return pending status.
+	// See comments on pj_ioqueue_read
+	// return bytesWritten;
+	return PJ_IOQUEUE_PENDING;
+    }
+    */
+    key->send_overlapped.wsabuf.buf = (void*)data;
+    key->send_overlapped.wsabuf.len = datalen;
+    rc = WSASend((SOCKET)key->hnd, &key->send_overlapped.wsabuf, 1, &bytesWritten, 0,
+		 &key->send_overlapped.overlapped, NULL);
+    if (rc == SOCKET_ERROR) {
+	DWORD dwStatus = WSAGetLastError();
+	return dwStatus==WSA_IO_PENDING ? PJ_IOQUEUE_PENDING : -1;
+    } else {
+	// Must always return pending status.
+	// See comments on pj_ioqueue_read
+	//return bytesRead;
+	return PJ_IOQUEUE_PENDING;
+    }}
+
+/*
+ * Initiate overlapped SendTo operation.
+ */
+PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+			       pj_ioqueue_key_t *key,
+			       const void *data,
+			       pj_size_t datalen,
+			       const pj_sockaddr_t *addr,
+			       int addrlen)
+{
+    BOOL rc;
+    DWORD bytesSent;
+    DWORD dwFlags;
+
+    PJ_UNUSED_ARG(ioque);
+
+    pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped));
+    key->send_overlapped.operation = PJ_IOQUEUE_OP_SEND_TO;
+    key->send_overlapped.wsabuf.buf = (char*)data;
+    key->send_overlapped.wsabuf.len = datalen;
+    dwFlags = 0;
+    rc = WSASendTo((SOCKET)key->hnd, &key->send_overlapped.wsabuf, 1, 
+		   &bytesSent, dwFlags, addr, 
+		   addrlen, &key->send_overlapped.overlapped, NULL);
+    if (rc == SOCKET_ERROR) {
+	DWORD dwStatus = WSAGetLastError();
+	return dwStatus==WSA_IO_PENDING ? PJ_IOQUEUE_PENDING : -1;
+    } else {
+	// Must always return pending status.
+	// See comments on pj_ioqueue_read
+	// return bytesSent;
+	return PJ_IOQUEUE_PENDING;
+    }
+}
+
+#if PJ_HAS_TCP
+
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+			       pj_ioqueue_key_t *key,
+			       pj_sock_t *new_sock,
+			       pj_sockaddr_t *local,
+			       pj_sockaddr_t *remote,
+			       int *addrlen)
+{
+    BOOL rc;
+    DWORD bytesReceived;
+
+    PJ_UNUSED_ARG(ioqueue)
+
+    pj_assert(key->accept_overlapped.operation == 0);
+
+    if (key->accept_overlapped.newsock == PJ_INVALID_SOCKET) {
+	pj_sock_t sock;
+	sock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, PJ_SOCK_ASYNC);
+	if (sock == PJ_INVALID_SOCKET) {
+	    return -1;
+	}
+	key->accept_overlapped.newsock = sock;
+    }
+    key->accept_overlapped.operation = PJ_IOQUEUE_OP_ACCEPT;
+    key->accept_overlapped.addrlen = addrlen;
+    key->accept_overlapped.local = local;
+    key->accept_overlapped.remote = remote;
+    key->accept_overlapped.newsock_ptr = new_sock;
+    pj_memset(&key->accept_overlapped.overlapped, 0, 
+	      sizeof(key->accept_overlapped.overlapped));
+
+    rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)key->accept_overlapped.newsock,
+		   key->accept_overlapped.accept_buf,
+		   0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN,
+		   &bytesReceived,
+		   &key->accept_overlapped.overlapped);
+
+    if (rc == TRUE) {
+	ioqueue_on_accept_complete(&key->accept_overlapped);
+	key->cb.on_accept_complete(key, 0);
+	return 0;
+    } else {
+	DWORD dwStatus = WSAGetLastError();
+	return dwStatus==WSA_IO_PENDING ? PJ_IOQUEUE_PENDING : -1;
+    }
+}
+
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+					pj_ioqueue_key_t *key,
+					const pj_sockaddr_t *addr,
+					int addrlen )
+{
+    unsigned long optval = 1;
+    HANDLE hEvent;
+
+    /* Set socket to non-blocking. */
+    if (ioctlsocket((pj_sock_t)key->hnd, PJ_FIONBIO, &optval) != PJ_OK) {
+	DWORD dwStatus;
+	dwStatus = WSAGetLastError();
+	return -1;
+    }
+
+    /* Initiate connect() */
+    if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) {
+	DWORD dwStatus;
+	dwStatus = WSAGetLastError();
+	if (dwStatus != WSAEWOULDBLOCK) {
+	    /* Permanent error */
+	    return -1;
+	} else {
+	    /* Pending operation. This is what we're looking for. */
+	}
+    } else {
+	/* Connect has completed immediately! */
+	/* Restore to blocking mode. */
+	optval = 0;
+	if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != PJ_OK) {
+	    return -1;
+	}
+
+	key->cb.on_connect_complete(key, 0);
+	return PJ_OK;
+    }
+
+    /* Add to the array of connecting socket to be polled */
+    pj_mutex_lock(ioqueue->mutex);
+
+    if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) {
+	pj_mutex_unlock(ioqueue->mutex);
+	return -1;
+    }
+
+    /* Get or create event object. */
+    if (ioqueue->event_count) {
+	hEvent = ioqueue->event_pool[ioqueue->event_count - 1];
+	--ioqueue->event_count;
+    } else {
+	hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (hEvent == NULL) {
+	    pj_mutex_unlock(ioqueue->mutex);
+	    return -1;
+	}
+    }
+
+    /* Mark key as connecting.
+     * We can't use array index since key can be removed dynamically. 
+     */
+    key->connecting = 1;
+
+    /* Associate socket events to the event object. */
+    if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) {
+	CloseHandle(hEvent);
+	pj_mutex_unlock(ioqueue->mutex);
+	return -1;
+    }
+
+    /* Add to array. */
+    ioqueue->connecting_keys[ ioqueue->connecting_count ] = key;
+    ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent;
+    ioqueue->connecting_count++;
+
+    pj_mutex_unlock(ioqueue->mutex);
+
+    return PJ_IOQUEUE_PENDING;
+}
+#endif	/* #if PJ_HAS_TCP */
+
+#endif	/* #if PJ_IOQUEUE_USE_WIN32_IOCP */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/list.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/list.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/list.c	(revision 1753)
@@ -0,0 +1,29 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/list.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/list.h>
+
+#if !PJ_FUNCTIONS_ARE_INLINED
+#  include "list_i.h"
+#endif
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/list.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/list.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/list.h	(revision 1753)
@@ -0,0 +1,272 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/list.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_LIST_H__
+#define __PJ_LIST_H__
+
+/**
+ * @file list.h
+ * @brief Linked List data structure.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_DS Data Structure.
+ * @ingroup PJ
+ */
+/**
+ * @defgroup PJ_LIST Linked List
+ * @ingroup PJ_DS
+ * @{
+ *
+ * List in PJLIB is implemented as doubly-linked list, and it won't require
+ * dynamic memory allocation (just as all PJLIB data structures). The list here
+ * should be viewed more like a low level C list instead of high level C++ list
+ * (which normally are easier to use but require dynamic memory allocations),
+ * therefore all caveats with C list apply here too (such as you can NOT put
+ * a node in more than one lists).
+ *
+ * It's best to describe the list using a simple example below.
+ *
+ * Sample:
+ \verbatim
+  #include <pj/list.h>
+  #include <stdio.h>
+
+  struct my_node
+  {
+    // This must be the first member declared in the struct!
+    PJ_DECL_LIST_MEMBER(struct my_node)
+    int value;
+  };
+ 
+  int main()
+  {
+     struct my_node nodes[10];
+     struct my_node list;
+     struct my_node iterator;
+     int i;
+ 
+     // Initialize the list as empty.
+     pj_list_init(&list);
+ 
+     // Insert nodes.
+     for (i=0; i<10; ++i) {
+        nodes[i].value = i;
+        pj_list_insert_before(&list, &nodes[i]);
+     }
+ 
+     // Iterate list nodes.
+     iterator = list.next;
+     while (iterator != &list) {
+        printf("value = %d\n", iterator->value);
+        iterator = iterator->next;
+     }
+
+     // Erase all nodes.
+     for (i=0; i<10; ++i) {
+	pj_list_erase(&nodes[i]);
+     }
+
+     // List must be empty by now.
+     pj_assert( pj_list_empty(&list) );
+
+     return 0;
+  };
+ \endverbatim
+ *
+ */
+
+
+/**
+ * Use this macro in the start of the structure declaration to declare that
+ * the structure can be used in the linked list operation.
+ */
+#define PJ_DECL_LIST_MEMBER(type)  type *prev, *next;
+
+
+/**
+ * This structure describes generic list node and list. The owner of this list
+ * must initialize the 'value' member to an appropriate value (typically the
+ * owner itself).
+ */
+struct pj_list
+{
+    PJ_DECL_LIST_MEMBER(void)
+};
+
+
+/**
+ * Initialize the list.
+ * Initially, the list will have no member, and function pj_list_empty() will
+ * always return nonzero (which indicates TRUE) for the newly initialized 
+ * list.
+ *
+ * @param node The list head.
+ */
+PJ_IDECL(void) pj_list_init(pj_list_type * node);
+
+
+/**
+ * Check that the list is empty.
+ *
+ * @param node	The list head.
+ *
+ * @return Non-zero if the list is not-empty, or zero if it is empty.
+ *
+ */
+PJ_IDECL(int) pj_list_empty(const pj_list_type * node);
+
+
+/**
+ * Insert the node to the list before the specified element position.
+ *
+ * @param pos	The element to which the node will be inserted before. 
+ * @param node	The element to be inserted.
+ *
+ * @return void.
+ */
+PJ_IDECL(void)	pj_list_insert_before(pj_list_type *pos, pj_list_type *node);
+
+
+/**
+ * Remove elements from the source list, and insert them to the destination
+ * list. The elements of the source list will occupy the
+ * front elements of the target list. Note that the node pointed by \a list2
+ * itself is not considered as a node, but rather as the list descriptor, so
+ * it will not be inserted to the \a list1. The elements to be inserted starts
+ * at \a list2->next. If \a list2 is to be included in the operation, use
+ * \a pj_list_insert_nodes_before.
+ *
+ * @param list1	The destination list.
+ * @param list2	The source list.
+ *
+ * @return void.
+ */
+PJ_IDECL(void) pj_list_merge_first(pj_list_type *list1, pj_list_type *list2);
+
+
+/**
+ * Inserts all nodes in \a nodes to the target list.
+ *
+ * @param lst	    The target list.
+ * @param nodes	    Nodes list.
+ */
+PJ_IDECL(void) pj_list_insert_nodes_before(pj_list_type *lst,
+					   pj_list_type *nodes);
+
+/**
+ * Insert all nodes in \a nodes to the target list.
+ *
+ * @param lst	    The target list.
+ * @param nodes	    Nodes list.
+ */
+PJ_IDECL(void) pj_list_insert_nodes_after(pj_list_type *lst,
+					  pj_list_type *nodes);
+
+/**
+ * Insert a node to the list after the specified element position.
+ *
+ * @param pos	    The element in the list which will precede the inserted 
+ *		    element.
+ * @param node	    The element to be inserted after the position element.
+ *
+ * @return void.
+ */
+PJ_IDECL(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node);
+
+
+/**
+ * Remove elements from the second list argument, and insert them to the list 
+ * in the first argument. The elements from the second list will be appended
+ * to the first list. Note that the node pointed by \a list2
+ * itself is not considered as a node, but rather as the list descriptor, so
+ * it will not be inserted to the \a list1. The elements to be inserted starts
+ * at \a list2->next. If \a list2 is to be included in the operation, use
+ * \a pj_list_insert_nodes_before.
+ *
+ * @param list1	    The element in the list which will precede the inserted 
+ *		    element.
+ * @param list2	    The element in the list to be inserted.
+ *
+ * @return void.
+ */
+PJ_IDECL(void) pj_list_merge_last( pj_list_type *list1, pj_list_type *list2);
+
+
+/**
+ * Erase the node from the list it currently belongs.
+ *
+ * @param node	    The element to be erased.
+ */
+PJ_IDECL(void) pj_list_erase(pj_list_type *node);
+
+
+/**
+ * Find node in the list.
+ *
+ * @param list	    The list head.
+ * @param node	    The node element to be searched.
+ *
+ * @return The node itself if it is found in the list, or NULL if it is not 
+ *         found in the list.
+ */
+PJ_IDECL(pj_list_type*) pj_list_find_node(pj_list_type *list, 
+					  pj_list_type *node);
+
+
+/**
+ * Search the list for the specified value, using the specified comparison
+ * function. This function iterates on nodes in the list, started with the
+ * first node, and call the user supplied comparison function until the
+ * comparison function returns ZERO.
+ *
+ * @param list	    The list head.
+ * @param value	    The user defined value to be passed in the comparison 
+ *		    function
+ * @param comp	    The comparison function, which should return ZERO to 
+ *		    indicate that the searched value is found.
+ *
+ * @return The first node that matched, or NULL if it is not found.
+ */
+PJ_IDECL(pj_list_type*) pj_list_search(pj_list_type *list, void *value,
+				       int (*comp)(void *value, 
+						   const pj_list_type *node)
+				       );
+
+
+/**
+ * @}
+ */
+
+#if PJ_FUNCTIONS_ARE_INLINED
+#  include "list_i.h"
+#endif
+
+PJ_END_DECL
+
+#endif	/* __PJ_LIST_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/list_i.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/list_i.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/list_i.h	(revision 1753)
@@ -0,0 +1,120 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/list_i.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+
+/* Internal */
+PJ_IDEF(void) pj_link_node(pj_list_type *prev, pj_list_type *next)
+{
+    ((pj_list*)prev)->next = next;
+    ((pj_list*)next)->prev = prev;
+}
+
+PJ_IDEF(void) 
+pj_list_init(pj_list_type * node)
+{
+    ((pj_list*)node)->next = ((pj_list*)node)->prev = node;
+}
+
+PJ_IDEF(int) pj_list_empty(const pj_list_type * node)
+{
+    return ((pj_list*)node)->next == node;
+}
+
+PJ_IDEF(void) 
+pj_list_insert_after(pj_list_type *pos, pj_list_type *node)
+{
+    ((pj_list*)node)->prev = pos;
+    ((pj_list*)node)->next = ((pj_list*)pos)->next;
+    ((pj_list*) ((pj_list*)pos)->next) ->prev = node;
+    ((pj_list*)pos)->next = node;
+}
+
+
+PJ_IDEF(void) 
+pj_list_insert_before(pj_list_type *pos, pj_list_type *node)
+{
+    pj_list_insert_after(((pj_list*)pos)->prev, node);
+}
+
+
+PJ_IDEF(void)	    
+pj_list_insert_nodes_after(pj_list_type *pos, pj_list_type *lst)
+{
+    pj_list *lst_last = (pj_list *) ((pj_list*)lst)->prev;
+    pj_list *pos_next = (pj_list *) ((pj_list*)pos)->next;
+
+    pj_link_node(pos, lst);
+    pj_link_node(lst_last, pos_next);
+}
+
+PJ_IDEF(void) 
+pj_list_insert_nodes_before(pj_list_type *pos, pj_list_type *lst)
+{
+    pj_list_insert_nodes_after(((pj_list*)pos)->prev, lst);
+}
+
+PJ_IDEF(void)
+pj_list_merge_last(pj_list_type *lst1, pj_list_type *lst2)
+{
+    pj_link_node(((pj_list*)lst1)->prev, ((pj_list*)lst2)->next);
+    pj_link_node(((pj_list*)lst2)->prev, lst1);
+    pj_list_init(lst2);
+}
+
+PJ_IDEF(void)
+pj_list_merge_first(pj_list_type *lst1, pj_list_type *lst2)
+{
+    pj_link_node(((pj_list*)lst2)->prev, ((pj_list*)lst1)->next);
+    pj_link_node(((pj_list*)lst1), ((pj_list*)lst2)->next);
+    pj_list_init(lst2);
+}
+
+PJ_IDEF(void) 
+pj_list_erase(pj_list_type *node)
+{
+    pj_link_node( ((pj_list*)node)->prev, ((pj_list*)node)->next);
+}
+
+
+PJ_IDEF(pj_list_type*) 
+pj_list_find_node(pj_list_type *list, pj_list_type *node)
+{
+    pj_list *p = (pj_list *) ((pj_list*)list)->next;
+    while (p != list && p != node)
+	p = (pj_list *) p->next;
+
+    return p==node ? p : NULL;
+}
+
+
+PJ_IDEF(pj_list_type*) 
+pj_list_search(pj_list_type *list, void *value,
+	       int (*comp)(void *value, const pj_list_type *node))
+{
+    pj_list *p = (pj_list *) ((pj_list*)list)->next;
+    while (p != list && (*comp)(value, p) != 0)
+	p = (pj_list *) p->next;
+
+    return p==list ? NULL : p;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/log.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/log.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/log.c	(revision 1753)
@@ -0,0 +1,142 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/log.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/types.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#if PJ_LOG_MAX_LEVEL >= 1
+
+static int log_max_level = PJ_LOG_MAX_LEVEL;
+static pj_log_func *log_writer = &pj_log_to_stdout;
+
+PJ_DEF(void) pj_log_set_level(int level)
+{
+    log_max_level = level;
+}
+
+static void pj_log(const char *obj, int level, const char *format, va_list marker)
+{
+    pj_time_val now;
+    pj_parsed_time ptime;
+    char buffer[1500];
+    int len;
+
+    if (level > log_max_level)
+	return;
+
+    /* Get current date/time. */
+    pj_gettimeofday(&now);
+    pj_time_decode(&now, &ptime);
+
+    /* Print the whole message to the string buffer. */
+    len = snprintf(buffer, sizeof(buffer), "%02d:%02d:%03d %-12.12s ", 
+		   ptime.min, ptime.sec, ptime.msec, obj);
+    len = len + vsnprintf(buffer+len, sizeof(buffer)-len, format, marker);
+    if (len > 0 && len < sizeof(buffer)-1) {
+	buffer[len++] = '\n';
+	buffer[len++] = '\0';
+    } else {
+	len = sizeof(buffer)-1;
+	buffer[sizeof(buffer)-2] = '\n';
+	buffer[sizeof(buffer)-1] = '\0';
+    }
+
+    if (log_writer)
+	(*log_writer)(level, buffer, len);
+}
+
+PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
+{
+    log_writer = func;
+}
+
+PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 0, format, arg);
+    va_end(arg);
+}
+
+PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 1, format, arg);
+    va_end(arg);
+}
+#endif	/* PJ_LOG_MAX_LEVEL >= 1 */
+
+#if PJ_LOG_MAX_LEVEL >= 2
+PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 2, format, arg);
+    va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 3
+PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 3, format, arg);
+    va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 4
+PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 4, format, arg);
+    va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 5
+PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 5, format, arg);
+    va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 6
+PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 6, format, arg);
+    va_end(arg);
+}
+#endif
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/log.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/log.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/log.h	(revision 1753)
@@ -0,0 +1,177 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/log.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_LOG_H__
+#define __PJ_LOG_H__
+
+/**
+ * @file log.h
+ * @brief Logging Utility.
+ */
+
+#include <pj/types.h>
+#include <stdio.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_MISC Miscelaneous
+ * @ingroup PJ
+ */
+
+/**
+ * @defgroup PJ_LOG Logging Utility
+ * @ingroup PJ_MISC
+ * @{
+ */
+
+/**
+ * Declare maximum logging level/verbosity. Lower number indicates higher
+ * importance, with the highest importance has level zero. The least
+ * important level is five in this implementation, but this can be extended
+ * by supplying the appropriate implementation.
+ *
+ * The level conventions:
+ *  - 0: fatal error
+ *  - 1: error
+ *  - 2: warning
+ *  - 3: info
+ *  - 4: debug
+ *  - 5: trace
+ *  - 6: more detailed trace
+ */
+#ifndef PJ_LOG_MAX_LEVEL
+#  define PJ_LOG_MAX_LEVEL   4
+#endif
+
+/**
+ * Logging macro.
+ * @param level The logging verbosity level. Lower number indicates higher
+ *              importance, with level zero indicates fatal error.
+ * @param obj_format_argf Enclosed 'printf' like arguments, with the first 
+ *	        argument is the format string and the following arguments are 
+ *              the arguments suitable for the format string.
+ *
+ * Sample:
+ * \verbatim
+   PJ_LOG(2, (__FILE__, "current value is %d", value));
+   \endverbatim
+ */
+#define PJ_LOG(level,obj_format_argf)	pj_log_wrapper_##level(obj_format_argf)
+
+/**
+ * Logging function.
+ *
+ * @param level	    Log level.
+ * @param data	    Log message.
+ * @param len	    Message length.
+ */
+typedef void pj_log_func(int level, const char *data, int len);
+
+/**
+ * Default logging function which writes to stdout.
+ *
+ * @param level	    Log level.
+ * @param buffer    Log message.
+ * @param len	    Message length.
+ */
+PJ_DECL(void) pj_log_to_stdout(int level, const char *buffer, int len);
+
+
+#if PJ_LOG_MAX_LEVEL >= 1
+
+/**
+ * Set log output.
+ * @param file the output file.
+ */
+PJ_DECL(void) pj_log_set_log_func( pj_log_func *func );
+
+/**
+ * Set maximum log level.
+ * @param level the maximum level.
+ */
+PJ_DECL(void) pj_log_set_level(int level);
+
+#else	/* #if PJ_LOG_MAX_LEVEL >= 1 */
+
+#  define pj_log_set_log_func(x)
+#  define pj_log_set_level(x)
+
+#endif	/* #if PJ_LOG_MAX_LEVEL >= 1 */
+
+/** 
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Log functions implementation prototypes.
+ */
+
+#if PJ_LOG_MAX_LEVEL >= 1
+    #define pj_log_wrapper_1(arg)	pj_log_1 arg
+    PJ_DECL(void) pj_log_1(const char *, const char *format, ...);
+#else
+    #define pj_log_wrapper_1(arg)
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 2
+    #define pj_log_wrapper_2(arg)	pj_log_2 arg
+    PJ_DECL(void) pj_log_2(const char *, const char *format, ...);
+#else
+    #define pj_log_wrapper_2(arg)
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 3
+    #define pj_log_wrapper_3(arg)	pj_log_3 arg
+    PJ_DECL(void) pj_log_3(const char *, const char *format, ...);
+#else
+    #define pj_log_wrapper_3(arg)
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 4
+    #define pj_log_wrapper_4(arg)	pj_log_4 arg
+    PJ_DECL(void) pj_log_4(const char *, const char *format, ...);
+#else
+    #define pj_log_wrapper_4(arg)
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 5
+    #define pj_log_wrapper_5(arg)	pj_log_5 arg
+    PJ_DECL(void) pj_log_5(const char *, const char *format, ...);
+#else
+    #define pj_log_wrapper_5(arg)
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 6
+    #define pj_log_wrapper_6(arg)	pj_log_6 arg
+    PJ_DECL(void) pj_log_6(const char *, const char *format, ...);
+#else
+    #define pj_log_wrapper_6(arg)
+#endif
+
+
+PJ_END_DECL
+
+#endif
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/log_stdout.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/log_stdout.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/log_stdout.c	(revision 1753)
@@ -0,0 +1,76 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/log_stdout.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if !defined(PJ_WIN32_WINCE)
+
+#define CLR_FATAL    (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R)
+#define CLR_WARNING  (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G)
+#define CLR_INFO     (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | \
+		      PJ_TERM_COLOR_B)
+#define CLR_DEFAULT  (PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B)
+
+static void term_set_color(int level)
+{
+    unsigned attr = 0;
+    switch (level) {
+    case 0:
+    case 1: attr = CLR_FATAL; 
+	break;
+    case 2: attr = CLR_WARNING; 
+	break;
+    case 3: attr = CLR_INFO; 
+	break;
+    default:
+	attr = CLR_DEFAULT;
+	break;
+    }
+
+    pj_term_set_color(attr);
+}
+
+PJ_DEF(void) pj_log_to_stdout(int level, const char *buffer, int len)
+{
+    PJ_UNUSED_ARG(len)
+
+    /* Copy to terminal/file. */
+    term_set_color(level);
+    fputs(buffer, stdout);
+    pj_term_set_color(CLR_DEFAULT);
+
+    fflush(stdout);
+}
+
+#else
+
+static void term_set_color(int level)
+{
+}
+
+PJ_DEF(void) pj_log_to_stdout(int level, const char *buffer, int len)
+{
+}
+
+#endif
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/md5.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/md5.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/md5.c	(revision 1753)
@@ -0,0 +1,416 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/md5.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+	http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+	either statically or dynamically; added missing #include <string.h>
+	in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+	type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+	unsigned in ANSI C, signed in traditional"; made test program
+	self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER	/* 1 = big-endian, -1 = little-endian, 0 = unknown */
+
+/*
+#ifdef ARCH_IS_BIG_ENDIAN
+#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#  define BYTE_ORDER 0
+#endif
+*/
+/* pjlib: */
+#include <pj/config.h>
+#if PJ_IS_LITTLE_ENDIAN
+#  define BYTE_ORDER -1
+#elif PJ_IS_BIG_ENDIAN
+#  define BYTE_ORDER 1
+#else
+#  error Endianess is not known!
+#endif
+
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+    md5_word_t
+	a = pms->abcd[0], b = pms->abcd[1],
+	c = pms->abcd[2], d = pms->abcd[3];
+    md5_word_t t;
+#if BYTE_ORDER > 0
+    /* Define storage only for big-endian CPUs. */
+    md5_word_t X[16];
+#else
+    /* Define storage for little-endian or both types of CPUs. */
+    md5_word_t xbuf[16];
+    const md5_word_t *X;
+#endif
+
+    {
+#if BYTE_ORDER == 0
+	/*
+	 * Determine dynamically whether this is a big-endian or
+	 * little-endian machine, since we can use a more efficient
+	 * algorithm on the latter.
+	 */
+	static const int w = 1;
+
+	if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0		/* little-endian */
+	{
+	    /*
+	     * On little-endian machines, we can process properly aligned
+	     * data without copying it.
+	     */
+	    if (!((data - (const md5_byte_t *)0) & 3)) {
+		/* data are properly aligned */
+		X = (const md5_word_t *)data;
+	    } else {
+		/* not aligned */
+		memcpy(xbuf, data, 64);
+		X = xbuf;
+	    }
+	}
+#endif
+#if BYTE_ORDER == 0
+	else			/* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0		/* big-endian */
+	{
+	    /*
+	     * On big-endian machines, we must arrange the bytes in the
+	     * right order.
+	     */
+	    const md5_byte_t *xp = data;
+	    int i;
+
+#  if BYTE_ORDER == 0
+	    X = xbuf;		/* (dynamic only) */
+#  else
+#    define xbuf X		/* (static only) */
+#  endif
+	    for (i = 0; i < 16; ++i, xp += 4)
+		xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+	}
+#endif
+    }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+	return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+	pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+	int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+	memcpy(pms->buf + offset, p, copy);
+	if (offset + copy < 64)
+	    return;
+	p += copy;
+	left -= copy;
+	md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+	md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+	memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/md5.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/md5.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/md5.h	(revision 1753)
@@ -0,0 +1,113 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/md5.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.1 2005/12/02 20:02:29 nn Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+	http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+	references to Ghostscript; clarified derivation from RFC 1321;
+	now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+	added conditionalization for C++ compilation from Martin
+	Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/** Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];	/**< message length in bits, lsw first */
+    md5_word_t abcd[4];		/**< digest buffer */
+    md5_byte_t buf[64];		/**< accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+/** Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/** Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/** Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/os.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/os.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/os.h	(revision 1753)
@@ -0,0 +1,726 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/os.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_OS_H__
+#define __PJ_OS_H__
+
+/**
+ * @file os.h
+ * @brief OS dependent functions
+ */
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_OS Operating System Dependent Functionality.
+ * @ingroup PJ
+ */
+
+/**
+ * Log the last error message.
+ * @param format format of string message preceeding the error.
+ */
+PJ_DECL(void) pj_perror(const char *src, const char *format, ...);
+
+/**
+ * Wrapper for pj_perror.
+ * Sample usage: PJ_PERROR((__FILE__, "bind() to addr %s error", addr));
+ */
+#define PJ_PERROR(ARGS)   pj_perror ARGS
+
+/**
+ * Get error/status of the last operation.
+ * @return OS dependent error code.
+ */
+PJ_DECL(pj_status_t) pj_getlasterror(void);
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_THREAD Threads
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/**
+ * Thread creation flags:
+ * - PJ_THREAD_SUSPENDED: specify that the thread should be created suspended.
+ * -
+ */
+typedef enum pj_thread_create_flags
+{
+    PJ_THREAD_SUSPENDED = 1
+} pj_thread_create_flags;
+
+
+/**
+ * Type of thread entry function.
+ */
+typedef void *(PJ_THREAD_FUNC pj_thread_proc)(void*);
+
+/**
+ * Thread structure, to thread's state when the thread is created by external
+ * or native API. 
+ */
+typedef pj_uint8_t pj_thread_desc[32];
+
+/**
+ * Get process ID.
+ * @return process ID.
+ */
+PJ_DECL(pj_uint32_t) pj_getpid(void);
+
+/**
+ * Create a new thread.
+ * @param pool 
+ *	       The memory pool from which the thread record will be allocated 
+ *             from.
+ * @param thread_name 
+ *		The optional name to be assigned to the thread.
+ * @param proc 
+ *		Thread entry function.
+ * @param arg  
+ *	        Argument to be passed to the thread entry function.
+ * @param stack_size 
+ *		The size of the stack for the new thread, or ZERO for the
+ *	        default stack size.
+ * @param stack_ptr
+ *		Optional pointer to the buffer to be used by the thread, or
+ *		NULL if the caller doesn't wish to supply the buffer. Not
+ *		all operating systems support this feature. If the OS doesn't
+ *		support this feature, then this argument must be NULL.
+ * @param flags
+ *		Flags for thread creation, which is bitmask combination from
+ *		enum pj_thread_create_flags.
+ *
+ * @return
+ *  The handle for the new thread, or NULL if the thread can NOT be created.
+ */
+PJ_DECL(pj_thread_t*) pj_thread_create( pj_pool_t *pool, const char *thread_name,
+				        pj_thread_proc *proc, void *arg,
+				        pj_size_t stack_size, void *stack_ptr, 
+				        unsigned flags);
+
+/**
+ * Register a thread that was created by external or native API to PJLIB.
+ * This function must be called in the context of the thread being registered.
+ * When the thread is created by external function or API call,
+ * it must be 'registered' to PJLIB using pj_thread_register(), so that it can
+ * cooperate with PJLIB's framework. During registration, some data needs to
+ * be maintained, and this data must remain available during the thread's 
+ * lifetime.
+ * @param thread_name
+ *	    The optional name to be assigned to the thread.
+ * @param desc
+ *	    Thread descriptor, which must be available throughout the lifetime
+ *	    of the thread.
+ * @return
+ *  The handle for the thread.
+ */
+PJ_DECL(pj_thread_t*) pj_thread_register (const char *thread_name,
+					  pj_thread_desc desc);
+
+/**
+ * Get thread name.
+ *
+ * @param thread    The thread handle.
+ *
+ * @return Thread name as null terminated string.
+ */
+PJ_DECL(const char*) pj_thread_get_name(pj_thread_t *thread);
+
+/**
+ * Resume a suspended thread.
+ *
+ * @param thread    The thread handle.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_thread_resume(pj_thread_t *thread);
+
+/**
+ * Get the current thread.
+ *
+ * @return Thread handle of current thread.
+ */
+PJ_DECL(pj_thread_t*) pj_thread_this(void);
+
+/**
+ * Join thread.
+ * This function will block the caller thread until the specified thread exits.
+ *
+ * @param thread    The thread handle.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_thread_join(pj_thread_t *thread);
+
+
+/**
+ * Destroy thread and release resources allocated for the thread.
+ * However, the memory allocated for the pj_thread_t itself will only be released
+ * when the pool used to create the thread is destroyed.
+ *
+ * @param thread    The thread handle.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_thread_destroy(pj_thread_t *thread);
+
+
+/**
+ * Put the current thread to sleep for the specified miliseconds.
+ *
+ * @param msec Miliseconds delay.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_thread_sleep(unsigned msec);
+
+/**
+ * Put the current thread to sleep for the specified microseconds.
+ *
+ * @param usec Microseconds delay.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_thread_usleep(unsigned usec);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_TLS Thread Local Storage.
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/** 
+ * Allocate thread local storage index. The initial value of the variable at
+ * the index is zero.
+ *
+ * @return the index, or -1 when an index can not be allocated.
+ */
+PJ_DECL(long) pj_thread_local_alloc(void);
+
+/**
+ * Deallocate thread local variable.
+ *
+ * @param index the variable index.
+ */
+PJ_DECL(void) pj_thread_local_free(long index);
+
+/**
+ * Set the value of thread local variable.
+ *
+ * @param index	    the index of the variable.
+ * @param value	    the value.
+ */
+PJ_DECL(void) pj_thread_local_set(long index, void *value);
+
+/**
+ * Get the value of thread local variable.
+ *
+ * @param index the index of the variable.
+ * @return the value.
+ */
+PJ_DECL(void*) pj_thread_local_get(long index);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_ATOMIC Atomic Operations.
+ * @ingroup PJ_OS
+ * @{
+ */
+
+
+/**
+ * Create atomic variable.
+ *
+ * @param pool	    the pool.
+ * @param initial   the initial value of the atomic variable.
+ *
+ * @return the atomic variable, or NULL if failed.
+ */
+PJ_DECL(pj_atomic_t*) pj_atomic_create( pj_pool_t *pool, long initial );
+
+/**
+ * Destroy atomic variable.
+ *
+ * @param atomic_var	the atomic variable.
+ *
+ * @return PJ_OK if success.
+ */
+PJ_DECL(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var );
+
+/**
+ * Set the value of an atomic type, and return the previous value.
+ *
+ * @param atomic_var	the atomic variable.
+ * @param value		value to be set to the variable.
+ *
+ * @return the previous value of the variable.
+ */
+PJ_DECL(long) pj_atomic_set(pj_atomic_t *atomic_var, long value);
+
+/**
+ * Get the value of an atomic type.
+ *
+ * @param atomic_var	the atomic variable.
+ *
+ * @return the value of the atomic variable.
+ */
+PJ_DECL(long) pj_atomic_get(pj_atomic_t *atomic_var);
+
+/**
+ * Increment the value of an atomic type.
+ *
+ * @param atomic_var	the atomic variable.
+ *
+ * @return the result.
+ */
+PJ_DECL(long) pj_atomic_inc(pj_atomic_t *atomic_var);
+
+/**
+ * Decrement the value of an atomic type.
+ *
+ * @param atomic_var	the atomic variable.
+ *
+ * @return the result.
+ */
+PJ_DECL(long) pj_atomic_dec(pj_atomic_t *atomic_var);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_MUTEX Mutexes.
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/**
+ * Mutex types:
+ *  - PJ_MUTEX_DEFAULT: default mutex type, which is system dependent.
+ *  - PJ_MUTEX_SIMPLE: non-recursive mutex.
+ *  - PJ_MUTEX_RECURSIVE: recursive mutex.
+ */
+typedef enum pj_mutex_type_e
+{
+    PJ_MUTEX_DEFAULT,
+    PJ_MUTEX_SIMPLE,
+    PJ_MUTEX_RECURSE,
+} pj_mutex_type_e;
+
+/**
+ * Create mutex.
+ *
+ * @param pool	    the pool.
+ * @param name	    Name to be associated with the mutex (for debugging).
+ * @param type	    The type of the mutex, of type \a pj_mutex_type_e.
+ *
+ * @return the mutex, or NULL if failed.
+ */
+PJ_DECL(pj_mutex_t*) pj_mutex_create(pj_pool_t *pool, const char *name,
+				     int type);
+
+/**
+ * Acquire mutex lock.
+ *
+ * @param mutex the mutex.
+ * @return PJ_OK (zero) if success.
+ */
+PJ_DECL(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex);
+
+/**
+ * Release mutex lock.
+ *
+ * @param mutex the mutex.
+ * @return PJ_OK (zero) if success.
+ */
+PJ_DECL(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex);
+
+/**
+ * Try to acquire mutex lock.
+ *
+ * @param mutex the mutex.
+ * @return PJ_OK (zero) if success, or -1 if lock can not be acquired.
+ */
+PJ_DECL(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex);
+
+/**
+ * Destroy mutex.
+ *
+ * @param mutex the mutex.
+ * @return PJ_OK (zero) if success.
+ */
+PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex);
+
+#if PJ_DEBUG
+/**
+ * Determine whether calling thread is owning the mutex (only available when
+ * PJ_DEBUG is set).
+ * @param mutex the mutex.
+ * @return non-zero if yes.
+ */
+PJ_DECL(pj_status_t) pj_mutex_is_locked(pj_mutex_t *mutex);
+#endif
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_CRIT_SEC Critical sections.
+ * @ingroup PJ_OS
+ * @{
+ */
+/**
+ * Enter critical section.
+ */
+PJ_DECL(void) pj_enter_critical_section(void);
+
+/**
+ * Leave critical section.
+ */
+PJ_DECL(void) pj_leave_critical_section(void);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_SEM Semaphores.
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/**
+ * Create semaphore.
+ *
+ * @param pool	    The pool.
+ * @param name	    Name to be assigned to the semaphore (for logging purpose)
+ * @param initial   The initial count of the semaphore.
+ * @param max	    The maximum count of the semaphore.
+ *
+ * @return semaphore handle, or NULL if failed.
+ */
+PJ_DECL(pj_sem_t*) pj_sem_create(pj_pool_t *pool, const char *name,
+				 unsigned initial, unsigned max);
+
+/**
+ * Wait for semaphore.
+ *
+ * @param sem	The semaphore.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_sem_wait(pj_sem_t *sem);
+
+/**
+ * Try wait for semaphore.
+ *
+ * @param sem	The semaphore.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_sem_trywait(pj_sem_t *sem);
+
+/**
+ * Release semaphore.
+ *
+ * @param sem	The semaphore.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_sem_post(pj_sem_t *sem);
+
+/**
+ * Destroy semaphore.
+ *
+ * @param sem	The semaphore.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_sem_destroy(pj_sem_t *sem);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_EVENT Event Object.
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/**
+ * Create event object.
+ *
+ * @param pool		The pool.
+ * @param name		The name of the event object (for logging purpose).
+ * @param manual_reset	Specify whether the event is manual-reset
+ * @param initial	Specify the initial state of the event object.
+ *
+ * @return event handle, or NULL if failed.
+ */
+PJ_DECL(pj_event_t*) pj_event_create(pj_pool_t *pool, const char *name,
+				     pj_bool_t manual_reset, pj_bool_t initial);
+
+/**
+ * Wait for event to be signaled.
+ *
+ * @param event	    The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_wait(pj_event_t *event);
+
+/**
+ * Try wait for event object to be signalled.
+ *
+ * @param event The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_trywait(pj_event_t *event);
+
+/**
+ * Set the event object state to signaled. For auto-reset event, this 
+ * will only release the first thread that are waiting on the event. For
+ * manual reset event, the state remains signaled until the event is reset.
+ * If there is no thread waiting on the event, the event object state 
+ * remains signaled.
+ *
+ * @param event	    The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_set(pj_event_t *event);
+
+/**
+ * Set the event object to signaled state to release appropriate number of
+ * waiting threads and then reset the event object to non-signaled. For
+ * manual-reset event, this function will release all waiting threads. For
+ * auto-reset event, this function will only release one waiting thread.
+ *
+ * @param event	    The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_pulse(pj_event_t *event);
+
+/**
+ * Set the event object state to non-signaled.
+ *
+ * @param event	    The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_reset(pj_event_t *event);
+
+/**
+ * Destroy the event object.
+ *
+ * @param event	    The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_destroy(pj_event_t *event);
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJ_TIME Time Data Type and Manipulation.
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/**
+ * Get current time of day in local representation.
+ *
+ * @param tv	Variable to store the result.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_gettimeofday(pj_time_val *tv);
+
+
+/**
+ * Parse time value into date/time representation.
+ *
+ * @param tv	The time.
+ * @param pt	Variable to store the date time result.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt);
+
+/**
+ * Encode date/time to time value.
+ *
+ * @param pt	The date/time.
+ * @param tv	Variable to store time value result.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
+
+/**
+ * Convert local time to GMT.
+ *
+ * @param tv	Time to convert.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ *
+ * @param tv	Time to convert.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_TERM Terminal
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/**
+ * Set current terminal color.
+ *
+ * @param color	    The RGB color.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_term_set_color(pj_color_t color);
+
+/**
+ * Get current terminal foreground color.
+ *
+ * @return RGB color.
+ */
+PJ_DECL(pj_color_t) pj_term_get_color(void);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * High resolution timer.
+ */
+#if PJ_HAS_HIGH_RES_TIMER
+
+/**
+ * This structure represents high resolution (64bit) time value. The meaning
+ * of the value depends on the resolution of the timer used.
+ */
+typedef union pj_hr_timestamp
+{
+    struct
+    {
+#if PJ_IS_LITTLE_ENDIAN
+	pj_uint32_t lo;
+	pj_uint32_t hi;
+#else
+#   error "You've got to see and fix this!"
+#endif
+    } u32;
+
+#if PJ_HAS_INT64
+    pj_uint64_t u64;
+#endif
+} pj_hr_timestamp;
+
+/**
+ * Acquire high resolution timer value.
+ *
+ * @param ts	High resolution timer value.
+ */
+PJ_INLINE(void) pj_hr_gettimestamp(pj_hr_timestamp *ts)
+{
+#if defined(_MSC_VER) && defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM != 0
+    __asm 
+    {
+	RDTSC
+	MOV EBX, DWORD PTR [ts]
+	MOV DWORD ptr [EBX]+4, EDX
+	MOV DWORD ptr [EBX], EAX
+    }
+#elif defined(__GNUC__) && defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM != 0
+    __asm__ __volatile__ ( "rdtsc"
+			   : "=a" (ts->u32.lo), "=d" (ts->u32.hi));
+#elif defined(PJ_WIN32)
+    ts->hi = 0;
+    ts->lo = GetTickCount();
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    ts->hi = tv.tv_sec;
+    ts->lo = tv.tv_usec;
+#endif
+}
+
+
+#endif	/* PJ_HAS_HIGH_RES_TIMER */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Private functions.
+ */
+pj_status_t pj_thread_init(void);
+
+
+PJ_END_DECL
+
+#endif
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/os_unix.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/os_unix.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/os_unix.c	(revision 1753)
@@ -0,0 +1,828 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/os_unix.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <stddef.h>
+#include <sys/timeb.h>
+#include <time.h>
+#include <stdlib.h>
+#include <semaphore.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/time.h>
+#define __USE_GNU
+#include <pthread.h>
+
+#define THIS_FILE   "osunix"
+
+struct pj_thread_t
+{
+    char	    obj_name[PJ_MAX_OBJ_NAME];
+    pthread_t	    thread;
+    pj_thread_proc *proc;
+    void	   *arg;
+};
+
+struct pj_atomic_t
+{
+    pj_mutex_t	*mutex;
+    long	 value;
+};
+
+struct pj_mutex_t
+{
+    pthread_mutex_t     mutex;
+    char		obj_name[PJ_MAX_OBJ_NAME];
+#if PJ_DEBUG
+    int		        nesting_level;
+    pj_thread_t	       *owner;
+#endif
+};
+
+
+struct pj_sem_t
+{
+    sem_t		sem;
+    char		obj_name[PJ_MAX_OBJ_NAME];
+};
+
+struct pj_event_t
+{
+    char		obj_name[PJ_MAX_OBJ_NAME];
+};
+
+#if PJ_HAS_THREADS
+static pj_thread_t main_thread;
+static int thread_tls_id;
+static pj_mutex_t critical_section;
+#endif
+
+
+#if !PJ_HAS_THREADS
+#define MAX_THREADS 16
+static int tls_flag[MAX_THREADS];
+static void *tls[MAX_THREADS];
+#endif
+
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type);
+
+PJ_DEF(pj_status_t) pj_init(void)
+{
+    char dummy_guid[PJ_GUID_LENGTH];
+    pj_str_t guid;
+
+    PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
+
+    /* Init this thread's TLS. */
+#if PJ_HAS_THREADS
+    if (pj_thread_init() != 0) {
+	PJ_LOG(1, ("pj_init", "Thread initialization has returned an error"));
+	return -1;
+    }
+
+    /* Critical section. */
+    if (init_mutex(&critical_section, "critsec", PJ_MUTEX_SIMPLE) != 0)
+	return -1;
+
+#endif
+    
+    /* Init random seed. */
+    srand( clock() );
+
+    /* Startup GUID. */
+    guid.ptr = dummy_guid;
+    pj_generate_unique_string( &guid );
+
+
+    return PJ_OK;
+}
+
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+    return getpid();
+}
+
+PJ_DEF(void) pj_perror(const char *src, const char *format, ...)
+{
+    char title[80];
+    va_list marker;
+
+    va_start(marker, format);
+    vsnprintf(title, sizeof(title), format, marker);
+    va_end(marker);
+
+    /* Do not do title[len]=X here. 
+     * len may be greater than sizeof(title) according to C99.
+     */
+
+    PJ_LOG(1, (src, "%s: (errno=%d) %s", title, errno, strerror(errno)));
+}
+
+PJ_DEF(pj_status_t) pj_getlasterror(void)
+{
+    return errno;
+}
+
+PJ_DEF(pj_thread_t*) pj_thread_register (const char *cstr_thread_name,
+					 pj_thread_desc desc)
+{
+#if PJ_HAS_THREADS
+    pj_thread_t *thread = (pj_thread_t *)desc;
+    pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+    /* Size sanity check. */
+    if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+	pj_assert(!"Not enough pj_thread_desc size!");
+	return NULL;
+    }
+
+    /* If a thread descriptor has been registered before, just return it. */
+    if (pj_thread_local_get (thread_tls_id) != 0) {
+	return (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+    }
+
+    /* Initialize and set the thread entry. */
+    pj_memset(desc, 0, sizeof(pj_thread_desc));
+    thread->thread = pthread_self();
+
+    if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+	sprintf(thread->obj_name, cstr_thread_name, thread->thread);
+    else
+	sprintf(thread->obj_name, "thr%p", (void*)thread->thread);
+    
+    pj_thread_local_set(thread_tls_id, thread);
+
+    return thread;
+#else
+    pj_thread_t *thread = (pj_thread_t*)desc;
+    return thread;
+#endif
+}
+
+
+pj_status_t pj_thread_init(void)
+{
+#if PJ_HAS_THREADS
+    pj_memset(&main_thread, 0, sizeof(main_thread));
+    main_thread.thread = pthread_self();
+    sprintf(main_thread.obj_name, "thr%p", &main_thread);
+
+    thread_tls_id = pj_thread_local_alloc();
+    if (thread_tls_id == -1) {
+	return -1;
+    }
+
+    pj_thread_local_set(thread_tls_id, &main_thread);
+    return PJ_OK;
+#else
+    PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!"));
+    return -1;
+#endif
+}
+
+#if PJ_HAS_THREADS
+static void *thread_main(void *param)
+{
+    pj_thread_t *rec = param;
+    void *result;
+
+    PJ_LOG(6,(rec->obj_name, "Thread started"));
+
+    pj_thread_local_set(thread_tls_id, rec);
+    result = (*rec->proc)(rec->arg);
+
+    PJ_LOG(6,(rec->obj_name, "Thread quitting"));
+    return result;
+}
+#endif
+
+PJ_DEF(pj_thread_t*) pj_thread_create(pj_pool_t *pool, const char *thread_name,
+				      pj_thread_proc *proc, void *arg,
+				      pj_size_t stack_size, void *stack, 
+				      unsigned flags)
+{
+#if PJ_HAS_THREADS
+    pj_thread_t *rec;
+
+    /* We don't implement stack setting for now.. */
+    PJ_TODO(IMPLEMENT_THREAD_STACK);    
+
+    /* We don't implement suspended state for threads */
+    if (flags & PJ_THREAD_SUSPENDED) {
+	PJ_LOG(2, (THIS_FILE, "Unsupported: PJ_THREAD_SUSPENDED flag in pj_thread_create()"));
+	return NULL;
+    }
+
+    /* Create thread record and assign name for the thread */
+    rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t));
+    if (!rec) {
+	return NULL;
+    }
+    /* Set name. */
+    if (!thread_name) {
+	thread_name = "thr%p";
+    }
+    if (strchr(thread_name, '%')) {
+	pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
+    } else {
+	strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+	rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (rec->obj_name, "Thread created"));
+
+    /* Create the thread. */
+    rec->proc = proc;
+    rec->arg = arg;
+    if (pthread_create( &rec->thread, NULL, thread_main, rec) != 0) {
+	PJ_LOG(2, (THIS_FILE, "Error creating thread"));
+	return NULL;
+    }
+
+    return rec;
+#else
+    PJ_LOG(2,(THIS_FILE, "Unable to create thread. Threading is not enabled!"));
+    return NULL;
+#endif
+}
+
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
+{
+#if PJ_HAS_THREADS
+    pj_thread_t *rec = (pj_thread_t*)p;
+    return rec->obj_name;
+#else
+    return "";
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
+{
+    PJ_LOG(2, (THIS_FILE, "Unsupported: pj_thread_resume()"));
+    return -1;
+}
+
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+#if PJ_HAS_THREADS
+    pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
+    pj_assert(rec != NULL);
+    return rec;
+#else
+    PJ_LOG(2, (THIS_FILE, "pj_thread_this() unsupported: thread is not enabled!"));
+    return 0;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+#if PJ_HAS_THREADS
+    pj_thread_t *rec = (pj_thread_t *)p;
+    void *ret;
+
+    PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
+    return pthread_join( rec->thread, &ret);
+#else
+    PJ_LOG(2, (THIS_FILE, "pj_thread_join() unsupported: thread is not enabled!"));
+    return 0;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
+{
+    return PJ_OK;
+}
+
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+    return usleep(msec * 1000);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_atomic_t*) pj_atomic_create( pj_pool_t *pool, long value)
+{
+    pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
+    if (!t)
+	return NULL;
+#if PJ_HAS_THREADS
+    t->mutex = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE);
+    if (t->mutex == NULL)
+	return NULL;
+#endif
+    t->value = value;
+    return t;
+}
+
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
+{
+#if PJ_HAS_THREADS
+    return pj_mutex_destroy( atomic_var->mutex );
+#else
+    return 0;
+#endif
+}
+
+PJ_DEF(long) pj_atomic_set(pj_atomic_t *atomic_var, long value)
+{
+    long oldval;
+    
+#if PJ_HAS_THREADS
+    pj_mutex_lock( atomic_var->mutex );
+#endif
+    oldval = atomic_var->value;
+    atomic_var->value = value;
+#if PJ_HAS_THREADS
+    pj_mutex_unlock( atomic_var->mutex);
+#endif 
+    return oldval;
+}
+
+PJ_DEF(long) pj_atomic_get(pj_atomic_t *atomic_var)
+{
+    long oldval;
+    
+#if PJ_HAS_THREADS
+    pj_mutex_lock( atomic_var->mutex );
+#endif
+    oldval = atomic_var->value;
+#if PJ_HAS_THREADS
+    pj_mutex_unlock( atomic_var->mutex);
+#endif
+    return oldval;
+}
+
+PJ_DEF(long) pj_atomic_inc(pj_atomic_t *atomic_var)
+{
+    long newval;
+
+#if PJ_HAS_THREADS
+    pj_mutex_lock( atomic_var->mutex );
+#endif
+    newval = atomic_var->value++;
+#if PJ_HAS_THREADS
+    pj_mutex_unlock( atomic_var->mutex);
+#endif
+    return newval;
+}
+
+PJ_DEF(long) pj_atomic_dec(pj_atomic_t *atomic_var)
+{
+    long newval;
+
+#if PJ_HAS_THREADS
+    pj_mutex_lock( atomic_var->mutex );
+#endif
+    newval = atomic_var->value--;
+#if PJ_HAS_THREADS
+    pj_mutex_unlock( atomic_var->mutex);
+#endif
+    return newval;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(long) pj_thread_local_alloc(void)
+{
+#if PJ_HAS_THREADS
+    pthread_key_t key;
+
+    pj_assert( sizeof(pthread_key_t) <= sizeof(long));
+    if (pthread_key_create(&key, NULL))
+	return -1;
+    return key;
+#else
+    int i;
+    for (i=0; i<MAX_THREADS; ++i) {
+	if (tls_flag[i] == 0)
+	    break;
+    }
+    if (i == MAX_THREADS) {
+	PJ_LOG(2,(THIS_FILE, "Unable to allocate thread local: too many threads!"));
+	return -1;
+    }
+    tls_flag[i] = 1;
+    tls[i] = NULL;
+    return i;
+#endif
+}
+
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+#if PJ_HAS_THREADS
+    pthread_key_delete(index);
+#else
+    tls_flag[index] = 0;
+#endif
+}
+
+PJ_DEF(void) pj_thread_local_set(long index, void *value)
+{
+#if PJ_HAS_THREADS
+    pthread_setspecific(index, value);
+#else
+    pj_assert(index >= 0 && index < MAX_THREADS);
+    tls[index] = value;
+#endif
+}
+
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+#if PJ_HAS_THREADS
+    return pthread_getspecific(index);
+#else
+    pj_assert(index >= 0 && index < MAX_THREADS);
+    return tls[index];
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+#if PJ_HAS_THREADS
+    pj_mutex_lock(&critical_section);
+#endif
+}
+
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+#if PJ_HAS_THREADS
+    pj_mutex_unlock(&critical_section);
+#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type)
+{
+#if PJ_HAS_THREADS
+    PJ_UNUSED_ARG(type)
+
+    if (type == PJ_MUTEX_SIMPLE) {
+	pthread_mutex_t the_mutex = PTHREAD_MUTEX_INITIALIZER;
+	mutex->mutex = the_mutex;
+    } else {
+	pthread_mutex_t the_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+	mutex->mutex = the_mutex;
+    }
+    
+#if PJ_DEBUG
+    /* Set owner. */
+    mutex->nesting_level = 0;
+    mutex->owner = NULL;
+#endif
+
+    /* Set name. */
+    if (!name) {
+	name = "mtx%p";
+    }
+    if (strchr(name, '%')) {
+	pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
+    } else {
+	strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
+	mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (mutex->obj_name, "Mutex created"));
+    return 0;
+#else /* PJ_HAS_THREADS */
+    return 0;
+#endif
+}
+
+PJ_DEF(pj_mutex_t*) pj_mutex_create(pj_pool_t *pool, const char *name, int type)
+{
+#if PJ_HAS_THREADS
+    pj_mutex_t *mutex = pj_pool_alloc(pool, sizeof(*mutex));
+    if (init_mutex(mutex, name, type) == 0)
+	return mutex;
+    else
+	return NULL;
+#else /* PJ_HAS_THREADS */
+    return (pj_mutex_t*)1;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+    pj_status_t status;
+
+    PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", 
+				pj_thread_this()->obj_name));
+
+    status = pthread_mutex_lock( &mutex->mutex );
+
+    PJ_LOG(6,(mutex->obj_name, 
+	      (status==PJ_OK ? "Mutex acquired by thread %s" : "FAILED by %s"),
+	      pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+    if (status == PJ_OK) {
+	mutex->owner = pj_thread_this();
+	++mutex->nesting_level;
+    }
+#endif
+
+    return status;
+#else	/* PJ_HAS_THREADS */
+    pj_assert( mutex == (pj_mutex_t*)1 );
+    return 0;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+    pj_status_t status;
+
+#if PJ_DEBUG
+    pj_assert(mutex->owner == pj_thread_this());
+    if (--mutex->nesting_level == 0) {
+	mutex->owner = NULL;
+    }
+#endif
+
+    PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", 
+				pj_thread_this()->obj_name));
+
+    status = pthread_mutex_unlock( &mutex->mutex );
+    return status;
+#else /* PJ_HAS_THREADS */
+    pj_assert( mutex == (pj_mutex_t*)1 );
+    return 0;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+    pj_status_t status;
+
+    status = pthread_mutex_trylock( &mutex->mutex );
+
+    if (status==PJ_OK) {
+	PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+	mutex->owner = pj_thread_this();
+	++mutex->nesting_level;
+#endif
+    }
+    return status;
+#else	/* PJ_HAS_THREADS */
+    pj_assert( mutex == (pj_mutex_t*)1);
+    return 0;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+    PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
+    return pthread_mutex_destroy( &mutex->mutex );
+#else
+    pj_assert( mutex == (pj_mutex_t*)1 );
+    return 0;
+#endif
+}
+
+#if PJ_DEBUG
+PJ_DEF(pj_status_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+    return mutex->owner == pj_thread_this();
+#else
+    return 1;
+#endif
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_sem_t*) pj_sem_create(pj_pool_t *pool, const char *name,
+				unsigned initial, unsigned max)
+{
+#if PJ_HAS_THREADS
+    pj_sem_t *sem;
+
+    sem = pj_pool_alloc(pool, sizeof(*sem));    
+
+    if (sem_init( &sem->sem, 0, initial) != 0) {
+	PJ_LOG(3, (THIS_FILE, "Error creating semaphore"));
+	return NULL;
+    }
+    
+    /* Set name. */
+    if (!name) {
+	name = "sem%p";
+    }
+    if (strchr(name, '%')) {
+	pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
+    } else {
+	strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
+	sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (sem->obj_name, "Semaphore created"));
+    return sem;
+#else
+    return (pj_sem_t*)1;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+    int result;
+
+    PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", 
+			      pj_thread_this()->obj_name));
+
+    result = sem_wait( &sem->sem );
+    
+    if (result == 0) {
+	PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+    } else {
+	PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", 
+				  pj_thread_this()->obj_name));
+    }
+
+    return result;
+#else
+    pj_assert( sem == (pj_sem_t*) 1 );
+    return 0;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+    int result;
+
+    result = sem_trywait( &sem->sem );
+    
+    if (result == 0) {
+	PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+    } 
+    return result;
+#else
+    pj_assert( sem == (pj_sem_t*)1 );
+    return 0;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+    PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
+			      pj_thread_this()->obj_name));
+    return sem_post( &sem->sem );
+#else
+    pj_assert( sem == (pj_sem_t*) 1);
+    return 0;
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+    PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
+			      pj_thread_this()->obj_name));
+    return sem_destroy( &sem->sem );
+#else
+    pj_assert( sem == (pj_sem_t*) 1 );
+    return 0;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_event_t*) pj_event_create(pj_pool_t *pool, const char *name,
+				    pj_bool_t manual_reset, pj_bool_t initial)
+{
+    PJ_LOG(3, (THIS_FILE, "pj_event_create(): operation is not supported!"));
+    return NULL;
+}
+
+PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
+{
+    return -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
+{
+    struct timeval t;
+    int status = gettimeofday(&t, NULL);
+    tv->sec = t.tv_sec;
+    tv->msec = t.tv_usec / 1000;
+    return status;
+}
+
+PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
+{
+    struct tm *local_time;
+
+    local_time = localtime((time_t*)&tv->sec);
+
+    pt->year = local_time->tm_year;
+    pt->mon = local_time->tm_mon;
+    pt->day = local_time->tm_mday;
+    pt->hour = local_time->tm_hour;
+    pt->min = local_time->tm_min;
+    pt->sec = local_time->tm_sec;
+    pt->wday = local_time->tm_wday;
+    pt->yday = local_time->tm_yday;
+    pt->msec = tv->msec;
+
+    return PJ_OK;
+}
+
+/**
+ * Encode parsed time to time value.
+ */
+PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
+
+/**
+ * Convert local time to GMT.
+ */
+PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ */
+PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Terminal
+ */
+
+/**
+ * Set terminal color.
+ */
+PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
+{
+    return -1;
+}
+
+/**
+ * Get current terminal foreground color.
+ */
+PJ_DEF(pj_color_t) pj_term_get_color(void)
+{
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/os_win32.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/os_win32.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/os_win32.c	(revision 1753)
@@ -0,0 +1,876 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/os_win32.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <stddef.h>
+#ifndef PJ_WIN32_WINCE
+#  include <sys/timeb.h>
+#else
+
+#  include <windows.h>
+
+struct timeb {
+	time_t time;
+	unsigned short millitm;
+};
+
+static void ftime( struct timeb *tb )
+{
+	SYSTEMTIME st;
+	int days, years, leapyears;
+	
+	if(tb == NULL)
+	{
+		//nlSetError(NL_NULL_POINTER);
+		assert(tb);
+		return;
+	}
+	GetSystemTime(&st);
+	leapyears = (st.wYear - 1970 + 1) / 4;
+	years = st.wYear - 1970 - leapyears;
+	
+	days = years * 365 + leapyears * 366;
+	
+	switch (st.wMonth) {
+	case 1:
+	case 3:
+	case 5:
+	case 7:
+	case 8:
+	case 10:
+	case 12:
+		days += 31;
+		break;
+	case 4:
+	case 6:
+	case 9:
+	case 11:
+		days += 30;
+		break;
+	case 2:
+		days += (st.wYear%4 == 0) ? 29 : 28;
+		break;
+	default:
+		break;
+	}
+	days += st.wDay;
+	tb->time = days * 86400 + st.wHour * 3600 + st.wMinute * 60 + st.wSecond;
+	tb->millitm = st.wMilliseconds;
+}
+
+time_t time(time_t *t)
+{
+	struct timeb tb;
+	
+	ftime(&tb);
+	*t = tb.time;
+	
+	return *t;
+} 
+#endif
+
+#include <time.h>
+#include <stdlib.h>
+
+#define WIN32_LEAN_AND_MEAN
+#define _WIN32_WINNT	PJ_WIN32_WINNT
+#include <winsock.h>
+
+struct pj_thread_t
+{
+    char	    obj_name[PJ_MAX_OBJ_NAME];
+    HANDLE	    hthread;
+    DWORD	    idthread;
+    pj_thread_proc *proc;
+    void	   *arg;
+};
+
+
+struct pj_mutex_t
+{
+#if PJ_WIN32_WINNT >= 0x0400
+    CRITICAL_SECTION	crit;
+#else
+    HANDLE		hMutex;
+#endif
+    char		obj_name[PJ_MAX_OBJ_NAME];
+#if PJ_DEBUG
+    int		        nesting_level;
+    pj_thread_t	       *owner;
+#endif
+};
+
+
+typedef struct pj_sem_t
+{
+    HANDLE		hSemaphore;
+    char		obj_name[PJ_MAX_OBJ_NAME];
+} pj_mem_t;
+
+struct pj_event_t
+{
+    HANDLE		hEvent;
+    char		obj_name[PJ_MAX_OBJ_NAME];
+};
+
+static pj_thread_desc main_thread;
+static int thread_tls_id;
+static pj_mutex_t critical_section_mutex;
+
+static int init_mutex(pj_mutex_t *mutex, const char *name);
+
+PJ_DEF(pj_status_t) pj_init(void)
+{
+    WSADATA wsa;
+    char dummy_guid[32]; /* use maximum GUID length */
+    pj_str_t guid;
+
+    PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
+
+    /* Init Winsock.. */
+    if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
+	PJ_LOG(1, ("pj_init", "Winsock initialization has returned an error"));
+	return -1;
+    }
+
+    /* Init this thread's TLS. */
+    if (pj_thread_init() != 0) {
+	PJ_LOG(1, ("pj_init", "Thread initialization has returned an error"));
+	return -1;
+    }
+    
+    /* Init random seed. */
+    srand( GetCurrentProcessId() );
+
+    /* Startup GUID. */
+    guid.ptr = dummy_guid;
+    pj_generate_unique_string( &guid );
+
+    /* Initialize critical section. */
+    if (init_mutex(&critical_section_mutex, "pj%p") != 0)
+	return -1;
+
+    return PJ_OK;
+}
+
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+    return GetCurrentProcessId();
+}
+
+PJ_DEF(void) pj_perror(const char *src, const char *format, ...)
+{
+    char msg[256];
+    int len;
+    va_list marker;
+
+    va_start(marker, format);
+    len = _vsnprintf(msg, sizeof(msg), format, marker);
+    va_end(marker);
+
+    msg[len++] = ':';
+    msg[len++] = ' ';
+    len += sprintf( &msg[len], "error %u", (unsigned)GetLastError());
+    msg[len] = '\0';
+
+    PJ_LOG(1, (src, "%s", msg));
+}
+
+PJ_DEF(pj_status_t) pj_getlasterror(void)
+{
+    return (pj_status_t) GetLastError();
+}
+
+PJ_DEF(pj_thread_t*) pj_thread_register (const char *cstr_thread_name,
+					 pj_thread_desc desc)
+{
+    pj_thread_t *thread = (pj_thread_t *)desc;
+    pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+    /* Size sanity check. */
+    if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+	pj_assert(!"Not enough pj_thread_desc size!");
+	return NULL;
+    }
+
+    /* If a thread descriptor has been registered before, just return it. */
+    if (pj_thread_local_get (thread_tls_id) != 0) {
+	return (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+    }
+
+    /* Initialize and set the thread entry. */
+    pj_memset(desc, 0, sizeof(pj_thread_desc));
+    thread->hthread = GetCurrentThread();
+    thread->idthread = GetCurrentThreadId();
+
+    if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+	sprintf(thread->obj_name, cstr_thread_name, thread->idthread);
+    else
+	sprintf(thread->obj_name, "thr%p", (void*)thread->idthread);
+    
+    pj_thread_local_set(thread_tls_id, thread);
+
+    return thread;
+}
+
+pj_status_t pj_thread_init(void)
+{
+    thread_tls_id = pj_thread_local_alloc();
+    if (thread_tls_id == -1)
+	return -1;
+
+    if (pj_thread_register("thr%p", main_thread) == NULL)
+	return -1;
+
+    return PJ_OK;
+}
+
+static DWORD WINAPI thread_main(void *param)
+{
+    pj_thread_t *rec = param;
+    void *result;
+
+    PJ_LOG(6,(rec->obj_name, "Thread started"));
+
+    pj_thread_local_set(thread_tls_id, rec);
+    result = (*rec->proc)(rec->arg);
+
+    PJ_LOG(6,(rec->obj_name, "Thread quitting"));
+    return (DWORD)result;
+}
+
+PJ_DEF(pj_thread_t*) pj_thread_create(pj_pool_t *pool, const char *thread_name,
+				      pj_thread_proc *proc, void *arg,
+				      pj_size_t stack_size, void *stack, 
+				      unsigned flags)
+{
+    DWORD dwflags = 0;
+    pj_thread_t *rec;
+
+    /* Stack argument must be NULL on WIN32 */
+    if (stack != NULL)
+	return NULL;
+
+    /* Set flags */
+    if (flags & PJ_THREAD_SUSPENDED)
+	dwflags |= CREATE_SUSPENDED;
+
+    /* Create thread record and assign name for the thread */
+    rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t));
+    if (!rec) {
+	return NULL;
+    }
+    /* Set name. */
+    if (!thread_name) {
+	thread_name = "thr%p";
+    }
+    if (strchr(thread_name, '%')) {
+	pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
+    } else {
+	strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+	rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (rec->obj_name, "Thread created"));
+
+    /* Create the thread. */
+    rec->proc = proc;
+    rec->arg = arg;
+    rec->hthread = CreateThread(NULL, stack_size, 
+				thread_main, rec,
+				dwflags, &rec->idthread);
+    if (rec->hthread == NULL) {
+	return NULL;
+    }
+    return rec;
+}
+
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
+{
+    pj_thread_t *rec = (pj_thread_t*)p;
+    return rec->obj_name;
+}
+
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
+{
+    pj_thread_t *rec = (pj_thread_t*)p;
+    return ResumeThread(rec->hthread) == -1 ? -1 : PJ_OK;
+}
+
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+    pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
+    pj_assert(rec != NULL);
+    return rec;
+}
+
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+    pj_thread_t *rec = (pj_thread_t *)p;
+    PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
+    return WaitForSingleObject(rec->hthread, INFINITE)==WAIT_OBJECT_0 ? PJ_OK : -1;
+}
+
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
+{
+    pj_thread_t *rec = (pj_thread_t *)p;
+    CloseHandle(rec->hthread);
+    return PJ_OK;
+}
+
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+    Sleep(msec);
+    return PJ_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+struct pj_atomic_t
+{
+    long value;
+};
+
+PJ_DEF(pj_atomic_t*) pj_atomic_create( pj_pool_t *pool, long initial )
+{
+    pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t));
+    if (!atomic_var)
+	return NULL;
+
+    atomic_var->value = initial;
+    return atomic_var;
+}
+
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
+{
+    PJ_UNUSED_ARG(var)
+    return 0;
+}
+
+PJ_DEF(long) pj_atomic_set(pj_atomic_t *atomic_var, long value)
+{
+    return InterlockedExchange(&atomic_var->value, value);
+}
+
+PJ_DEF(long) pj_atomic_get(pj_atomic_t *atomic_var)
+{
+    return atomic_var->value;
+}
+
+PJ_DEF(long) pj_atomic_inc(pj_atomic_t *atomic_var)
+{
+    PJ_TODO(RUNTIME_DETECTION_OF_WIN32_OSES);
+
+#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+    return InterlockedIncrement(&atomic_var->value);
+#elif defined(PJ_WIN32_WINCE)
+    return InterlockedIncrement(&atomic_var->value);
+#else
+#   error Fix Me
+#endif
+}
+
+PJ_DEF(long) pj_atomic_dec(pj_atomic_t *atomic_var)
+{
+    PJ_TODO(RUNTIME_DETECTION_OF_WIN32_OSES);
+
+#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+    return InterlockedDecrement(&atomic_var->value);
+#elif defined(PJ_WIN32_WINCE)
+    return InterlockedIncrement(&atomic_var->value);
+#else
+#   error Fix me
+#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(long) pj_thread_local_alloc(void)
+{
+    return (long)TlsAlloc();
+}
+
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+    TlsFree(index);
+}
+
+PJ_DEF(void) pj_thread_local_set(long index, void *value)
+{
+    TlsSetValue(index, value);
+}
+
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+    return TlsGetValue(index);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static int init_mutex(pj_mutex_t *mutex, const char *name)
+{
+#if PJ_WIN32_WINNT >= 0x0400
+    InitializeCriticalSection(&mutex->crit);
+#else
+    mutex->hMutex = CreateMutex(NULL, FALSE, NULL);
+    if (!mutex->hMutex) {
+	return -1;
+    }
+#endif
+
+#if PJ_DEBUG
+    /* Set owner. */
+    mutex->nesting_level = 0;
+    mutex->owner = NULL;
+#endif
+
+    /* Set name. */
+    if (!name) {
+	name = "mtx%p";
+    }
+    if (strchr(name, '%')) {
+	pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
+    } else {
+	strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
+	mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (mutex->obj_name, "Mutex created"));
+    return 0;
+}
+
+PJ_DEF(pj_mutex_t*) pj_mutex_create(pj_pool_t *pool, const char *name, int type)
+{
+    pj_mutex_t *mutex = pj_pool_alloc(pool, sizeof(*mutex));
+
+    PJ_UNUSED_ARG(type)
+
+    return init_mutex(mutex, name)==0 ? mutex : NULL;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+    pj_status_t status;
+
+    PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", 
+				pj_thread_this()->obj_name));
+
+#if PJ_WIN32_WINNT >= 0x0400
+    EnterCriticalSection(&mutex->crit);
+    status=PJ_OK;
+#else
+    status=WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0 ? PJ_OK : -1;
+#endif
+    PJ_LOG(6,(mutex->obj_name, 
+	      (status==PJ_OK ? "Mutex acquired by thread %s" : "FAILED by %s"),
+	      pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+    if (status == PJ_OK) {
+	mutex->owner = pj_thread_this();
+	++mutex->nesting_level;
+    }
+#endif
+
+    return status;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+    pj_status_t status;
+
+#if PJ_DEBUG
+    pj_assert(mutex->owner == pj_thread_this());
+    if (--mutex->nesting_level == 0) {
+	mutex->owner = NULL;
+    }
+#endif
+
+    PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", 
+				pj_thread_this()->obj_name));
+
+#if PJ_WIN32_WINNT >= 0x0400
+    LeaveCriticalSection(&mutex->crit);
+    status=PJ_OK;
+#else
+    status=ReleaseMutex(mutex->hMutex) ? PJ_OK : -1;
+#endif
+    return status;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+    pj_status_t status;
+
+#if PJ_WIN32_WINNT >= 0x0400
+    status=TryEnterCriticalSection(&mutex->crit) ? PJ_OK : -1;
+#else
+    status=WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ? PJ_OK : -1;
+#endif
+    if (status==PJ_OK) {
+	PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+	mutex->owner = pj_thread_this();
+	++mutex->nesting_level;
+#endif
+    }
+    return status;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+    PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
+
+#if PJ_WIN32_WINNT >= 0x0400
+    DeleteCriticalSection(&mutex->crit);
+    return PJ_OK;
+#else
+    return CloseHandle(mutex->hMutex) ? PJ_OK : -1;
+#endif
+}
+
+#if PJ_DEBUG
+PJ_DEF(pj_status_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+    return mutex->owner == pj_thread_this();
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+    pj_mutex_lock(&critical_section_mutex);
+}
+
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+    pj_mutex_unlock(&critical_section_mutex);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_sem_t*) pj_sem_create(pj_pool_t *pool, const char *name,
+				unsigned initial, unsigned max)
+{
+    pj_sem_t *sem;
+
+    sem = pj_pool_alloc(pool, sizeof(*sem));    
+    sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL);
+    if (!sem->hSemaphore)
+	return NULL;
+
+    /* Set name. */
+    if (!name) {
+	name = "sem%p";
+    }
+    if (strchr(name, '%')) {
+	pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
+    } else {
+	strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
+	sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (sem->obj_name, "Semaphore created"));
+    return sem;
+}
+
+static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout)
+{
+    DWORD result;
+
+    PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", 
+			      pj_thread_this()->obj_name));
+
+    result = WaitForSingleObject(sem->hSemaphore, timeout);
+    if (result == WAIT_OBJECT_0) {
+	PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+    } else {
+	PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", 
+				  pj_thread_this()->obj_name));
+    }
+
+    return result==WAIT_OBJECT_0 ? PJ_OK : -1;
+}
+
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+    return pj_sem_wait_for(sem, INFINITE);
+}
+
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+    return pj_sem_wait_for(sem, 0);
+}
+
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+    PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
+			      pj_thread_this()->obj_name));
+    return ReleaseSemaphore(sem->hSemaphore, 1, NULL) ? 0 : -1;
+}
+
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+    PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
+			      pj_thread_this()->obj_name));
+    return CloseHandle(sem->hSemaphore) ? 0 : -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_event_t*) pj_event_create(pj_pool_t *pool, const char *name,
+				    pj_bool_t manual_reset, pj_bool_t initial)
+{
+    pj_event_t *event = pj_pool_alloc(pool, sizeof(*event));
+
+    event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, 
+				initial?TRUE:FALSE, NULL);
+
+    if (!event->hEvent)
+	return NULL;
+
+    /* Set name. */
+    if (!name) {
+	name = "evt%p";
+    }
+    if (strchr(name, '%')) {
+	pj_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event);
+    } else {
+	strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME);
+	event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (event->obj_name, "Event created"));
+    return event;
+}
+
+static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout)
+{
+    DWORD result;
+
+    PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting", 
+			        pj_thread_this()->obj_name));
+
+    result = WaitForSingleObject(event->hEvent, timeout);
+    if (result == WAIT_OBJECT_0) {
+	PJ_LOG(6, (event->obj_name, "Event: thread %s is released", 
+				    pj_thread_this()->obj_name));
+    } else {
+	PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire", 
+				    pj_thread_this()->obj_name));
+    }
+
+    return result==WAIT_OBJECT_0 ? PJ_OK : -1;
+}
+
+PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
+{
+    return pj_event_wait_for(event, INFINITE);
+}
+
+PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
+{
+    return pj_event_wait_for(event, 0);
+}
+
+PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
+{
+    PJ_LOG(6, (event->obj_name, "Setting event"));
+    return SetEvent(event->hEvent) ? 0 : -1;
+}
+
+PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
+{
+    PJ_LOG(6, (event->obj_name, "Pulsing event"));
+    return PulseEvent(event->hEvent) ? 0 : -1;
+}
+
+PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
+{
+    PJ_LOG(6, (event->obj_name, "Event is reset"));
+    return ResetEvent(event->hEvent) ? 0 : -1;
+}
+
+PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
+{
+    PJ_LOG(6, (event->obj_name, "Event is destroying"));
+    return CloseHandle(event->hEvent) ? 0 : -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
+{
+    struct timeb tb;
+    ftime(&tb);
+    tv->sec = tb.time;
+    tv->msec = tb.millitm;
+    return PJ_OK;
+}
+
+PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
+{
+    #if defined(PJ_WIN32_WINCE)
+	SYSTEMTIME local_time;
+    GetLocalTime(&local_time);
+	
+    pt->year = local_time.wYear;
+    pt->mon = local_time.wMonth;
+    pt->day = local_time.wDay;
+    pt->hour = local_time.wHour;
+    pt->min = local_time.wMinute;
+    pt->sec = local_time.wSecond;
+    pt->wday = local_time.wDayOfWeek;
+    pt->yday = 0; //note this
+    pt->msec = local_time.wMilliseconds;
+#else
+    struct tm *local_time;
+    local_time = localtime((time_t*)&tv->sec);
+
+    pt->year = local_time->tm_year+1900;
+    pt->mon = local_time->tm_mon;
+    pt->day = local_time->tm_mday;
+    pt->hour = local_time->tm_hour;
+    pt->min = local_time->tm_min;
+    pt->sec = local_time->tm_sec;
+    pt->wday = local_time->tm_wday;
+    pt->yday = local_time->tm_yday;
+    pt->msec = tv->msec;
+#endif
+
+
+    return PJ_OK;
+}
+
+/**
+ * Encode parsed time to time value.
+ */
+PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
+
+/**
+ * Convert local time to GMT.
+ */
+PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ */
+PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Terminal
+ */
+#if !defined(PJ_WIN32_WINCE)
+
+static WORD pj_color_to_os_attr(pj_color_t color)
+{
+    WORD attr = 0;
+
+    if (color & PJ_TERM_COLOR_R)
+	attr |= FOREGROUND_RED;
+    if (color & PJ_TERM_COLOR_G)
+	attr |= FOREGROUND_GREEN;
+    if (color & PJ_TERM_COLOR_B)
+	attr |= FOREGROUND_BLUE;
+    if (color & PJ_TERM_COLOR_BRIGHT)
+	attr |= FOREGROUND_INTENSITY;
+
+    return attr;
+}
+
+static pj_color_t os_attr_to_pj_color(WORD attr)
+{
+    int color = 0;
+
+    if (attr & FOREGROUND_RED)
+	color |= PJ_TERM_COLOR_R;
+    if (attr & FOREGROUND_GREEN)
+	color |= PJ_TERM_COLOR_G;
+    if (attr & FOREGROUND_BLUE)
+	color |= PJ_TERM_COLOR_B;
+    if (attr & FOREGROUND_INTENSITY)
+	color |= PJ_TERM_COLOR_BRIGHT;
+
+    return color;
+}
+
+
+/**
+ * Set terminal color.
+ */
+PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
+{
+    BOOL rc;
+    WORD attr = 0;
+
+    attr = pj_color_to_os_attr(color);
+    rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr);
+    return rc ? PJ_OK : -1;
+}
+
+/**
+ * Get current terminal foreground color.
+ */
+PJ_DEF(pj_color_t) pj_term_get_color(void)
+{
+    CONSOLE_SCREEN_BUFFER_INFO info;
+    GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info);
+    return os_attr_to_pj_color(info.wAttributes);
+}
+
+#else
+
+static short pj_color_to_os_attr(pj_color_t color)
+{
+     return 0;
+}
+
+static pj_color_t os_attr_to_pj_color(short attr)
+{
+    return 0;
+}
+
+
+/**
+ * Set terminal color.
+ */
+PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
+{
+    return PJ_OK;
+}
+
+/**
+ * Get current terminal foreground color.
+ */
+PJ_DEF(pj_color_t) pj_term_get_color(void)
+{
+    return 0;
+}
+
+
+#endif
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool.c	(revision 1753)
@@ -0,0 +1,248 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/pool.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#include <pj/pool.h>
+
+#include <pj/log.h>
+#include <pj/except.h>
+#include <stdlib.h>
+
+/* Include inline definitions when inlining is disabled. */
+#if !PJ_FUNCTIONS_ARE_INLINED
+#  include "pool_i.h"
+#endif
+
+
+static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size)
+{
+    pj_pool_block *block;
+
+    PJ_LOG(4, (pool->obj_name, "create_block(sz=%d), cur.cap=%d, cur.used=%d", 
+				size, pool->capacity, pool->used_size));
+
+    // Request memory from allocator.
+    block = (pj_pool_block*) (*pool->factory->policy.block_alloc)(pool->factory, size);
+    if (block == NULL) {
+	(*pool->callback)(pool, size);
+	return NULL;
+    }
+
+    // Add capacity.
+    pool->capacity += size;
+    pool->used_size += sizeof(pj_pool_block);
+
+    // Set block attribytes.
+    block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
+    block->end = ((unsigned char*)block) + size;
+
+    // Insert in the front of the list.
+    pj_list_insert_after(&pool->block_list, block);
+
+    PJ_LOG(5, (pool->obj_name, " block created, buffer=%p to %p", block->buf, block->end));
+
+    return block;
+}
+
+PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size)
+{
+    pj_pool_block *block = pool->block_list.next;
+    void *p;
+    unsigned block_size;
+
+    while (block != &pool->block_list) {
+	p = pj_pool_alloc_from_block(pool, block, size);
+	if (p != NULL)
+	    return p;
+	block = block->next;
+    }
+
+    /*
+    if (pool->increment_size < size + sizeof(pj_pool_block)) {
+	PJ_LOG(2, (pool->obj_name, "Not enough increment to allocate %d bytes "
+		   "from pool (used=%d, cap=%d)",
+		   size, pool->used_size, pool->capacity));
+	(*pool->callback)(pool);
+	return NULL;
+    }
+    */
+    /* Normalize size. */
+    if (size & 0x03) {
+	size &= ~0x03;
+	size += 4;
+    }
+
+    /* Add block header overhead. */
+    block_size = size + sizeof(pj_pool_block);
+
+    if (block_size < pool->increment_size)
+	block_size = pool->increment_size;
+
+    PJ_LOG(4, (pool->obj_name, "Resizing pool by %d bytes (used=%d, cap=%d)",
+	       block_size, pool->used_size, pool->capacity));
+
+    block = pj_pool_create_block(pool, block_size);
+    if (!block)
+	return NULL;
+
+    p = pj_pool_alloc_from_block(pool, block, size);
+#ifndef NDEBUG
+    if (p == NULL) {
+	p = p;
+    }
+#endif
+    pj_assert(p != NULL);
+    return p;
+}
+
+PJ_DEF(void) pj_pool_init_int(  pj_pool_t *pool, 
+				const char *name,
+				pj_size_t increment_size,
+				pj_pool_callback *callback)
+{
+    pj_pool_block *block;
+
+    pool->increment_size = increment_size;
+    pool->callback = callback;
+    pool->used_size = sizeof(*pool);
+    block = pool->block_list.next;
+    while (block != &pool->block_list) {
+	pool->used_size += sizeof(pj_pool_block);
+	block = block->next;
+    }
+
+    if (name) {
+	if (strchr(name, '%') != NULL) {
+	    sprintf(pool->obj_name, name, pool);
+	} else {
+	    strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
+	}
+    } else {
+	pool->obj_name[0] = '\0';
+    }
+}
+
+PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name,
+				       pj_size_t initial_size, 
+				       pj_size_t increment_size,
+				       pj_pool_callback *callback)
+{
+    pj_pool_t *pool;
+    pj_pool_block *block;
+    unsigned char *buffer;
+
+    buffer = (*f->policy.block_alloc)(f, initial_size);
+    if (!buffer)
+	return NULL;
+
+    // Set pool administrative data.
+    pool = (pj_pool_t*)buffer;
+    memset(pool, 0, sizeof(*pool));
+
+    pj_list_init(&pool->block_list);
+    pool->factory = f;
+
+    // Create the first block from the memory.
+    block = (pj_pool_block*) (buffer + sizeof(*pool));
+    block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
+    block->end = buffer + initial_size;
+    pj_list_insert_after(&pool->block_list, block);
+
+    pj_pool_init_int(pool, name, increment_size, callback);
+
+    // Pool initial capacity and used size
+    pool->capacity = initial_size;
+
+    PJ_LOG(4, (pool->obj_name, "pool created, size=%u", pool->capacity));
+    return pool;
+}
+
+static void reset_pool(pj_pool_t *pool)
+{
+    pj_pool_block *block;
+
+    block = pool->block_list.prev;
+    if (block == &pool->block_list)
+	return;
+
+    /* Skip the first block because it is occupying the same memory
+       as the pool itself.
+    */
+    block = block->prev;
+    
+    while (block != &pool->block_list) {
+	pj_pool_block *prev = block->prev;
+	pj_list_erase(block);
+	(*pool->factory->policy.block_free)(pool->factory, block, 
+					    block->end - (unsigned char*)block);
+	block = prev;
+    }
+
+    block = pool->block_list.next;
+    block->cur = block->buf;
+    pool->capacity = block->end - (unsigned char*)pool;
+    pool->used_size = 0;
+}
+
+PJ_DEF(void) pj_pool_reset(pj_pool_t *pool)
+{
+    PJ_LOG(5, (pool->obj_name, "reset(): cap=%d, used=%d(%d%%)", 
+	       pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
+
+    reset_pool(pool);
+}
+
+
+PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool)
+{
+    pj_size_t initial_size;
+
+    PJ_LOG(4, (pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p", 
+	       pool->capacity, pool->used_size, pool->used_size*100/pool->capacity,
+	       ((pj_pool_block*)pool->block_list.next)->buf, 
+	       ((pj_pool_block*)pool->block_list.next)->end));
+
+    reset_pool(pool);
+    initial_size = ((pj_pool_block*)pool->block_list.next)->end - 
+		   (unsigned char*)pool;
+    (*pool->factory->policy.block_free)(pool->factory, pool, initial_size);
+}
+
+
+
+/*
+PJ_DEF(char*) pj_pool_strdup(pj_pool_t *pool, const char *str)
+{
+    int size;
+    char *buf;
+
+    if (!str)
+	return NULL;
+
+    size = strlen(str)+1;
+    buf = pj_pool_alloc(pool, size);
+    if (!buf)
+	return NULL;
+
+    return (char*)memcpy( buf, str, size+1 );
+}
+*/
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool.h	(revision 1753)
@@ -0,0 +1,523 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/pool.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_POOL_H__
+#define __PJ_POOL_H__
+
+/**
+ * @file pool.h
+ * @brief Memory Pool.
+ */
+
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_POOL_GROUP Memory Pool Management
+ * @ingroup PJ
+ * @brief
+ * Memory pool management provides API to allocate and deallocate memory from
+ * memory pool and to manage and establish policy for pool creation and
+ * destruction in pool factory.
+ *
+ * \section PJ_POOL_FACTORY_SEC Pool Factory
+ * See: \ref PJ_POOL_FACTORY "Pool Factory"
+ *
+ * A memory pool must be created through a factory. A factory not only provides
+ * generic interface functions to create and release pool, but also provides 
+ * strategy to manage the life time of pools. One sample implementation, 
+ * \a pj_caching_pool, can be set to keep the pools released by application for
+ * future use as long as the total memory is below the limit.
+ * 
+ * The pool factory interface declared in PJLIB is designed to be extensible.
+ * Application can define its own strategy by creating it's own pool factory
+ * implementation, and this strategy can be used even by existing library
+ * without recompilation.
+ *
+ *
+ * \section PJ_POOL_POLICY_SEC Pool Factory Policy
+ * See: \ref PJ_POOL_FACTORY "Pool Factory Policy"
+ *
+ * A pool factory only defines functions to create and release pool and how
+ * to manage pools, but the rest of the functionalities are controlled by
+ * policy. A pool policy defines:
+ *  - how memory block is allocated and deallocated (the default implementation
+ *    allocates and deallocate memory by calling malloc() and free()).
+ *  - callback to be called when memory allocation inside a pool fails (the
+ *    default implementation will throw PJ_NO_MEMORY_EXCEPTION exception).
+ *  - concurrency when creating and releasing pool from/to the factory.
+ *
+ * A pool factory can be given different policy during creation to make
+ * it behave differently. For example, caching pool factory can be configured
+ * to allocate and deallocate from a static/contiguous/preallocated memory 
+ * instead of using malloc()/free().
+ * 
+ * What strategy/factory and what policy to use is not defined by PJLIB, but
+ * instead is left to application to make use whichever is most efficient for
+ * itself.
+ *
+ *
+ * \section PJ_POOL_POOL_SEC The Pool
+ * See: \ref PJ_POOL "Pool"
+ *
+ * The memory pool is an opaque object created by pool factory.
+ * Application uses this object to request a memory chunk, by calling
+ * #pj_pool_alloc or #pj_pool_calloc. When the application has finished using
+ * the pool, it must call #pj_pool_release to free all the chunks previously
+ * allocated and release the pool back to the factory.
+ *
+ * \section PJ_POOL_THREADING_SEC More on Threading Policies:
+ * - By design, memory allocation from a pool is not thread safe. We assumed 
+ *   that a pool will be owned by an object, and thread safety should be 
+ *   handled by that object. Thus these functions are not thread safe: 
+ *	- #pj_pool_alloc, 
+ *	- #pj_pool_calloc, 
+ *	- and other pool statistic functions.
+ * - Threading in the pool factory is decided by the policy set for the
+ *   factory when it was created.
+ */
+
+/**
+ * @defgroup PJ_POOL Memory Pool.
+ * @ingroup PJ_POOL_GROUP
+ * @brief
+ * A memory pool is initialized with an initial amount of memory, which is
+ * called a block. Pool can be configured to dynamically allocate more memory 
+ * blocks when it runs out of memory. Subsequent memory allocations by user 
+ * will use up portions of these block. 
+ * The pool doesn't keep track of individual memory allocations
+ * by user, and the user doesn't have to free these indidual allocations. This
+ * makes memory allocation simple and very fast. All the memory allocated from
+ * the pool will be destroyed when the pool itself is destroyed.
+ * @{
+ */
+
+/**
+ * The type for function to receive callback from the pool when it is unable
+ * to allocate memory. The elegant way to handle this condition is to throw
+ * exception, and this is what is expected by most of this library 
+ * components.
+ */
+typedef void pj_pool_callback(pj_pool_t *pool, pj_size_t size);
+
+/**
+ * This class, which is used internally by the pool, describes a single 
+ * block of memory from which user memory allocations will be allocated from.
+ */
+typedef struct pj_pool_block
+{
+    PJ_DECL_LIST_MEMBER(struct pj_pool_block)
+    unsigned char    *buf;
+    unsigned char    *cur;
+    unsigned char    *end;
+} pj_pool_block;
+
+
+/**
+ * This structure describes the memory pool. Only implementors of pool factory
+ * need to care about the contents of this structure.
+ */
+struct pj_pool_t
+{
+    PJ_DECL_LIST_MEMBER(struct pj_pool_t)
+
+    /** Pool name */
+    pj_obj_name	    obj_name;
+
+    /** Pool factory. */
+    pj_pool_factory *factory;
+
+    /** Current capacity allocated by the pool. */
+    pj_size_t	    capacity;
+
+    /** Number of memory used/allocated. */
+    pj_size_t	    used_size;
+
+    /** Size of memory block to be allocated when the pool runs out of memory */
+    pj_size_t	    increment_size;
+
+    /** List of memory blocks allcoated by the pool. */
+    pj_pool_block   block_list;
+
+    /** The callback to be called when the pool is unable to allocate memory. */
+    pj_pool_callback *callback;
+
+};
+
+
+/**
+ * Guidance on how much memory required for initial pool administrative data.
+ */
+#define PJ_POOL_SIZE	    (sizeof(struct pj_pool_t))
+
+/**
+ * Create a new pool from the pool factory. This wrapper will call create_pool
+ * member of the pool factory.
+ *
+ * @param factory	    The pool factory.
+ * @param name		    The name to be assigned to the pool. The name should 
+ *			    not be longer than PJ_MAX_OBJ_NAME (32 chars), or 
+ *			    otherwise it will be truncated.
+ * @param initial_size	    The size of initial memory blocks taken by the pool.
+ *			    Note that the pool will take 68+20 bytes for 
+ *			    administrative area from this block.
+ * @param increment_size    the size of each additional blocks to be allocated
+ *			    when the pool is running out of memory. If user 
+ *			    requests memory which is larger than this size, then 
+ *			    an error occurs.
+ *			    Note that each time a pool allocates additional block, 
+ *			    it needs PJ_POOL_SIZE more to store some 
+ *			    administrative info.
+ * @param callback	    Callback to be called when error occurs in the pool.
+ *			    If this value is NULL, then the callback from pool
+ *			    factory policy will be used.
+ *			    Note that when an error occurs during pool creation, 
+ *			    the callback itself is not called. Instead, NULL 
+ *			    will be returned.
+ *
+ * @return the memory pool, or NULL.
+ */
+PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *f, 
+				    const char *name,
+				    pj_size_t initial_size, 
+				    pj_size_t increment_size,
+				    pj_pool_callback *callback);
+
+/**
+ * Release the pool back to pool factory.
+ *
+ * @param pool	    Memory pool.
+ */
+PJ_IDECL(void) pj_pool_release( pj_pool_t *pool );
+
+/**
+ * Get pool object name.
+ *
+ * @param pool the pool.
+ *
+ * @return pool name as NULL terminated string.
+ */
+PJ_IDECL(const char *) pj_pool_getobjname( const pj_pool_t *pool );
+
+/**
+ * Reset the pool to its state when it was initialized.
+ * This means that if additional blocks have been allocated during runtime, 
+ * then they will be freed. Only the original block allocated during 
+ * initialization is retained. This function will also reset the internal 
+ * counters, such as pool capacity and used size.
+ *
+ * @param pool the pool.
+ */
+PJ_DECL(void) pj_pool_reset( pj_pool_t *pool );
+
+
+/**
+ * Get the pool capacity, that is, the system storage that have been allocated
+ * by the pool, and have been used/will be used to allocate user requests.
+ * There's no guarantee that the returned value represent a single
+ * contiguous block, because the capacity may be spread in several blocks.
+ *
+ * @param pool	the pool.
+ *
+ * @return the capacity.
+ */
+PJ_IDECL(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool );
+
+/**
+ * Get the total size of user allocation request.
+ *
+ * @param pool	the pool.
+ *
+ * @return the total size.
+ */
+PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool );
+
+/**
+ * Allocate storage with the specified size from the pool.
+ * If there's no storage available in the pool, then the pool can allocate more
+ * blocks if the increment size is larger than the requested size.
+ *
+ * @param pool	    the pool.
+ * @param size	    the requested size.
+ *
+ * @return pointer to the allocated memory.
+ */
+PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size);
+
+/**
+ * Allocate storage  from the pool, and initialize it to zero.
+ * This function behaves like pj_pool_alloc(), except that the storage will
+ * be initialized to zero.
+ *
+ * @param pool	    the pool.
+ * @param count	    the number of elements in the array.
+ * @param elem	    the size of individual element.
+ *
+ * @return pointer to the allocated memory.
+ */
+PJ_IDECL(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, 
+				pj_size_t elem);
+
+
+/**
+ * @}	// PJ_POOL
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_POOL_FACTORY Pool Factory and Policy.
+ * @ingroup PJ_POOL_GROUP
+ * @brief
+ * Pool factory declares an interface to create and destroy pool. There may
+ * be several strategies for pool creation, and these strategies should 
+ * implement the interface defined by pool factory.
+ *
+ * \section PJ_POOL_FACTORY_ITF Pool Factory Interface
+ * \ref 
+ * The pool factory defines the following interface:
+ *  - \a policy: the memory pool factory policy.
+ *  - \a create_pool(): create a new memory pool.
+ *  - \a release_pool(): release memory pool back to factory.
+ *
+ * \section PJ_POOL_FACTORY_POL Pool Factory Policy.
+ * The pool factory policy controls the behaviour of memory factories, and
+ * defines the following interface:
+ *  - \a block_alloc(): allocate memory block from backend memory mgmt/system.
+ *  - \a block_free(): free memory block back to backend memory mgmt/system.
+ * @{
+ */
+
+/* We unfortunately don't have support for factory policy options as now,
+   so we keep this commented at the moment.
+enum PJ_POOL_FACTORY_OPTION
+{
+    PJ_POOL_FACTORY_SERIALIZE = 1
+};
+*/
+
+/**
+ * This structure declares pool factory interface.
+ */
+typedef struct pj_pool_factory_policy
+{
+    /**
+     * Allocate memory block (for use by pool). This function is called
+     * by memory pool to allocate memory block.
+     * 
+     * @param factory	Pool factory.
+     * @param size	The size of memory block to allocate.
+     *
+     * @return		Memory block.
+     */
+    void* (*block_alloc)(pj_pool_factory *factory, pj_size_t size);
+
+    /**
+     * Free memory block.
+     *
+     * @param factory	Pool factory.
+     * @param mem	Memory block previously allocated by block_alloc().
+     * @param size	The size of memory block.
+     */
+    void (*block_free)(pj_pool_factory *factory, void *mem, pj_size_t size);
+
+    /**
+     * Default callback to be called when memory allocation fails.
+     */
+    pj_pool_callback *callback;
+
+    /**
+     * Option flags.
+     */
+    unsigned flags;
+
+} pj_pool_factory_policy;
+
+/**
+ * This constant denotes the exception number that will be thrown by default
+ * memory factory policy when memory allocation fails.
+ */
+#define PJ_NO_MEMORY_EXCEPTION	1
+
+/**
+ * This global variable points to default memory pool factory policy.
+ * The behaviour of the default policy is:
+ *  - block allocation and deallocation use malloc() and free().
+ *  - callback will raise PJ_NO_MEMORY_EXCEPTION exception.
+ *  - access to pool factory is not serialized (i.e. not thread safe).
+ */
+extern pj_pool_factory_policy pj_pool_factory_default_policy;
+
+/**
+ * This structure contains the declaration for pool factory interface.
+ */
+struct pj_pool_factory
+{
+    /**
+     * Memory pool policy.
+     */
+    pj_pool_factory_policy policy;
+
+    /**
+    * Create a new pool from the pool factory.
+    *
+    * @param factory	The pool factory.
+    * @param name	the name to be assigned to the pool. The name should 
+    *			not be longer than PJ_MAX_OBJ_NAME (32 chars), or 
+    *			otherwise it will be truncated.
+    * @param initial_size the size of initial memory blocks taken by the pool.
+    *			Note that the pool will take 68+20 bytes for 
+    *			administrative area from this block.
+    * @param increment_size the size of each additional blocks to be allocated
+    *			when the pool is running out of memory. If user 
+    *			requests memory which is larger than this size, then 
+    *			an error occurs.
+    *			Note that each time a pool allocates additional block, 
+    *			it needs 20 bytes (equal to sizeof(pj_pool_block)) to 
+    *			store some administrative info.
+    * @param callback	Cllback to be called when error occurs in the pool.
+    *			Note that when an error occurs during pool creation, 
+    *			the callback itself is not called. Instead, NULL 
+    *			will be returned.
+    *
+    * @return the memory pool, or NULL.
+    */
+    pj_pool_t*	(*create_pool)( pj_pool_factory *factory,
+				const char *name,
+				pj_size_t initial_size, 
+				pj_size_t increment_size,
+				pj_pool_callback *callback);
+
+    /**
+     * Release the pool to the pool factory.
+     *
+     * @param factory	The pool factory.
+     * @param pool	The pool to be released.
+    */
+    void (*release_pool)( pj_pool_factory *factory, pj_pool_t *pool );
+
+    /**
+     * Dump pool status to log.
+     *
+     * @param factory	The pool factory.
+     */
+    void (*dump_status)( pj_pool_factory *factory, pj_bool_t detail );
+};
+
+/**
+ * This function is intended to be used by pool factory implementors.
+ */
+PJ_DECL(pj_pool_t*) pj_pool_create_int(	pj_pool_factory *f, 
+					const char *name,
+					pj_size_t initial_size, 
+					pj_size_t increment_size,
+					pj_pool_callback *callback);
+
+/**
+ * This function is intended to be used by pool factory implementors.
+ */
+PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool, 
+				const char *name,
+				pj_size_t increment_size,
+				pj_pool_callback *callback);
+
+/**
+ * This function is intended to be used by pool factory implementors.
+ */
+PJ_DECL(void) pj_pool_destroy_int( pj_pool_t *pool );
+
+
+/**
+ *  @}	// PJ_POOL_FACTORY
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @defgroup PJ_CACHING_POOL Caching Pool Factory.
+ * @ingroup PJ_POOL_GROUP
+ * @brief
+ * Caching pool is one sample implementation of pool factory where the
+ * factory can reuse memory to create a pool. Application defines what the 
+ * maximum memory the factory can hold, and when a pool is released the
+ * factory decides whether to destroy the pool or to keep it for future use.
+ * If the total amount of memory in the internal cache is still within the
+ * limit, the factory will keep the pool in the internal cache, otherwise the
+ * pool will be destroyed, thus releasing the memory back to the system.
+ *
+ * @{
+ */
+
+#define PJ_CACHING_POOL_ARRAY_SIZE	16
+
+/**
+ * Declaration for caching pool. Application doesn't normally need to
+ * care about the contents of this struct, it is only provided here because
+ * application need to define an instance of this struct (we can not allocate
+ * the struct from a pool since there is no pool factory yet!).
+ */
+struct pj_caching_pool 
+{
+    pj_pool_factory factory;
+    pj_size_t	    capacity, max_capacity, used_count;
+    pj_list	    free_list[PJ_CACHING_POOL_ARRAY_SIZE];
+    pj_list	    used_list;
+};
+
+
+
+/**
+ * Initialize caching pool.
+ *
+ * @param ch_pool	The caching pool factory to be initialized.
+ * @param policy	Pool factory policy.
+ * @param max_capacity	The total capacity to be retained in the cache. When
+ *			the pool is returned to the cache, it will be kept in
+ *			recycling list if the total capacity of pools in this
+ *			list plus the capacity of the pool is still below this
+ *			value.
+ */
+PJ_DECL(void) pj_caching_pool_init( pj_caching_pool *ch_pool, 
+				    const pj_pool_factory_policy *policy,
+				    pj_size_t max_capacity);
+
+
+/**
+ * Destroy caching pool, and release all the pools in the recycling list.
+ *
+ * @param ch_pool	The caching pool.
+ */
+PJ_DECL(void) pj_caching_pool_destroy( pj_caching_pool *ch_pool );
+
+/**
+ * @}	// PJ_CACHING_POOL
+ */
+
+#  if PJ_FUNCTIONS_ARE_INLINED
+#    include "pool_i.h"
+#  endif
+
+PJ_END_DECL
+    
+#endif	/* __PJ_POOL_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_caching.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_caching.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_caching.c	(revision 1753)
@@ -0,0 +1,218 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/pool_caching.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <stdlib.h> /* malloc, free */
+
+
+static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, 
+				    const char *name,
+				    pj_size_t initial_size, 
+				    pj_size_t increment_sz,
+				    pj_pool_callback *callback);
+static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool);
+static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail );
+
+static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] = 
+{
+    256, 512, 1024, 2048, 4096, 8192, 12288, 16384, 
+    20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536
+};
+
+
+PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, 
+				   const pj_pool_factory_policy *policy,
+				   pj_size_t max_capacity)
+{
+    int i;
+
+    pj_memset(cp, 0, sizeof(*cp));
+    
+    cp->max_capacity = max_capacity;
+    pj_list_init(&cp->used_list);
+    for (i=0; i<PJ_CACHING_POOL_ARRAY_SIZE; ++i)
+	pj_list_init(&cp->free_list[i]);
+
+    pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy));
+    cp->factory.create_pool = &cpool_create_pool;
+    cp->factory.release_pool = &cpool_release_pool;
+    cp->factory.dump_status = &cpool_dump_status;
+}
+
+PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp )
+{
+    int i;
+    pj_pool_t *pool;
+
+    /* Delete all pool in free list */
+    for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) {
+	pj_pool_t *pool = cp->free_list[i].next;
+	pj_pool_t *next;
+	for (; pool != (void*)&cp->free_list[i]; pool = next) {
+	    next = pool->next;
+	    pj_list_erase(pool);
+	    pj_pool_destroy_int(pool);
+	}
+    }
+
+    /* Delete all pools in used list */
+    pool = cp->used_list.next;
+    while (pool != (pj_pool_t*) &cp->used_list) {
+	pj_pool_t *next = pool->next;
+	pj_list_erase(pool);
+	pj_pool_destroy_int(pool);
+	pool = next;
+    }
+}
+
+static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, 
+					      const char *name, 
+					      pj_size_t initial_size, 
+					      pj_size_t increment_sz, 
+					      pj_pool_callback *callback)
+{
+    pj_caching_pool *cp = (pj_caching_pool*)pf;
+    pj_pool_t *pool;
+    int idx;
+
+    /* Use pool factory's policy when callback is NULL */
+    if (callback == NULL) {
+	callback = pf->policy.callback;
+    }
+
+    /* Search the suitable size for the pool. 
+     * We'll just do linear search to the size array, as the array size itself
+     * is only a few elements. Binary search I suspect will be less efficient
+     * for this purpose.
+     */
+    for (idx=0; 
+	 idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size; 
+	 ++idx)
+	;
+
+    /* Check whether there's a pool in the list. */
+    if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) {
+	/* No pool is available. */
+	/* Set minimum size. */
+	if (idx < PJ_CACHING_POOL_ARRAY_SIZE)
+	    initial_size =  pool_sizes[idx];
+
+	/* Create new pool */
+	pool = pj_pool_create_int(&cp->factory, name, initial_size, 
+				  increment_sz, callback);
+	if (!pool)
+	    return NULL;
+
+    } else {
+	/* Get one pool from the list. */
+	pool = cp->free_list[idx].next;
+	pj_list_erase(pool);
+
+	/* Initialize the pool. */
+	pj_pool_init_int(pool, name, increment_sz, callback);
+
+	/* Update pool manager's free capacity. */
+	cp->capacity -= pj_pool_get_capacity(pool);
+
+	PJ_LOG(5, (pool->obj_name, "pool reused, size=%u", pool->capacity));
+    }
+
+    /* Put in used list. */
+    pj_list_insert_before( &cp->used_list, pool );
+
+    /* Increment used count. */
+    ++cp->used_count;
+    return pool;
+}
+
+static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool)
+{
+    pj_caching_pool *cp = (pj_caching_pool*)pf;
+    int i;
+
+    /* Erase from the used list. */
+    pj_list_erase(pool);
+
+    /* Decrement used count. */
+    --cp->used_count;
+
+    /* Destroy the pool if the size is greater than our size or if the total
+     * capacity in our recycle list (plus the size of the pool) exceeds 
+     * maximum capacity.
+   . */
+    if (pool->capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] ||
+	cp->capacity + pool->capacity > cp->max_capacity)
+    {
+	pj_pool_destroy_int(pool);
+	return;
+    }
+
+    /* Reset pool. */
+    PJ_LOG(4, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)", 
+	       pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
+    pj_pool_reset(pool);
+
+    /*
+     * Otherwise put the pool in our recycle list.
+     */
+    for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[i] != pool->capacity; ++i)
+	;
+
+    pj_assert( i != PJ_CACHING_POOL_ARRAY_SIZE );
+    if (i == PJ_CACHING_POOL_ARRAY_SIZE) {
+	/* Something has gone wrong with the pool. */
+	pj_pool_destroy_int(pool);
+	return;
+    }
+
+    pj_list_insert_after(&cp->free_list[i], pool);
+    cp->capacity += pool->capacity;
+}
+
+static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail )
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+    pj_caching_pool *cp = (pj_caching_pool*)factory;
+    PJ_LOG(3,("cachpool", " Dumping caching pool:"));
+    PJ_LOG(3,("cachpool", "   Capacity=%u, max_capacity=%u, used_cnt=%u", \
+			     cp->capacity, cp->max_capacity, cp->used_count));
+    if (detail) {
+	pj_pool_t *pool = cp->used_list.next;
+	pj_uint32_t total_used = 0, total_capacity = 0;
+        PJ_LOG(3,("cachpool", "  Dumping all active pools:"));
+	while (pool != (void*)&cp->used_list) {
+	    PJ_LOG(3,("cachpool", "   %12s: %8d of %8d (%d%%) used", pool->obj_name, 
+				  pool->used_size, pool->capacity,
+				  pool->used_size*100/pool->capacity));
+	    total_used += pool->used_size;
+	    total_capacity += pool->capacity;
+	    pool = pool->next;
+	}
+	PJ_LOG(3,("cachpool", "  Total %9d of %9d (%d %%) used!",
+			      total_used, total_capacity,
+			      total_used * 100 / total_capacity));
+    }
+#endif
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_dbg_win32.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_dbg_win32.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_dbg_win32.c	(revision 1753)
@@ -0,0 +1,225 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/pool_dbg_win32.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#include <pj/pool.h>
+
+/* Only if we ARE debugging memory allocations. */
+#if PJ_POOL_DEBUG
+
+#include <pj/list.h>
+#include <pj/log.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef struct memory_entry
+{
+    PJ_DECL_LIST_MEMBER(struct memory_entry)
+    void *ptr;
+    char *file;
+    int   line;
+} memory_entry;
+
+struct pj_pool_t
+{
+    char	    obj_name[32];
+    HANDLE	    hHeap;
+    memory_entry    first;
+    pj_size_t	    initial_size;
+    pj_size_t	    increment;
+    pj_size_t	    used_size;
+    char	   *file;
+    int		    line;
+};
+
+PJ_DEF(void) pj_pool_set_functions( void *(*malloc_func)(pj_size_t),
+				     void (*free_func)(void *ptr, pj_size_t))
+{
+    /* Ignored. */
+    PJ_UNUSED_ARG(malloc_func)
+    PJ_UNUSED_ARG(free_func)
+}
+
+PJ_DEF(pj_pool_t*) pj_pool_create_dbg( const char *name,
+				       pj_size_t initial_size, 
+				       pj_size_t increment_size,
+				       pj_pool_callback *callback,
+				       char *file, int line)
+{
+    pj_pool_t *pool;
+    HANDLE hHeap;
+
+    PJ_UNUSED_ARG(callback)
+
+    /* Create Win32 heap for the pool. */
+    hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+		       initial_size, 0);
+    if (!hHeap) {
+	return NULL;
+    }
+
+
+    /* Create and initialize the pool structure. */
+    pool = HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, 
+		     sizeof(*pool));
+    memset(pool, 0, sizeof(*pool));
+    pool->file = file;
+    pool->line = line;
+    pool->hHeap = hHeap;
+    pool->initial_size = initial_size;
+    pool->increment = increment_size;
+    pool->used_size = 0;
+
+    /* Set name. */
+    if (name) {
+	if (strchr(name, '%') != NULL) {
+	    sprintf(pool->obj_name, name, pool);
+	} else {
+	    strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
+	}
+    } else {
+	pool->obj_name[0] = '\0';
+    }
+
+    /* List pool's entry. */
+    pj_list_init(&pool->first);
+
+    PJ_LOG(3,(pool->obj_name, "Pool created"));
+    return pool;
+}
+
+PJ_DEF(void) pj_pool_destroy( pj_pool_t *pool )
+{
+    memory_entry *entry;
+
+    PJ_LOG(3,(pool->obj_name, "Destoying pool, init_size=%u, used=%u",
+			      pool->initial_size, pool->used_size));
+
+    if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, pool)) {
+	PJ_LOG(2,(pool->obj_name, "Corrupted pool structure, allocated in %s:%d", 
+		  pool->file, pool->line));
+    }
+
+    /* Validate all memory entries in the pool. */
+    for (entry=pool->first.next; entry != &pool->first; entry = entry->next) {
+	if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry)) {
+	    PJ_LOG(2,(pool->obj_name, "Corrupted pool entry, allocated in %s:%d", 
+		      entry->file, entry->line));
+	}
+
+	if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry->ptr)) {
+	    PJ_LOG(2,(pool->obj_name, "Corrupted pool memory, allocated in %s:%d", 
+		      entry->file, entry->line));
+	}
+    }
+
+    /* Destroy heap. */
+    HeapDestroy(pool->hHeap);
+}
+
+PJ_DEF(void) pj_pool_reset( pj_pool_t *pool )
+{
+    /* Do nothing. */
+    PJ_UNUSED_ARG(pool)
+}
+
+PJ_DEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(pool)
+    return 0;
+}
+
+PJ_DEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(pool)
+    return 0;
+}
+
+PJ_DEF(pj_size_t) pj_pool_get_request_count( pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(pool)
+    return 0;
+}
+
+PJ_DEF(void*) pj_pool_alloc_dbg( pj_pool_t *pool, pj_size_t size, 
+				 char *file, int line)
+{
+    memory_entry *entry;
+    int entry_size;
+
+    entry_size = sizeof(*entry);
+    entry = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+		      entry_size);
+    entry->file = file;
+    entry->line = line;
+    entry->ptr = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+			   size);
+    pj_list_insert_before( &pool->first, entry);
+
+    pool->used_size += size;
+    return entry->ptr;
+}
+
+PJ_DEF(void*) pj_pool_calloc_dbg( pj_pool_t *pool, pj_size_t count, pj_size_t elem,
+				  char *file, int line)
+{
+    void *ptr;
+    ptr = pj_pool_alloc_dbg(pool, count*elem, file, line);
+    memset(ptr, 0, count*elem);
+    return ptr;
+}
+
+
+PJ_DEF(void) pj_pool_pool_init( pj_pool_pool_t *pool_pool, 
+				 pj_size_t max_capacity)
+{
+    PJ_UNUSED_ARG(pool_pool)
+    PJ_UNUSED_ARG(max_capacity)
+}
+
+PJ_DEF(void) pj_pool_pool_destroy( pj_pool_pool_t *pool_pool )
+{
+    PJ_UNUSED_ARG(pool_pool)
+}
+
+PJ_DEF(pj_pool_t*) pj_pool_pool_create_pool( pj_pool_pool_t *pool_pool,
+					      const char *name,
+					      pj_size_t initial_size, 
+					      pj_size_t increment_size,
+					      pj_pool_callback *callback)
+{
+    PJ_UNUSED_ARG(pool_pool)
+    return pj_pool_create(name, initial_size, increment_size, callback);
+}
+
+PJ_DEF(void) pj_pool_pool_release_pool( pj_pool_pool_t *pool_pool,
+					pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(pool_pool)
+    pj_pool_destroy(pool);
+}
+
+
+#endif	/* PJ_POOL_DEBUG */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_i.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_i.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_i.h	(revision 1753)
@@ -0,0 +1,95 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/pool_i.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+
+#include <pj/string.h>
+#include <string.h>
+
+PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size);
+
+PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
+{
+    return pool->capacity;
+}
+
+PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
+{
+    return pool->used_size;
+}
+
+PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_t *pool,
+					 pj_pool_block *block, pj_size_t size )
+{
+    /* The operation below is valid for size==0. 
+     * When size==0, the function will return the pointer to the pool
+     * memory address, but no memory will be allocated.
+     */
+    if (size & 0x03) {
+	size &= ~0x03;
+	size += 4;
+    }
+    if ((unsigned)(block->end - block->cur) >= size) {
+	void *ptr = block->cur;
+	block->cur += size;
+	pool->used_size += size;
+	return ptr;
+    }
+    return NULL;
+}
+
+PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size)
+{
+    pj_pool_block *block = pool->block_list.next;
+    void *ptr = pj_pool_alloc_from_block(pool, block, size);
+    if (!ptr)
+	ptr = pj_pool_allocate_find(pool, size);
+    return ptr;
+}
+
+
+PJ_IDEF(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, pj_size_t size)
+{
+    void *buf = pj_pool_alloc( pool, size*count);
+    if (buf)
+	memset(buf, 0, size * count);
+    return buf;
+}
+
+PJ_IDEF(const char *) pj_pool_getobjname( const pj_pool_t *pool )
+{
+    return pool->obj_name;
+}
+
+PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f, 
+				    const char *name,
+				    pj_size_t initial_size, 
+				    pj_size_t increment_size,
+				    pj_pool_callback *callback)
+{
+    return (*f->create_pool)(f, name, initial_size, increment_size, callback);
+}
+
+PJ_IDEF(void) pj_pool_release( pj_pool_t *pool )
+{
+    (*pool->factory->release_pool)(pool->factory, pool);
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_policy.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_policy.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/pool_policy.c	(revision 1753)
@@ -0,0 +1,61 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/pool_policy.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/pool.h>
+#include <pj/except.h>
+
+/*
+ * This file contains pool default policy definition and implementation.
+ */
+#include <stdlib.h>
+
+static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
+{
+    PJ_UNUSED_ARG(factory)
+    PJ_UNUSED_ARG(size)
+
+    return malloc(size);
+}
+
+static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size)
+{
+    PJ_UNUSED_ARG(factory)
+    PJ_UNUSED_ARG(size)
+
+    free(mem);
+}
+
+static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
+{
+    PJ_UNUSED_ARG(pool)
+    PJ_UNUSED_ARG(size)
+
+    PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
+}
+
+pj_pool_factory_policy pj_pool_factory_default_policy = 
+{
+    &default_block_alloc,
+    &default_block_free,
+    &default_pool_callback,
+    0
+};
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/rbtree.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/rbtree.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/rbtree.c	(revision 1753)
@@ -0,0 +1,404 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/rbtree.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/rbtree.h>
+
+static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node ) 
+{
+    pj_rbtree_node *rnode, *parent;
+	
+    rnode = node->right;
+    if (rnode == tree->null)
+        return;
+    
+    node->right = rnode->left;
+    if (rnode->left != tree->null)
+        rnode->left->parent = node;
+    parent = node->parent;
+    rnode->parent = parent;
+    if (parent != tree->null) {
+        if (parent->left == node)
+	   parent->left = rnode;
+        else
+	   parent->right = rnode;
+    } else {
+        tree->root = rnode;
+    }
+    rnode->left = node;
+    node->parent = rnode;
+}
+
+static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node ) 
+{
+    pj_rbtree_node *lnode, *parent;
+
+    lnode = node->left;
+    if (lnode == tree->null)
+        return;
+
+    node->left = lnode->right;
+    if (lnode->right != tree->null)
+	lnode->right->parent = node;
+    parent = node->parent;
+    lnode->parent = parent;
+
+    if (parent != tree->null) {
+        if (parent->left == node)
+	    parent->left = lnode;
+	else
+	    parent->right = lnode;
+    } else {
+        tree->root = lnode;
+    }
+    lnode->right = node;
+    node->parent = lnode;
+}
+
+static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node ) 
+{
+    pj_rbtree_node *temp, *parent;
+	
+    while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) {
+        parent = node->parent;
+        if (parent == parent->parent->left) {
+	    temp = parent->parent->right;
+	    if (temp->color == PJ_RBCOLOR_RED) {
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        node = parent;
+	        node->color = PJ_RBCOLOR_BLACK;
+	        node = node->parent;
+	        node->color = PJ_RBCOLOR_RED;
+	    } else {
+	        if (node == parent->right) {
+		   node = parent;
+		   left_rotate(tree, node);
+	        }
+	        temp = node->parent;
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        temp = temp->parent;
+	        temp->color = PJ_RBCOLOR_RED;
+	        right_rotate( tree, temp);
+	    }
+        } else {
+	    temp = parent->parent->left;
+	    if (temp->color == PJ_RBCOLOR_RED) {
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        node = parent;
+	        node->color = PJ_RBCOLOR_BLACK;
+	        node = node->parent;
+	        node->color = PJ_RBCOLOR_RED;
+	    } else {
+	        if (node == parent->left) {
+		    node = parent;
+		    right_rotate(tree, node);
+	        }
+	        temp = node->parent;
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        temp = temp->parent;
+	        temp->color = PJ_RBCOLOR_RED;
+	        left_rotate(tree, temp);
+	   }
+        }
+    }
+	
+    tree->root->color = PJ_RBCOLOR_BLACK;
+}
+
+
+static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node )
+{
+    pj_rbtree_node *temp;
+		
+    while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) {
+        if (node->parent->left == node) {
+	    temp = node->parent->right;
+	    if (temp->color == PJ_RBCOLOR_RED) {
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        node->parent->color = PJ_RBCOLOR_RED;
+	        left_rotate(tree, node->parent);
+	        temp = node->parent->right;
+	    }
+	    if (temp->left->color == PJ_RBCOLOR_BLACK && 
+	        temp->right->color == PJ_RBCOLOR_BLACK) 
+	    {
+	        temp->color = PJ_RBCOLOR_RED;
+	        node = node->parent;
+	    } else {
+	        if (temp->right->color == PJ_RBCOLOR_BLACK) {
+		    temp->left->color = PJ_RBCOLOR_BLACK;
+		    temp->color = PJ_RBCOLOR_RED;
+		    right_rotate( tree, temp);
+		    temp = node->parent->right;
+	        }
+	        temp->color = node->parent->color;
+	        temp->right->color = PJ_RBCOLOR_BLACK;
+	        node->parent->color = PJ_RBCOLOR_BLACK;
+	        left_rotate(tree, node->parent);
+	        node = tree->root;
+	    }
+        } else {
+	    temp = node->parent->left;
+	    if (temp->color == PJ_RBCOLOR_RED) {
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        node->parent->color = PJ_RBCOLOR_RED;
+	        right_rotate( tree, node->parent);
+	        temp = node->parent->left;
+	    }
+	    if (temp->right->color == PJ_RBCOLOR_BLACK && 
+		temp->left->color == PJ_RBCOLOR_BLACK) 
+	    {
+	        temp->color = PJ_RBCOLOR_RED;
+	        node = node->parent;
+	    } else {
+	        if (temp->left->color == PJ_RBCOLOR_BLACK) {
+		    temp->right->color = PJ_RBCOLOR_BLACK;
+		    temp->color = PJ_RBCOLOR_RED;
+		    left_rotate( tree, temp);
+		    temp = node->parent->left;
+	        }
+	        temp->color = node->parent->color;
+	        node->parent->color = PJ_RBCOLOR_BLACK;
+	        temp->left->color = PJ_RBCOLOR_BLACK;
+	        right_rotate(tree, node->parent);
+	        node = tree->root;
+	    }
+        }
+    }
+	
+    node->color = PJ_RBCOLOR_BLACK;
+}
+
+
+PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp )
+{
+    tree->null = tree->root = &tree->null_node;
+    tree->null->key = NULL;
+    tree->null->user_data = NULL;
+    tree->size = 0;
+    tree->null->left = tree->null->right = tree->null->parent = tree->null;
+    tree->null->color = PJ_RBCOLOR_BLACK;
+    tree->comp = comp;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree )
+{
+    register pj_rbtree_node *node = tree->root;
+    register pj_rbtree_node *null = tree->null;
+    
+    while (node->left != null)
+	node = node->left;
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree )
+{
+    register pj_rbtree_node *node = tree->root;
+    register pj_rbtree_node *null = tree->null;
+    
+    while (node->right != null)
+	node = node->right;
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, 
+					register pj_rbtree_node *node )
+{
+    register pj_rbtree_node *null = tree->null;
+    
+    if (node->right != null) {
+	for (node=node->right; node->left!=null; node = node->left)
+	    /* void */;
+    } else {
+        register pj_rbtree_node *temp = node->parent;
+        while (temp!=null && temp->right==node) {
+	    node = temp;
+	    temp = temp->parent;
+	}
+	node = temp;
+    }    
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, 
+					register pj_rbtree_node *node )
+{
+    register pj_rbtree_node *null = tree->null;
+    
+    if (node->left != null) {
+        for (node=node->left; node->right!=null; node=node->right)
+	   /* void */;
+    } else {
+        register pj_rbtree_node *temp = node->parent;
+        while (temp!=null && temp->left==node) {
+	    node = temp;
+	    temp = temp->parent;
+        }
+        node = temp;
+    }    
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree, 
+			      pj_rbtree_node *element )
+{
+    int rv = 0;
+    pj_rbtree_node *node, *parent = tree->null, 
+		   *null = tree->null;
+    pj_rbtree_comp *comp = tree->comp;
+	
+    node = tree->root;	
+    while (node != null) {
+        rv = (*comp)(element->key, node->key);
+        if (rv == 0) {
+	    /* found match, i.e. entry with equal key already exist */
+	    return -1;
+	}    
+	parent = node;
+        node = rv < 0 ? node->left : node->right;
+    }
+
+    element->color = PJ_RBCOLOR_RED;
+    element->left = element->right = null;
+
+    node = element;
+    if (parent != null) {
+        node->parent = parent;
+        if (rv < 0)
+	   parent->left = node;
+        else
+	   parent->right = node;
+        insert_fixup( tree, node);
+    } else {
+        tree->root = node;
+        node->parent = null;
+        node->color = PJ_RBCOLOR_BLACK;
+    }
+	
+    ++tree->size;
+    return 0;
+}
+
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
+					const void *key )
+{
+    int rv;
+    pj_rbtree_node *node = tree->root;
+    pj_rbtree_node *null = tree->null;
+    pj_rbtree_comp *comp = tree->comp;
+    
+    while (node != null) {
+        rv = (*comp)(key, node->key);
+        if (rv == 0)
+	    return node;
+        node = rv < 0 ? node->left : node->right;
+    }
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
+					 pj_rbtree_node *node )
+{
+    pj_rbtree_node *succ;
+    pj_rbtree_node *null = tree->null;
+    pj_rbtree_node *child;
+    pj_rbtree_node *parent;
+    
+    if (node->left == null || node->right == null) {
+        succ = node;
+    } else {
+        for (succ=node->right; succ->left!=null; succ=succ->left)
+	   /* void */;
+    }
+
+    child = succ->left != null ? succ->left : succ->right;
+    parent = succ->parent;
+    child->parent = parent;
+    
+    if (parent != null) {
+	if (parent->left == succ)
+	    parent->left = child;
+        else
+	   parent->right = child;
+    } else
+        tree->root = child;
+
+    if (succ != node) {
+        succ->parent = node->parent;
+        succ->left = node->left;
+        succ->right = node->right;
+        succ->color = node->color;
+
+        parent = node->parent;
+        if (parent != null) {
+	   if (parent->left==node)
+	        parent->left=succ;
+	   else
+		parent->right=succ;
+        }
+        if (node->left != null)
+	   node->left->parent = succ;;
+        if (node->right != null)
+	    node->right->parent = succ;
+
+        if (tree->root == node)
+	   tree->root = succ;
+    }
+
+    if (succ->color == PJ_RBCOLOR_BLACK) {
+	if (child != null) 
+	    delete_fixup(tree, child);
+        tree->null->color = PJ_RBCOLOR_BLACK;
+    }
+
+    --tree->size;
+    return node;
+}
+
+
+PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
+				       pj_rbtree_node *node )
+{
+    unsigned l, r;
+    
+    if (node==NULL) 
+	node = tree->root;
+    
+    l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0;
+    r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0;
+    return l > r ? l : r;
+}
+
+PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
+				       pj_rbtree_node *node )
+{
+    unsigned l, r;
+    
+    if (node==NULL) 
+	node=tree->root;
+    
+    l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0;
+    r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0;
+    return l > r ? r : l;
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/rbtree.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/rbtree.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/rbtree.h	(revision 1753)
@@ -0,0 +1,212 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/rbtree.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_RBTREE_H__
+#define __PJ_RBTREE_H__
+
+/**
+ * @file rbtree.h
+ * @brief Red/Black Tree
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_RBTREE Red/Black Balanced Tree
+ * @ingroup PJ_DS
+ * @brief
+ * Red/Black tree is the variant of balanced tree, where the search, insert, 
+ * and delete operation is \b guaranteed to take at most \a O( lg(n) ).
+ * @{
+ */
+/**
+ * Color type for Red-Black tree.
+ */ 
+typedef enum pj_rbcolor_t
+{
+    PJ_RBCOLOR_BLACK,
+    PJ_RBCOLOR_RED
+} pj_rbcolor_t;
+
+/**
+ * The type of the node of the R/B Tree.
+ */
+typedef struct pj_rbtree_node 
+{
+    /** Pointers to the node's parent, and left and right siblings. */
+    struct pj_rbtree_node *parent, *left, *right;
+
+    /** Key associated with the node. */
+    const void *key;
+
+    /** User data associated with the node. */
+    void *user_data;
+
+    /** The R/B Tree node color. */
+    pj_rbcolor_t color;
+
+} pj_rbtree_node;
+
+
+/**
+ * The type of function use to compare key value of tree node.
+ * @return
+ *  0 if the keys are equal
+ * <0 if key1 is lower than key2
+ * >0 if key1 is greater than key2.
+ */
+typedef int pj_rbtree_comp(const void *key1, const void *key2);
+
+
+/**
+ * Declaration of a red-black tree. All elements in the tree must have UNIQUE
+ * key.
+ * A red black tree always maintains the balance of the tree, so that the
+ * tree height will not be greater than lg(N). Insert, search, and delete
+ * operation will take lg(N) on the worst case. But for insert and delete,
+ * there is additional time needed to maintain the balance of the tree.
+ */
+typedef struct pj_rbtree
+{
+    pj_rbtree_node null_node, *null, *root;
+    unsigned size;
+    pj_rbtree_comp *comp;
+} pj_rbtree;
+
+
+/**
+ * Guidance on how much memory required for each of the node.
+ */
+#define PJ_RBTREE_NODE_SIZE	    (sizeof(pj_rbtree_node))
+
+
+/**
+ * Guidance on memory required for the tree.
+ */
+#define PJ_RBTREE_SIZE		    (sizeof(pj_rbtree))
+
+
+/**
+ * Initialize the tree.
+ * @param tree the tree to be initialized.
+ * @param comp key comparison function to be used for this tree.
+ */
+PJ_DECL(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp);
+
+/**
+ * Get the first element in the tree.
+ * The first element always has the least value for the key, according to
+ * the comparison function.
+ * @param tree the tree.
+ * @return the tree node, or NULL if the tree has no element.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree );
+
+/**
+ * Get the last element in the tree.
+ * The last element always has the greatest key value, according to the
+ * comparison function defined for the tree.
+ * @param tree the tree.
+ * @return the tree node, or NULL if the tree has no element.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree );
+
+/**
+ * Get the successive element for the specified node.
+ * The successive element is an element with greater key value.
+ * @param tree the tree.
+ * @param node the node.
+ * @return the successive node, or NULL if the node has no successor.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, 
+					 pj_rbtree_node *node );
+
+/**
+ * The the previous node for the specified node.
+ * The previous node is an element with less key value.
+ * @param tree the tree.
+ * @param node the node.
+ * @return the previous node, or NULL if the node has no previous node.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, 
+					 pj_rbtree_node *node );
+
+/**
+ * Insert a new node. 
+ * The node will be inserted at sorted location. The key of the node must 
+ * be UNIQUE, i.e. it hasn't existed in the tree.
+ * @param tree the tree.
+ * @param node the node to be inserted.
+ * @return zero on success, or -1 if the key already exist.
+ */
+PJ_DECL(int) pj_rbtree_insert( pj_rbtree *tree, 
+			       pj_rbtree_node *node );
+
+/**
+ * Find a node which has the specified key.
+ * @param tree the tree.
+ * @param key the key to search.
+ * @return the tree node with the specified key, or NULL if the key can not
+ *         be found.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
+					 const void *key );
+
+/**
+ * Erase a node from the tree.
+ * @param tree the tree.
+ * @param node the node to be erased.
+ * @return the tree node itself.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
+					  pj_rbtree_node *node );
+
+/**
+ * Get the maximum tree height from the specified node.
+ * @param tree the tree.
+ * @param node the node, or NULL to get the root of the tree.
+ * @return the maximum height, which should be at most lg(N)
+ */
+PJ_DECL(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
+					pj_rbtree_node *node );
+
+/**
+ * Get the minumum tree height from the specified node.
+ * @param tree the tree.
+ * @param node the node, or NULL to get the root of the tree.
+ * @return the height
+ */
+PJ_DECL(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
+					pj_rbtree_node *node );
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJ_RBTREE_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/scanner.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/scanner.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/scanner.c	(revision 1753)
@@ -0,0 +1,518 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/scanner.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/scanner.h>
+#include <pj/string.h>
+#include <pj/except.h>
+#include <ctype.h>
+
+#if !PJ_FUNCTIONS_ARE_INLINED
+#  include "scanner_i.h"
+#endif
+
+PJ_DEF(void) pj_cs_init( pj_char_spec cs)
+{
+    memset(cs, 0, sizeof(cs));
+}
+
+PJ_DEF(void) pj_cs_set( pj_char_spec cs, int c)
+{
+    cs[c] = 1;
+}
+
+PJ_DEF(void) pj_cs_add_range( pj_char_spec cs, int cstart, int cend)
+{
+    while (cstart != cend)
+	cs[cstart++] = 1;
+}
+
+PJ_DEF(void) pj_cs_add_alpha( pj_char_spec cs)
+{
+    pj_cs_add_range( cs, 'a', 'z'+1);
+    pj_cs_add_range( cs, 'A', 'Z'+1);
+}
+
+PJ_DEF(void) pj_cs_add_num( pj_char_spec cs)
+{
+    pj_cs_add_range( cs, '0', '9'+1);
+}
+
+PJ_DEF(void) pj_cs_add_str( pj_char_spec cs, const char *str)
+{
+    while (*str) {
+        cs[(int)*str] = 1;
+	++str;
+    }
+}
+
+PJ_DEF(void) pj_cs_del_range( pj_char_spec cs, int cstart, int cend)
+{
+    while (cstart != cend)
+	cs[cstart++] = 0;
+}
+
+PJ_DEF(void) pj_cs_del_str( pj_char_spec cs, const char *str)
+{
+    while (*str) {
+        cs[(int)*str] = 0;
+	++str;
+    }
+}
+
+PJ_DEF(void) pj_cs_invert( pj_char_spec cs )
+{
+    unsigned i;
+    for (i=0; i<sizeof(pj_char_spec)/sizeof(cs[0]); ++i) {
+	cs[i] = (pj_char_spec_element_t) !cs[i];
+    }
+}
+
+PJ_DEF(void) pj_scan_init( pj_scanner *scanner, char *bufstart, int buflen, 
+			   unsigned options, pj_syn_err_func_ptr callback )
+{
+    scanner->begin = scanner->current = bufstart;
+    scanner->end = bufstart + buflen;
+    scanner->line = 1;
+    scanner->col = 1;
+    scanner->callback = callback;
+    scanner->skip_ws = options;
+
+    if (scanner->skip_ws) 
+	pj_scan_skip_whitespace(scanner);
+
+    scanner->col = scanner->current - scanner->begin + 1;
+}
+
+
+PJ_DEF(void) pj_scan_fini( pj_scanner *scanner )
+{
+    PJ_UNUSED_ARG(scanner)
+}
+
+PJ_DEF(void) pj_scan_skip_whitespace( pj_scanner *scanner )
+{
+    register char *s = scanner->current;
+
+    while (PJ_SCAN_IS_SPACE(*s)) {
+	++s;
+    }
+
+    if ((scanner->skip_ws & PJ_SCAN_AUTOSKIP_NEWLINE) && PJ_SCAN_IS_NEWLINE(*s)) {
+	for (;;) {
+	    if (*s == '\r') {
+		++s;
+		if (*s == '\n') ++s;
+		++scanner->line;
+		scanner->col = 1;
+		scanner->current = s;
+	    } else if (*s == '\n') {
+		++s;
+		++scanner->line;
+		scanner->col = 1;
+		scanner->current = s;
+	    } else if (PJ_SCAN_IS_SPACE(*s)) {
+		do {
+		    ++s;
+		} while (PJ_SCAN_IS_SPACE(*s));
+	    } else {
+		break;
+	    }
+	}
+    }
+
+    if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_WS_HEADER)==PJ_SCAN_AUTOSKIP_WS_HEADER) {
+	/* Check for header continuation. */
+	scanner->col += s - scanner->current;
+	scanner->current = s;
+
+	if (*s == '\r') {
+	    ++s;
+	}
+	if (*s == '\n') {
+	    ++s;
+	}
+	if (PJ_SCAN_IS_SPACE(*s)) {
+	    register char *t = s;
+	    do {
+		++t;
+	    } while (PJ_SCAN_IS_SPACE(*t));
+
+	    ++scanner->line;
+	    scanner->col = t-s;
+	    scanner->current = t;
+	}
+    } else {
+	scanner->col += s - scanner->current;
+	scanner->current = s;
+    }
+}
+
+PJ_DEF(int) pj_scan_peek( pj_scanner *scanner,
+			   const pj_char_spec spec, pj_str_t *out)
+{
+    register char *s = scanner->current;
+    register char *end = scanner->end;
+
+    if (pj_scan_is_eof(scanner)) {
+	pj_scan_syntax_err(scanner);
+	return -1;
+    }
+
+    while (PJ_SCAN_CHECK_EOF(s) && pj_cs_match(spec, *s))
+	++s;
+
+    pj_strset3(out, scanner->current, s);
+    return s < scanner->end ? *s : 0;
+}
+
+
+PJ_DEF(int) pj_scan_peek_n( pj_scanner *scanner,
+			     pj_size_t len, pj_str_t *out)
+{
+    char *endpos = scanner->current + len;
+    if (endpos > scanner->end) {
+	pj_scan_syntax_err(scanner);
+	return -1;
+    }
+
+    pj_strset(out, scanner->current, len);
+    return *endpos;
+}
+
+
+PJ_DEF(int) pj_scan_peek_until( pj_scanner *scanner,
+				  const pj_char_spec spec, 
+				  pj_str_t *out)
+{
+    register char *s = scanner->current;
+    register char *end = scanner->end;
+
+    if (pj_scan_is_eof(scanner)) {
+	pj_scan_syntax_err(scanner);
+	return -1;
+    }
+
+    while (PJ_SCAN_CHECK_EOF(s) && !pj_cs_match( spec, *s))
+	++s;
+
+    pj_strset3(out, scanner->current, s);
+    return s!=scanner->end ? *s : 0;
+}
+
+
+PJ_DEF(void) pj_scan_get( pj_scanner *scanner,
+			   const pj_char_spec spec, pj_str_t *out)
+{
+    register char *s = scanner->current;
+    register char *end = scanner->end;
+    char *start = s;
+
+    if (pj_scan_is_eof(scanner) || !pj_cs_match(spec, *s)) {
+	pj_scan_syntax_err(scanner);
+	return;
+    }
+
+    do {
+	++s;
+    } while (PJ_SCAN_CHECK_EOF(s) && pj_cs_match(spec, *s));
+
+    pj_strset3(out, scanner->current, s);
+
+    scanner->col += (s - start);
+    scanner->current = s;
+
+    if (scanner->skip_ws) {
+	pj_scan_skip_whitespace(scanner);    
+    }
+}
+
+
+PJ_DEF(void) pj_scan_get_quote( pj_scanner *scanner,
+				 int begin_quote, int end_quote, 
+				 pj_str_t *out)
+{
+    register char *s = scanner->current;
+    register char *end = scanner->end;
+    char *start = s;
+    
+    /* Check and eat the begin_quote. */
+    if (*s != begin_quote) {
+	pj_scan_syntax_err(scanner);
+	return;
+    }
+    ++s;
+
+    /* Loop until end_quote is found. 
+     */
+    do {
+	/* loop until end_quote is found. */
+	do {
+	    ++s;
+	} while (s != end && *s != '\n' && *s != end_quote);
+
+	/* check that no backslash character precedes the end_quote. */
+	if (*s == end_quote) {
+	    if (*(s-1) == '\\') {
+		if (s-2 == scanner->begin) {
+		    break;
+		} else {
+		    char *q = s-2;
+		    char *r = s-2;
+
+		    while (r != scanner->begin && *r == '\\') {
+			--r;
+		    }
+		    /* break from main loop if we have odd number of backslashes */
+		    if (((unsigned)(q-r) & 0x01) == 1) {
+			break;
+		    }
+		}
+	    } else {
+		/* end_quote is not preceeded by backslash. break now. */
+		break;
+	    }
+	} else {
+	    /* loop ended by non-end_quote character. break now. */
+	    break;
+	}
+    } while (1);
+
+    /* Check and eat the end quote. */
+    if (*s != end_quote) {
+	pj_scan_syntax_err(scanner);
+	return;
+    }
+    ++s;
+
+    pj_strset3(out, scanner->current, s);
+
+    scanner->col += (s - start);
+    scanner->current = s;
+
+    if (scanner->skip_ws) {
+	pj_scan_skip_whitespace(scanner);
+    }
+}
+
+PJ_DEF(void) pj_scan_get_n( pj_scanner *scanner,
+			     unsigned N, pj_str_t *out)
+{
+    register char *s = scanner->current;
+    char *start = scanner->current;
+
+    if (scanner->current + N > scanner->end) {
+	pj_scan_syntax_err(scanner);
+	return;
+    }
+
+    pj_strset(out, s, N);
+    
+    s += N;
+    scanner->col += (s - start);
+    scanner->current = s;
+
+    if (scanner->skip_ws) {
+	pj_scan_skip_whitespace(scanner);
+    }
+}
+
+
+PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner )
+{
+    char *start = scanner->current;
+    int chr = *start;
+
+    if (pj_scan_is_eof(scanner)) {
+	pj_scan_syntax_err(scanner);
+	return 0;
+    }
+
+    ++scanner->current;
+    scanner->col += (scanner->current - start);
+
+    if (scanner->skip_ws) {
+	pj_scan_skip_whitespace(scanner);
+    }
+    return chr;
+}
+
+
+PJ_DEF(void) pj_scan_get_newline( pj_scanner *scanner )
+{
+    if (!PJ_SCAN_IS_NEWLINE(*scanner->current)) {
+	pj_scan_syntax_err(scanner);
+	return;
+    }
+
+    if (*scanner->current == '\r') {
+	++scanner->current;
+    }
+    if (*scanner->current == '\n') {
+	++scanner->current;
+    }
+
+    ++scanner->line;
+    scanner->col = 1;
+
+    if (scanner->skip_ws) {
+	pj_scan_skip_whitespace(scanner);
+    }
+}
+
+
+PJ_DEF(void) pj_scan_get_until( pj_scanner *scanner,
+				 const pj_char_spec spec, pj_str_t *out)
+{
+    register char *s = scanner->current;
+    register char *end = scanner->end;
+    char *start = s;
+
+    if (pj_scan_is_eof(scanner)) {
+	pj_scan_syntax_err(scanner);
+	return;
+    }
+
+    while (PJ_SCAN_CHECK_EOF(s) && !pj_cs_match(spec, *s)) {
+	++s;
+    }
+
+    pj_strset3(out, scanner->current, s);
+
+    scanner->col += (s - start);
+    scanner->current = s;
+
+    if (scanner->skip_ws) {
+	pj_scan_skip_whitespace(scanner);
+    }
+}
+
+
+PJ_DEF(void) pj_scan_get_until_ch( pj_scanner *scanner, 
+				    int until_char, pj_str_t *out)
+{
+    register char *s = scanner->current;
+    register char *end = scanner->end;
+    char *start = s;
+
+    if (pj_scan_is_eof(scanner)) {
+	pj_scan_syntax_err(scanner);
+	return;
+    }
+
+    while (PJ_SCAN_CHECK_EOF(s) && *s != until_char) {
+	++s;
+    }
+
+    pj_strset3(out, scanner->current, s);
+
+    scanner->col += (s - start);
+    scanner->current = s;
+
+    if (scanner->skip_ws) {
+	pj_scan_skip_whitespace(scanner);
+    }
+}
+
+
+PJ_DEF(void) pj_scan_get_until_chr( pj_scanner *scanner,
+				     const char *until_spec, pj_str_t *out)
+{
+    register char *s = scanner->current;
+    register char *end = scanner->end;
+    char *start = scanner->current;
+
+    if (pj_scan_is_eof(scanner)) {
+	pj_scan_syntax_err(scanner);
+	return;
+    }
+
+    while (PJ_SCAN_CHECK_EOF(s) && !strchr(until_spec, *s)) {
+	++s;
+    }
+
+    pj_strset3(out, scanner->current, s);
+
+    scanner->col += (s - start);
+    scanner->current = s;
+
+    if (scanner->skip_ws) {
+	pj_scan_skip_whitespace(scanner);
+    }
+}
+
+PJ_DEF(void) pj_scan_advance_n( pj_scanner *scanner,
+				 unsigned N, pj_bool_t skip_ws)
+{
+    char *start = scanner->current;
+
+    if (scanner->current + N > scanner->end) {
+	pj_scan_syntax_err(scanner);
+	return;
+    }
+
+    scanner->current += N;
+    scanner->col += (scanner->current - start);
+
+    if (skip_ws) {
+	pj_scan_skip_whitespace(scanner);
+    }
+}
+
+
+PJ_DEF(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len)
+{
+    if (scanner->current + len > scanner->end) {
+	pj_scan_syntax_err(scanner);
+	return -1;
+    }
+    return strncmp(scanner->current, s, len);
+}
+
+
+PJ_DEF(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len)
+{
+    if (scanner->current + len > scanner->end) {
+	pj_scan_syntax_err(scanner);
+	return -1;
+    }
+    //return strnicmp(scanner->current, s, len);
+    return strncasecmp(scanner->current, s, len);
+}
+
+
+PJ_DEF(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state)
+{
+    state->current = scanner->current;
+    state->line = scanner->line;
+    state->col = scanner->col;
+}
+
+
+PJ_DEF(void) pj_scan_restore_state( pj_scanner *scanner, 
+				     pj_scan_state *state)
+{
+    scanner->current = state->current;
+    scanner->line = state->line;
+    scanner->col = state->col;
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/scanner.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/scanner.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/scanner.h	(revision 1753)
@@ -0,0 +1,466 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/scanner.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_PARSER_H__
+#define __PJ_PARSER_H__
+
+/**
+ * @file scanner.h
+ * @brief Text Scanning.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_SCAN Text Scanning
+ * @ingroup PJ_MISC
+ * @brief
+ * Text scanning utility.
+ */
+
+/**
+ * @defgroup PJ_CHARSPEC Character Filter Specification
+ * @ingroup PJ_SCAN
+ * @brief
+ * The type pj_char_spec is a specification of character set used in
+ * scanner. Application can define multiple character specs, such as to
+ * scan alpha numerics, numbers, tokens, etc.
+ * @{
+ */
+
+typedef pj_uint8_t pj_char_spec_element_t;
+
+/**
+ * The character specification is implemented as array of boolean flags. Each
+ * flag indicates the membership of the character in the spec. If the flag
+ * at one position is non-zero, then the character at that position belongs
+ * to the specification, and vice versa.
+ */
+typedef pj_char_spec_element_t pj_char_spec[256];
+// Note: it's got to be 256 (not 128) to cater for extended character in input.
+
+/**
+ * Initialize character spec.
+ * @param cs the scanner character specification.
+ */
+PJ_DECL(void) pj_cs_init( pj_char_spec cs);
+
+/**
+ * Set the membership of the specified character to TRUE.
+ * @param cs the scanner character specification.
+ * @param c the character.
+ */
+PJ_DECL(void) pj_cs_set( pj_char_spec cs, int c);
+
+/**
+ * Add the characters in the specified range '[cstart, cend)' to the 
+ * specification (the last character itself ('cend') is not added).
+ * @param cs the scanner character specification.
+ * @param cstart the first character in the range.
+ * @param cend the next character after the last character in the range.
+ */
+PJ_DECL(void) pj_cs_add_range( pj_char_spec cs, int cstart, int cend);
+
+/**
+ * Add alphabetic characters to the specification.
+ * @param cs the scanner character specification.
+ */
+PJ_DECL(void) pj_cs_add_alpha( pj_char_spec cs);
+
+/**
+ * Add numeric characters to the specification.
+ * @param cs the scanner character specification.
+ */
+PJ_DECL(void) pj_cs_add_num( pj_char_spec cs);
+
+/**
+ * Add the characters in the string to the specification.
+ * @param cs the scanner character specification.
+ * @param str the string.
+ */
+PJ_DECL(void) pj_cs_add_str( pj_char_spec cs, const char *str);
+
+/**
+ * Delete characters in the specified range from the specification.
+ * @param cs the scanner character specification.
+ * @param cstart the first character in the range.
+ * @param cend the next character after the last character in the range.
+ */
+PJ_DECL(void) pj_cs_del_range( pj_char_spec cs, int cstart, int cend);
+
+/**
+ * Delete characters in the specified string from the specification.
+ * @param cs the scanner character specification.
+ * @param str the string.
+ */
+PJ_DECL(void) pj_cs_del_str( pj_char_spec cs, const char *str);
+
+/**
+ * Invert specification.
+ * @param cs the scanner character specification.
+ */
+PJ_DECL(void) pj_cs_invert( pj_char_spec cs );
+
+/**
+ * Check whether the specified character belongs to the specification.
+ * @param cs the scanner character specification.
+ * @param c the character to check for matching.
+ */
+PJ_INLINE(int) pj_cs_match( const pj_char_spec cs, int c )
+{
+    return cs[c];
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup PJ_SCANNER Text Scanner
+ * @ingroup PJ_SCAN
+ * @{
+ */
+
+/**
+ * Flags for scanner.
+ */
+enum
+{
+    /** This flags specifies that the scanner should automatically skip
+	whitespaces 
+     */
+    PJ_SCAN_AUTOSKIP_WS = 1,
+
+    /** This flags specifies that the scanner should automatically skip
+        SIP header continuation. This flag implies PJ_SCAN_AUTOSKIP_WS.
+     */
+    PJ_SCAN_AUTOSKIP_WS_HEADER = 3,
+
+    /** Auto-skip new lines.
+     */
+    PJ_SCAN_AUTOSKIP_NEWLINE = 4,
+};
+
+
+/* Forward decl. */
+struct pj_scanner;
+
+
+/**
+ * The callback function type to be called by the scanner when it encounters
+ * syntax error.
+ */
+typedef void (*pj_syn_err_func_ptr)(struct pj_scanner *);
+
+
+/**
+ * The text scanner structure.
+ */
+typedef struct pj_scanner
+{
+    char *begin, *end, *current;
+    int  line, col;
+    int  skip_ws;
+    pj_syn_err_func_ptr callback;
+} pj_scanner;
+
+
+/**
+ * This structure can be used by application to store the state of the parser,
+ * so that the scanner state can be rollback to this state when necessary.
+ */
+typedef struct pj_scan_state
+{
+    char *current;
+    int   line, col;
+} pj_scan_state;
+
+
+/**
+ * Initialize the scanner. Note that the input string buffer must have
+ * length at least buflen+1 because the scanner will NULL terminate the
+ * string during initialization.
+ *
+ * @param scanner   The scanner to be initialized.
+ * @param bufstart  The input buffer to scan. Note that buffer[buflen] will be 
+ *		    filled with '\0' until scanner is destroyed, so
+ *		    the actual buffer length must be at least buflen+1.
+ * @param buflen    The length of the input buffer, which normally is
+ *		    strlen(bufstart).
+ * @param options   Zero, or combination of PJ_SCAN_AUTOSKIP_WS or
+ *		    PJ_SCAN_AUTOSKIP_WS_HEADER
+ * @param callback  Callback to be called when the scanner encounters syntax
+ *		    error condition.
+ */
+PJ_DECL(void) pj_scan_init( pj_scanner *scanner, char *bufstart, int buflen, 
+			    unsigned options,
+			    pj_syn_err_func_ptr callback );
+
+
+/** 
+ * Call this function when application has finished using the scanner.
+ *
+ * @param scanner   The scanner.
+ */
+PJ_DECL(void) pj_scan_fini( pj_scanner *scanner );
+
+
+/** 
+ * Determine whether the EOF condition for the scanner has been met.
+ *
+ * @param scanner   The scanner.
+ *
+ * @return Non-zero if scanner is EOF.
+ */
+PJ_INLINE(int) pj_scan_is_eof( const pj_scanner *scanner)
+{
+    return scanner->current >= scanner->end;
+}
+
+
+/** 
+ * Peek strings in current_ position according to parameter spec, and return
+ * the strings in parameter out. The current scanner position will not be
+ * moved. If the scanner is already in EOF state, syntax error callback will
+ * be called thrown.
+ *
+ * @param scanner   The scanner.
+ * @param spec	    The spec to match input string.
+ * @param out	    String to store the result.
+ *
+ * @return the character right after the peek-ed position or zero if there's
+ *	   no more characters.
+ */
+PJ_DECL(int) pj_scan_peek( pj_scanner *scanner,
+			    const pj_char_spec spec, pj_str_t *out);
+
+
+/** 
+ * Peek len characters in current position, and return them in out parameter.
+ * Note that whitespaces or newlines will be returned as it is, regardless
+ * of PJ_SCAN_AUTOSKIP_WS settings. If the character left is less than len, 
+ * syntax error callback will be called.
+ *
+ * @param scanner   The scanner.
+ * @param len	    Length to peek.
+ * @param out	    String to store the result.
+ *
+ * @return the character right after the peek-ed position or zero if there's
+ *	   no more characters.
+ */
+PJ_DECL(int) pj_scan_peek_n( pj_scanner *scanner,
+			      pj_size_t len, pj_str_t *out);
+
+
+/** 
+ * Peek strings in current position until spec is matched, and return
+ * the strings in parameter out. The current scanner position will not be
+ * moved. If the scanner is already in EOF state, syntax error callback will
+ * be called.
+ *
+ * @param scanner   The scanner.
+ * @param spec	    The peeking will stop when the input match this spec.
+ * @param out	    String to store the result.
+ *
+ * @return the character right after the peek-ed position.
+ */
+PJ_DECL(int) pj_scan_peek_until( pj_scanner *scanner,
+				  const pj_char_spec spec, 
+				  pj_str_t *out);
+
+
+/** 
+ * Get characters from the buffer according to the spec, and return them
+ * in out parameter. The scanner will attempt to get as many characters as
+ * possible as long as the spec matches. If the first character doesn't
+ * match the spec, or scanner is already in EOF when this function is called,
+ * an exception will be thrown.
+ *
+ * @param scanner   The scanner.
+ * @param spec	    The spec to match input string.
+ * @param out	    String to store the result.
+ */
+PJ_DECL(void) pj_scan_get( pj_scanner *scanner,
+			    const pj_char_spec spec, pj_str_t *out);
+
+
+/** 
+ * Get characters between quotes. If current input doesn't match begin_quote,
+ * syntax error will be thrown.
+ *
+ * @param scanner	The scanner.
+ * @param begin_quote	The character to begin the quote.
+ * @param end_quote	The character to end the quote.
+ * @param out		String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_quote( pj_scanner *scanner,
+				  int begin_quote, int end_quote, 
+				  pj_str_t *out);
+
+/** 
+ * Get N characters from the scanner.
+ *
+ * @param scanner   The scanner.
+ * @param N	    Number of characters to get.
+ * @param out	    String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_n( pj_scanner *scanner,
+			      unsigned N, pj_str_t *out);
+
+
+/** 
+ * Get one character from the scanner.
+ *
+ * @param scanner   The scanner.
+ *
+ * @return (unknown)
+ */
+PJ_DECL(int) pj_scan_get_char( pj_scanner *scanner );
+
+
+/** 
+ * Get a newline from the scanner. A newline is defined as '\\n', or '\\r', or
+ * "\\r\\n". If current input is not newline, syntax error will be thrown.
+ *
+ * @param scanner   The scanner.
+ */
+PJ_DECL(void) pj_scan_get_newline( pj_scanner *scanner );
+
+
+/** 
+ * Get characters from the scanner and move the scanner position until the
+ * current character matches the spec.
+ *
+ * @param scanner   The scanner.
+ * @param spec	    Get until the input match this spec.
+ * @param out	    String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_until( pj_scanner *scanner,
+				  const pj_char_spec spec, pj_str_t *out);
+
+
+/** 
+ * Get characters from the scanner and move the scanner position until the
+ * current character matches until_char.
+ *
+ * @param scanner	The scanner.
+ * @param until_char    Get until the input match this character.
+ * @param out		String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_until_ch( pj_scanner *scanner, 
+				     int until_char, pj_str_t *out);
+
+
+/** 
+ * Get characters from the scanner and move the scanner position until the
+ * current character matches until_char.
+ *
+ * @param scanner	The scanner.
+ * @param until_spec	Get until the input match any of these characters.
+ * @param out		String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_until_chr( pj_scanner *scanner,
+				      const char *until_spec, pj_str_t *out);
+
+/** 
+ * Advance the scanner N characters, and skip whitespace
+ * if necessary.
+ *
+ * @param scanner   The scanner.
+ * @param N	    Number of characters to skip.
+ * @param skip	    Flag to specify whether whitespace should be skipped
+ *		    after skipping the characters.
+ */
+PJ_DECL(void) pj_scan_advance_n( pj_scanner *scanner,
+				  unsigned N, pj_bool_t skip);
+
+
+/** 
+ * Compare string in current position with the specified string.
+ * 
+ * @param scanner   The scanner.
+ * @param s	    The string to compare with.
+ * @param len	    Length of the string to compare.
+ *
+ * @return zero, <0, or >0 (just like strcmp()).
+ */
+PJ_DECL(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len);
+
+
+/** 
+ * Case-less string comparison of current position with the specified
+ * string.
+ *
+ * @param scanner   The scanner.
+ * @param s	    The string to compare with.
+ * @param len	    Length of the string to compare with.
+ *
+ * @return zero, <0, or >0 (just like strcmp()).
+ */
+PJ_DECL(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len);
+
+
+/** 
+ * Manually skip whitespaces according to flag that was specified when
+ * the scanner was initialized.
+ *
+ * @param scanner   The scanner.
+ */
+PJ_DECL(void) pj_scan_skip_whitespace( pj_scanner *scanner );
+
+
+/** 
+ * Save the full scanner state.
+ *
+ * @param scanner   The scanner.
+ * @param state	    Variable to store scanner's state.
+ */
+PJ_DECL(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state);
+
+
+/** 
+ * Restore the full scanner state.
+ * Note that this would not restore the string if application has modified
+ * it. This will only restore the scanner scanning position.
+ *
+ * @param scanner   The scanner.
+ * @param state	    State of the scanner.
+ */
+PJ_DECL(void) pj_scan_restore_state( pj_scanner *scanner, 
+				      pj_scan_state *state);
+
+/**
+ * @}
+ */
+
+#if PJ_FUNCTIONS_ARE_INLINED
+#  include "scanner_i.h"
+#endif
+
+
+PJ_END_DECL
+
+#endif
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/scanner_i.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/scanner_i.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/scanner_i.h	(revision 1753)
@@ -0,0 +1,36 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/scanner_i.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+
+#define PJ_SCAN_IS_SPACE(c)	((c)==' ' || (c)=='\t')
+#define PJ_SCAN_IS_NEWLINE(c)	((c)=='\r' || (c)=='\n')
+#define PJ_SCAN_CHECK_EOF(s)	(s != end)
+//#define PJ_SCAN_CHECK_EOF(s)	(1)
+
+
+PJ_IDEF(void) pj_scan_syntax_err(pj_scanner *scanner)
+{
+    (*scanner->callback)(scanner);
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/sock.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/sock.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/sock.c	(revision 1753)
@@ -0,0 +1,227 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/sock.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/sock.h>
+#include <pj/pool.h>
+#if !PJ_WIN32_WINCE
+#include <errno.h>
+#endif
+
+#if !PJ_FUNCTIONS_ARE_INLINED
+#  include <pj/sock_i.h>
+#endif
+
+#if defined(PJ_WIN32_WINNT) || PJ_WIN32_WINNT >= 0x0350
+
+PJ_DEF(pj_sock_t) pj_sock_socket( int af, int type, int proto,
+				  pj_uint32_t flag)
+{
+    SOCKET sock;
+
+    if (flag & PJ_SOCK_ASYNC) {
+	flag = WSA_FLAG_OVERLAPPED;
+	sock = WSASocket(af, type, proto, NULL, 0, flag);
+    } else {
+	flag = 0;
+	sock = socket(af, type, proto);
+    }
+
+    return sock;
+}
+
+#else
+
+PJ_DEF(pj_sock_t) pj_sock_socket(int af, int type, int proto,
+				 pj_uint32_t flag)
+{
+    int sock;
+
+    sock = socket(af, type, proto);
+    if (sock && (flag & PJ_SOCK_ASYNC)) {
+	pj_uint32_t val=1;
+	if (pj_sock_ioctl(sock, PJ_FIONBIO, &val)) {
+	    pj_sock_close(sock);
+	    return -1;
+	}
+    }
+    return sock;
+}
+
+#endif
+
+
+PJ_DEF(const pj_str_t*) pj_gethostname(void)
+{
+    static char buf[PJ_MAX_HOSTNAME];
+    static pj_str_t hostname;
+
+    if (hostname.ptr == NULL) {
+	hostname.ptr = buf;
+	if (gethostname(buf, sizeof(buf)) != 0) {
+	    hostname.ptr[0] = '\0';
+	    hostname.slen = 0;
+	    return &hostname;
+	}
+	hostname.slen = strlen(buf);
+    }
+    return &hostname;
+}
+
+PJ_DEF(pj_uint32_t) pj_gethostaddr(void)
+{
+    pj_sockaddr_in addr;
+    const pj_str_t *hostname = pj_gethostname();
+
+    if (pj_sockaddr_set_str_addr(&addr, hostname) != 0)
+	return 0;
+
+    return addr.sin_addr.s_addr;
+}
+
+PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, 
+				     pj_uint32_t addr32,
+				     pj_uint16_t port)
+{
+    pj_sockaddr_in addr;
+    pj_memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = pj_htonl(addr32);
+    addr.sin_port = pj_htons(port);
+    return bind(sock, (struct sockaddr*)&addr, sizeof(addr));
+}
+
+PJ_DEF(int) pj_sock_select( int nfds,
+			    pj_fdset_t *readfd,
+			    pj_fdset_t *writefd,
+			    pj_fdset_t *exfd,
+			    const pj_time_val *timeout)
+{
+    struct timeval *tv_timeout = NULL, tv;
+    if (timeout) {
+	tv.tv_sec = timeout->sec;
+	tv.tv_usec = timeout->msec * 1000;
+	tv_timeout = &tv;
+    }
+    return select(nfds, readfd, writefd, exfd, tv_timeout);
+}
+
+PJ_DEF(pj_status_t) pj_sock_getlasterror(void)
+{
+#if defined(PJ_WIN32) && (PJ_WIN32==1 || PJ_WIN32==2)
+    return WSAGetLastError();
+#else
+    return errno;
+#endif
+}
+
+
+PJ_DEF(pj_uint32_t) pj_inet_addr(const pj_str_t *addr)
+{
+    char hostname[PJ_MAX_HOSTNAME];
+    
+    if (addr->slen >= PJ_MAX_HOSTNAME)
+	return PJ_INADDR_NONE;
+
+    pj_memcpy(hostname, addr->ptr, addr->slen);
+    hostname[ addr->slen ] = '\0';
+
+    return pj_inet_addr2(hostname);
+}
+
+PJ_DEF(pj_status_t) pj_sockaddr_set_str_addr2( pj_sockaddr_in *addr,
+					       const char *hostname)
+{
+    addr->sin_family = AF_INET;
+    if (hostname) {
+	addr->sin_addr.s_addr = pj_inet_addr2(hostname);
+	if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
+	    struct hostent *he;
+
+	    he = gethostbyname(hostname);
+	    if (he) {
+		addr->sin_addr.s_addr = *(pj_uint32_t*)he->h_addr;
+	    } else {
+		return -1;
+	    }
+	}
+    } else {
+	addr->sin_addr.s_addr = 0;
+    }
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pj_sockaddr_set_str_addr( pj_sockaddr_in *addr,
+					      const pj_str_t *str_addr)
+{
+    if (str_addr && str_addr->slen) {
+	char hostname[PJ_MAX_HOSTNAME];
+	if (str_addr->slen >= PJ_MAX_HOSTNAME)
+	    return -1;
+	pj_memcpy(hostname, str_addr->ptr, str_addr->slen);
+	hostname[ str_addr->slen ] = '\0';
+
+	return pj_sockaddr_set_str_addr2(addr, hostname);
+    } else {
+	return pj_sockaddr_set_str_addr2(addr, NULL);
+    }
+}
+
+PJ_DEF(pj_status_t) pj_sockaddr_init( pj_sockaddr_in *addr,
+				       const pj_str_t *str_addr,
+				       int port)
+{
+    addr->sin_family = PJ_AF_INET;
+    pj_sockaddr_set_port(addr, port);
+    return pj_sockaddr_set_str_addr(addr, str_addr);
+}
+
+PJ_DEF(pj_status_t) pj_sockaddr_init2( pj_sockaddr_in *addr,
+				       const char *str_addr,
+				       int port)
+{
+    addr->sin_family = PJ_AF_INET;
+    pj_sockaddr_set_port(addr, port);
+    return pj_sockaddr_set_str_addr2(addr, str_addr);
+}
+
+PJ_DEF(int) pj_sockaddr_cmp( const pj_sockaddr_in *addr1,
+			     const pj_sockaddr_in *addr2)
+{
+    unsigned a1 = pj_ntohl(addr1->sin_addr.s_addr);
+    unsigned a2 = pj_ntohl(addr2->sin_addr.s_addr);
+
+    if (a1<a2)
+	return -1;
+    if (a1>a2)
+	return 1;
+
+    a1 = pj_ntohs(addr1->sin_port);
+    a2 = pj_ntohs(addr2->sin_port);
+    if (a1<a2)
+	return -1;
+    if (a1>a2)
+	return 1;
+
+    return 0;
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/sock.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/sock.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/sock.h	(revision 1753)
@@ -0,0 +1,420 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/sock.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_SOCK_H__
+#define __PJ_SOCK_H__
+
+/**
+ * @file sock.h
+ * @brief Socket Abstraction.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL 
+
+#if defined(PJ_WIN32) && PJ_WIN32==1
+#  define WIN32_LEAN_AND_MEAN
+#  include <winsock2.h>
+#elif defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE==1
+#  include <winsock.h>
+#else
+#  include <sys/types.h>
+#  include <sys/socket.h>
+#  include <sys/select.h>
+#  include <arpa/inet.h>
+#  include <sys/ioctl.h>	/* FBIONBIO */
+#  include <errno.h>
+#  include <netdb.h>
+#  include <unistd.h>
+#endif
+
+/**
+ * @defgroup PJ_SOCK Socket Abstraction
+ * @ingroup PJ_IO
+ * @{
+ */
+
+/** 
+ * Macro alias for AF_INET. 
+ */
+#define PJ_AF_INET	    AF_INET
+
+/** 
+ * Macro alias for SOCK_STREAM (only available if PJ_HAS_TCP != 0). 
+ */
+#if PJ_HAS_TCP
+#  define PJ_SOCK_STREAM    SOCK_STREAM
+#endif
+
+/** 
+ * Macro alias for SOCK_DGRAM. 
+ */
+#define PJ_SOCK_DGRAM	    SOCK_DGRAM
+
+/**
+ * Macro for invalid address value.
+ */
+#define PJ_INADDR_NONE	    INADDR_NONE
+
+/**
+ * Macro for INADDR_ANY
+ */
+#define PJ_INADDR_ANY	    INADDR_ANY
+
+/*
+ * Socket flags.
+ */
+#define PJ_SOCK_ASYNC	    1
+
+
+/*
+ * Socket options.
+ */
+#define PJ_FIONBIO	    FIONBIO
+
+/*
+ * Socket errors.
+ */
+#ifdef _WIN32
+#define PJ_EWOULDBLOCK	    WSAEWOULDBLOCK
+#define PJ_ECONNRESET	    WSAECONNRESET
+#define PJ_EINPROGRESS	    WSAEINPROGRESS
+#else
+#define PJ_EWOULDBLOCK	    EWOULDBLOCK
+#define PJ_EINPROGRESS	    EINPROGRESS
+#define PJ_ECONNRESET	    ECONNRESET
+#endif
+
+/**
+ * Invalid socket.
+ */
+#define PJ_INVALID_SOCKET   (-1)
+
+/**
+ * Type definition for sockaddr_in.
+ */
+typedef struct sockaddr_in pj_sockaddr_in;
+
+/**
+ * Type alias for fd_set.
+ */
+typedef fd_set pj_fdset_t;
+
+/**
+ * Clear all handles.
+ */
+#define PJ_FD_ZERO(a)	    FD_ZERO(a)
+
+/**
+ * Set handle membership.
+ */
+PJ_INLINE(void) PJ_FD_SET(pj_sock_t sock, pj_fdset_t *set)
+{
+#ifdef _MSC_VER
+#  pragma warning(disable:4018)
+#  pragma warning(disable:4389)	// sign/unsigned mismatch in comparison
+#endif
+
+    FD_SET(sock,set);
+
+#ifdef _MSC_VER
+#  pragma warning(default:4018)
+#  pragma warning(default:4389)
+#endif
+}
+
+/**
+ * Check handle membership.
+ */
+#define PJ_FD_ISSET(s,a)    FD_ISSET(s,a)
+
+/**
+ * Clear handle membership.
+ */
+PJ_INLINE(void) PJ_FD_CLR(pj_sock_t sock, pj_fdset_t *set)
+{
+#ifdef _MSC_VER
+#  pragma warning(disable:4018)
+#  pragma warning(disable:4389)	// sign/unsigned mismatch in comparison
+#endif
+
+    FD_CLR(sock,set);
+
+#ifdef _MSC_VER
+#  pragma warning(default:4018)
+#  pragma warning(default:4389)
+#endif
+}
+
+#define pj_ntohs	ntohs
+#define pj_ntohl	ntohl
+#define pj_htons	htons
+#define pj_htonl	htonl
+#define pj_inet_ntoa	inet_ntoa
+
+
+/**
+ * Get the port number of an address.
+ */
+#define pj_sockaddr_get_port(addr)	pj_ntohs((addr)->sin_port)
+
+/**
+ * Get the address.
+ */
+#define pj_sockaddr_get_addr(addr)	pj_ntohl((addr)->sin_addr.s_addr)
+
+/**
+ * Set the port number.
+ */
+#define pj_sockaddr_set_port(addr,port)	((addr)->sin_port = pj_htons((unsigned short)port))
+
+/**
+ * Set the address.
+ */
+#define pj_sockaddr_set_addr(addr,ad)	((addr)->sin_addr.s_addr = pj_htonl(ad))
+
+/**
+ * Get the string address.
+ */
+#define pj_sockaddr_get_str_addr(addr)	inet_ntoa((addr)->sin_addr)
+
+/**
+ * Convert string representation to IP address.
+ */
+PJ_DECL(pj_uint32_t) pj_inet_addr(const pj_str_t *addr);
+
+/**
+ * Convert string representation to IP address.
+ */
+PJ_IDECL(pj_uint32_t) pj_inet_addr2(const char *addr);
+
+/**
+ * Set the address from string address, resolve the host if necessary.
+ */
+PJ_DECL(pj_status_t) pj_sockaddr_set_str_addr( pj_sockaddr_in *addr,
+					       const pj_str_t *str_addr);
+
+/**
+ * Set the address from string address, resolve the host if necessary.
+ */
+PJ_DECL(pj_status_t) pj_sockaddr_set_str_addr2( pj_sockaddr_in *addr,
+					 	const char *str_addr);
+
+/**
+ * Set the address and port.
+ */
+PJ_DECL(pj_status_t) pj_sockaddr_init( pj_sockaddr_in *addr,
+				        const pj_str_t *str_addr,
+				        int port);
+
+PJ_DECL(pj_status_t) pj_sockaddr_init2( pj_sockaddr_in *addr,
+				        const char *str_addr,
+				        int port);
+
+/**
+ * Compare addresses.
+ * @return zero if equal, -1 if addr1<addr2, 1 if addr1>addr2.
+ */
+PJ_DECL(int) pj_sockaddr_cmp( const pj_sockaddr_in *addr1,
+			      const pj_sockaddr_in *addr2);
+
+/**
+ * Get system's host name.
+ * @return Pointer to string
+ */
+PJ_DECL(const pj_str_t*) pj_gethostname(void);
+
+/**
+ * Get host's IP address.
+ * @return host's IP address.
+ */
+PJ_DECL(pj_uint32_t) pj_gethostaddr(void);
+
+/*
+ *
+ * SOCKET API.
+ *
+ */
+
+/**
+ * Create socket.
+ * If PJ_SOCK_ASYNC is specified during creation, then for WINNT sockets, the
+ * socket will be created with flag FILE_FLAG_OVERLAPPED. This flag doesn't 
+ * have any effects on other types of socket.
+ */
+PJ_DECL(pj_sock_t) pj_sock_socket(int af, int type, int proto,
+				  pj_uint32_t flag);
+
+/**
+ * Bind socket.
+ */
+PJ_IDECL(pj_status_t) pj_sock_bind(pj_sock_t sock, 
+				   const pj_sockaddr_t *addr,
+				   int len);
+
+/**
+ * Bind IP socket.
+ */
+PJ_DECL(pj_status_t) pj_sock_bind_in( pj_sock_t sock, 
+				      pj_uint32_t addr,
+				      pj_uint16_t port);
+
+/**
+ * Accept new connection (only available if PJ_HAS_TCP != 0).
+ */
+#if PJ_HAS_TCP
+PJ_IDECL(pj_sock_t) pj_sock_accept(pj_sock_t sock,
+				   pj_sockaddr_t *addr,
+				   int *addrlen);
+#endif
+
+/**
+ * Close socket.
+ */
+PJ_IDECL(pj_status_t) pj_sock_close(pj_sock_t sock);
+
+
+/**
+ * Connect socket.
+ */
+PJ_IDECL(pj_status_t) pj_sock_connect(pj_sock_t sock,
+				      const pj_sockaddr_t *addr,
+				      int namelen);
+
+/**
+ * Get peer address.
+ */
+PJ_IDECL(pj_status_t) pj_sock_getpeername(pj_sock_t sock,
+					  pj_sockaddr_t *addr,
+					  int *namelen);
+
+/**
+ * Get local address.
+ */
+PJ_IDECL(pj_status_t) pj_sock_getsockname(pj_sock_t sock,
+					  pj_sockaddr_t *addr,
+					  int *namelen);
+
+/**
+ * Get socket option.
+ */
+PJ_IDECL(pj_status_t) pj_sock_getsockopt(pj_sock_t sock,
+					 int level,
+					 int optname,
+					 void *optval,
+					 int *optlen);
+/**
+ * Set socket option.
+ */
+PJ_IDECL(pj_status_t) pj_sock_setsockopt(pj_sock_t sock,
+					 int level,
+					 int optname,
+					 const void *optval,
+					 int optlen);
+
+/**
+ * Set socket option.
+ */
+PJ_IDECL(pj_status_t) pj_sock_ioctl(pj_sock_t sock,
+				    long cmd,
+				    pj_uint32_t *val);
+
+/**
+ * Listen for incoming connection (only available if PJ_HAS_TCP != 0).
+ */
+#if PJ_HAS_TCP
+PJ_IDECL(pj_status_t) pj_sock_listen(pj_sock_t sock,
+				     int backlog);
+#endif
+
+/**
+ * Receives data.
+ */
+PJ_IDECL(int) pj_sock_recv(pj_sock_t sock,
+			   void *buf,
+			   int len,
+			   int flags);
+
+/**
+ * Receive data.
+ */
+PJ_IDECL(int) pj_sock_recvfrom(pj_sock_t sock,
+			       void *buf,
+			       int len,
+			       int flags,
+			       pj_sockaddr_t *from,
+			       int *fromlen);
+
+/**
+ * Legacy select().
+ */
+PJ_DECL(int) pj_sock_select( int nfds,
+			     pj_fdset_t *readfd,
+			     pj_fdset_t *writefd,
+			     pj_fdset_t *exfd,
+			     const pj_time_val *timeout);
+
+/**
+ * Send data.
+ */
+PJ_IDECL(int) pj_sock_send(pj_sock_t sock,
+			   const void *buf,
+			   int len,
+			   int flags);
+
+/**
+ * Send data.
+ */
+PJ_IDECL(int) pj_sock_sendto(pj_sock_t sock,
+			     const void *buf,
+			     int len,
+			     int flags,
+			     const pj_sockaddr_t *to,
+			     int tolen);
+
+/**
+ * Shutdown socket (only available if PJ_HAS_TCP != 0).
+ */
+#if PJ_HAS_TCP
+PJ_IDECL(pj_status_t) pj_sock_shutdown(pj_sock_t sock,
+				       int how);
+#endif
+
+/**
+ * Get last error of the network/socket operation.
+ */
+PJ_IDECL(pj_status_t) pj_sock_getlasterror(void);
+
+/**
+ * @}
+ */
+
+#if PJ_FUNCTIONS_ARE_INLINED
+#  include <pj/sock_i.h>
+#endif
+
+PJ_END_DECL
+
+#endif	/* __PJ_SOCK_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/sock_i.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/sock_i.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/sock_i.h	(revision 1753)
@@ -0,0 +1,158 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/sock_i.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+
+#include <pj/string.h>
+
+PJ_IDEF(pj_uint32_t) pj_inet_addr2(const char *addr)
+{
+    return inet_addr(addr);
+}
+
+PJ_IDEF(pj_status_t) pj_sock_bind( pj_sock_t sock, 
+				  const pj_sockaddr_t *addr,
+				  int len)
+{
+    return bind(sock, (struct sockaddr*)addr, len);
+}
+
+
+PJ_IDEF(pj_status_t) pj_sock_close(pj_sock_t sock)
+{
+#ifdef PJ_WIN32
+    return closesocket(sock);
+#else
+    return close(sock);
+#endif
+}
+
+PJ_IDEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock,
+					 pj_sockaddr_t *addr,
+					 int *namelen)
+{
+    return getpeername(sock, (struct sockaddr*)addr, namelen);
+}
+
+PJ_IDEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock,
+					 pj_sockaddr_t *addr,
+					 int *namelen)
+{
+    return getsockname(sock, (struct sockaddr*)addr, namelen);
+}
+
+PJ_IDEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock,
+					int level,
+					int optname,
+					void *optval,
+					int *optlen)
+{
+    return getsockopt(sock, level, optname, (char*)optval, optlen);
+}
+
+PJ_IDEF(int) pj_sock_send( pj_sock_t sock,
+			  const void *buf,
+			  int len,
+			  int flags)
+{
+    return send(sock, (const char*)buf, len, flags);
+}
+
+
+PJ_IDEF(int) pj_sock_sendto( pj_sock_t sock,
+			    const void *buf,
+			    int len,
+			    int flags,
+			    const pj_sockaddr_t *to,
+			    int tolen)
+{
+    return sendto(sock, (const char*)buf, len, flags, 
+		  (const struct sockaddr*)to, tolen);
+}
+
+PJ_IDEF(int) pj_sock_recv( pj_sock_t sock,
+			  void *buf,
+			  int len,
+			  int flags)
+{
+    return recv(sock, (char*)buf, len, flags);
+}
+
+PJ_IDEF(int) pj_sock_recvfrom( pj_sock_t sock,
+			      void *buf,
+			      int len,
+			      int flags,
+			      pj_sockaddr_t *from,
+			      int *fromlen)
+{
+    return recvfrom(sock, (char*)buf, len, flags, 
+		    (struct sockaddr*)from, fromlen);
+}
+
+PJ_IDEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock,
+					int level,
+					int optname,
+					const void *optval,
+					int optlen)
+{
+    return setsockopt(sock, level, optname, (const char*)optval, optlen);
+}
+
+#if PJ_HAS_TCP
+PJ_IDEF(pj_status_t) pj_sock_shutdown(pj_sock_t sock,
+					int how)
+{
+    return shutdown(sock, how);
+}
+
+PJ_IDEF(pj_status_t) pj_sock_listen( pj_sock_t sock,
+				    int backlog)
+{
+    return listen(sock, backlog);
+}
+
+PJ_IDEF(pj_status_t) pj_sock_connect( pj_sock_t sock,
+				     const pj_sockaddr_t *addr,
+				     int namelen)
+{
+    return connect(sock, (struct sockaddr*)addr, namelen);
+}
+
+PJ_IDEF(pj_sock_t) pj_sock_accept( pj_sock_t sock,
+				  pj_sockaddr_t *addr,
+				  int *addrlen)
+{
+    return accept(sock, (struct sockaddr*)addr, addrlen);
+}
+#endif	/* PJ_HAS_TCP */
+
+PJ_IDEF(pj_status_t) pj_sock_ioctl( pj_sock_t sock,
+				   long cmd,
+				   pj_uint32_t *val)
+{
+#if defined(PJ_WIN32) && (PJ_WIN32==1 || PJ_WIN32==2)
+    return ioctlsocket(sock, cmd, (unsigned long*)val);
+#else
+    return ioctl(sock, cmd, (unsigned long*)val);
+#endif
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/string.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/string.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/string.c	(revision 1753)
@@ -0,0 +1,177 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/string.c,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <ctype.h>	/* isspace() */
+#include <stdlib.h>	/* rand() */
+
+#if defined(PJ_WIN32_WINCE)
+
+int strncasecmp( const char *s1, const char *s2, size_t count ) {
+	int i;
+	char *r1 = s1;
+	char *r2 = s2;
+
+	for (i=0; i<count; i++) {
+		if (*r1=='\0' && *r2=='\0')
+			break;
+		if (*r1=='\0' || *r2=='\0')
+			return -1;
+		if (tolower(*r1)!=tolower(*r2)) {
+			return -1;
+		}
+		r1++;
+		r2++;
+	}
+	
+	return 0;
+}
+
+int strcasecmp( const char *s1, const char *s2 )
+{
+	int i = 0;
+	char *r1 = s1;
+	char *r2 = s2;
+
+	while (1) {
+		if (*r1=='\0' && *r2=='\0')
+			break;
+		if (*r1=='\0' || *r2=='\0')
+			return -1;
+		if (tolower(*r1)!=tolower(*r2)) {
+			return -1;
+		}
+		r1++;
+		r2++;
+		i++;
+		if (i>1024) return -1;
+	}
+	
+	return 0;
+}
+
+#endif
+
+#if PJ_FUNCTIONS_ARE_INLINED==0
+#  include <pj/string_i.h>
+#endif
+
+#ifdef PJ_WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  include <windows.h>
+#  include <objbase.h>
+#  include <winsock2.h>
+#endif
+
+static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+		     '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str )
+{
+    register char *p = str->ptr;
+    while (isspace(*p))
+	++p;
+    str->slen -= (p - str->ptr);
+    str->ptr = p;
+    return str;
+}
+
+PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str )
+{
+    char *end = str->ptr + str->slen;
+    register char *p = end - 1;
+    while (p >= str->ptr && isspace(*p))
+        --p;
+    str->slen -= ((end - p) - 1);
+    return str;
+}
+
+PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p)
+{
+    *p++ = hex[ (value & 0xF0) >> 4 ];
+    *p++ = hex[ (value & 0x0F) ];
+}
+
+PJ_DEF(pj_str_t*) pj_create_random_string(pj_str_t *str, int len)
+{
+    int i;
+    char *p = str->ptr;
+
+    for (i=0; i<len/8; ++i) {
+	unsigned val = pj_rand();
+	pj_val_to_hex_digit( (val & 0xFF000000) >> 24, p+0 );
+	pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 );
+	pj_val_to_hex_digit( (val & 0x0000FF00) >>  8, p+4 );
+	pj_val_to_hex_digit( (val & 0x000000FF) >>  0, p+6 );
+	p += 8;
+    }
+    for (i=i * 8; i<len; ++i) {
+	*p++ = hex[ pj_rand() & 0x0F ];
+    }
+    str->slen = len;
+    return str;
+}
+
+
+PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str)
+{
+    unsigned long value;
+    unsigned i;
+
+    value = 0;
+    for (i=0; i<(unsigned)str->slen; ++i) {
+	value = value * 10 + (str->ptr[i] - '0');
+    }
+    return value;
+}
+
+PJ_DEF(int) pj_utoa(unsigned val, char *buf)
+{
+    char *p;
+    int len;
+
+    p = buf;
+    do {
+        unsigned digval = (unsigned) (val % 10);
+        val /= 10;
+        *p++ = (char) (digval + '0');
+    } while (val > 0);
+
+    len = p-buf;
+    *p-- = '\0';
+
+    do {
+        char temp = *p;
+        *p = *buf;
+        *buf = temp;
+        --p;
+        ++buf;
+    } while (buf < p);
+
+    return len;
+}
+
+PJ_DEF(int) pj_rand()
+{
+    return rand();
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/string.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/string.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/string.h	(revision 1753)
@@ -0,0 +1,470 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/string.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_STRING_H__
+#define __PJ_STRING_H__
+
+/**
+ * @file string.h
+ * @brief PJLIB String Operations.
+ */
+
+#include <pj/types.h>
+#include <string.h>
+
+#if defined(PJ_WIN32) && PJ_WIN32==1
+#  define strcasecmp	stricmp
+#  define strncasecmp	strnicmp
+#  define snprintf	_snprintf
+#  define vsnprintf	_vsnprintf	
+#elif defined(PJ_WIN32_WINCE)
+//#  define strcasecmp	stricmp
+//#  define strncasecmp	strnicmp
+#  define snprintf	_snprintf
+#  define vsnprintf	_vsnprintf	
+#else
+#  define stricmp	strcasecmp
+#  define strnicmp	strncasecmp
+#endif
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_PSTR String Operations
+ * @ingroup PJ_DS
+ * @{
+ */
+
+/**
+ * Create string initializer from a normal C string.
+ *
+ * @param str	Null terminated string to be stored.
+ *
+ * @return pj_str_t.
+ */
+PJ_IDECL(pj_str_t) pj_str(char *str);
+
+/**
+ * Set the pointer and length to the specified value.
+ *
+ * @param str	    the string.
+ * @param ptr	    pointer to set.
+ * @param length    length to set.
+ *
+ * @return the string.
+ */
+PJ_IDECL(pj_str_t*) pj_strset( pj_str_t *str, char *ptr, pj_size_t length);
+
+/**
+ * Set the pointer and length of the string to the source string, which
+ * must be NULL terminated.
+ *
+ * @param str	    the string.
+ * @param src	    pointer to set.
+ *
+ * @return the string.
+ */
+PJ_IDECL(pj_str_t*) pj_strset2( pj_str_t *str, char *src);
+
+/**
+ * Set the pointer and the length of the string.
+ *
+ * @param str	    The target string.
+ * @param begin	    The start of the string.
+ * @param end	    The end of the string.
+ *
+ * @return the target string.
+ */
+PJ_IDECL(pj_str_t*) pj_strset3( pj_str_t *str, char *begin, char *end );
+
+/**
+ * Assign string.
+ *
+ * @param dst	    The target string.
+ * @param src	    The source string.
+ *
+ * @return the target string.
+ */
+PJ_IDECL(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src );
+
+/**
+ * Copy string contents.
+ *
+ * @param dst	    The target string.
+ * @param src	    The source string.
+ *
+ * @return the target string.
+ */
+PJ_IDECL(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src);
+
+/**
+ * Copy string contents.
+ *
+ * @param dst	    The target string.
+ * @param src	    The source string.
+ *
+ * @return the target string.
+ */
+PJ_IDECL(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src);
+
+/**
+ * Duplicate string.
+ *
+ * @param pool	    The pool.
+ * @param dst	    The string result.
+ * @param src	    The string to duplicate.
+ *
+ * @return the string result.
+ */
+PJ_IDECL(pj_str_t*) pj_strdup(pj_pool_t *pool,
+			      pj_str_t *dst,
+			      const pj_str_t *src);
+
+/**
+ * Duplicate string and NULL terminate the destination string.
+ *
+ * @param pool
+ * @param dst
+ * @param src
+ */
+PJ_IDECL(pj_str_t*) pj_strdup_with_null(pj_pool_t *pool,
+					pj_str_t *dst,
+					const pj_str_t *src);
+
+/**
+ * Duplicate string.
+ *
+ * @param pool	    The pool.
+ * @param dst	    The string result.
+ * @param src	    The string to duplicate.
+ *
+ * @return the string result.
+ */
+PJ_IDECL(pj_str_t*) pj_strdup2(pj_pool_t *pool,
+			       pj_str_t *dst,
+			       const char *src);
+
+/**
+ * Duplicate string.
+ *
+ * @param pool	    The pool.
+ * @param src	    The string to duplicate.
+ *
+ * @return the string result.
+ */
+PJ_IDECL(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src);
+
+/**
+ * Return the length of the string.
+ *
+ * @param str	    The string.
+ *
+ * @return the length of the string.
+ */
+PJ_IDECL(pj_size_t) pj_strlen( const pj_str_t *str );
+
+/**
+ * Return the pointer to the string data.
+ *
+ * @param str	    The string.
+ *
+ * @return the pointer to the string buffer.
+ */
+PJ_IDECL(const char*) pj_strbuf( const pj_str_t *str );
+
+/**
+ * Compare strings. 
+ *
+ * @param str1	    The string to compare.
+ * @param str2	    The string to compare.
+ *
+ * @return 
+ *	- < 0 if str1 is less than str2
+ *      - 0   if str1 is identical to str2
+ *      - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2);
+
+/**
+ * Compare strings.
+ *
+ * @param str1	    The string to compare.
+ * @param str2	    The string to compare.
+ *
+ * @return 
+ *	- < 0 if str1 is less than str2
+ *      - 0   if str1 is identical to str2
+ *      - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strcmp2( const pj_str_t *str1, const char *str2 );
+
+/**
+ * Compare strings. 
+ *
+ * @param str1	    The string to compare.
+ * @param str2	    The string to compare.
+ * @param len	    The maximum number of characters to compare.
+ *
+ * @return 
+ *	- < 0 if str1 is less than str2
+ *      - 0   if str1 is identical to str2
+ *      - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, 
+			  pj_size_t len);
+
+/**
+ * Compare strings. 
+ *
+ * @param str1	    The string to compare.
+ * @param str2	    The string to compare.
+ * @param len	    The maximum number of characters to compare.
+ *
+ * @return 
+ *	- < 0 if str1 is less than str2
+ *      - 0   if str1 is identical to str2
+ *      - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strncmp2( const pj_str_t *str1, const char *str2, 
+			   pj_size_t len);
+
+/**
+ * Perform lowercase comparison to the strings.
+ *
+ * @param str1	    The string to compare.
+ * @param str2	    The string to compare.
+ *
+ * @return 
+ *	- < 0 if str1 is less than str2
+ *      - 0   if str1 is identical to str2
+ *      - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2);
+
+/**
+ * Perform lowercase comparison to the strings.
+ *
+ * @param str1	    The string to compare.
+ * @param str2	    The string to compare.
+ *
+ * @return 
+ *	- < 0 if str1 is less than str2
+ *      - 0   if str1 is identical to str2
+ *      - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_stricmp2( const pj_str_t *str1, const char *str2);
+
+/**
+ * Perform lowercase comparison to the strings.
+ *
+ * @param str1	    The string to compare.
+ * @param str2	    The string to compare.
+ * @param len	    The maximum number of characters to compare.
+ *
+ * @return 
+ *	- < 0 if str1 is less than str2
+ *      - 0   if str1 is identical to str2
+ *      - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, 
+			   pj_size_t len);
+
+/**
+ * Perform lowercase comparison to the strings.
+ *
+ * @param str1	    The string to compare.
+ * @param str2	    The string to compare.
+ * @param len	    The maximum number of characters to compare.
+ *
+ * @return 
+ *	- < 0 if str1 is less than str2
+ *      - 0   if str1 is identical to str2
+ *      - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, 
+			    pj_size_t len);
+
+/**
+ * Concatenate strings.
+ *
+ * @param dst	    The destination string.
+ * @param src	    The source string.
+ */
+PJ_IDECL(void) pj_strcat(pj_str_t *dst, const pj_str_t *src);
+
+/**
+ * Finds a character in a string.
+ *
+ * @param str	    The string.
+ * @param chr	    The character to find.
+ *
+ * @return the pointer to first character found, or NULL.
+ */
+PJ_IDECL(char*) pj_strchr( pj_str_t *str, int chr);
+
+/**
+ * Remove (trim) leading whitespaces from the string.
+ *
+ * @param str	    The string.
+ *
+ * @return the string.
+ */
+PJ_DECL(pj_str_t*) pj_strltrim( pj_str_t *str );
+
+/**
+ * Remove (trim) the trailing whitespaces from the string.
+ *
+ * @param str	    The string.
+ *
+ * @return the string.
+ */
+PJ_DECL(pj_str_t*) pj_strrtrim( pj_str_t *str );
+
+/**
+ * Remove (trim) leading and trailing whitespaces from the string.
+ *
+ * @param str	    The string.
+ *
+ * @return the string.
+ */
+PJ_IDECL(pj_str_t*) pj_strtrim( pj_str_t *str );
+
+/**
+ * Create a randomize string with the specified length. The string is NOT
+ * guaranteed to be globally unique, but it will be unique only in one 
+ * instance of the program.
+ *
+ * @param str	    the string to store the result.
+ * @param length    the length of the random string to generate.
+ *
+ * @return the string.
+ */
+PJ_DECL(pj_str_t*) pj_create_random_string(pj_str_t *str, int length);
+
+/**
+ * Convert string to unsigned integer.
+ *
+ * @param str	the string.
+ *
+ * @return the unsigned integer.
+ */
+PJ_DECL(unsigned long) pj_strtoul(const pj_str_t *str);
+
+/**
+ * Utility to convert unsigned integer to string.
+ *
+ * @param val	    the unsigned integer
+ * @param buf	    the buffer
+ *
+ * @return the number of characters written
+ */
+PJ_DECL(int) pj_utoa(unsigned val, char *buf);
+
+/**
+ * Wrapper for rand().
+ *
+ * @return	    integer.
+ */
+PJ_DECL(int) pj_rand(void);
+
+/**
+ * snprintf.
+ * @return the number of characters printed.
+ */
+#if defined(PJ_WIN32) && !defined(PJ_WIN32_WINCE)
+#   define pj_vsnprintf _vsnprintf 
+#   define pj_snprintf _snprintf
+#else
+#   define pj_vsnprintf vsnprintf
+#   define pj_snprintf snprintf
+#endif
+
+/**
+ * Fill the memory location with value.
+ *
+ * @param dst	    The destination buffer.
+ * @param c	    Character to set.
+ * @param size	    The number of characters.
+ *
+ * @return the value of dst.
+ */
+PJ_IDECL(void*) pj_memset(void *dst, int c, pj_size_t size);
+
+/**
+ * Copy buffer.
+ *
+ * @param dst	    The destination buffer.
+ * @param src	    The source buffer.
+ * @param size	    The size to copy.
+ *
+ * @return the destination buffer.
+ */
+PJ_IDECL(void*) pj_memcpy(void *dst, const void *src, pj_size_t size);
+
+/**
+ * Move memory.
+ *
+ * @param dst	    The destination buffer.
+ * @param src	    The source buffer.
+ * @param size	    The size to copy.
+ *
+ * @return the destination buffer.
+ */
+PJ_IDECL(void*) pj_memmove(void *dst, const void *src, pj_size_t size);
+
+/**
+ * Compare buffers.
+ *
+ * @param buf1	    The first buffer.
+ * @param buf2	    The second buffer.
+ * @param size	    The size to compare.
+ *
+ * @return negative, zero, or positive value.
+ */
+PJ_IDECL(int) pj_memcmp(const void *buf1, const void *buf2, pj_size_t size);
+
+/**
+ * Find character in the buffer.
+ *
+ * @param buf	    The buffer.
+ * @param c	    The character to find.
+ * @param size	    The size to check.
+ *
+ * @return the pointer to location where the character is found, or NULL if
+ *         not found.
+ */
+PJ_IDECL(void*) pj_memchr(const void *buf, int c, pj_size_t size);
+
+/**
+ * @}
+ */
+
+#if PJ_FUNCTIONS_ARE_INLINED
+#  include <pj/string_i.h>
+#endif
+
+PJ_END_DECL
+
+#endif	/* __PJ_STRING_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/string_i.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/string_i.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/string_i.h	(revision 1753)
@@ -0,0 +1,246 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/string_i.h,v 1.1 2005/12/02 20:02:30 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+PJ_IDEF(void*) pj_memset(void *dst, int c, pj_size_t size)
+{
+    return memset(dst, c, size);
+}
+
+PJ_IDEF(void*) pj_memcpy(void *dst, const void *src, pj_size_t size)
+{
+    return memcpy(dst, src, size);
+}
+
+PJ_IDEF(void*) pj_memmove(void *dst, const void *src, pj_size_t size)
+{
+    return memmove(dst, src, size);
+}
+
+PJ_IDEF(int) pj_memcmp(const void *buf1, const void *buf2, pj_size_t size)
+{
+    return memcmp(buf1, buf2, size);
+}
+
+PJ_IDEF(void*) pj_memchr(const void *buf, int c, pj_size_t size)
+{
+    return memchr(buf, c, size);
+}
+
+PJ_IDEF(pj_str_t) pj_str(char *str)
+{
+    pj_str_t dst;
+    dst.ptr = str;
+    dst.slen = str ? strlen(str) : 0;
+    return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strset( pj_str_t *str, char *ptr, pj_size_t length)
+{
+    str->ptr = ptr;
+    str->slen = length;
+    return str;
+}
+
+
+PJ_IDEF(pj_str_t*) pj_strset2( pj_str_t *str, char *src)
+{
+    str->ptr = src;
+    str->slen = src ? strlen(src) : 0;
+    return str;
+}
+
+PJ_IDEF(pj_str_t*) pj_strset3( pj_str_t *str, char *begin, char *end )
+{
+    str->ptr = begin;
+    str->slen = end-begin;
+    return str;
+}
+
+PJ_IDEF(pj_str_t*) pj_strdup(pj_pool_t *pool,
+			      pj_str_t *dst,
+			      const pj_str_t *src)
+{
+    if (src->slen) {
+	dst->ptr = (char*)pj_pool_alloc(pool, src->slen);
+	pj_memcpy(dst->ptr, src->ptr, src->slen);
+    }
+    dst->slen = src->slen;
+    return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strdup_with_null( pj_pool_t *pool,
+					pj_str_t *dst,
+					const pj_str_t *src)
+{
+    if (src->slen) {
+	dst->ptr = (char*)pj_pool_alloc(pool, src->slen+1);
+	pj_memcpy(dst->ptr, src->ptr, src->slen);
+    } else {
+	dst->ptr = (char*)pj_pool_alloc(pool, 1);
+    }
+    dst->slen = src->slen;
+    dst->ptr[dst->slen] = '\0';
+    return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strdup2(pj_pool_t *pool,
+			      pj_str_t *dst,
+			      const char *src)
+{
+    dst->slen = src ? strlen(src) : 0;
+    if (dst->slen) {
+	dst->ptr = (char*)pj_pool_alloc(pool, dst->slen);
+	pj_memcpy(dst->ptr, src, dst->slen);
+    } else {
+	dst->ptr = NULL;
+    }
+    return dst;
+}
+
+
+PJ_IDEF(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src)
+{
+    pj_str_t temp;
+    pj_strdup2(pool, &temp, src);
+    return temp;
+}
+
+PJ_IDEF(pj_size_t) pj_strlen( const pj_str_t *str )
+{
+    return str->slen;
+}
+
+PJ_IDEF(const char*) pj_strbuf( const pj_str_t *str )
+{
+    return str->ptr;
+}
+
+PJ_IDEF(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src )
+{
+    dst->ptr = src->ptr;
+    dst->slen = src->slen;
+    return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src)
+{
+    dst->slen = src->slen;
+    if (src->slen > 0) {
+	pj_memcpy(dst->ptr, src->ptr, src->slen);
+    }
+    return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src)
+{
+    dst->slen = src ? strlen(src) : 0;
+    if (dst->slen > 0)
+	pj_memcpy(dst->ptr, src, dst->slen);
+    return dst;
+}
+
+PJ_IDEF(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2)
+{
+    pj_ssize_t diff;
+
+    diff = str1->slen - str2->slen;
+    if (diff) {
+	return (int)diff;
+    } else if (str1->ptr) {
+	return strncmp(str1->ptr, str2->ptr, str1->slen);
+    } else {
+	return 0;
+    }
+}
+
+PJ_IDEF(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, 
+			 pj_size_t len)
+{
+    return (str1->ptr && str2->ptr) ? strncmp(str1->ptr, str2->ptr, len) :
+	   (str1->ptr == str2->ptr ? 0 : 1);
+}
+
+PJ_IDEF(int) pj_strncmp2( const pj_str_t *str1, const char *str2, 
+			  pj_size_t len)
+{
+    return (str1->ptr && str2) ? strncmp(str1->ptr, str2, len) :
+	   (str1->ptr==str2 ? 0 : 1);
+}
+
+PJ_IDEF(int) pj_strcmp2( const pj_str_t *str1, const char *str2 )
+{
+    return pj_strncmp2( str1, str2, str1->slen);
+}
+
+PJ_IDEF(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2)
+{
+    pj_ssize_t diff;
+
+    diff = str1->slen - str2->slen;
+    if (diff) {
+	return (int)diff;
+    } else {
+	return strncasecmp(str1->ptr, str2->ptr, str1->slen);//return strnicmp(str1->ptr, str2->ptr, str1->slen);
+    }
+}
+
+PJ_IDEF(int) pj_stricmp2( const pj_str_t *str1, const char *str2)
+{
+    return (str1->ptr && str2) ? strncasecmp(str1->ptr, str2, str1->slen) ://return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, str1->slen) : 
+	    (str1->ptr==str2 ? 0 : 1);
+}
+
+PJ_IDEF(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, 
+			  pj_size_t len)
+{
+    return (str1->ptr && str2->ptr) ? strncasecmp(str1->ptr, str2->ptr, len) ://return (str1->ptr && str2->ptr) ? strnicmp(str1->ptr, str2->ptr, len) : 
+	    (str1->ptr == str2->ptr ? 0 : 1);
+}
+
+PJ_IDEF(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, 
+			   pj_size_t len)
+{
+    return (str1->ptr && str2) ? strncasecmp(str1->ptr, str2, len) ://return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, len) :
+	   (str1->ptr == str2 ? 0 : 1);
+}
+
+PJ_IDEF(void) pj_strcat(pj_str_t *dst, const pj_str_t *src)
+{
+    if (src->slen) {
+	pj_memcpy(dst->ptr + dst->slen, src->ptr, src->slen);
+	dst->slen += src->slen;
+    }
+}
+
+PJ_IDEF(char*) pj_strchr( pj_str_t *str, int chr)
+{
+    return (char*) memchr(str->ptr, chr, str->slen);
+}
+
+PJ_IDEF(pj_str_t*) pj_strtrim( pj_str_t *str )
+{
+    pj_strltrim(str);
+    pj_strrtrim(str);
+    return str;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/stun.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/stun.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/stun.c	(revision 1753)
@@ -0,0 +1,125 @@
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/stun.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/sock.h>
+
+#define THIS_FILE   "stun"
+
+PJ_DEF(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, 
+					     void **msg, pj_size_t *len,
+					     pj_uint32_t id_hi, 
+					     pj_uint32_t id_lo)
+{
+    pj_stun_msg_hdr *hdr;
+    
+    PJ_LOG(5,(THIS_FILE, "pj_stun_create_bind_req"));
+
+    hdr = pj_pool_calloc(pool, 1, sizeof(pj_stun_msg_hdr));
+    if (!hdr) {
+	PJ_LOG(5,(THIS_FILE, "Error allocating memory!"));
+	return -1;
+    }
+
+    hdr->type = pj_htons(PJ_STUN_BINDING_REQUEST);
+    hdr->tsx[2] = pj_htonl(id_hi);
+    hdr->tsx[3] = pj_htonl(id_lo);
+    *msg = hdr;
+    *len = sizeof(pj_stun_msg_hdr);
+
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len, 
+				       pj_stun_msg *msg)
+{
+    pj_uint16_t msg_type, msg_len;
+    char *p_attr;
+
+    PJ_LOG(5,(THIS_FILE, "pj_stun_parse_msg %p, len=%d", buf, len));
+
+    msg->hdr = (pj_stun_msg_hdr*)buf;
+    msg_type = pj_ntohs(msg->hdr->type);
+
+    switch (msg_type) {
+    case PJ_STUN_BINDING_REQUEST:
+    case PJ_STUN_BINDING_RESPONSE:
+    case PJ_STUN_BINDING_ERROR_RESPONSE:
+    case PJ_STUN_SHARED_SECRET_REQUEST:
+    case PJ_STUN_SHARED_SECRET_RESPONSE:
+    case PJ_STUN_SHARED_SECRET_ERROR_RESPONSE:
+	break;
+    default:
+	PJ_LOG(5,(THIS_FILE, "Error: unknown msg type %d", msg_type));
+	return -1;
+    }
+
+    msg_len = pj_ntohs(msg->hdr->length);
+    if (msg_len != len - sizeof(pj_stun_msg_hdr)) {
+	PJ_LOG(5,(THIS_FILE, "Error: invalid msg_len %d (expecting %d)", 
+			     msg_len, len - sizeof(pj_stun_msg_hdr)));
+	return -1;
+    }
+
+    msg->attr_count = 0;
+    p_attr = (char*)buf + sizeof(pj_stun_msg_hdr);
+
+    while (msg_len > 0) {
+	pj_stun_attr_hdr **attr = &msg->attr[msg->attr_count];
+	pj_uint32_t len;
+
+	*attr = (pj_stun_attr_hdr*)p_attr;
+	len = pj_ntohs((pj_uint16_t) ((*attr)->length)) + sizeof(pj_stun_attr_hdr);
+
+	if (msg_len < len) {
+	    PJ_LOG(5,(THIS_FILE, "Error: length mismatch in attr %d", 
+				 msg->attr_count));
+	    return -1;
+	}
+
+	if (pj_ntohs((*attr)->type) > PJ_STUN_ATTR_REFLECTED_FORM) {
+	    PJ_LOG(5,(THIS_FILE, "Error: invalid attr type %d in attr %d",
+				 pj_ntohs((*attr)->type), msg->attr_count));
+	    return -1;
+	}
+
+	msg_len = (pj_uint16_t)(msg_len - len);
+	p_attr += len;
+	++msg->attr_count;
+    }
+
+    return 0;
+}
+
+PJ_DEF(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t)
+{
+    int i;
+    
+    for (i=0; i<msg->attr_count; ++i) {
+	pj_stun_attr_hdr *attr = msg->attr[i];
+	if (pj_ntohs(attr->type) == t)
+	    return attr;
+    }
+
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/stun.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/stun.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/stun.h	(revision 1753)
@@ -0,0 +1,143 @@
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJ_STUN_H__
+#define __PJ_STUN_H__
+
+#include <pj/types.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+#define PJ_STUN_MAX_ATTR    16
+
+typedef enum pj_stun_msg_type
+{
+    PJ_STUN_BINDING_REQUEST		    = 0x0001,
+    PJ_STUN_BINDING_RESPONSE		    = 0x0101,
+    PJ_STUN_BINDING_ERROR_RESPONSE	    = 0x0111,
+    PJ_STUN_SHARED_SECRET_REQUEST	    = 0x0002,
+    PJ_STUN_SHARED_SECRET_RESPONSE	    = 0x0102,
+    PJ_STUN_SHARED_SECRET_ERROR_RESPONSE    = 0x0112
+} pj_stun_msg_type;
+
+typedef enum pj_stun_attr_type
+{
+    PJ_STUN_ATTR_MAPPED_ADDR = 1,
+    PJ_STUN_ATTR_RESPONSE_ADDR,
+    PJ_STUN_ATTR_CHANGE_REQUEST,
+    PJ_STUN_ATTR_SOURCE_ADDR,
+    PJ_STUN_ATTR_CHANGED_ADDR,
+    PJ_STUN_ATTR_USERNAME,
+    PJ_STUN_ATTR_PASSWORD,
+    PJ_STUN_ATTR_MESSAGE_INTEGRITY,
+    PJ_STUN_ATTR_ERROR_CODE,
+    PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES,
+    PJ_STUN_ATTR_REFLECTED_FORM
+} pj_stun_attr_type;
+
+typedef struct pj_stun_msg_hdr
+{
+    pj_uint16_t		type;
+    pj_uint16_t		length;
+    pj_uint32_t		tsx[4];
+} pj_stun_msg_hdr;
+
+typedef struct pj_stun_attr_hdr
+{
+    pj_uint16_t		type;
+    pj_uint16_t		length;
+} pj_stun_attr_hdr;
+
+typedef struct pj_stun_mapped_addr_attr
+{
+    pj_stun_attr_hdr	hdr;
+    pj_uint8_t		ignored;
+    pj_uint8_t		family;
+    pj_uint16_t		port;
+    pj_uint32_t		addr;
+} pj_stun_mapped_addr_attr;
+
+typedef pj_stun_mapped_addr_attr pj_stun_response_addr_attr;
+typedef pj_stun_mapped_addr_attr pj_stun_changed_addr_attr;
+typedef pj_stun_mapped_addr_attr pj_stun_src_addr_attr;
+typedef pj_stun_mapped_addr_attr pj_stun_reflected_form_attr;
+
+typedef struct pj_stun_change_request_attr
+{
+    pj_stun_attr_hdr	hdr;
+    pj_uint32_t		value;
+} pj_stun_change_request_attr;
+
+typedef struct pj_stun_username_attr
+{
+    pj_stun_attr_hdr	hdr;
+    pj_uint32_t		value[1];
+} pj_stun_username_attr;
+
+typedef pj_stun_username_attr pj_stun_password_attr;
+
+typedef struct pj_stun_error_code_attr
+{
+    pj_stun_attr_hdr	hdr;
+    pj_uint16_t		ignored;
+    pj_uint8_t		err_class;
+    pj_uint8_t		number;
+    char		reason[4];
+} pj_stun_error_code_attr;
+
+typedef struct pj_stun_msg
+{
+    pj_stun_msg_hdr    *hdr;
+    int			attr_count;
+    pj_stun_attr_hdr   *attr[PJ_STUN_MAX_ATTR];
+} pj_stun_msg;
+
+/* STUN message API (stun.c). */
+
+PJ_DECL(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, 
+					      void **msg, pj_size_t *len,
+					      pj_uint32_t id_hi,
+					      pj_uint32_t id_lo);
+PJ_DECL(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len,
+				        pj_stun_msg *msg);
+PJ_DECL(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t);
+
+/* STUN simple client API (stun_client.c) */
+enum pj_stun_err_code {
+    PJ_STUN_ERR_MEMORY		= (-2),
+    PJ_STUN_ERR_RESOLVE		= (-3),
+    PJ_STUN_ERR_TRANSPORT	= (-4),
+    PJ_STUN_ERR_INVALID_MSG	= (-5),
+    PJ_STUN_ERR_NO_RESPONSE	= (-6),
+    PJ_STUN_ERR_SYMETRIC	= (-7),
+};
+
+PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
+					      int sock_cnt, pj_sock_t sock[],
+					      const pj_str_t *srv1, int port1,
+					      const pj_str_t *srv2, int port2,
+					      pj_sockaddr_in mapped_addr[]);
+PJ_DECL(const char*) pj_stun_get_err_msg(pj_status_t status);
+
+PJ_END_DECL
+
+#endif	/* __PJ_STUN_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/stun_client.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/stun_client.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/stun_client.c	(revision 1753)
@@ -0,0 +1,273 @@
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/stun.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+enum { MAX_REQUEST = 3 };
+static int stun_timer[] = {1600, 1600, 1600 };
+
+#define THIS_FILE	"stunclient"
+#define LOG_ADDR(addr)	pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
+
+
+PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
+					      int sock_cnt, pj_sock_t sock[],
+					      const pj_str_t *srv1, int port1,
+					      const pj_str_t *srv2, int port2,
+					      pj_sockaddr_in mapped_addr[])
+{
+    pj_sockaddr_in srv_addr[2];
+    int i, j, rc, send_cnt = 0;
+    pj_pool_t *pool;
+    struct {
+	struct {
+	    pj_uint32_t	mapped_addr;
+	    pj_uint32_t	mapped_port;
+	} srv[2];
+    } *rec;
+    void       *out_msg;
+    pj_size_t	out_msg_len;
+    int wait_resp = 0;
+    int mapped_status = 0;
+
+    /* Create pool. */
+    pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL);
+    if (!pool) {
+	mapped_status = PJ_STUN_ERR_MEMORY; 
+	return -1;
+    }
+
+    /* Allocate client records */
+    rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
+    if (!rec) {
+	mapped_status = PJ_STUN_ERR_MEMORY; 
+	goto on_error;
+    }
+
+    /* Create the outgoing BIND REQUEST message template */
+    rc = pj_stun_create_bind_req( pool, &out_msg, &out_msg_len, 0, 0);
+    if (rc != 0) {
+	mapped_status = -1;
+	goto on_error;
+    }
+
+    /* Resolve servers. */
+    if (pj_sockaddr_init(&srv_addr[0], srv1, port1) != 0) {
+	mapped_status = PJ_STUN_ERR_RESOLVE; 
+	goto on_error;
+    }
+    if (pj_sockaddr_init(&srv_addr[1], srv2, port2) != 0) {
+	mapped_status = PJ_STUN_ERR_RESOLVE;
+	goto on_error;
+    }
+
+    /* Init mapped addresses to zero */
+    pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
+
+    /* Main retransmission loop. */
+    for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
+	pj_time_val next_tx, now;
+	pj_fdset_t r;
+	int select_rc;
+
+	PJ_LOG(4,(THIS_FILE, "STUN retransmit %d, wait_resp=%d", 
+			     send_cnt, wait_resp));
+
+	PJ_FD_ZERO(&r);
+
+	/* Send messages to servers that has not given us response. */
+	for (i=0; i<sock_cnt && mapped_status==0; ++i) {
+	    for (j=0; j<2 && mapped_status==0; ++j) {
+		pj_stun_msg_hdr *msg_hdr = out_msg;
+
+		if (rec[i].srv[j].mapped_port != 0)
+		    continue;
+
+		/* Modify message so that we can distinguish response. */
+		msg_hdr->tsx[2] = pj_htonl(i);
+		msg_hdr->tsx[3] = pj_htonl(j);
+
+		/* Send! */
+		rc = pj_sock_sendto(sock[i], out_msg, out_msg_len, 0,
+				    (pj_sockaddr_t*)&srv_addr[j], 
+				    sizeof(pj_sockaddr_in));
+		if (rc != (int)out_msg_len) {
+		    PJ_LOG(4,(THIS_FILE, 
+			      "Error sending STUN request to %s:%d",
+			      LOG_ADDR(srv_addr[j])));
+		    mapped_status = PJ_STUN_ERR_TRANSPORT; 
+		} else {
+		    ++wait_resp;
+		}
+	    }
+	}
+
+	/* All requests sent.
+	 * The loop below will wait for responses until all responses have
+	 * been received (i.e. wait_resp==0) or timeout occurs, which then
+	 * we'll go to the next retransmission iteration.
+	 */
+
+	/* Calculate time of next retransmission. */
+	pj_gettimeofday(&next_tx);
+	next_tx.sec += (stun_timer[send_cnt]/1000);
+	next_tx.msec += (stun_timer[send_cnt]%1000);
+	pj_time_val_normalize(&next_tx);
+
+	for (pj_gettimeofday(&now), select_rc=1; 
+	     mapped_status==0 && select_rc==1 && wait_resp>0 && PJ_TIME_VAL_LT(now, next_tx); 
+	     pj_gettimeofday(&now)) 
+	{
+	    pj_time_val timeout;
+
+	    timeout = next_tx;
+	    PJ_TIME_VAL_SUB(timeout, now);
+
+	    for (i=0; i<sock_cnt; ++i) {
+		PJ_FD_SET(sock[i], &r);
+	    }
+
+	    select_rc = pj_sock_select(FD_SETSIZE, &r, NULL, NULL, &timeout);
+	    if (select_rc < 1)
+		continue;
+
+	    for (i=0; i<sock_cnt; ++i) {
+		int len, sock_idx, srv_idx;
+		pj_stun_msg msg;
+		pj_sockaddr_in addr;
+		int addrlen = sizeof(addr);
+		pj_stun_mapped_addr_attr *attr;
+		char recv_buf[128];
+
+		if (!FD_ISSET(sock[i], &r))
+		    continue;
+
+		len = pj_sock_recvfrom( sock[i], recv_buf, 
+					sizeof(recv_buf), 0,
+					(pj_sockaddr_t*)&addr,
+					&addrlen);
+
+		--wait_resp;
+
+		if (len < 1) {
+		    mapped_status = PJ_STUN_ERR_TRANSPORT; 
+		    continue;
+		}
+
+		if (pj_stun_parse_msg(recv_buf, len, &msg) != 0) {
+		    PJ_LOG(4,(THIS_FILE, 
+				"Error parsing STUN response from %s:%d",
+				LOG_ADDR(addr)));
+		    mapped_status = PJ_STUN_ERR_INVALID_MSG;
+		    continue;
+		}
+
+		sock_idx = pj_ntohl(msg.hdr->tsx[2]);
+		srv_idx = pj_ntohl(msg.hdr->tsx[3]);
+
+		if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) {
+		    PJ_LOG(4,(THIS_FILE, 
+				"Invalid transaction ID from %s:%d", 
+				LOG_ADDR(addr)));
+		    mapped_status = PJ_STUN_ERR_INVALID_MSG;
+		    continue;
+		}
+
+		if (pj_ntohs(msg.hdr->type) != PJ_STUN_BINDING_RESPONSE) {
+		    PJ_LOG(4,(THIS_FILE, 
+				"Non binding response %d from %s:%d", 
+				pj_ntohs(msg.hdr->type), LOG_ADDR(addr)));
+		    mapped_status = PJ_STUN_ERR_INVALID_MSG;
+		    continue;
+		}
+
+		if (pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_ERROR_CODE) != NULL) {
+		    PJ_LOG(4,(THIS_FILE, 
+				"Got STUN error attribute from %s:%d",
+				LOG_ADDR(addr)));
+		    mapped_status = PJ_STUN_ERR_INVALID_MSG;
+		    continue;
+		}
+
+		attr = (void*)pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_MAPPED_ADDR);
+		if (!attr) {
+		    PJ_LOG(4,(THIS_FILE,
+				"No mapped address in response from %s:%d",
+				LOG_ADDR(addr)));
+		    mapped_status = PJ_STUN_ERR_INVALID_MSG;
+		    continue;
+		}
+
+		rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
+		rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
+	    }
+	}
+
+	/* The best scenario is if all requests have been replied.
+	 * Then we don't need to go to the next retransmission iteration.
+	 */
+	if (wait_resp <= 0)
+	    break;
+    }
+
+    for (i=0; i<sock_cnt && mapped_status==0; ++i) {
+	if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
+	    rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
+	{
+	    mapped_addr[i].sin_family = PJ_AF_INET;
+	    mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
+	    mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
+
+	    if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
+		mapped_status = PJ_STUN_ERR_NO_RESPONSE;
+	    }
+	} else {
+	    mapped_status = PJ_STUN_ERR_SYMETRIC;
+	}
+    }
+
+    pj_pool_release(pool);
+
+    return mapped_status;
+
+on_error:
+    if (pool) pj_pool_release(pool);
+    return -1;
+}
+
+PJ_DEF(const char*) pj_stun_get_err_msg(pj_status_t status)
+{
+    switch (status) {
+    case 0:			    return "No error";
+    case -1:			    return "General error";
+    case PJ_STUN_ERR_MEMORY:	    return "Memory allocation failed";
+    case PJ_STUN_ERR_RESOLVE:	    return "Invalid IP or unable to resolve STUN server";
+    case PJ_STUN_ERR_TRANSPORT:	    return "Unable to contact STUN server";
+    case PJ_STUN_ERR_INVALID_MSG:   return "Invalid response from STUN server";
+    case PJ_STUN_ERR_NO_RESPONSE:   return "No response from STUN server";
+    case PJ_STUN_ERR_SYMETRIC:	    return "Different mappings are returned from servers";
+    }
+    return "Unknown error";
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/timer.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/timer.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/timer.c	(revision 1753)
@@ -0,0 +1,448 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/timer.c,v 1.1 2005/12/02 20:02:31 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+/* (C)1993-2003 Douglas C. Schmidt
+ *
+ * This file is originaly from ACE library by Doug Schmidt
+ * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and his research 
+ * group at Washington University, University of California, Irvine, and Vanderbilt 
+ * University Copyright (c) 1993-2003, all rights reserved.
+ */
+#include <pj/timer.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/string.h>
+
+
+
+#define HEAP_PARENT(X)	(X == 0 ? 0 : (((X) - 1) / 2))
+#define HEAP_LEFT(X)	(((X)+(X))+1)
+
+
+/**
+ * The implementation of timer heap.
+ */
+struct pj_timer_heap_t
+{
+    /** Pool from which the timer heap resize will get the storage from */
+    pj_pool_t *pool;
+
+    /** Maximum size of the heap. */
+    pj_size_t max_size;
+
+    /** Current size of the heap. */
+    pj_size_t cur_size;
+
+    /** Mutex for synchronization, or NULL */
+    pj_mutex_t *mutex;
+
+    /**
+     * Current contents of the Heap, which is organized as a "heap" of
+     * pj_timer_entry *'s.  In this context, a heap is a "partially
+     * ordered, almost complete" binary tree, which is stored in an
+     * array.
+     */
+    pj_timer_entry **heap;
+
+    /**
+     * An array of "pointers" that allows each pj_timer_entry in the
+     * <heap_> to be located in O(1) time.  Basically, <timer_id_[i]>
+     * contains the slot in the <heap_> array where an pj_timer_entry
+     * with timer id <i> resides.  Thus, the timer id passed back from
+     * <schedule> is really an slot into the <timer_ids> array.  The
+     * <timer_ids_> array serves two purposes: negative values are
+     * treated as "pointers" for the <freelist_>, whereas positive
+     * values are treated as "pointers" into the <heap_> array.
+     */
+    pj_timer_id_t *timer_ids;
+
+    /**
+     * "Pointer" to the first element in the freelist contained within
+     * the <timer_ids_> array, which is organized as a stack.
+     */
+    pj_timer_id_t timer_ids_freelist;
+
+    /** Callback to be called when a timer expires. */
+    pj_timer_heap_callback *callback;
+
+};
+
+
+
+PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht )
+{
+    if (ht->mutex) {
+	pj_mutex_lock(ht->mutex);
+    }
+}
+
+PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht )
+{
+    if (ht->mutex) {
+	pj_mutex_unlock(ht->mutex);
+    }
+}
+
+
+static void copy_node( pj_timer_heap_t *ht, int slot, pj_timer_entry *moved_node )
+{
+    // Insert <moved_node> into its new location in the heap.
+    ht->heap[slot] = moved_node;
+    
+    // Update the corresponding slot in the parallel <timer_ids_> array.
+    ht->timer_ids[moved_node->_timer_id] = slot;
+}
+
+static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht )
+{
+    // We need to truncate this to <int> for backwards compatibility.
+    pj_timer_id_t new_id = ht->timer_ids_freelist;
+    
+    // The freelist values in the <timer_ids_> are negative, so we need
+    // to negate them to get the next freelist "pointer."
+    ht->timer_ids_freelist =
+	-ht->timer_ids[ht->timer_ids_freelist];
+    
+    return new_id;
+    
+}
+
+static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id)
+{
+    // The freelist values in the <timer_ids_> are negative, so we need
+    // to negate them to get the next freelist "pointer."
+    ht->timer_ids[old_id] = -ht->timer_ids_freelist;
+    ht->timer_ids_freelist = old_id;
+}
+
+
+static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,
+                        size_t slot, size_t child)
+{
+    // Restore the heap property after a deletion.
+    
+    while (child < ht->cur_size)
+    {
+	// Choose the smaller of the two children.
+	if (child + 1 < ht->cur_size
+	    && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value))
+	    child++;
+	
+	// Perform a <copy> if the child has a larger timeout value than
+	// the <moved_node>.
+	if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value))
+        {
+	    copy_node( ht, slot, ht->heap[child]);
+	    slot = child;
+	    child = HEAP_LEFT(child);
+        }
+	else
+	    // We've found our location in the heap.
+	    break;
+    }
+    
+    copy_node( ht, slot, moved_node);
+}
+
+static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,
+		       size_t slot, size_t parent)
+{
+    // Restore the heap property after an insertion.
+    
+    while (slot > 0)
+    {
+	// If the parent node is greater than the <moved_node> we need
+	// to copy it down.
+	if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value))
+        {
+	    copy_node(ht, slot, ht->heap[parent]);
+	    slot = parent;
+	    parent = HEAP_PARENT(slot);
+        }
+	else
+	    break;
+    }
+    
+    // Insert the new node into its proper resting place in the heap and
+    // update the corresponding slot in the parallel <timer_ids> array.
+    copy_node(ht, slot, moved_node);
+}
+
+
+static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot)
+{
+    pj_timer_entry *removed_node = ht->heap[slot];
+    
+    // Return this timer id to the freelist.
+    push_freelist( ht, removed_node->_timer_id );
+    
+    // Decrement the size of the heap by one since we're removing the
+    // "slot"th node.
+    ht->cur_size--;
+    
+    // Set the ID
+    removed_node->_timer_id = -1;
+
+    // Only try to reheapify if we're not deleting the last entry.
+    
+    if (slot < ht->cur_size)
+    {
+	int parent;
+	pj_timer_entry *moved_node = ht->heap[ht->cur_size];
+	
+	// Move the end node to the location being removed and update
+	// the corresponding slot in the parallel <timer_ids> array.
+	copy_node( ht, slot, moved_node);
+	
+	// If the <moved_node->time_value_> is great than or equal its
+	// parent it needs be moved down the heap.
+	parent = HEAP_PARENT (slot);
+	
+	if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value))
+	    reheap_down( ht, moved_node, slot, HEAP_LEFT(slot));
+	else
+	    reheap_up( ht, moved_node, slot, parent);
+    }
+    
+    return removed_node;
+}
+
+static void grow_heap(pj_timer_heap_t *ht)
+{
+    // All the containers will double in size from max_size_
+    size_t new_size = ht->max_size * 2;
+    pj_timer_id_t *new_timer_ids;
+    pj_size_t i;
+    
+    // First grow the heap itself.
+    
+    pj_timer_entry **new_heap = 0;
+    
+    new_heap = pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size);
+    memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*));
+    //delete [] this->heap_;
+    ht->heap = new_heap;
+    
+    // Grow the array of timer ids.
+    
+    new_timer_ids = 0;
+    new_timer_ids = pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t));
+    
+    memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t));
+    
+    //delete [] timer_ids_;
+    ht->timer_ids = new_timer_ids;
+    
+    // And add the new elements to the end of the "freelist".
+    for (i = ht->max_size; i < new_size; i++)
+	ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
+    
+    ht->max_size = new_size;
+}
+
+static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node)
+{
+    if (ht->cur_size + 2 >= ht->max_size)
+	grow_heap(ht);
+    
+    reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size));
+    ht->cur_size++;
+}
+
+
+static pj_status_t schedule( pj_timer_heap_t *ht,
+			     pj_timer_entry *entry, 
+			     const pj_time_val *future_time )
+{
+    if (ht->cur_size < ht->max_size)
+    {
+	// Obtain the next unique sequence number.
+	// Set the entry
+	entry->_timer_id = pop_freelist(ht);
+	entry->_timer_value = *future_time;
+	insert_node( ht, entry);
+	return 0;
+    }
+    else
+	return -1;
+}
+
+
+static int cancel( pj_timer_heap_t *ht, 
+		   pj_timer_entry *entry, 
+		   int dont_call)
+{
+  long timer_node_slot;
+
+  // Check to see if the timer_id is out of range
+  if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size)
+    return 0;
+
+  timer_node_slot = ht->timer_ids[entry->_timer_id];
+
+  if (timer_node_slot < 0) // Check to see if timer_id is still valid.
+    return 0;
+
+  if (entry != ht->heap[timer_node_slot])
+    {
+      pj_assert(entry == ht->heap[timer_node_slot]);
+      return 0;
+    }
+  else
+    {
+      remove_node( ht, timer_node_slot);
+
+      if (dont_call == 0)
+        // Call the close hook.
+	(*ht->callback)(ht, entry);
+      return 1;
+    }
+}
+
+
+/*
+ * Create a new timer heap.
+ */
+PJ_DEF(pj_timer_heap_t*) pj_timer_heap_create( pj_pool_t *pool,
+					       pj_size_t size,
+					       unsigned flag )
+{
+    pj_timer_heap_t *ht;
+    pj_size_t i;
+
+    /* Magic? */
+    size += 2;
+
+    /* Allocate timer heap data structure from the pool */
+    ht = pj_pool_alloc(pool, sizeof(pj_timer_heap_t));
+
+    /* Initialize timer heap sizes */
+    ht->max_size = size;
+    ht->cur_size = 0;
+    ht->timer_ids_freelist = 1;
+    ht->pool = pool;
+
+    /* Mutex. */
+    if (flag & PJ_TIMER_HEAP_NO_SYNCHRONIZE) {
+	ht->mutex = NULL;
+    } else {
+	/* Mutex must be the recursive types. See commented code inside pj_timer_heap_poll() */
+	ht->mutex = pj_mutex_create(pool, "tmhp%p", PJ_MUTEX_RECURSE);
+	if (!ht->mutex) {
+	    return NULL;
+	}
+    }
+
+    // Create the heap array.
+    ht->heap = pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size);
+
+    // Create the parallel
+    ht->timer_ids = pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size);
+
+    // Initialize the "freelist," which uses negative values to
+    // distinguish freelist elements from "pointers" into the <heap_>
+    // array.
+    for (i=0; i<size; ++i)
+	ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
+
+    return ht;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+					    pj_timer_entry *entry, 
+					    const pj_time_val *delay)
+{
+    pj_status_t status;
+    pj_time_val expires;
+
+    pj_gettimeofday(&expires);
+    PJ_TIME_VAL_ADD(expires, *delay);
+    
+    lock_timer_heap(ht);
+    status = schedule(ht, entry, &expires);
+    unlock_timer_heap(ht);
+
+    return status;
+}
+
+PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+				  pj_timer_entry *entry)
+{
+    int count;
+
+    lock_timer_heap(ht);
+    count = cancel(ht, entry, 1);
+    unlock_timer_heap(ht);
+
+    return count;
+}
+
+PJ_DEF(int) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay )
+{
+    pj_time_val now;
+    int count;
+
+    if (!ht->cur_size && next_delay) {
+	next_delay->sec = next_delay->msec = PJ_MAXLONG;
+	return 0;
+    }
+
+    count = 0;
+    pj_gettimeofday(&now);
+
+    lock_timer_heap(ht);
+    while ( ht->cur_size && 
+	    PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) ) 
+    {
+	pj_timer_entry *node = remove_node(ht, 0);
+	++count;
+
+	//Better not to temporarily release mutex to save some syscalls.
+	//But then make sure the mutex must be the recursive types (PJ_MUTEX_RECURSE)!
+	//unlock_timer_heap(ht);
+	(*node->cb)(ht, node);
+	//lock_timer_heap(ht);
+    }
+    if (ht->cur_size && next_delay) {
+	*next_delay = ht->heap[0]->_timer_value;
+	PJ_TIME_VAL_SUB(*next_delay, now);
+    } else if (next_delay) {
+	next_delay->sec = next_delay->msec = PJ_MAXLONG;
+    }
+    unlock_timer_heap(ht);
+
+    return count;
+}
+
+PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
+{
+    return ht->cur_size;
+}
+
+PJ_DEF(void) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
+					  pj_time_val *timeval)
+{
+    lock_timer_heap(ht);
+    *timeval = ht->heap[0]->_timer_value;
+    unlock_timer_heap(ht);
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/timer.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/timer.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/timer.h	(revision 1753)
@@ -0,0 +1,196 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/timer.h,v 1.1 2005/12/02 20:02:31 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+/* (C)1993-2003 Douglas C. Schmidt
+ *
+ * This file is originaly from ACE library by Doug Schmidt
+ * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and his research 
+ * group at Washington University, University of California, Irvine, and Vanderbilt 
+ * University Copyright (c) 1993-2003, all rights reserved.
+ *
+ */
+
+#ifndef __PJ_TIMER_H__
+#define __PJ_TIMER_H__
+
+/**
+ * @file timer.h
+ * @brief Timer Heap
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_TIMER Timer Heap Management.
+ * @ingroup PJ_MISC
+ * @brief
+ * The timer scheduling implementation here is based on ACE library's 
+ * ACE_Timer_Heap, with only little modification to suit our library's style
+ * (I even left most of the comments in the original source).
+ *
+ * To quote the original quote in ACE_Timer_Heap_T class:
+ *
+ *      This implementation uses a heap-based callout queue of
+ *      absolute times.  Therefore, in the average and worst case,
+ *      scheduling, canceling, and expiring timers is O(log N) (where
+ *      N is the total number of timers).  In addition, we can also
+ *      preallocate as many \a ACE_Timer_Nodes as there are slots in
+ *      the heap.  This allows us to completely remove the need for
+ *      dynamic memory allocation, which is important for real-time
+ *      systems.
+ * @{
+ */
+
+
+/**
+ * The type for internal timer ID.
+ */
+typedef int pj_timer_id_t;
+
+
+struct pj_timer_entry;
+
+/**
+ * The type of callback function to be called by timer scheduler when a timer
+ * has expired.
+ */
+typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap,
+				    struct pj_timer_entry *entry);
+
+
+/**
+ * This structure represents an entry to the timer.
+ */
+struct pj_timer_entry
+{
+    /** Arbitrary ID assigned by the user/owner of this entry. */
+    int id;
+
+    /** User data to be associated with this entry. */
+    void *user_data;
+    
+    /** Callback to be called when the timer expires. */
+    pj_timer_heap_callback *cb;
+
+    /** Internal unique timer ID, which is assigned by the timer heap. 
+	Application should not touch this ID.
+     */
+    pj_timer_id_t _timer_id;
+
+    /** The future time when the timer expires, which the value is updated
+     *  by timer heap when the timer is scheduled.
+     */
+    pj_time_val _timer_value;
+};
+
+
+/**
+ * Guidance on how much memory is required per timer item.
+ */
+#define PJ_TIMER_ENTRY_SIZE	(2 * sizeof(void*) + sizeof(pj_timer_entry))
+
+/**
+ * Guidance on how much memory is required for the timer heap.
+ */
+#define PJ_TIMER_HEAP_SIZE	(8 * sizeof(void*) + 2 * PJ_TIMER_ENTRY_SIZE)
+
+/**
+ * Flag to indicate that thread synchronization is NOT needed for the timer heap.
+ */
+#define PJ_TIMER_HEAP_SYNCHRONIZE	(0)
+#define PJ_TIMER_HEAP_NO_SYNCHRONIZE	(1)
+
+/**
+ * Create a timer heap.
+ * @param pool the pool where allocations in the timer heap will be allocated.
+ *             The timer heap will dynamicly allocate more storate from the
+ *	       pool if the number of timer entries registered is more than
+ *	       the size originally requested when calling this function.
+ * @param count the maximum number of timer entries to be supported initially.
+ *		If the application registers more entries during runtime,
+ *		then the timer heap will resize.
+ * @param flag Creation flag, currently only PJ_TIMER_HEAP_NO_SYNCHRONIZE is
+ *	       recognized..
+ * @return the timer heap, or NULL.
+ */
+PJ_DECL(pj_timer_heap_t*) pj_timer_heap_create( pj_pool_t *pool,
+					        pj_size_t count,
+						unsigned flag );
+
+/**
+ * Schedule a timer entry which will expire AFTER the specified delay.
+ * @param ht the timer heap.
+ * @param entry the entry to be registered. 
+ * @param delay the interval to expire.
+ * @return zero on success, or -1 on error.
+ */
+PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+					     pj_timer_entry *entry, 
+					     const pj_time_val *delay);
+
+/**
+ * Cancel a previously registered timer.
+ * @param ht the timer heap.
+ * @param entry the entry to be cancelled.
+ * @return the number of timer cancelled, which should be one if the
+ *         entry has really been registered, or zero if no timer was
+ *         cancelled.
+ */
+PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+				   pj_timer_entry *entry);
+
+/**
+ * Get the number of timer entries.
+ * @param ht the timer heap.
+ * @return the number of timer entries.
+ */
+PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht );
+
+/**
+ * Get the earliest time registered in the timer heap.
+ * @param ht the timer heap.
+ * @param timeval the time deadline of the earliest timer entry.
+ */
+PJ_DECL(void) pj_timer_heap_earliest_time( pj_timer_heap_t *ht, 
+					   pj_time_val *timeval);
+
+/**
+ * Poll the timer heap, check for expired timers and call the callback for
+ * each of the expired timers.
+ * @param ht the timer heap.
+ * @param next_delay If this parameter is not NULL, it will be filled up with
+ *		     the time delay until the next timer elapsed, or -1 in
+ *		     the sec part if no entry exist.
+ * @return the number of timers expired.
+ */
+PJ_DECL(int) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJ_TIMER_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/timer_i.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/timer_i.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/timer_i.h	(revision 1753)
@@ -0,0 +1,26 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/timer_i.h,v 1.1 2005/12/02 20:02:31 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+
+/* UNUSED AS 24/03/03 */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/tounix
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/tounix	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/tounix	(revision 1753)
@@ -0,0 +1,4 @@
+#!/bin/sh
+cp $1 /tmp
+cat /tmp/$1 | tr -d '\r' > $1
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/types.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/types.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/types.c	(revision 1753)
@@ -0,0 +1,48 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/types.c,v 1.1 2005/12/02 20:02:31 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/types.h>
+
+void pj_time_val_normalize(pj_time_val *t)
+{
+    if (t->msec >= 1000) {
+	do {
+	    t->sec++;
+	    t->msec -= 1000;
+        } while (t->msec >= 1000);
+    }
+    else if (t->msec <= -1000) {
+	do {
+	    t->sec--;
+	    t->msec += 1000;
+        } while (t->msec <= -1000);
+    }
+
+    if (t->sec >= 1 && t->msec < 0) {
+	t->sec--;
+	t->msec += 1000;
+
+    } else if (t->sec < 0 && t->msec > 0) {
+	t->sec++;
+	t->msec -= 1000;
+    }
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/types.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/types.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/types.h	(revision 1753)
@@ -0,0 +1,426 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/types.h,v 1.1 2005/12/02 20:02:31 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_TYPES_H__
+#define __PJ_TYPES_H__
+
+
+/**
+ * @defgroup PJ PJ Library
+ */
+/**
+ * @file types.h
+ * @brief Declaration of basic types and utility.
+ */
+/**
+ * @defgroup PJ_BASIC Basic Data Types and Library Functionality.
+ * @ingroup PJ_DS
+ * @{
+ */
+#include <pj/config.h>
+#include <pj/compat.h>
+
+PJ_BEGIN_DECL
+
+///////////////////////////////////////////////////////////////////////////////
+/* 
+ * Basic types.
+ */
+#if defined(_MSC_VER)
+  typedef __int64	    pj_int64_t;
+  typedef unsigned __int64  pj_uint64_t;
+#elif defined(__GNUC__)
+  typedef long long	    pj_int64_t;
+  typedef unsigned long long pj_uint64_t;
+#else
+# error Got to define 64bit integers for this compiler.
+#endif
+
+/** Unsigned 32bit integer. */
+typedef int		pj_int32_t;
+
+/** Signed 32bit integer. */
+typedef unsigned int	pj_uint32_t;
+
+/** Unsigned 16bit integer. */
+typedef short		pj_int16_t;
+
+/** Signed 16bit integer. */
+typedef unsigned short	pj_uint16_t;
+
+/** Unsigned 8bit integer. */
+typedef signed char	pj_int8_t;
+
+/** Signed 16bit integer. */
+typedef unsigned char	pj_uint8_t;
+
+/** Large unsigned integer. */
+typedef size_t		pj_size_t;
+
+/** Large signed integer. */
+typedef long		pj_ssize_t;
+
+/** Status code. */
+typedef int		pj_status_t;
+
+/** Boolean. */
+typedef int		pj_bool_t;
+
+/** Status is OK. */
+#define PJ_OK	    0
+
+/** True value. */
+#define PJ_TRUE	    1
+
+/** False value. */
+#define PJ_FALSE    0
+
+/* Input or output argument type. */
+#define PJ_ARG_IN
+#define PJ_ARG_OUT
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Data structure types.
+ */
+/**
+ * This type is used as replacement to legacy C string, and used throughout
+ * the library. By convention, the string is NOT null terminated.
+ */
+struct pj_str_t
+{
+    /** Buffer pointer, which is by convention NOT null terminated. */
+    char       *ptr;
+
+    /** The length of the string. */
+    pj_ssize_t  slen;
+};
+
+
+/**
+ * The opaque data type for linked list, which is used as arguments throughout
+ * the linked list operations.
+ */
+typedef void pj_list_type;
+
+/** 
+ * List.
+ */
+typedef struct pj_list pj_list;
+
+/**
+ * Opaque data type for hash tables.
+ */
+typedef struct pj_hash_table_t pj_hash_table_t;
+
+/**
+ * Opaque data type for hash entry (only used internally by hash table).
+ */
+typedef struct pj_hash_entry pj_hash_entry;
+
+/**
+ * Data type for hash search iterator.
+ */
+struct pj_hash_iterator_t
+{
+    pj_uint32_t	     index;
+    pj_hash_entry   *entry;
+};
+
+typedef struct pj_hash_iterator_t pj_hash_iterator_t;
+
+/**
+ * Forward declaration for memory pool factory.
+ */
+typedef struct pj_pool_factory pj_pool_factory;
+
+/**
+ * Opaque data type for memory pool.
+ */
+typedef struct pj_pool_t pj_pool_t;
+
+/**
+ * Forward declaration for caching pool, a pool factory implementation.
+ */
+typedef struct pj_caching_pool pj_caching_pool;
+
+/**
+ * This type is used as replacement to legacy C string, and used throughout
+ * the library.
+ */
+typedef struct pj_str_t pj_str_t;
+
+/**
+ * Opaque data type for I/O Queue structure.
+ */
+typedef struct pj_ioqueue_t pj_ioqueue_t;
+
+/**
+ * Opaque data type for key that identifies a handle registered to the
+ * I/O queue framework.
+ */
+typedef struct pj_ioqueue_key_t pj_ioqueue_key_t;
+
+/**
+ * Opaque data to identify timer heap.
+ */
+typedef struct pj_timer_heap_t pj_timer_heap_t;
+
+/**
+ * Forward declaration for timer entry.
+ */
+typedef struct pj_timer_entry pj_timer_entry;
+
+/** 
+ * Opaque data type for atomic operations.
+ */
+typedef struct pj_atomic_t pj_atomic_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Thread handle. */
+typedef struct pj_thread_t pj_thread_t;
+
+/** Mutex handle. */
+typedef struct pj_mutex_t pj_mutex_t;
+
+/** Semaphore handle. */
+typedef struct pj_sem_t pj_sem_t;
+
+/** Event object. */
+typedef struct pj_event_t pj_event_t;
+
+/** Unidirectional stream pipe object. */
+typedef struct pj_pipe_t pj_pipe_t;
+
+/** Operating system handle. */
+typedef void *pj_oshandle_t;
+
+/** Socket handle. */
+typedef long pj_sock_t;
+
+/** Generic socket address. */
+typedef void pj_sockaddr_t;
+
+/** Color type. */
+typedef unsigned int pj_color_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Utility macro to compute the number of elements in static array. */
+#define PJ_ARRAY_SIZE(a)    (sizeof(a)/sizeof(a[0]))
+
+/* Debugging flag. */
+#ifndef NDEBUG
+#   define PJ_DEBUG 1
+#else
+#   define PJ_DEBUG 0
+#endif
+
+#define PJ_MAXLONG  0x7FFFFFFFL
+/*
+ * Object name identification.
+ */
+#define PJ_MAX_OBJ_NAME	16
+typedef char pj_obj_name[PJ_MAX_OBJ_NAME];
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * General.
+ */
+/**
+ * Initialize the PJ Library.
+ * This function must be called before using the library. The purpose of this
+ * function is to initialize static library data, such as character table used
+ * in random string generation, and to initialize operating system dependent
+ * functionality (such as WSAStartup() in Windows).
+ */
+PJ_DECL(pj_status_t) pj_init(void);
+
+
+/**
+ * @}
+ */
+/**
+ * @addtogroup PJ_TIME Time Data Type and Manipulation.
+ * @ingroup PJ_MISC
+ * @{
+ */
+
+/**
+ * Representation of time value in this library.
+ * This type can be used to represent either an interval or a specific time
+ * or date. 
+ */
+typedef struct pj_time_val
+{
+    /** The seconds part of the time. */
+    long    sec;
+
+    /** The miliseconds fraction of the time. */
+    long    msec;
+
+} pj_time_val;
+
+/**
+ * Normalize the value in time value.
+ */
+PJ_DECL(void) pj_time_val_normalize(pj_time_val *t);
+
+/**
+ * Convert time value to miliseconds.
+ */
+#define PJ_TIME_VAL_MSEC(t)	((t).sec * 1000 + (t).msec)
+
+/**
+ * This macros returns non-zero if t1==t2.
+ */
+#define PJ_TIME_VAL_EQ(t1, t2)	((t1).sec==(t2).sec && (t1).msec==(t2).msec)
+
+/**
+ * This macros returns non-zero if t1 > t2
+ */
+#define PJ_TIME_VAL_GT(t1, t2)	((t1).sec>(t2).sec || ((t1).sec==(t2).sec && (t1).msec>(t2).msec))
+
+/**
+ * This macros returns non-zero if t1 >= t2
+ */
+#define PJ_TIME_VAL_GTE(t1, t2)	(PJ_TIME_VAL_GT(t1,t2) || PJ_TIME_VAL_EQ(t1,t2))
+
+/**
+ * This macros returns non-zero if t1 < t2
+ */
+#define PJ_TIME_VAL_LT(t1, t2)	(!(PJ_TIME_VAL_GTE(t1,t2)))
+
+/**
+ * This macros returns non-zero if t1 <= t2.
+ */
+#define PJ_TIME_VAL_LTE(t1, t2)	(!PJ_TIME_VAL_GT(t1, t2))
+
+/**
+ * Add t2 to t1 (t1 = t1 + t2).
+ */
+#define PJ_TIME_VAL_ADD(t1, t2)	    do {			    \
+					(t1).sec += (t2).sec;	    \
+					(t1).msec += (t2).msec;	    \
+					pj_time_val_normalize(&(t1)); \
+				    } while (0)
+
+
+/**
+ * Substract t2 from t1 (t1 = t1 - t2).
+ */
+#define PJ_TIME_VAL_SUB(t1, t2)	    do {			    \
+					(t1).sec -= (t2).sec;	    \
+					(t1).msec -= (t2).msec;	    \
+					pj_time_val_normalize(&(t1)); \
+				    } while (0)
+
+
+/**
+ * Parsed value of time.
+ */
+typedef struct pj_parsed_time
+{
+    /** Day of week: 0=Sunday */
+    int wday;
+
+    /** Day of the year, 0-365. */
+    int yday;
+
+    /** Day of month: 1-31 */
+    int day;
+
+    /** Month: 0 - 11 */
+    int mon;
+
+    /** Year: 1900+ */
+    int year;
+
+    /** Second: 0-59 */
+    int sec;
+
+    /** Minute: 0-59 */
+    int min;
+
+    /** Hour: 0-23 */
+    int hour;
+
+    /** Miliseconds. */
+    int msec;
+
+} pj_parsed_time;
+
+
+/**
+ * @}	// Time Management
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Terminal.
+ */
+/**
+ * Color code combination.
+ */
+enum {
+    PJ_TERM_COLOR_R	= 2,
+    PJ_TERM_COLOR_G	= 4,
+    PJ_TERM_COLOR_B	= 1,
+    PJ_TERM_COLOR_BRIGHT = 8
+};
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+/* 
+ * Util macros.
+ */
+
+/**
+ * Assertion macro.
+ */
+#define pj_assert(expr)	    assert(expr)
+
+///////////////////////////////////////////////////////////////////////////////
+/** 
+ * Base class for PJ classes.
+ * Currently it used only to give name to instances, and this is only used
+ * for logging purpose. 
+ */
+typedef struct pj_object
+{
+    char obj_name[32];
+} pj_object;
+
+
+#define PJ_OBJ_DECL_DERIVED_FROM(obj_type)   obj_type	obj_type##__
+#define PJ_OBJ(var,obj_type)		     (&((var)->obj_type##__))
+
+
+PJ_END_DECL
+
+
+#endif /* __PJ_TYPES_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/xml.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/xml.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/xml.c	(revision 1753)
@@ -0,0 +1,385 @@
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/xml.h>
+#include <pj/scanner.h>
+#include <pj/except.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/log.h>
+
+#define EX_SYNTAX_ERROR	12
+#define THIS_FILE	"xml.c"
+
+static void on_syntax_error(struct pj_scanner *scanner)
+{
+    PJ_UNUSED_ARG(scanner)
+    PJ_THROW(EX_SYNTAX_ERROR);
+}
+
+static pj_xml_node *alloc_node( pj_pool_t *pool )
+{
+    pj_xml_node *node;
+
+    node = pj_pool_calloc(pool, 1, sizeof(pj_xml_node));
+    pj_list_init( &node->attr_head );
+    pj_list_init( &node->node_head );
+
+    return node;
+}
+
+static pj_xml_attr *alloc_attr( pj_pool_t *pool )
+{
+    return pj_pool_calloc(pool, 1, sizeof(pj_xml_attr));
+}
+
+static pj_xml_node *xml_parse_node( pj_pool_t *pool, pj_scanner *scanner)
+{
+    pj_xml_node *node;
+    pj_str_t end_name;
+
+    if (*scanner->current != '<')
+	on_syntax_error(scanner);
+
+    /* Handle Processing Instructino (PI) construct (i.e. "<?") */
+    if (*scanner->current == '<' && *(scanner->current+1) == '?') {
+	pj_scan_advance_n(scanner, 2, PJ_FALSE);
+	for (;;) {
+	    pj_str_t dummy;
+	    pj_scan_get_until_ch(scanner, '?', &dummy);
+	    if (*scanner->current=='?' && *(scanner->current+1)=='>') {
+		pj_scan_advance_n(scanner, 2, PJ_TRUE);
+		break;
+	    } else {
+		pj_scan_advance_n(scanner, 1, PJ_FALSE);
+	    }
+	}
+	return xml_parse_node(pool, scanner);
+    }
+
+    /* Handle comments construct (i.e. "<!--") */
+    if (pj_scan_strcmp(scanner, "<!--", 4) == 0) {
+	pj_scan_advance_n(scanner, 4, PJ_FALSE);
+	for (;;) {
+	    pj_str_t dummy;
+	    pj_scan_get_until_ch(scanner, '-', &dummy);
+	    if (pj_scan_strcmp(scanner, "-->", 3) == 0) {
+		pj_scan_advance_n(scanner, 3, PJ_TRUE);
+		break;
+	    } else {
+		pj_scan_advance_n(scanner, 1, PJ_FALSE);
+	    }
+	}
+	return xml_parse_node(pool, scanner);
+    }
+
+    /* Alloc node. */
+    node = alloc_node(pool);
+
+    /* Get '<' */
+    pj_scan_get_char(scanner);
+
+    /* Get node name. */
+    pj_scan_get_until_chr( scanner, " />\t", &node->name);
+
+    /* Get attributes. */
+    while (*scanner->current != '>' && *scanner->current != '/') {
+	pj_xml_attr *attr = alloc_attr(pool);
+	
+	pj_scan_get_until_chr( scanner, "=> \t", &attr->name);
+	if (*scanner->current == '=') {
+	    pj_scan_get_char( scanner );
+	    pj_scan_get_quote(scanner, '"', '"', &attr->value);
+	    /* remove quote characters */
+	    ++attr->value.ptr;
+	    attr->value.slen -= 2;
+	}
+	
+	pj_list_insert_before( &node->attr_head, attr );
+    }
+
+    if (*scanner->current == '/') {
+	pj_scan_get_char(scanner);
+	if (pj_scan_get_char(scanner) != '>')
+	    on_syntax_error(scanner);
+	return node;
+    }
+
+    /* Enclosing bracket. */
+    if (pj_scan_get_char(scanner) != '>')
+	on_syntax_error(scanner);
+
+    /* Sub nodes. */
+    while (*scanner->current == '<' && *(scanner->current+1) != '/') {
+	pj_xml_node *sub_node = xml_parse_node(pool, scanner);
+	pj_list_insert_before( &node->node_head, sub_node );
+    }
+
+    /* Content. */
+    if (!pj_scan_is_eof(scanner) && *scanner->current != '<') {
+	pj_scan_get_until_ch(scanner, '<', &node->content);
+    }
+
+    /* Enclosing node. */
+    if (pj_scan_get_char(scanner) != '<' || pj_scan_get_char(scanner) != '/')
+	on_syntax_error(scanner);
+
+    pj_scan_get_until_chr(scanner, " \t>", &end_name);
+
+    /* Compare name. */
+    if (pj_stricmp(&node->name, &end_name) != 0)
+	on_syntax_error(scanner);
+
+    /* Enclosing '>' */
+    if (pj_scan_get_char(scanner) != '>')
+	on_syntax_error(scanner);
+
+    return node;
+}
+
+PJ_DEF(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len)
+{
+    pj_xml_node *node = NULL;
+    pj_scanner scanner;
+    PJ_USE_EXCEPTION;
+
+    if (!msg || !len || !pool)
+	return NULL;
+
+    pj_scan_init( &scanner, msg, len, PJ_SCAN_AUTOSKIP_WS|PJ_SCAN_AUTOSKIP_NEWLINE, &on_syntax_error);
+    PJ_TRY {
+	node =  xml_parse_node(pool, &scanner);
+    }
+    PJ_DEFAULT {
+	PJ_LOG(4,(THIS_FILE, "Syntax error parsing XML in line %d column %d",
+		  scanner.line, scanner.col));
+    }
+    PJ_END;
+    pj_scan_fini( &scanner );
+    return node;
+}
+
+static int xml_print_node( const pj_xml_node *node, int indent, 
+			   char *buf, pj_size_t len )
+{
+    int i;
+    char *p = buf;
+    pj_xml_attr *attr;
+    pj_xml_node *sub_node;
+
+#define SIZE_LEFT()	((int)(len - (p-buf)))
+
+    /* Print name. */
+    if (SIZE_LEFT() < node->name.slen + indent + 5)
+	return -1;
+    for (i=0; i<indent; ++i)
+	*p++ = ' ';
+    *p++ = '<';
+    pj_memcpy(p, node->name.ptr, node->name.slen);
+    p += node->name.slen;
+
+    /* Print attributes. */
+    attr = node->attr_head.next;
+    while (attr != &node->attr_head) {
+
+	if (SIZE_LEFT() < attr->name.slen + attr->value.slen + 4)
+	    return -1;
+
+	*p++ = ' ';
+
+	/* Attribute name. */
+	pj_memcpy(p, attr->name.ptr, attr->name.slen);
+	p += attr->name.slen;
+
+	/* Attribute value. */
+	if (attr->value.slen) {
+	    *p++ = '=';
+	    *p++ = '"';
+	    pj_memcpy(p, attr->value.ptr, attr->value.slen);
+	    p += attr->value.slen;
+	    *p++ = '"';
+	}
+
+	attr = attr->next;
+    }
+
+    /* Check for empty node. */
+    if (node->content.slen==0 &&
+	node->node_head.next==(pj_xml_node*)&node->node_head)
+    {
+	*p++ = ' ';
+	*p++ = '/';
+	*p++ = '>';
+	return p-buf;
+    }
+
+    /* Enclosing '>' */
+    if (SIZE_LEFT() < 1) return -1;
+    *p++ = '>';
+
+    /* Print sub nodes. */
+    sub_node = node->node_head.next;
+    while (sub_node != (pj_xml_node*)&node->node_head) {
+	int printed;
+
+	if (SIZE_LEFT() < indent + 3)
+	    return -1;
+	//*p++ = '\r';
+	*p++ = '\n';
+
+	printed = xml_print_node(sub_node, indent + 1, p, SIZE_LEFT());
+	if (printed < 0)
+	    return -1;
+
+	p += printed;
+	sub_node = sub_node->next;
+    }
+
+    /* Content. */
+    if (node->content.slen) {
+	if (SIZE_LEFT() < node->content.slen) return -1;
+	pj_memcpy(p, node->content.ptr, node->content.slen);
+	p += node->content.slen;
+    }
+
+    /* Enclosing node. */
+    if (node->node_head.next != (pj_xml_node*)&node->node_head) {
+	if (SIZE_LEFT() < node->name.slen + 5 + indent)
+	    return -1;
+	//*p++ = '\r';
+	*p++ = '\n';
+	for (i=0; i<indent; ++i)
+	    *p++ = ' ';
+    } else {
+	if (SIZE_LEFT() < node->name.slen + 3)
+	    return -1;
+    }
+    *p++ = '<';
+    *p++ = '/';
+    pj_memcpy(p, node->name.ptr, node->name.slen);
+    p += node->name.slen;
+    *p++ = '>';
+
+#undef SIZE_LEFT
+
+    return p - buf;
+}
+
+PJ_DEF(int) pj_xml_print(const pj_xml_node *node, char *buf, pj_size_t len,
+			 pj_bool_t include_prolog)
+{
+    int prolog_len = 0;
+    int printed;
+
+    if (!node || !buf || !len)
+	return 0;
+
+    if (include_prolog) {
+	pj_str_t prolog = {"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", 39};
+	if ((int)len < prolog.slen)
+	    return -1;
+	pj_memcpy(buf, prolog.ptr, prolog.slen);
+	prolog_len = prolog.slen;
+    }
+
+    printed = xml_print_node(node, 0, buf+prolog_len, len-prolog_len) + prolog_len;
+    if (printed > 0 && len-printed >= 1) {
+	buf[printed++] = '\n';
+    }
+    return printed;
+}
+
+
+PJ_DEF(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node )
+{
+    pj_list_insert_before(&parent->node_head, node);
+}
+
+PJ_DEF(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr )
+{
+    pj_list_insert_before(&node->attr_head, attr);
+}
+
+PJ_DEF(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name)
+{
+    pj_xml_node *node = parent->node_head.next;
+    while (node != (void*)&parent->node_head) {
+	if (pj_stricmp(&node->name, name) == 0)
+	    return node;
+	node = node->next;
+    }
+    return NULL;
+}
+
+
+PJ_DEF(pj_xml_node*) pj_xml_find_next_node( pj_xml_node *parent, pj_xml_node *node,
+					    const pj_str_t *name)
+{
+    node = node->next;
+    while (node != (void*)&parent->node_head) {
+	if (pj_stricmp(&node->name, name) == 0)
+	    return node;
+	node = node->next;
+    }
+    return NULL;
+}
+
+
+PJ_DEF(pj_xml_attr*) pj_xml_find_attr( pj_xml_node *node, const pj_str_t *name,
+				       const pj_str_t *value)
+{
+    pj_xml_attr *attr = node->attr_head.next;
+    while (attr != (void*)&node->attr_head) {
+	if (pj_stricmp(&attr->name, name)==0) {
+	    if (value) {
+		if (pj_stricmp(&attr->value, value)==0)
+		    return attr;
+	    } else {
+		return attr;
+	    }
+	}
+	attr = attr->next;
+    }
+    return NULL;
+}
+
+
+
+PJ_DEF(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name,
+				  const void *data, 
+				  pj_bool_t (*match)(pj_xml_node *, const void*))
+{
+    pj_xml_node *head = (void*)&parent->node_head, *node = head->next;
+
+    while (node != (void*)head) {
+	if (name && pj_stricmp(&node->name, name)==0) {
+	    if (match) {
+		if (match(node, data))
+		    return node;
+	    } else {
+		return node;
+	    }
+	}
+	node = node->next;
+    }
+    return NULL;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/xml.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/xml.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pj/xml.h	(revision 1753)
@@ -0,0 +1,176 @@
+/* $Header: /cvs/pjproject-0.2.9.3/pjlib/src/pj/xml.h,v 1.1 2005/12/02 20:02:31 nn Exp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJ_XML_H__
+#define __PJ_XML_H__
+
+/**
+ * @file xml.h
+ * @brief PJLIB XML Parser/Helper.
+ */
+
+#include <pj/types.h>
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_XML XML Parser/Helper.
+ * @ingroup PJ_DS
+ * @{
+ */
+
+/** Typedef for XML attribute. */
+typedef struct pj_xml_attr pj_xml_attr;
+
+/** Typedef for XML nodes. */
+typedef struct pj_xml_node pj_xml_node;
+
+/** This structure declares XML attribute. */
+struct pj_xml_attr
+{
+    PJ_DECL_LIST_MEMBER(pj_xml_attr)
+    pj_str_t	name;	    /**< Attribute name. */
+    pj_str_t	value;	    /**< Attribute value. */
+};
+
+/** This structure describes XML node head inside XML node structure.
+ */
+typedef struct pj_xml_node_head
+{
+    PJ_DECL_LIST_MEMBER(pj_xml_node)
+} pj_xml_node_head;
+
+/** This structure describes XML node. */
+struct pj_xml_node
+{
+    PJ_DECL_LIST_MEMBER(pj_xml_node)
+    pj_str_t		name;		/** Node name. */
+    pj_xml_attr		attr_head;      /** Attribute list. */
+    pj_xml_node_head	node_head;      /** Node list. */
+    pj_str_t		content;	/** Node content. */
+};
+
+/**
+ * Parse XML message into XML document with a single root node. The parser
+ * is capable of parsing XML processing instruction construct ("<?") and 
+ * XML comments ("<!--"), however such constructs will be ignored and will not 
+ * be included in the resulted XML node tree.
+ *
+ * @param pool	    Pool to allocate memory from.
+ * @param msg	    The XML message to parse.
+ * @param len	    The length of the message.
+ *
+ * @return	    XML root node, or NULL if the XML document can not be parsed.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len);
+
+
+/**
+ * Print XML into XML message. Note that the function WILL NOT NULL terminate
+ * the output.
+ *
+ * @param node	    The XML node to print.
+ * @param buf	    Buffer to hold the output message.
+ * @param len	    The length of the buffer.
+ * @param prolog    If set to nonzero, will print XML prolog ("<?xml..")
+ *
+ * @return	    The size of the printed message, or -1 if there is not 
+ *		    sufficient space in the buffer to print the message.
+ */
+PJ_DECL(int) pj_xml_print( const pj_xml_node *node, char *buf, pj_size_t len,
+			   pj_bool_t include_prolog);
+
+/**
+ * Add node to another node.
+ *
+ * @param parent    Parent node.
+ * @param node	    Node to be added to parent.
+ */
+PJ_DECL(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node );
+
+
+/**
+ * Add attribute to a node.
+ *
+ * @param node	    Node.
+ * @param attr	    Attribute to add to node.
+ */
+PJ_DECL(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr );
+
+/**
+ * Find first node with the specified name.
+ *
+ * @param parent    Parent node.
+ * @param name	    Node name to find.
+ *
+ * @return	    XML node found or NULL.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name);
+
+/**
+ * Find first node with the specified name.
+ *
+ * @param parent    Parent node.
+ * @param name	    Node name to find.
+ *
+ * @return	    XML node found or NULL.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_find_next_node(pj_xml_node *parent, pj_xml_node *node,
+					    const pj_str_t *name);
+
+/**
+ * Find first attribute within a node with the specified name and optional value.
+ *
+ * @param node	    XML Node.
+ * @param name	    Attribute name to find.
+ * @param value	    Optional value to match.
+ *
+ * @return	    XML attribute found, or NULL.
+ */
+PJ_DECL(pj_xml_attr*) pj_xml_find_attr(pj_xml_node *node, const pj_str_t *name,
+				       const pj_str_t *value);
+
+
+/**
+ * Find a direct child node with the specified name and match the function.
+ *
+ * @param node	    Parent node.
+ * @param name	    Optional name.
+ * @param data	    Data to be passed to matching function.
+ * @param match	    Optional matching function.
+ *
+ * @return	    The first matched node, or NULL.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name,
+				   const void *data, 
+				   pj_bool_t (*match)(pj_xml_node *, const void*));
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJ_XML_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pjlib++.hpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pjlib++.hpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pjlib++.hpp	(revision 1753)
@@ -0,0 +1,38 @@
+/* $Header: /pjproject/pjlib/src/pjlib++.hpp 1     7/05/05 12:58a Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJLIBPP_H__
+#define __PJLIBPP_H__
+
+#include <pj++/pool.hpp>
+#include <pj++/hash.hpp>
+//#include <pj++/ioqueue.hpp>
+#include <pj++/list.hpp>
+#include <pj++/os.hpp>
+#include <pj++/proactor.hpp>
+#include <pj++/scanner.hpp>
+#include <pj++/sock.hpp>
+#include <pj++/string.hpp>
+#include <pj++/timer.hpp>
+#include <pj++/tree.hpp>
+
+#endif	/* __PJLIBPP_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/pjlib.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/pjlib.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/pjlib.h	(revision 1753)
@@ -0,0 +1,45 @@
+/* $Header: /pjproject/pjlib/src/pjlib.h 2     6/04/05 4:29p Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJLIB_H__
+#define __PJLIB_H__
+
+#include <pj/types.h>
+#include <pj/array.h>
+#include <pj/except.h>
+#include <pj/fifobuf.h>
+#include <pj/guid.h>
+#include <pj/hash.h>
+#include <pj/ioqueue.h>
+#include <pj/list.h>
+#include <pj/log.h>
+#include <pj/md5.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/rbtree.h>
+#include <pj/scanner.h>
+#include <pj/sock.h>
+#include <pj/string.h>
+#include <pj/timer.h>
+#include <pj/xml.h>
+
+#endif
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/exception_test.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/exception_test.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/exception_test.cpp	(revision 1753)
@@ -0,0 +1,109 @@
+/* $Header: /pjproject/pjlib/src/test/exception_test.cpp 2     2/24/05 10:34a Bennylp $
+ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/except.h>
+
+#define	ID_1	1
+#define ID_2	2
+
+static int throw_id_1()
+{
+    PJ_THROW( ID_1 );
+    return -1;
+}
+
+static int throw_id_2()
+{
+    PJ_THROW( ID_2 );
+    return -1;
+}
+
+
+static int test()
+{
+    PJ_USE_EXCEPTION;
+    int rc = 0;
+
+    // Must be able to catch exception that's thrown
+    PJ_TRY {
+	rc = throw_id_1();
+    }
+    PJ_CATCH( ID_1 ) {
+    	rc = 0;
+    }
+    PJ_DEFAULT {
+	rc = -1;
+    }
+    PJ_END;
+
+    if (rc != 0)
+	return -2;
+
+    // Must be able to handle multiple exceptions.
+    PJ_TRY {
+	rc = throw_id_2();
+    }
+    PJ_CATCH( ID_1 ) {
+	rc = -3;
+    }
+    PJ_CATCH( ID_2 ) {
+	rc = 0;
+    }
+    PJ_DEFAULT {
+	rc = -4;
+    }
+    PJ_END;
+
+    if (rc != 0)
+	return -5;
+
+    // Test default handler.
+    PJ_TRY {
+	rc = throw_id_1();
+    }
+    PJ_CATCH( ID_2 ) {
+	rc = -6;
+    }
+    PJ_DEFAULT {
+	rc = 0;
+    }
+    PJ_END;
+
+    if (rc != 0)
+	return -7;
+
+    return 0;
+}
+
+int exception_test()
+{
+    int i, rc;
+    enum { LOOP = 10 };
+
+    for (i=0; i<LOOP; ++i) {
+	if ((rc=test()) != 0)
+	    return rc;
+    }
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/fifobuf_test.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/fifobuf_test.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/fifobuf_test.cpp	(revision 1753)
@@ -0,0 +1,110 @@
+/* $Header: /pjproject/pjlib/src/test/fifobuf_test.cpp 4     5/12/05 9:53p Bennylp $
+ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/fifobuf.h>
+#include <pj/pool.h>
+#include "libpj_test.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+int fifobuf_test()
+{
+    enum { SIZE = 1024, MAX_ENTRIES = 128, MIN_SIZE = 4, MAX_SIZE = 64, LOOP=10000 };
+    pj_pool_t *pool;
+    pj_fifobuf_t fifo;
+    unsigned available = SIZE;
+    void *entries[MAX_ENTRIES];
+    void *buffer;
+    int i;
+
+    pool = (*mem->create_pool)(mem, NULL, SIZE+256, 0, NULL);
+    if (!pool)
+	return -1;
+
+    buffer = pj_pool_alloc(pool, SIZE);
+    if (!buffer)
+	return -1;
+
+    pj_fifobuf_init (&fifo, buffer, SIZE);
+    
+    // Test 1
+    for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
+	int size;
+	int c, f;
+	c = i%2;
+	f = (i+1)%2;
+	do {
+	    size = MIN_SIZE+(rand() % MAX_SIZE);
+	    entries[c] = pj_fifobuf_alloc (&fifo, size);
+	} while (entries[c] == 0);
+	if ( i!=0) {
+	    pj_fifobuf_free(&fifo, entries[f]);
+	}
+    }
+    if (entries[(i+1)%2])
+	pj_fifobuf_free(&fifo, entries[(i+1)%2]);
+
+    if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+	pj_assert(0);
+	return -1;
+    }
+
+    // Test 2
+    entries[0] = pj_fifobuf_alloc (&fifo, MIN_SIZE);
+    if (!entries[0]) return -1;
+    for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
+	int size = MIN_SIZE+(rand() % MAX_SIZE);
+	entries[1] = pj_fifobuf_alloc (&fifo, size);
+	if (entries[1])
+	    pj_fifobuf_unalloc(&fifo, entries[1]);
+    }
+    pj_fifobuf_unalloc(&fifo, entries[0]);
+    if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+	pj_assert(0);
+	return -2;
+    }
+
+    // Test 3
+    for (i=0; i<LOOP; ++i) {
+	int count, j;
+	for (count=0; available>=MIN_SIZE+4 && count < MAX_ENTRIES;) {
+	    int size = MIN_SIZE+(rand() % MAX_SIZE);
+	    entries[count] = pj_fifobuf_alloc (&fifo, size);
+	    if (entries[count]) {
+		available -= (size+4);
+		++count;
+	    }
+	}
+	for (j=0; j<count; ++j) {
+	    pj_fifobuf_free (&fifo, entries[j]);
+	}
+	available = SIZE;
+    }
+
+    if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+	pj_assert(0);
+	return -3;
+    }
+    pj_pool_release(pool);
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/ioqueue_test.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/ioqueue_test.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/ioqueue_test.cpp	(revision 1753)
@@ -0,0 +1,27 @@
+/* $Header: /pjproject/pjlib/src/test/ioqueue_test.cpp 2     2/24/05 10:34a Bennylp $
+ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/*
+ * This file should contain common utilities for ioqueue_tests.
+ */
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/ioqueue_test_tcp.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/ioqueue_test_tcp.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/ioqueue_test_tcp.cpp	(revision 1753)
@@ -0,0 +1,405 @@
+/* $Header: /pjproject/pjlib/src/test/ioqueue_test_tcp.cpp 6     5/24/05 12:17a Bennylp $
+ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/ioqueue.h>
+#include <pj/sock.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include "libpj_test.h"
+#include <stdio.h>
+
+#if PJ_HAS_TCP
+
+#define PORT		    50000
+#define NON_EXISTANT_PORT   50000
+#define LOOP		    100
+#define BUF_MIN_SIZE	    32
+//#define BUF_MAX_SIZE	    2048
+#define BUF_MAX_SIZE	    BUF_MIN_SIZE
+#define SOCK_INACTIVE_MIN   (4-2)
+//#define SOCK_INACTIVE_MAX   (PJ_IOQUEUE_MAX_HANDLES - 2)
+#define SOCK_INACTIVE_MAX   SOCK_INACTIVE_MIN
+#define POOL_SIZE	    (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
+
+static pj_ioqueue_key_t *polled_key;
+static pj_ssize_t	 polled_size_status;
+static pj_ioqueue_operation_e polled_op;
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read)
+{
+    polled_key = key;
+    polled_size_status = bytes_read;
+    polled_op = PJ_IOQUEUE_OP_READ;
+}
+
+static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_written)
+{
+    polled_key = key;
+    polled_size_status = bytes_written;
+    polled_op = PJ_IOQUEUE_OP_WRITE;
+}
+
+static void on_ioqueue_accept(pj_ioqueue_key_t *key, int status)
+{
+    polled_key = key;
+    polled_size_status = status;
+    polled_op = PJ_IOQUEUE_OP_ACCEPT;
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+    polled_key = key;
+    polled_size_status = status;
+    polled_op = PJ_IOQUEUE_OP_CONNECT;
+}
+
+static pj_ioqueue_callback test_cb = 
+{
+    &on_ioqueue_read,
+    &on_ioqueue_write,
+    &on_ioqueue_accept,
+    &on_ioqueue_connect,
+};
+
+static int send_recv_test(pj_ioqueue_t *ioque,
+			  pj_ioqueue_key_t *skey,
+			  pj_ioqueue_key_t *ckey,
+			  void *send_buf,
+			  void *recv_buf,
+			  pj_ssize_t bufsize,
+			  pj_hr_timestamp *t_elapsed)
+{
+    int rc;
+    pj_ssize_t bytes;
+    pj_str_t temp;
+    pj_hr_timestamp t1, t2;
+    int pending_op = 0;
+
+    // Start reading on the server side.
+    rc = pj_ioqueue_read(ioque, skey, recv_buf, bufsize);
+    if (rc != 0 && rc != PJ_IOQUEUE_PENDING) {
+	return -100;
+    }
+    
+    ++pending_op;
+
+    // Randomize send buffer.
+    temp.ptr = (char*)send_buf;
+    pj_create_random_string(&temp, bufsize);
+
+    // Starts send on the client side.
+    bytes = pj_ioqueue_write(ioque, ckey, send_buf, bufsize);
+    if (bytes != bufsize && bytes != PJ_IOQUEUE_PENDING) {
+	return -120;
+    }
+    if (bytes == PJ_IOQUEUE_PENDING) {
+	++pending_op;
+    }
+
+    // Begin time.
+    pj_hr_gettimestamp(&t1);
+
+    // Poll the queue until we've got completion event in the server side.
+    rc = 0;
+    while (pending_op > 0) {
+	rc = pj_ioqueue_poll(ioque, NULL);
+	if (rc == 1) {
+	    if (polled_size_status != bufsize) {
+		return -160;
+	    }
+	    --pending_op;
+	}
+	if (rc < 0) {
+	    return -170;
+	}
+    }
+
+    // End time.
+    pj_hr_gettimestamp(&t2);
+    t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo);
+
+    if (rc < 0) {
+	return -150;
+    }
+
+    // Compare recv buffer with send buffer.
+    if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
+	return -180;
+    }
+
+    // Success
+    return 0;
+}
+
+
+/*
+ * Compliance test for success scenario.
+ */
+static int compliance_test_0()
+{
+    pj_sock_t ssock=-1, csock0=-1, csock1=-1;
+    pj_sockaddr_in addr, client_addr, rmt_addr;
+    int client_addr_len;
+    pj_pool_t *pool = NULL;
+    char *send_buf, *recv_buf;
+    pj_ioqueue_t *ioque = NULL;
+    pj_ioqueue_key_t *skey, *ckey0, *ckey1;
+    int bufsize = BUF_MIN_SIZE;
+    pj_ssize_t status = -1;
+    int pending_op = 0;
+    pj_hr_timestamp t_elapsed;
+
+    // Create pool.
+    pool = (*mem->create_pool)(mem, NULL, POOL_SIZE, 0, NULL);
+
+    // Allocate buffers for send and receive.
+    send_buf = (char*)pj_pool_alloc(pool, bufsize);
+    recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+    // Create server socket and client socket for connecting
+    ssock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, PJ_SOCK_ASYNC);
+    csock1 = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, PJ_SOCK_ASYNC);
+    if (ssock==PJ_INVALID_SOCKET || csock1==PJ_INVALID_SOCKET) {
+	status=-1; goto on_error;
+    }
+
+    // Bind server socket.
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(PORT);
+    if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
+	status=-10; goto on_error;
+    }
+
+    // Create I/O Queue.
+    ioque = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES);
+    if (!ioque) {
+	status=-20; goto on_error;
+    }
+
+    // Register server socket and client socket.
+    skey = pj_ioqueue_register(pool, ioque, (pj_oshandle_t)ssock, NULL, &test_cb);
+    ckey1 = pj_ioqueue_register(pool, ioque, (pj_oshandle_t)csock1, NULL, &test_cb);
+    if (skey==NULL || ckey1==NULL) {
+	status=-23; goto on_error;
+    }
+
+    // Server socket listen().
+    if (pj_sock_listen(ssock, 5)) {
+	status=-25; goto on_error;
+    }
+
+    // Server socket accept()
+    client_addr_len = sizeof(pj_sockaddr_in);
+    status = pj_ioqueue_accept(ioque, skey, &csock0, &client_addr, &rmt_addr, &client_addr_len);
+    if (status != PJ_IOQUEUE_PENDING) {
+	status=-30; goto on_error;
+    }
+    if (status==PJ_IOQUEUE_PENDING) {
+	++pending_op;
+    }
+
+    // Initialize remote address.
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(PORT);
+    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+    // Client socket connect()
+    status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr));
+    if (status!=PJ_OK && status != PJ_IOQUEUE_PENDING) {
+	status=-40; goto on_error;
+    }
+    if (status==PJ_IOQUEUE_PENDING) {
+	++pending_op;
+    }
+
+    // Poll until connected
+    while (pending_op) {
+	pj_time_val timeout = {1, 0};
+
+	status=pj_ioqueue_poll(ioque, &timeout);
+	if (status==1) {
+	    if (polled_op == PJ_IOQUEUE_OP_ACCEPT) {
+		pj_assert(polled_key == skey);
+	    } else if (polled_op == PJ_IOQUEUE_OP_CONNECT) {
+		pj_assert(polled_key == ckey1);
+		if (polled_size_status != 0) {
+		    status = -50;
+		    goto on_error;
+		}
+	    }
+	    --pending_op;
+	    if (pending_op == 0) {
+		status = 0;
+	    }
+	}
+    }
+
+    // Check accepted socket.
+    if (csock0 == PJ_INVALID_SOCKET) {
+	status = -69;
+	PJ_LOG(2,("tcptest", "accept() error (err %d)", pj_getlasterror()));
+	goto on_error;
+    }
+
+    // Register newly accepted socket.
+    ckey0 = pj_ioqueue_register(pool, ioque, (pj_oshandle_t)csock0, NULL, &test_cb);
+    if (ckey0 == NULL) {
+	status = -70;
+	goto on_error;
+    }
+
+    // Test send and receive.
+    t_elapsed.u32.lo = 0;
+    status = send_recv_test(ioque, ckey0, ckey1, send_buf, recv_buf, bufsize, &t_elapsed);
+    if (status != 0) {
+	goto on_error;
+    }
+
+    // Success
+    status = 0;
+
+on_error:
+    if (ssock != PJ_INVALID_SOCKET)
+	pj_sock_close(ssock);
+    if (csock1 != PJ_INVALID_SOCKET)
+	pj_sock_close(csock1);
+    if (csock0 != PJ_INVALID_SOCKET)
+	pj_sock_close(csock0);
+    if (ioque != NULL)
+	pj_ioqueue_destroy(ioque);
+    pj_pool_release(pool);
+    return status;
+
+}
+
+/*
+ * Compliance test for failed scenario.
+ * In this case, the client connects to a non-existant service.
+ */
+static int compliance_test_1()
+{
+    pj_sock_t csock1=-1;
+    pj_sockaddr_in addr;
+    pj_pool_t *pool = NULL;
+    pj_ioqueue_t *ioque = NULL;
+    pj_ioqueue_key_t *ckey1;
+    pj_ssize_t status = -1;
+    int pending_op = 0;
+
+    // Create pool.
+    pool = (*mem->create_pool)(mem, NULL, POOL_SIZE, 0, NULL);
+
+    // Create I/O Queue.
+    ioque = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES);
+    if (!ioque) {
+	status=-20; goto on_error;
+    }
+
+    // Create client socket
+    csock1 = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, PJ_SOCK_ASYNC);
+    if (csock1==PJ_INVALID_SOCKET) {
+	status=-1; goto on_error;
+    }
+
+    // Register client socket.
+    ckey1 = pj_ioqueue_register(pool, ioque, (pj_oshandle_t)csock1, NULL, &test_cb);
+    if (ckey1==NULL) {
+	status=-23; goto on_error;
+    }
+
+    // Initialize remote address.
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(NON_EXISTANT_PORT);
+    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+    // Client socket connect()
+    status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr));
+    if (status==PJ_OK) {
+	// unexpectedly success!
+	status = -30;
+	goto on_error;
+    }
+    if (status != PJ_IOQUEUE_PENDING) {
+	// success
+    } else {
+	++pending_op;
+    }
+
+    // Poll until we've got result
+    while (pending_op) {
+	pj_time_val timeout = {1, 0};
+
+	status=pj_ioqueue_poll(ioque, &timeout);
+	if (status==1) {
+	    if (polled_op == PJ_IOQUEUE_OP_CONNECT) {
+		pj_assert(polled_key == ckey1);
+		if (polled_size_status == 0) {
+		    // unexpectedly connected!
+		    status = -50;
+		    goto on_error;
+		}
+	    }
+	    --pending_op;
+	    if (pending_op == 0) {
+		status = 0;
+	    }
+	}
+    }
+
+    // Success
+    status = 0;
+
+on_error:
+    if (csock1 != PJ_INVALID_SOCKET)
+	pj_sock_close(csock1);
+    if (ioque != NULL)
+	pj_ioqueue_destroy(ioque);
+    pj_pool_release(pool);
+    return status;
+}
+
+int tcp_ioqueue_test()
+{
+    int status;
+
+    puts("..compliance test 0 (success scenario)");
+    if ((status=compliance_test_0()) != 0) {
+	printf("....FAILED (status=%d)\n", status);
+	return status;
+    }
+    puts("..compliance test 1 (failed scenario)");
+    if ((status=compliance_test_1()) != 0) {
+	printf("....FAILED (status=%d)\n", status);
+	return status;
+    }
+
+    return 0;
+}
+
+#endif	/* PJ_HAS_TCP */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/ioqueue_test_udp.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/ioqueue_test_udp.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/ioqueue_test_udp.cpp	(revision 1753)
@@ -0,0 +1,401 @@
+/* $Header: /pjproject/pjlib/src/test/ioqueue_test_udp.cpp 7     5/28/05 11:07a Bennylp $
+ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/ioqueue.h>
+#include <pj/sock.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include "libpj_test.h"
+#include <stdio.h>
+
+
+#define PORT		    50000
+#define LOOP		    100
+#define BUF_MIN_SIZE	    32
+#define BUF_MAX_SIZE	    2048
+//#define BUF_MAX_SIZE	    BUF_MIN_SIZE
+#define SOCK_INACTIVE_MIN   (1)
+#define SOCK_INACTIVE_MAX   (PJ_IOQUEUE_MAX_HANDLES - 2)
+//#define SOCK_INACTIVE_MAX   SOCK_INACTIVE_MIN
+#define POOL_SIZE	    (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
+
+static pj_ioqueue_key_t *polled_key;
+static pj_ssize_t	 polled_size_status;
+static pj_ioqueue_operation_e polled_op;
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read)
+{
+    polled_key = key;
+    polled_size_status = bytes_read;
+    polled_op = PJ_IOQUEUE_OP_READ;
+}
+
+static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_read)
+{
+    polled_key = key;
+    polled_size_status = bytes_read;
+    polled_op = PJ_IOQUEUE_OP_WRITE;
+}
+
+static void on_ioqueue_accept(pj_ioqueue_key_t *key, int status)
+{
+#if PJ_HAS_TCP
+    polled_key = key;
+    polled_size_status = status;
+    polled_op = PJ_IOQUEUE_OP_ACCEPT;
+#else
+    PJ_UNUSED_ARG(key)
+    PJ_UNUSED_ARG(status)
+#endif
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+#if PJ_HAS_TCP
+    polled_key = key;
+    polled_size_status = status;
+    polled_op = PJ_IOQUEUE_OP_CONNECT;
+#else
+    PJ_UNUSED_ARG(key)
+    PJ_UNUSED_ARG(status)
+#endif
+}
+
+static pj_ioqueue_callback test_cb = 
+{
+    &on_ioqueue_read,
+    &on_ioqueue_write,
+    &on_ioqueue_accept,
+    &on_ioqueue_connect,
+};
+
+static int compliance_test()
+{
+    pj_sock_t ssock=-1, csock=-1;
+    pj_sockaddr_in addr;
+    int addrlen;
+    pj_pool_t *pool = NULL;
+    char *send_buf, *recv_buf;
+    pj_ioqueue_t *ioque = NULL;
+    pj_ioqueue_key_t *skey, *ckey;
+    int bufsize = BUF_MIN_SIZE;
+    pj_ssize_t bytes, status = -1;
+    pj_str_t temp;
+
+    // Create pool.
+    pool = (*mem->create_pool)(mem, NULL, POOL_SIZE, 0, NULL);
+
+    // Allocate buffers for send and receive.
+    send_buf = (char*)pj_pool_alloc(pool, bufsize);
+    recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+    // Allocate sockets for sending and receiving.
+    ssock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, PJ_SOCK_ASYNC);
+    csock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, PJ_SOCK_ASYNC);
+    if (ssock==PJ_INVALID_SOCKET || csock==PJ_INVALID_SOCKET) {
+	status=-1; goto on_error;
+    }
+
+    // Bind server socket.
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(PORT);
+    if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
+	status=-10; goto on_error;
+    }
+
+    // Create I/O Queue.
+    ioque = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES);
+    if (!ioque) {
+	status=-20; goto on_error;
+    }
+
+    // Register server and client socket.
+    // We put this after inactivity socket, hopefully this can represent the
+    // worst waiting time.
+    skey = pj_ioqueue_register(pool, ioque, (pj_oshandle_t)ssock, NULL, &test_cb);
+    ckey = pj_ioqueue_register(pool, ioque, (pj_oshandle_t)csock, NULL, &test_cb);
+
+    // Set destination address to send the packet.
+    temp.ptr = "localhost";
+    temp.slen = strlen(temp.ptr);
+    pj_sockaddr_init(&addr, &temp, PORT);
+
+    // Randomize send_buf.
+    temp.ptr = send_buf;
+    pj_create_random_string(&temp, bufsize);
+
+    // Write must return the number of bytes.
+    bytes = pj_ioqueue_sendto(ioque, ckey, send_buf, bufsize, &addr, sizeof(addr));
+    if (bytes != bufsize && bytes != PJ_IOQUEUE_PENDING) {
+	status=-30; goto on_error;
+    } else if (bytes == PJ_IOQUEUE_PENDING) {
+	puts(".....pj_ioqueue_sendto for UDP returned pending");
+    } else {
+	puts(".....pj_ioqueue_sendto for UDP returned immediate success");
+    }
+
+    // Read from ioqueue.
+    addrlen = sizeof(addr);
+    bytes = pj_ioqueue_recvfrom(ioque, skey, recv_buf, bufsize, &addr, &addrlen);
+    if (bytes < 0 && bytes != PJ_IOQUEUE_PENDING) {
+	status=-40; goto on_error;
+    } else if (bytes == PJ_IOQUEUE_PENDING) {
+	puts(".....pj_ioqueue_recvfrom for UDP returned pending");
+    } else {
+	puts(".....pj_ioqueue_recvfrom for UDP returned immediate success");
+    }
+
+    // Poll if pending.
+    if (bytes == PJ_IOQUEUE_PENDING) {
+	int rc;
+
+	do {
+	    rc = pj_ioqueue_poll(ioque, NULL);
+	} while (rc==1 && polled_key != skey);
+
+	if (rc != 1) {
+	    status=-50; goto on_error;
+	}
+	if (polled_size_status != bufsize) {
+	    status=-60; goto on_error;
+	}
+	if (polled_op != PJ_IOQUEUE_OP_READ) {
+	    status=-65; goto on_error;
+	}
+	if (memcmp(send_buf, recv_buf, bufsize) != 0) {
+	    printf("...ERROR: sendbuf='%s', recvbuf='%s'\n", send_buf, recv_buf);
+	    status=-70; goto on_error;
+	}
+    } else if (bytes == bufsize) {
+	if (memcmp(send_buf, recv_buf, bufsize) != 0) {
+	    status=-80; goto on_error;
+	}
+    } else {
+	status=-90; goto on_error;
+    }
+
+    // Success
+    status = 0;
+
+on_error:
+    if (ssock)
+	pj_sock_close(ssock);
+    if (csock)
+	pj_sock_close(csock);
+    if (ioque != NULL)
+	pj_ioqueue_destroy(ioque);
+    pj_pool_release(pool);
+    return status;
+
+}
+
+static int bench_test(int bufsize, int inactive_sock_count)
+{
+    pj_sock_t ssock=-1, csock=-1;
+    pj_sockaddr_in addr;
+    pj_pool_t *pool = NULL;
+    pj_sock_t *inactive_sock=NULL;
+    char *send_buf, *recv_buf;
+    pj_ioqueue_t *ioque = NULL;
+    pj_ioqueue_key_t *skey, *ckey, *key;
+    pj_hr_timestamp t1, t2, t_elapsed;
+    int rc=0, i;
+    pj_str_t temp;
+
+    // Create pool.
+    pool = (*mem->create_pool)(mem, NULL, POOL_SIZE, 0, NULL);
+
+    // Allocate buffers for send and receive.
+    send_buf = (char*)pj_pool_alloc(pool, bufsize);
+    recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+    // Allocate sockets for sending and receiving.
+    ssock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, PJ_SOCK_ASYNC);
+    csock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, PJ_SOCK_ASYNC);
+    if (ssock==PJ_INVALID_SOCKET || csock==PJ_INVALID_SOCKET)
+	goto on_error;
+
+    // Bind server socket.
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(PORT);
+    if (pj_sock_bind(ssock, &addr, sizeof(addr)))
+	goto on_error;
+
+    // Create I/O Queue.
+    ioque = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES);
+    if (!ioque)
+	goto on_error;
+
+    // Allocate inactive sockets, and bind them to some arbitrary address.
+    // Then register them to the I/O queue, and start a read operation.
+    inactive_sock = (pj_sock_t*)pj_pool_alloc(pool, inactive_sock_count*sizeof(pj_sock_t));
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    for (i=0; i<inactive_sock_count; ++i) {
+	inactive_sock[i] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, PJ_SOCK_ASYNC);
+	if (inactive_sock[i] == PJ_INVALID_SOCKET)
+	    goto on_error;
+	if (pj_sock_bind(inactive_sock[i], &addr, sizeof(addr))) {
+	    pj_sock_close(inactive_sock[i]);
+	    inactive_sock[i] = PJ_INVALID_SOCKET;
+	    goto on_error;
+	}
+	key = pj_ioqueue_register(pool, ioque, (pj_oshandle_t)inactive_sock[i], NULL, &test_cb);
+	if (key==NULL) {
+	    pj_sock_close(inactive_sock[i]);
+	    inactive_sock[i] = PJ_INVALID_SOCKET;
+	    goto on_error;
+	}
+	rc = pj_ioqueue_read(ioque, key, recv_buf, bufsize);
+	if ( rc < 0 && rc != PJ_IOQUEUE_PENDING) {
+	    pj_sock_close(inactive_sock[i]);
+	    inactive_sock[i] = PJ_INVALID_SOCKET;
+	    goto on_error;
+	}
+    }
+
+    // Register server and client socket.
+    // We put this after inactivity socket, hopefully this can represent the
+    // worst waiting time.
+    skey = pj_ioqueue_register(pool, ioque, (pj_oshandle_t)ssock, NULL, &test_cb);
+    ckey = pj_ioqueue_register(pool, ioque, (pj_oshandle_t)csock, NULL, &test_cb);
+
+    // Set destination address to send the packet.
+    temp.ptr = "localhost";
+    temp.slen = strlen(temp.ptr);
+    pj_sockaddr_init(&addr, &temp, PORT);
+
+    // Test loop.
+    t_elapsed.u32.lo = 0;
+    for (i=0; i<LOOP; ++i) {
+	pj_ssize_t bytes;
+
+	// Start reading on the server side.
+	rc = pj_ioqueue_read(ioque, skey, recv_buf, bufsize);
+	if (rc < 0 && rc != PJ_IOQUEUE_PENDING)
+	    break;
+
+	// Randomize send buffer.
+	temp.ptr = send_buf;
+	pj_create_random_string(&temp, bufsize);
+
+	// Starts send on the client side.
+	bytes = pj_ioqueue_sendto(ioque, ckey, send_buf, bufsize, 
+					&addr, sizeof(addr));
+	if (bytes != bufsize && bytes != PJ_IOQUEUE_PENDING) {
+	    rc = -1;
+	    break;
+	}
+
+	// Begin time.
+	pj_hr_gettimestamp(&t1);
+
+	// Poll the queue until we've got completion event in the server side.
+	do {
+	    rc = pj_ioqueue_poll(ioque, NULL);
+	} while (rc >= 0 && polled_key != skey);
+
+	// End time.
+	pj_hr_gettimestamp(&t2);
+	t_elapsed.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+	if (rc < 0)
+	    break;
+
+	// Compare recv buffer with send buffer.
+	if (polled_size_status != bufsize || memcmp(send_buf, recv_buf, bufsize)) {
+	    rc = -1;
+	    break;
+	}
+
+	// Poll until all events are exhausted, before we start the next loop.
+	do {
+	    pj_time_val timeout = { 0, 100 };
+	    rc = pj_ioqueue_poll(ioque, &timeout);
+	} while (rc==1);
+
+	rc = 0;
+    }
+
+    // Print results
+    if (rc == 0) {
+	printf("...ok (buf:%5d, fds:%5d, elapsed:%7u ticks)\n", 
+	       bufsize, inactive_sock_count+2,
+	       t_elapsed.u32.lo / LOOP);
+    } else {
+	printf("...ERROR (buf:%d, fds:%d)\n", bufsize, inactive_sock_count+2);
+    }
+
+    // Cleaning up.
+    for (i=0; i<inactive_sock_count; ++i)
+	pj_sock_close(inactive_sock[i]);
+    pj_sock_close(ssock);
+    pj_sock_close(csock);
+
+    pj_ioqueue_destroy(ioque);
+    pj_pool_release( pool);
+    return 0;
+
+on_error:
+    PJ_PERROR(("ioqueue_test", "error"));
+    if (ssock)
+	pj_sock_close(ssock);
+    if (csock)
+	pj_sock_close(csock);
+    for (i=0; i<inactive_sock_count && inactive_sock && inactive_sock[i]!=PJ_INVALID_SOCKET; ++i) {
+	pj_sock_close(inactive_sock[i]);
+    }
+    if (ioque != NULL)
+	pj_ioqueue_destroy(ioque);
+    pj_pool_release( pool);
+    return -1;
+}
+
+int udp_ioqueue_test()
+{
+    int status;
+    int bufsize, sock_count;
+
+    puts("...compliance test");
+    if ((status=compliance_test()) != 0)
+	return status;
+
+    puts("...benchmarking different buffer size:");
+    puts("... note: buf=bytes sent, fds=# of fds, elapsed=in timer ticks");
+    for (bufsize=BUF_MIN_SIZE; bufsize <= BUF_MAX_SIZE; bufsize *= 2) {
+	if (bench_test(bufsize, SOCK_INACTIVE_MIN))
+	    return -1;
+    }
+
+    bufsize = 512;
+    puts("...benchmarking multiple handles:");
+    for (sock_count=SOCK_INACTIVE_MIN+2; sock_count<=SOCK_INACTIVE_MAX+2; sock_count *= 4) {
+	if (bench_test(bufsize, sock_count-2))
+	    return -1;
+    }
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/libpj_test.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/libpj_test.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/libpj_test.h	(revision 1753)
@@ -0,0 +1,43 @@
+/* $Header: /pjproject/pjlib/src/test/libpj_test.h 5     6/04/05 4:30p Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJLIB_TEST_H__
+#define __PJLIB_TEST_H__
+
+#include <pj/types.h>
+
+int pool_test();
+int exception_test();
+int list_test();
+int timer_test();
+int rbtree_test();
+int os_test();
+int udp_ioqueue_test();
+int tcp_ioqueue_test();
+int fifobuf_test();
+int xml_test();
+
+extern pj_pool_factory *mem;
+
+
+#endif	/* __PJLIB_TEST_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/list_test.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/list_test.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/list_test.cpp	(revision 1753)
@@ -0,0 +1,193 @@
+/* $Header: /pjproject/pjlib/src/test/list_test.cpp 2     2/24/05 10:34a Bennylp $
+ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/types.h>
+#include <pj/list.h>
+
+typedef struct list_node
+{
+    PJ_DECL_LIST_MEMBER(struct list_node)
+    int value;
+} list_node;
+
+static int compare_node(void *value, const pj_list_type *nd)
+{
+    list_node *node = (list_node*)nd;
+    return ((int)value == node->value) ? 0 : -1;
+}
+
+#define PJ_SIGNED_ARRAY_SIZE(a)	((int)PJ_ARRAY_SIZE(a))
+
+int list_test()
+{
+    list_node nodes[4];    // must be even number of nodes
+    list_node list;
+    list_node list2;
+    list_node *p;
+    int i; // don't change to unsigned!
+
+    //
+    // Test insert_before().
+    //
+    list.value = (unsigned)-1;
+    pj_list_init(&list);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	nodes[i].value = i;
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    // check.
+    for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+
+    //
+    // Test insert_after()
+    //
+    pj_list_init(&list);
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
+	pj_list_insert_after(&list, &nodes[i]);
+    }
+    // check.
+    for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+
+    //
+    // Test merge_last()
+    //
+    // Init lists
+    pj_list_init(&list);
+    pj_list_init(&list2);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	pj_list_insert_before(&list2, &nodes[i]);
+    }
+    // merge
+    pj_list_merge_last(&list, &list2);
+    // check.
+    for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+    // check list is empty
+    pj_assert( pj_list_empty(&list2) );
+    if (!pj_list_empty(&list2)) {
+	return -1;
+    }
+
+    // 
+    // Check merge_first()
+    //
+    pj_list_init(&list);
+    pj_list_init(&list2);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	pj_list_insert_before(&list2, &nodes[i]);
+    }
+    // merge
+    pj_list_merge_first(&list2, &list);
+    // check (list2).
+    for (i=0, p=list2.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+    // check list is empty
+    pj_assert( pj_list_empty(&list) );
+    if (!pj_list_empty(&list)) {
+	return -1;
+    }
+
+    //
+    // Test insert_nodes_before()
+    //
+    // init list
+    pj_list_init(&list);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    // chain remaining nodes
+    pj_list_init(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2+1; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	pj_list_insert_before(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2], &nodes[i]);
+    }
+    // insert nodes
+    pj_list_insert_nodes_before(&list, &nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
+    // check
+    for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+
+    // erase test.
+    pj_list_init(&list);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	nodes[i].value = i;
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
+	pj_list_erase(&nodes[i]);
+	int j;
+	for (j=0, p=list.next; j<i; ++j, p=p->next) {
+	    pj_assert(p->value == j);
+	    if (p->value != j) {
+		return -1;
+	    }
+	}
+    }
+
+    // find and search
+    pj_list_init(&list);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	nodes[i].value = i;
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	p = (list_node*) pj_list_find_node(&list, &nodes[i]);
+	pj_assert( p == &nodes[i] );
+	if (p != &nodes[i]) {
+	    return -1;
+	}
+	p = (list_node*) pj_list_search(&list, (void*)i, &compare_node);
+	pj_assert( p == &nodes[i] );
+	if (p != &nodes[i]) {
+	    return -1;
+	}
+    }
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/main.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/main.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/main.cpp	(revision 1753)
@@ -0,0 +1,125 @@
+/* $Header: /pjproject/pjlib/src/test/main.cpp 7     6/04/05 4:30p Bennylp $
+ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include "libpj_test.h"
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/compat.h>
+#include <stdio.h>
+#ifdef _MSC_VER
+#  pragma warning(disable:4127)
+#endif
+
+#define DO_TEST(test)	do { \
+			    printf("Running %s...\n", #test);  \
+			    fflush(stdout); \
+			    int rc = test; \
+			    printf( "%s(%d)\n", (rc ? "..ERROR" : "..success"), rc); \
+			} while (0)
+
+
+#define INCLUDE_POOL_TEST	    1
+#define INCLUDE_EXCEPTION_TEST	    1
+#define INCLUDE_OS_TEST		    1
+#define INCLUDE_TIMER_TEST	    1
+#define INCLUDE_RBTREE_TEST	    1
+#define INCLUDE_UDP_IOQUEUE_TEST    1
+#define INCLUDE_TCP_IOQUEUE_TEST    1
+#define INCLUDE_LIST_TEST	    1
+#define INCLUDE_FIFOBUF_TEST	    1
+#define INCLUDE_XML_TEST	    1
+
+pj_pool_factory *mem;
+
+int main()
+{
+    pj_caching_pool caching_pool;
+
+    mem = &caching_pool.factory;
+
+    pj_log_set_level(4);
+    pj_dump_config();
+    pj_init();
+    pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
+
+#if INCLUDE_FIFOBUF_TEST
+    DO_TEST( fifobuf_test() );
+    fflush(stdout);
+#endif
+
+#if INCLUDE_POOL_TEST
+    DO_TEST( pool_test() );
+    fflush(stdout);
+#endif
+
+#if INCLUDE_EXCEPTION_TEST
+    DO_TEST( exception_test() );
+    fflush(stdout);
+#endif
+
+#if INCLUDE_OS_TEST
+    DO_TEST( os_test() );
+    fflush(stdout);
+#endif
+
+#if INCLUDE_TIMER_TEST
+    DO_TEST( timer_test() );
+    fflush(stdout);
+#endif
+
+#if INCLUDE_RBTREE_TEST
+    DO_TEST( rbtree_test() );
+    fflush(stdout);
+#endif
+
+#if INCLUDE_UDP_IOQUEUE_TEST
+    DO_TEST( udp_ioqueue_test() );
+    fflush(stdout);
+#endif
+
+#if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST
+    DO_TEST( tcp_ioqueue_test() );
+    fflush(stdout);
+#endif
+
+#if INCLUDE_LIST_TEST
+    DO_TEST( list_test() );
+    fflush(stdout);
+#endif
+
+#if INCLUDE_XML_TEST
+    DO_TEST( xml_test() );
+    fflush(stdout);
+#endif
+
+    pj_caching_pool_destroy( &caching_pool );
+
+    char temp[3];
+    puts("");
+    puts("Press <ENTER> to quit");
+    fflush(stdout);
+    fgets(temp, sizeof(temp), stdin);
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/os_test.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/os_test.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/os_test.cpp	(revision 1753)
@@ -0,0 +1,112 @@
+/* $Header: /pjproject/pjlib/src/test/os_test.cpp 3     5/12/05 9:53p Bennylp $
+ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <stdio.h>
+#include "libpj_test.h"
+
+
+#define LOOP	1024
+
+static unsigned int shared_var;
+static pj_mutex_t *mutex;
+
+static void * PJ_THREAD_FUNC thread_proc(void *arg)
+{
+    int i;
+    unsigned int *pvar = (unsigned int*)arg;
+
+    for (i=0; i<LOOP; ++i) {
+	pj_mutex_lock(mutex);
+	(*pvar)++;
+	shared_var++;
+	pj_mutex_unlock(mutex);
+	pj_thread_sleep(0);
+    }
+    return 0;
+}
+
+int os_test()
+{
+    pj_pool_t *pool;
+    int err=0;
+    unsigned var1=0, var2=0, var3=0;
+    pj_thread_t *t1, *t2, *t3;
+    unsigned starvation_level = 0;
+
+    pool = (*mem->create_pool)(mem, NULL, 4096, 0, NULL);
+    mutex = pj_mutex_create(pool, NULL, 0);
+    if (!mutex)
+	return -1;
+
+    t1 = pj_thread_create(pool, NULL, &thread_proc, &var1, 0, NULL, 0);
+    t2 = pj_thread_create(pool, NULL, &thread_proc, &var2, 0, NULL, 0);
+    t3 = pj_thread_create(pool, NULL, &thread_proc, &var3, 0, NULL, 0);
+
+    if (!t1 || !t2 || !t3) {
+	puts("...Error:Unable to create thread");
+	return -1;
+    }
+
+    do {
+	int break_flag = 0;
+	unsigned lowest = 0xFFFFFFFF, highest = 0;
+	pj_thread_sleep(0);
+
+	pj_mutex_lock(mutex);
+	if (shared_var == LOOP * 3)
+	    break_flag = 1;
+	if (var1 < lowest) lowest = var1;
+	if (var2 < lowest) lowest = var2;
+	if (var3 < lowest) lowest = var3;
+	if (var1 > highest) highest = var1;
+	if (var2 > highest) highest = var2;
+	if (var3 > highest) highest = var3;
+	pj_mutex_unlock(mutex);
+
+	if (highest - lowest > 1)
+	    starvation_level += (highest - lowest - 1);
+
+	if (break_flag)
+	    break;
+    } while(1);
+
+    pj_thread_join(t1); pj_thread_destroy(t1);
+    pj_thread_join(t2); pj_thread_destroy(t2);
+    pj_thread_join(t3); pj_thread_destroy(t3);
+
+    if (var1 != var2 || var2 != var3 || var3 != LOOP) {
+	printf("...Error: var1=%d, var2=%d, var3=%d\n", var1, var2, var3);
+	return -1;
+    }
+
+    if (shared_var != LOOP * 3) {
+	printf("...Error: shared_var=%d\n", shared_var);
+	return -1;
+    }
+
+    printf("...ok, starvation_level:%d\n", starvation_level);
+    return err;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/pidf-diff.xml
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/pidf-diff.xml	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/pidf-diff.xml	(revision 1753)
@@ -0,0 +1,63 @@
+   <?xml version="1.0" encoding="UTF-8"?>
+   <p:pidf-full xmlns="urn:ietf:params:xml:ns:pidf"
+          xmlns:p="urn:ietf:params:xml:ns:pidf-diff"
+          xmlns:r="urn:ietf:params:xml:ns:pidf:rpid"
+          xmlns:c="urn:ietf:params:xml:ns:pidf:caps"
+          entity="pres:someone@example.com"
+          version="567">
+
+    <tuple id="sg89ae">
+     <status>
+      <basic>open</basic>
+      <r:relationship>assistant</r:relationship>
+     </status>
+     <c:servcaps>
+      <c:audio>true</c:audio>
+      <c:video>false</c:video>
+      <c:message>true</c:message>
+     </c:servcaps>
+     <contact priority="0.8">tel:09012345678</contact>
+    </tuple>
+
+    <tuple id="cg231jcr">
+     <status>
+      <basic>open</basic>
+     </status>
+     <contact priority="1.0">im:pep@example.com</contact>
+    </tuple>
+
+    <tuple id="r1230d">
+     <status>
+      <basic>closed</basic>
+      <r:activity>meeting</r:activity>
+     </status>
+     <r:homepage>http://example.com/~pep/</r:homepage>
+     <r:icon>http://example.com/~pep/icon.gif</r:icon>
+     <r:card>http://example.com/~pep/card.vcd</r:card>
+     <contact priority="0.9">sip:pep@example.com</contact>
+    </tuple>
+
+    <note xml:lang="en">Full state presence document</note>
+
+    <r:person>
+     <r:status>
+      <r:activities>
+       <r:on-the-phone/>
+       <r:busy/>
+      </r:activities>
+     </r:status>
+    </r:person>
+
+    <r:device id="urn:esn:600b40c7">
+     <r:status>
+      <c:devcaps>
+       <c:mobility>
+        <c:supported>
+         <c:mobile/>
+        </c:supported>
+       </c:mobility>
+      </c:devcaps>
+     </r:status>
+    </r:device>
+
+   </p:pidf-full>
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/pool_test.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/pool_test.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/pool_test.cpp	(revision 1753)
@@ -0,0 +1,167 @@
+/* $Header: /pjproject/pjlib/src/test/pool_test.cpp 6     5/12/05 9:53p Bennylp $
+ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include "libpj_test.h"
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <malloc.h>
+#include <new>
+#include <stdlib.h>
+#include <stdio.h>
+
+#if !PJ_HAS_HIGH_RES_TIMER
+# error Need high resolution timer for this test.
+#endif
+
+
+#define LOOP	    10
+#define COUNT	    1024
+static unsigned	    sizes[COUNT];
+#define MIN_SIZE    4
+#define MAX_SIZE    512
+static unsigned total_size;
+
+static int pool_test_pool()
+{
+    pj_pool_t *pool = (*mem->create_pool)(mem, NULL, total_size + 4*COUNT, 0, NULL);
+    if (!pool)
+	return -1;
+
+    for (int i=0; i<COUNT; ++i) {
+	char *p;
+	if ( (p=(char*)pj_pool_alloc(pool, sizes[i])) == NULL)
+	    return -1;
+	*p = '\0';
+    }
+
+    pj_pool_release(pool);
+    return 0;
+}
+
+static int pool_test_malloc_free()
+{
+    char *p[COUNT];
+    int i;
+
+    for (i=0; i<COUNT; ++i) {
+	p[i] = (char*)malloc(sizes[i]);
+	if (!p[i]) {
+	    // Don't care for memory leak in this test
+	    return -1;
+	}
+	*p[i] = '\0';
+    }
+
+    for (i=0; i<COUNT; ++i) {
+	free(p[i]);
+    }
+
+    return 0;
+}
+
+static int pool_test_new_delete()
+{
+    char *p[COUNT];
+    int i;
+
+    try {
+	for (i=0; i<COUNT; ++i) {
+	    p[i] = new char[sizes[i]];
+	    if (!p[i])
+		return -1;
+	    *p[i] = '\0';
+	}
+
+	for (i=0; i<COUNT; ++i) {
+	    delete [] p[i];
+	}
+    }
+    catch (std::bad_alloc & ) {
+	// Don't care for memory leak in this test
+	return -1;
+    }
+
+    return 0;
+}
+
+int pool_test()
+{
+    unsigned i;
+    pj_uint32_t pool_time=0, malloc_time=0, new_time=0, pool_time2=0;
+    pj_hr_timestamp start, end;
+
+    // Initialize sizes.
+    for (i=0; i<COUNT; ++i) {
+	sizes[i] = MIN_SIZE + rand() % MAX_SIZE;
+	total_size += sizes[i];
+    }
+
+    printf("Benchmarking pool..\n");
+
+    // Warmup
+    pool_test_pool();
+    pool_test_malloc_free();
+    pool_test_new_delete();
+
+    for (i=0; i<LOOP; ++i) {
+	pj_hr_gettimestamp(&start);
+	if (pool_test_pool()) {
+	    printf("...Error: error in testing pool\n");
+	    return -1;
+	}
+	pj_hr_gettimestamp(&end);
+	pool_time += (end.u32.lo - start.u32.lo);
+
+	pj_hr_gettimestamp(&start);
+	if (pool_test_malloc_free()) {
+	    printf("...Error: error in testing malloc\n");
+	    return -1;
+	}
+	pj_hr_gettimestamp(&end);
+	malloc_time += (end.u32.lo - start.u32.lo);
+
+	pj_hr_gettimestamp(&start);
+	if (pool_test_new_delete()) {
+	    printf("...Error: error in testing new operator\n");
+	    return -1;
+	}
+	pj_hr_gettimestamp(&end);
+	new_time += (end.u32.lo - start.u32.lo);
+
+	pj_hr_gettimestamp(&start);
+	if (pool_test_pool()) {
+	    printf("...Error: error in testing pool\n");
+	    return -1;
+	}
+	pj_hr_gettimestamp(&end);
+	pool_time2 += (end.u32.lo - start.u32.lo);
+    }
+
+    printf("..LOOP count:                        %u\n", LOOP);
+    printf("..number of alloc/dealloc per loop:  %u\n", COUNT);
+    printf("..pool allocation/deallocation time: %u\n", pool_time);
+    printf("..malloc/free time:                  %u\n", malloc_time);
+    printf("..new/delete time:                   %u\n", new_time);
+    printf("..pool again, second invocation:     %u\n", pool_time);
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/rbtree_test.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/rbtree_test.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/rbtree_test.cpp	(revision 1753)
@@ -0,0 +1,158 @@
+/* $Header: /pjproject/pjlib/src/test/rbtree_test.cpp 4     5/12/05 9:53p Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include "libpj_test.h"
+#include <pj/rbtree.h>
+#include <pj/types.h>
+#include <pj/string.h>
+#include <pj/hash.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define LOOP	    256
+#define MIN_COUNT   1024
+#define MAX_COUNT   (LOOP * MIN_COUNT)
+#define STRSIZE	    16
+
+struct node_key
+{
+    pj_uint32_t hash;
+    char str[STRSIZE];
+};
+
+static int compare_node(const node_key *k1, const node_key *k2)
+{
+    if (k1->hash == k2->hash) {
+	return strcmp(k1->str, k2->str);
+    } else {
+	return k1->hash	< k2->hash ? -1 : 1;
+    }
+}
+
+void randomize_string(char *str, int len)
+{
+    for (int i=0; i<len-1; ++i)
+	str[i] = (char)('a' + rand() % 26);
+    str[len-1] = '\0';
+}
+
+static int test()
+{
+    pj_rbtree rb;
+    node_key *key;
+    pj_rbtree_node *node;
+    pj_pool_t *pool;
+    int err=0;
+
+    pj_rbtree_init(&rb, (pj_rbtree_comp*)&compare_node);
+    pool = (*mem->create_pool)( mem, "pool", 
+				MAX_COUNT*(sizeof(*key)+PJ_RBTREE_NODE_SIZE) + PJ_RBTREE_SIZE + PJ_POOL_SIZE, 
+				0, NULL);
+
+    key = (node_key *)pj_pool_alloc(pool, MAX_COUNT*sizeof(*key));
+    node = (pj_rbtree_node*)pj_pool_alloc(pool, MAX_COUNT*sizeof(*node));
+
+    puts("");
+    int count = MIN_COUNT;
+    for (int i=0; i<LOOP; ++i) {
+	int j;
+	pj_hr_timestamp t1, t2, t_setup, t_insert, t_search, t_erase;
+
+	pj_assert(rb.size == 0);
+
+	t_setup.u32.lo = t_insert.u32.lo = t_search.u32.lo = t_erase.u32.lo = 0;
+
+	for (j=0; j<count; j++) {
+	    randomize_string(key[j].str, STRSIZE);
+
+	    pj_hr_gettimestamp(&t1);
+	    node[j].key = &key[j];
+	    node[j].user_data = key[j].str;
+	    key[j].hash = pj_hash_calc(0, key[j].str, PJ_HASH_KEY_STRING);
+	    pj_hr_gettimestamp(&t2);
+	    t_setup.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+	    pj_hr_gettimestamp(&t1);
+	    pj_rbtree_insert(&rb, &node[j]);
+	    pj_hr_gettimestamp(&t2);
+	    t_insert.u32.lo += (t2.u32.lo - t1.u32.lo);
+	}
+
+	pj_assert(rb.size == (unsigned)count);
+
+	// Iterate key, make sure they're sorted.
+	pj_rbtree_node *prev, *it;
+	prev = NULL;
+	it = pj_rbtree_first(&rb);
+	while (it) {
+	    if (prev) {
+		if (compare_node((node_key*)prev->key, (node_key*)it->key) >= 0) {
+		    ++err;
+		    printf("Error: %s >= %s\n", (char*)prev->user_data, (char*)it->user_data);
+		}
+	    }
+	    prev = it;
+	    it = pj_rbtree_next(&rb, it);
+	}
+
+	// Search.
+	for (j=0; j<count; j++) {
+	    pj_hr_gettimestamp(&t1);
+	    it = pj_rbtree_find(&rb, &key[j]);
+	    pj_hr_gettimestamp(&t2);
+	    t_search.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+	    pj_assert(it != NULL);
+	    if (it == NULL)
+		++err;
+	}
+
+	// Erase node.
+	for (j=0; j<count; j++) {
+	    pj_hr_gettimestamp(&t1);
+	    it = pj_rbtree_erase(&rb, &node[j]);
+	    pj_hr_gettimestamp(&t2);
+	    t_erase.u32.lo += (t2.u32.lo - t1.u32.lo);
+	}
+
+	printf("...count:%d, setup:%d, insert:%d, search:%d, erase:%d\n",
+		count,
+		t_setup.u32.lo / count, t_insert.u32.lo / count,
+		t_search.u32.lo / count, t_erase.u32.lo / count);
+
+	count = 2 * count;
+	if (count > MAX_COUNT)
+	    break;
+    }
+
+    pj_pool_release(pool);
+    return err;
+}
+
+
+int rbtree_test()
+{
+    return test();
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/timer_test.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/timer_test.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/timer_test.cpp	(revision 1753)
@@ -0,0 +1,151 @@
+/* $Header: /pjproject/pjlib/src/test/timer_test.cpp 4     5/12/05 9:53p Bennylp $ */
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include "libpj_test.h"
+#include <pj/timer.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <stdio.h>	// puts(), printf()
+#include <stdlib.h>	// srand(), rand()
+#include <time.h>	// time()
+
+#define LOOP		256
+#define MIN_COUNT	1024
+#define MAX_COUNT	(LOOP * MIN_COUNT)
+#define DELAY		(MAX_COUNT / 32000)
+
+static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e)
+{
+    PJ_UNUSED_ARG(ht);
+    PJ_UNUSED_ARG(e);
+}
+
+static int test()
+{
+    int i, j;
+    pj_timer_entry *entry;
+    pj_pool_t *pool;
+    pj_timer_heap_t *timer;
+    pj_time_val delay;
+    int err=0;
+    unsigned count;
+
+    pool = (*mem->create_pool)( mem, NULL, MAX_COUNT*PJ_TIMER_ENTRY_SIZE + PJ_TIMER_HEAP_SIZE + PJ_POOL_SIZE, 0, NULL);
+    entry = (pj_timer_entry*)pj_pool_calloc(pool, MAX_COUNT, sizeof(*entry));
+    for (i=0; i<MAX_COUNT; ++i) {
+	entry[i].cb = &timer_callback;
+    }
+    timer = pj_timer_heap_create(pool, MAX_COUNT, 0);
+
+    puts("");
+    count = MIN_COUNT;
+    for (i=0; i<LOOP; ++i) {
+	int early = 0;
+	int done=0;
+	int cancelled=0;
+	int rc;
+	pj_hr_timestamp t1, t2, t_sched, t_cancel, t_poll;
+
+	//printf("...test %d of %d.. ", i, LOOP);
+	printf("...");
+
+	srand(time(NULL));
+	t_sched.u32.lo = t_cancel.u32.lo = t_poll.u32.lo = 0;
+
+	// Register timers
+	for (j=0; j<(int)count; ++j) {
+	    delay.sec = rand() % DELAY;
+	    delay.msec = rand() % 1000;
+
+	    // Schedule timer
+	    pj_hr_gettimestamp(&t1);
+	    pj_timer_heap_schedule(timer, &entry[j], &delay);
+	    pj_hr_gettimestamp(&t2);
+
+	    t_sched.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+	    // Poll timers.
+	    pj_hr_gettimestamp(&t1);
+	    rc = pj_timer_heap_poll(timer, NULL);
+	    pj_hr_gettimestamp(&t2);
+	    if (rc > 0) {
+		t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+		early += rc;
+	    }
+	}
+
+	// Set the time where all timers should finish
+	pj_time_val expire, now;
+	pj_gettimeofday(&expire);
+	delay.sec = DELAY; 
+	delay.msec = 0;
+	PJ_TIME_VAL_ADD(expire, delay);
+
+	// Wait unfil all timers finish, cancel some of them.
+	do {
+	    int index = rand() % count;
+	    pj_hr_gettimestamp(&t1);
+	    rc = pj_timer_heap_cancel(timer, &entry[index]);
+	    pj_hr_gettimestamp(&t2);
+	    if (rc > 0) {
+		cancelled += rc;
+		t_cancel.u32.lo += (t2.u32.lo - t1.u32.lo);
+	    }
+
+	    pj_gettimeofday(&now);
+
+	    pj_hr_gettimestamp(&t1);
+	    rc = pj_timer_heap_poll(timer, NULL);
+	    pj_hr_gettimestamp(&t2);
+	    if (rc > 0) {
+		done += rc;
+		t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+	    }
+
+	} while (PJ_TIME_VAL_LTE(now, expire) && pj_timer_heap_count(timer) > 0);
+
+	if (pj_timer_heap_count(timer)) {
+	    printf("ERROR: %d timers left\n", pj_timer_heap_count(timer));
+	    ++err;
+	}
+	t_sched.u32.lo /= count; 
+	t_cancel.u32.lo /= count;
+	t_poll.u32.lo /= count;
+	printf("ok (count:%d, early:%d, cancelled:%d, sched:%d, cancel:%d poll:%d)\n", 
+		count, early, cancelled, t_sched.u32.lo, t_cancel.u32.lo,
+		t_poll.u32.lo);
+
+	count = count * 2;
+	if (count > MAX_COUNT)
+	    break;
+    }
+
+    pj_pool_release(pool);
+    return err;
+}
+
+
+int timer_test()
+{
+    return test();
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/xml_test.cpp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/xml_test.cpp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjlib/src/test/xml_test.cpp	(revision 1753)
@@ -0,0 +1,84 @@
+/* 
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/xml.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pj/pool.h>
+#include "libpj_test.h"
+#include <string.h>
+
+static const char *test_files[] = {
+    "../src/test/pidf-diff.xml"
+};
+
+static int xml_parse_print_test(const char *file)
+{
+    FILE *fhnd = fopen(file, "rb");
+    if (!fhnd) {
+	printf("  Error: unable to open file %s\n", file);
+	return 1;
+    }
+
+    char *msg = (char*)malloc(10*1024);
+    int len = fread(msg, 1, 10*1024, fhnd);
+    if (len < 1) {
+	return -1;
+    }
+
+    fclose(fhnd);
+
+    pj_pool_t *pool = pj_pool_create(mem, "xml", 1024, 1024, NULL);
+    pj_xml_node *root = pj_xml_parse(pool, msg, len);
+    if (!root) {
+	printf("  Error: unable to parse XML file %s\n", file);
+	return -1;
+    }
+
+    char *output = (char*)malloc(len + 512);
+    memset(output, 0, len+512);
+    int output_len = pj_xml_print(root, output, len+512, PJ_TRUE);
+    if (output_len < 1) {
+	printf("  Error: buffer too small to print XML file %s\n", file);
+	return -1;
+    }
+    output[output_len] = '\0';
+
+    fhnd = fopen("out.xml", "wb");
+    fwrite(output, output_len, 1, fhnd);
+    fclose(fhnd);
+
+    pj_pool_release(pool);
+    free(output);
+    free(msg);
+
+    return 0;
+}
+
+int xml_test()
+{
+    int i;
+    for (i=0; i<sizeof(test_files)/sizeof(test_files[0]); ++i) {
+	if (xml_parse_print_test(test_files[i]) != 0)
+	    return -1;
+    }
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/LGPL.TXT
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/LGPL.TXT	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/LGPL.TXT	(revision 1753)
@@ -0,0 +1,513 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/Jbtest.dat
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/Jbtest.dat	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/Jbtest.dat	(revision 1753)
@@ -0,0 +1,62 @@
+#
+###############################################################################
+# This test demonstrates situation where there is no jitter. 
+# Jitter should go the minimum configured value.
+###############################################################################
+#
+#PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
+
+#
+###############################################################################
+# This test demonstrates situation where there is no jitter, but with
+# addition of silence compression. The jitter value should also go
+# to the minimum.
+###############################################################################
+#
+#PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG /* Start silence */ GGGGGGGGGGGGGGGGGGGGG /* End silence */ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
+
+#
+###############################################################################
+# This test demonstrates situation where there's about one-three packets jitter
+# in the network, without packet lost.
+###############################################################################
+#
+#PGPGPPGGPPGGPPGGGGPG PGPGPPGGPPPGGPPGGGPG PGPGPPGGPPPGGPPGGGPG PGPGPPGGPPGGPPPGGGPG PGPGPPGGPPGGPPPGGGPG
+
+#
+###############################################################################
+# Two gets two puts, no jitter
+###############################################################################
+#
+#PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG
+
+#
+###############################################################################
+# Three gets three puts, no packet lost
+###############################################################################
+#
+#PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG
+
+
+#
+###############################################################################
+# Three gets three puts, with packet lost
+###############################################################################
+#
+#PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG
+
+
+#
+###############################################################################
+# Three gets three puts, then stable
+###############################################################################
+#
+PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG 
+
+#
+###############################################################################
+# Some jitter
+###############################################################################
+#
+#PGPGPGPGPG /*Some frames missing here*/ GG /*Some frames arrive*/ PPPG /*Normal*/ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/Makefile
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/Makefile	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/Makefile	(revision 1753)
@@ -0,0 +1,67 @@
+include make-$(TARGET).inc
+
+export PJMEDIA_SRCEXT = .c
+export PJMEDIA_SRCDIR = ../src/pjmedia
+export PJMEDIA_SRCS = 	$(PJMEDIA_SOURCES) codec.c jbuf.c rtp.c mediamgr.c \
+		      	session.c g711.c  rtcp.c  stream.c sdp.c pasound.c \
+		      	$(PA_DIR)/pa_allocation.c \
+			$(PA_DIR)/pa_converters.c \
+			$(PA_DIR)/pa_cpuload.c \
+			$(PA_DIR)/pa_dither.c \
+			$(PA_DIR)/pa_front.c \
+			$(PA_DIR)/pa_process.c \
+			$(PA_DIR)/pa_skeleton.c \
+			$(PA_DIR)/pa_stream.c \
+			$(PA_DIR)/pa_trace.c
+
+export TEST_SRCEXT = .c
+export TEST_SRCDIR = ../src/test
+export TEST_SRCS = $(TEST_SOURCES) jbuf_test.c rtp_test.c main.c \
+		session_test.c sdptest.c
+		   
+
+export PJAUT_SRCEXT = .c
+export PJAUT_SRCDIR = ../src/test
+export PJAUT_SRCS = $(PJAUT_SOURCES) audio_tool.c
+
+export CCOUT CC AR RANLIB MV RM RMDIR MKDIR OBJEXT LD LDOUT 
+
+all: pjmedia test pjaut
+
+doc:
+	cd .. && doxygen docs/doxygen.cfg
+
+print:
+	$(MAKE) -f make-rules APP=PJMEDIA app=pjmedia print_lib
+	$(MAKE) -f make-rules APP=TEST app=test print_bin
+	$(MAKE) -f make-rules APP=PJAUT app=pjaut print_bin
+	
+depend:
+	$(MAKE) -f make-rules APP=PJMEDIA app=pjmedia depend
+	$(MAKE) -f make-rules APP=TEST app=test depend
+	$(MAKE) -f make-rules APP=PJAUT app=pjaut depend
+
+dep: depend
+
+pjmedia:
+	$(MAKE) -f make-rules APP=PJMEDIA app=pjmedia $(PJMEDIA_LIB)
+
+test:
+	$(MAKE) -f make-rules APP=TEST app=test $(TEST_EXE)
+
+pjaut:
+	$(MAKE) -f make-rules APP=PJAUT app=pjaut $(PJAUT_EXE)
+	
+clean:
+	$(MAKE) -f make-rules APP=PJMEDIA app=pjmedia clean
+	$(MAKE) -f make-rules APP=TEST app=test clean
+	$(MAKE) -f make-rules APP=PJAUT app=pjaut clean
+
+realclean:
+	$(MAKE) -f make-rules APP=PJMEDIA app=pjmedia realclean
+	$(MAKE) -f make-rules APP=TEST app=test realclean
+	$(MAKE) -f make-rules APP=PJAUT app=pjaut realclean
+
+distclean: realclean
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/make-linux-i386.inc
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/make-linux-i386.inc	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/make-linux-i386.inc	(revision 1753)
@@ -0,0 +1,32 @@
+include ../../pjlib/build/make-$(TARGET).inc
+
+PA_DIR := portaudio
+PA_CFLAGS := 	-DPA_LITTLE_ENDIAN -DPA_USE_OSS \
+		-I$(PA_DIR) -I../src/pjmedia/portaudio
+
+_CFLAGS := $(_CFLAGS) -I../src -I../../pjlib/src $(PA_CFLAGS)
+_LDFLAGS := $(_LDFLAGS) -L../lib -L../../pjlib/lib \
+		      -lpjmedia -lpj -lpthread
+
+export PA_SOURCES :=	$(PA_DIR)/pa_unix_hostapis.c \
+			$(PA_DIR)/pa_unix_util.c \
+			$(PA_DIR)/pa_unix_oss.c
+#			$(PA_DIR)/pa_linux_alsa.c
+
+
+export PJMEDIA_SOURCES = $(PA_SOURCES)
+export PJMEDIA_CFLAGS = $(_CFLAGS) 
+export PJMEDIA_LIB = ../lib/libpjmedia.a
+export PJMEDIA_EXTRA_DEP := ../../pjlib/build/$(PJLIB_LIB)
+
+export TEST_EXE := ../bin/test_linux
+export TEST_SOURCES := 
+export TEST_CFLAGS := $(PJMEDIA_CFLAGS)
+export TEST_LDFLAGS := $(_LDFLAGS)
+export TEST_EXTRA_DEP := $(PJMEDIA_LIB)
+
+export PJAUT_EXE := ../bin/pjaut_linux
+export PJAUT_SOURCES := 
+export PJAUT_CFLAGS := $(PJMEDIA_CFLAGS)
+export PJAUT_LDFLAGS = $(_LDFLAGS)
+export PJAUT_EXTRA_DEP := $(PJMEDIA_LIB)
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/make-mingw.inc
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/make-mingw.inc	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/make-mingw.inc	(revision 1753)
@@ -0,0 +1,30 @@
+include ../../pjlib/build/make-$(TARGET).inc
+
+PA_DIR := portaudio
+PA_CFLAGS := -DPA_NO_ASIO -DPA_NO_DS -I$(PA_DIR) -I../src/pjmedia/portaudio
+
+_CFLAGS := $(_CFLAGS) -I../src -I../../pjlib/src $(PA_CFLAGS)
+_LDFLAGS := -L../../pjlib/lib -lpjmedia $(_LDFLAGS) -lwinmm
+
+
+export PA_SOURCES := 	$(PA_DIR)/pa_win_hostapis.c \
+			$(PA_DIR)/pa_win_util.c \
+			$(PA_DIR)/pa_win_wmme.c
+#			$(PA_DIR)/pa_x86_plain_converters.c \
+
+export PJMEDIA_SOURCES := $(PA_SOURCES)
+export PJMEDIA_CFLAGS := $(_CFLAGS)
+export PJMEDIA_LIB := ../lib/libpjmedia.a
+export PJMEDIA_EXTRA_DEP := ../../pjlib/build/$(PJLIB_LIB)
+
+export TEST_EXE := ../bin/test_mingw.exe
+export TEST_SOURCES := 
+export TEST_CFLAGS := $(_CFLAGS)
+export TEST_LDFLAGS := $(_LDFLAGS)
+export TEST_EXTRA_DEP := $(PJMEDIA_LIB)
+
+export PJAUT_EXE := ../bin/pjaut_mingw.exe
+export PJAUT_SOURCES := 
+export PJAUT_CFLAGS = $(_CFLAGS)
+export PJAUT_LDFLAGS := $(_LDFLAGS)
+export PJAUT_EXTRA_DEP := $(PJMEDIA_LIB)
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/make-rules
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/make-rules	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/make-rules	(revision 1753)
@@ -0,0 +1,119 @@
+LIBDIR = ../lib
+BINDIR = ../bin
+
+#
+# The full path of output lib file (e.g. ../lib/libapp.a).
+#
+LIB = $($(APP)_LIB)
+
+#
+# The full path of output executable file (e.g. ../bin/app.exe).
+#
+EXE = $($(APP)_EXE)
+
+#
+# Source directory
+#
+SRCDIR = $($(APP)_SRCDIR)
+
+#
+# SRCEXT is .c
+# SRCS is file.c
+# FULL_SRCS is ../src/app/file.c
+#
+SRCEXT = $($(APP)_SRCEXT)
+SRCS = $($(APP)_SRCS)
+FULL_SRCS = $(foreach file, $(SRCS), $(SRCDIR)/$(file))
+
+
+#
+# Output directory for object files (i.e. output/target)
+#
+OBJDIR = ./output/$(app)-$(TARGET)
+
+#
+# OBJS1 is ./output/target/file.c
+# OBJS is ./output/target/file.o
+#
+OBJS1 = $(foreach file, $(SRCS), $(OBJDIR)/$(file))
+OBJS = $(OBJS1:%$(SRCEXT)=%$(OBJEXT))
+OBJDIRS := $(sort $(foreach file, $(SRCS), $(dir $(OBJDIR)/$(file))))
+
+
+#
+# When generating dependency (gcc -MM), ideally we use only either
+# CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work.
+#
+DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS)
+
+print_common:
+	@echo "###"
+	@echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):"
+	@echo "###"
+	@echo APP=$(APP)
+	@echo SRCEXT=$(SRCEXT)
+	@echo OBJDIR=$(OBJDIR)
+	@echo OBJS=$(OBJS)
+	@echo SRCDIR=$(SRCDIR)
+	@echo FULL_SRCS=$(FULL_SRCS)
+	@echo $(APP)_CFLAGS=$($(APP)_CFLAGS)
+	@echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS)
+	@echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS)
+	@echo DEPFLAGS=$(DEPFLAGS)
+
+print_bin: print_common
+	@echo EXE=$(EXE)
+	@echo BINDIR=$(BINDIR)
+
+print_lib: print_common
+	@echo LIB=$(LIB)
+	@echo LIBDIR=$(LIBDIR)
+
+$(LIB): $(LIBDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP)
+	$(AR) $(LIB) $(OBJS)
+	$(RANLIB) $(LIB)
+
+$(EXE): $(BINDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP)
+	$(LD) $(LDOUT) $(EXE) $(OBJS) $($(APP)_LDFLAGS)
+
+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c
+	$(CC) $($(APP)_CFLAGS) $< $(CCOUT) $@
+
+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp
+	$(CC) $($(APP)_CXXFLAGS) $< $(CCOUT) $@
+
+#$(OBJDIR):
+#	$(MKDIR) $(OBJDIR)
+$(OBJDIRS):
+	$(MKDIR) $@
+
+$(LIBDIR):
+	$(MKDIR) $(LIBDIR)
+
+$(BINDIR):
+	$(MKDIR) $(BINDIR)
+
+clean:
+	$(RM) -r $(OBJDIR)/*
+	$(RMDIR) $(OBJDIR)
+
+realclean: clean
+	$(RM) $(LIB) $(EXE)
+	$(RM) .$(app).depend
+
+depend:
+	$(RM) .$(app).depend
+	for F in $(FULL_SRCS); do \
+	   echo -n $(OBJDIR)/ >> .$(app).depend; \
+	   if gcc -MM $(DEPFLAGS) $$F >> .$(app).depend; then \
+		true; \
+	   else \
+		echo 'err:' >> .$(app).depend; \
+		exit 1; \
+	   fi; \
+	done
+
+dep: depend
+
+-include .$(app).depend
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.dsp	(revision 1753)
@@ -0,0 +1,327 @@
+# Microsoft Developer Studio Project File - Name="pjmedia" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pjmedia - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjmedia.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjmedia.mak" CFG="pjmedia - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjmedia - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pjmedia - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjsip/build", RIAAAAAA"
+# PROP Scc_LocalPath "..\..\pjsip\build"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjmedia - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\output\pjmedia_vc6_Release"
+# PROP BASE Intermediate_Dir ".\output\pjmedia_vc6_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\output\pjmedia_vc6_Release"
+# PROP Intermediate_Dir ".\output\pjmedia_vc6_Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /GX /O2 /I "../../pjlib/src" /I "../src" /I "../../pjsdp/src" /I "../src/pjmedia/portaudio" /D "NDEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjmedia_vc6s.lib"
+
+!ELSEIF  "$(CFG)" == "pjmedia - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\output\pjmedia_vc6_Debug"
+# PROP BASE Intermediate_Dir ".\output\pjmedia_vc6_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\pjmedia_vc6_Debug"
+# PROP Intermediate_Dir ".\output\pjmedia_vc6_Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../../pjlib/src" /I "../src" /I "../../pjsdp/src" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjmedia_vc6sd.lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjmedia - Win32 Release"
+# Name "pjmedia - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\pjmedia\codec.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\dsound.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\g711.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\jbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\mediamgr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\nullsound.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\pasound.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\rtcp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\rtp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\sdp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\session.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\stream.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\pjmedia\codec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\jbuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\mediamgr.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\rtcp.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\rtp.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\sdp.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\session.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\sound.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\stream.h
+# End Source File
+# End Group
+# Begin Group "PortAudio"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\dsound_wrapper.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\dsound_wrapper.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_allocation.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_allocation.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_converters.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_converters.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_cpuload.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_cpuload.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_dither.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_dither.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_endianness.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_front.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_hostapi.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_process.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_process.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_skeleton.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_stream.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_stream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_trace.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_trace.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_win_ds.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_win_hostapis.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_win_util.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_win_wmme.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_win_wmme.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_x86_plain_converters.c
+# ADD CPP /W3
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\pa_x86_plain_converters.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\portaudio\portaudio.h
+# End Source File
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.dsw
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.dsw	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.dsw	(revision 1753)
@@ -0,0 +1,103 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "pjlib"="..\..\pjlib\build\pjlib.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjmedia/build", TKAAAAAA
+    .
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjmedia"=".\pjmedia.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjmedia/build", TKAAAAAA
+    .
+    end source code control
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pjmedia_audio_tool"=".\pjmedia_audio_tool.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjmedia/build", TKAAAAAA
+    .
+    end source code control
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjmedia
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pjmedia_test"=".\pjmedia_test.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjmedia/build", TKAAAAAA
+    .
+    end source code control
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjmedia
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjsdp
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjmedia/build", MJCAAAAA
+    .
+    end source code control
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.sln
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.sln	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.sln	(revision 1753)
@@ -0,0 +1,90 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsdp", "..\..\pjsdp\build\pjsdp.vcproj", "{CC953678-66FC-4C91-9DC7-2783B7427B19}"
+	ProjectSection(ProjectDependencies) = postProject
+		{72790D99-35BB-45AC-9A23-3BB60C901E63} = {72790D99-35BB-45AC-9A23-3BB60C901E63}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjaudio_tool", "pjaudio_tool.vcproj", "{B5DAC8A2-E01F-41B8-8FFE-7CD396C183DD}"
+	ProjectSection(ProjectDependencies) = postProject
+		{E6181719-4557-4EB5-8DBA-1E21C5183670} = {E6181719-4557-4EB5-8DBA-1E21C5183670}
+		{CC953678-66FC-4C91-9DC7-2783B7427B19} = {CC953678-66FC-4C91-9DC7-2783B7427B19}
+		{72790D99-35BB-45AC-9A23-3BB60C901E63} = {72790D99-35BB-45AC-9A23-3BB60C901E63}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia", "pjmedia.vcproj", "{E6181719-4557-4EB5-8DBA-1E21C5183670}"
+	ProjectSection(ProjectDependencies) = postProject
+		{72790D99-35BB-45AC-9A23-3BB60C901E63} = {72790D99-35BB-45AC-9A23-3BB60C901E63}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia_test", "pjmedia_test.vcproj", "{13EF030B-5BB9-48AC-8EAA-723B6BD1DD7D}"
+	ProjectSection(ProjectDependencies) = postProject
+		{E6181719-4557-4EB5-8DBA-1E21C5183670} = {E6181719-4557-4EB5-8DBA-1E21C5183670}
+		{CC953678-66FC-4C91-9DC7-2783B7427B19} = {CC953678-66FC-4C91-9DC7-2783B7427B19}
+		{72790D99-35BB-45AC-9A23-3BB60C901E63} = {72790D99-35BB-45AC-9A23-3BB60C901E63}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib", "..\..\pjlib\build\pjlib.vcproj", "{72790D99-35BB-45AC-9A23-3BB60C901E63}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SourceCodeControl) = preSolution
+		SccNumberOfProjects = 6
+		SccProjectUniqueName0 = ..\\..\\pjsdp\\build\\pjsdp.vcproj
+		SccLocalPath0 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection0 = pjsdp\\build\\
+		SccProjectUniqueName1 = pjmedia.vcproj
+		SccLocalPath1 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection1 = pjmedia\\build\\
+		SccProjectUniqueName2 = ..\\..\\pjlib\\build\\pjlib.vcproj
+		SccLocalPath2 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection2 = pjlib\\build\\
+		SccProjectName3 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath3 = ..\\..
+		SccProvider3 = MSSCCI:Microsoft\u0020Visual\u0020SourceSafe
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection3 = pjmedia\\build\\
+		SolutionUniqueID = {ADA38C1E-12DE-4E20-AC46-530CA681F3E8}
+		SccProjectUniqueName4 = pjaudio_tool.vcproj
+		SccLocalPath4 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection4 = pjmedia\\build\\
+		SccProjectUniqueName5 = pjmedia_test.vcproj
+		SccLocalPath5 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection5 = pjmedia\\build\\
+	EndGlobalSection
+	GlobalSection(SolutionConfiguration) = preSolution
+		Debug = Debug
+		Release = Release
+	EndGlobalSection
+	GlobalSection(ProjectConfiguration) = postSolution
+		{CC953678-66FC-4C91-9DC7-2783B7427B19}.Debug.ActiveCfg = Debug|Win32
+		{CC953678-66FC-4C91-9DC7-2783B7427B19}.Debug.Build.0 = Debug|Win32
+		{CC953678-66FC-4C91-9DC7-2783B7427B19}.Release.ActiveCfg = Release|Win32
+		{CC953678-66FC-4C91-9DC7-2783B7427B19}.Release.Build.0 = Release|Win32
+		{B5DAC8A2-E01F-41B8-8FFE-7CD396C183DD}.Debug.ActiveCfg = Debug|Win32
+		{B5DAC8A2-E01F-41B8-8FFE-7CD396C183DD}.Debug.Build.0 = Debug|Win32
+		{B5DAC8A2-E01F-41B8-8FFE-7CD396C183DD}.Release.ActiveCfg = Release|Win32
+		{B5DAC8A2-E01F-41B8-8FFE-7CD396C183DD}.Release.Build.0 = Release|Win32
+		{E6181719-4557-4EB5-8DBA-1E21C5183670}.Debug.ActiveCfg = Debug|Win32
+		{E6181719-4557-4EB5-8DBA-1E21C5183670}.Debug.Build.0 = Debug|Win32
+		{E6181719-4557-4EB5-8DBA-1E21C5183670}.Release.ActiveCfg = Release|Win32
+		{E6181719-4557-4EB5-8DBA-1E21C5183670}.Release.Build.0 = Release|Win32
+		{13EF030B-5BB9-48AC-8EAA-723B6BD1DD7D}.Debug.ActiveCfg = Debug|Win32
+		{13EF030B-5BB9-48AC-8EAA-723B6BD1DD7D}.Debug.Build.0 = Debug|Win32
+		{13EF030B-5BB9-48AC-8EAA-723B6BD1DD7D}.Release.ActiveCfg = Release|Win32
+		{13EF030B-5BB9-48AC-8EAA-723B6BD1DD7D}.Release.Build.0 = Release|Win32
+		{72790D99-35BB-45AC-9A23-3BB60C901E63}.Debug.ActiveCfg = Debug|Win32
+		{72790D99-35BB-45AC-9A23-3BB60C901E63}.Debug.Build.0 = Debug|Win32
+		{72790D99-35BB-45AC-9A23-3BB60C901E63}.Release.ActiveCfg = Release|Win32
+		{72790D99-35BB-45AC-9A23-3BB60C901E63}.Release.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+	EndGlobalSection
+	GlobalSection(ExtensibilityAddIns) = postSolution
+	EndGlobalSection
+EndGlobal
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia.vcproj	(revision 1753)
@@ -0,0 +1,629 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjmedia_lib"
+	ProjectGUID="{EB8559B2-D738-4987-8591-4D217F8B0099}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjmedia_vc7_Release"
+			IntermediateDirectory=".\output\pjmedia_vc7_Release"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../../pjlib/src,../src,../../pjsdp/src,../src/pjmedia/portaudio"
+				PreprocessorDefinitions="PA_NO_ASIO;PA_NO_WMME;WIN32;NDEBUG;_LIB"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				PrecompiledHeaderFile=".\output\pjmedia_vc7_Release/pjmedia.pch"
+				AssemblerListingLocation=".\output\pjmedia_vc7_Release/"
+				ObjectFile=".\output\pjmedia_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjmedia_vc7_Release/"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjmedia_vc7s.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjmedia_vc7_Debug"
+			IntermediateDirectory=".\output\pjmedia_vc7_Debug"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../../pjlib/src,../src,../../pjsdp/src,../src/pjmedia/portaudio"
+				PreprocessorDefinitions="PA_NO_ASIO;PA_NO_WMME;WIN32;_DEBUG;_LIB"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile=".\output\pjmedia_vc7_Debug/pjmedia.pch"
+				AssemblerListingLocation=".\output\pjmedia_vc7_Debug/"
+				ObjectFile=".\output\pjmedia_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjmedia_vc7_Debug/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjmedia_vc7sd.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\pjmedia\codec.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\g711.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\jbuf.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\mediamgr.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\nullsound.c">
+				<FileConfiguration
+					Name="Release|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\pasound.c">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\rtcp.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\rtp.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\sdp.c">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\session.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\stream.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\pjmedia\codec.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\config.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\jbuf.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\mediamgr.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\rtcp.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\rtp.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\sdp.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\session.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\sound.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\stream.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="PortAudio Files"
+			Filter="">
+			<File
+				RelativePath="..\src\pjmedia\portaudio\dsound_wrapper.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\dsound_wrapper.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_allocation.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_allocation.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_converters.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_converters.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_cpuload.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_cpuload.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_dither.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_dither.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_endianness.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_front.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_hostapi.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_process.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_process.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_skeleton.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_stream.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_stream.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_trace.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_trace.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_types.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_util.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_win_ds.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_win_hostapis.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_win_util.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_win_wmme.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_win_wmme.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_x86_plain_converters.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						WarningLevel="3"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\pa_x86_plain_converters.h">
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\portaudio\portaudio.h">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_audio_tool.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_audio_tool.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_audio_tool.dsp	(revision 1753)
@@ -0,0 +1,102 @@
+# Microsoft Developer Studio Project File - Name="pjmedia_audio_tool" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=pjmedia_audio_tool - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjmedia_audio_tool.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjmedia_audio_tool.mak" CFG="pjmedia_audio_tool - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjmedia_audio_tool - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "pjmedia_audio_tool - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjmedia/build", TKAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjmedia_audio_tool - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\output\pjmedia_audio_tool_vc6_Release"
+# PROP BASE Intermediate_Dir ".\output\pjmedia_audio_tool_vc6_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\output\pjmedia_audio_tool_vc6_Release"
+# PROP Intermediate_Dir ".\output\pjmedia_audio_tool_vc6_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../pjlib/src" /I "../src" /I "../../pjsdp/src" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjaut_vc6.exe"
+
+!ELSEIF  "$(CFG)" == "pjmedia_audio_tool - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\output\pjmedia_audio_tool_vc6_Debug"
+# PROP BASE Intermediate_Dir ".\output\pjmedia_audio_tool_vc6_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\pjmedia_audio_tool_vc6_Debug"
+# PROP Intermediate_Dir ".\output\pjmedia_audio_tool_vc6_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../pjlib/src" /I "../src" /I "../../pjsdp/src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjaut_vc6d.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjmedia_audio_tool - Win32 Release"
+# Name "pjmedia_audio_tool - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\test\audio_tool.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_audio_tool.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_audio_tool.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_audio_tool.vcproj	(revision 1753)
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjmedia_audio_tool"
+	ProjectGUID="{5FD061CF-A0E8-43DF-990C-B046AA1BF5EE}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjmedia_audio_tool_vc7_Release"
+			IntermediateDirectory=".\output\pjmedia_audio_tool_vc7_Release"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../../pjlib/src,../src,../../pjsdp/src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjmedia_audio_tool_vc7_Release/pjmedia_audio_tool.pch"
+				AssemblerListingLocation=".\output\pjmedia_audio_tool_vc7_Release/"
+				ObjectFile=".\output\pjmedia_audio_tool_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjmedia_audio_tool_vc7_Release/"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib"
+				OutputFile="../bin/pjaut_vc7.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="TRUE"
+				ProgramDatabaseFile=".\output\pjmedia_audio_tool_vc7_Release/pjaut.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\output\pjmedia_audio_tool_vc7_Release/pjmedia_audio_tool.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjmedia_audio_tool_vc7_Debug"
+			IntermediateDirectory=".\output\pjmedia_audio_tool_vc7_Debug"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../../pjlib/src,../src,../../pjsdp/src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjmedia_audio_tool_vc7_Debug/pjmedia_audio_tool.pch"
+				AssemblerListingLocation=".\output\pjmedia_audio_tool_vc7_Debug/"
+				ObjectFile=".\output\pjmedia_audio_tool_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjmedia_audio_tool_vc7_Debug/"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib"
+				OutputFile="../bin/pjaut_vc7d.exe"
+				LinkIncremental="2"
+				SuppressStartupBanner="TRUE"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile=".\output\pjmedia_audio_tool_vc7_Debug/pjaut.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\output\pjmedia_audio_tool_vc7_Debug/pjmedia_audio_tool.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\test\audio_tool.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_test.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_test.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_test.dsp	(revision 1753)
@@ -0,0 +1,122 @@
+# Microsoft Developer Studio Project File - Name="pjmedia_test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=pjmedia_test - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjmedia_test.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjmedia_test.mak" CFG="pjmedia_test - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjmedia_test - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "pjmedia_test - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjmedia/build", TKAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjmedia_test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\output\pjmedia_test_vc6_Release"
+# PROP BASE Intermediate_Dir ".\output\pjmedia_test_vc6_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\output\pjmedia_test_vc6_Release"
+# PROP Intermediate_Dir ".\output\pjmedia_test_vc6_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../pjlib/src" /I "../src" /I "../../pjsdp/src" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjmedia_test_vc6.exe"
+
+!ELSEIF  "$(CFG)" == "pjmedia_test - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\output\pjmedia_test_vc6_Debug"
+# PROP BASE Intermediate_Dir ".\output\pjmedia_test_vc6_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\pjmedia_test_vc6_Debug"
+# PROP Intermediate_Dir ".\output\pjmedia_test_vc6_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../pjlib/src" /I "../src" /I "../../pjsdp/src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjmedia_test_vc6d.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjmedia_test - Win32 Release"
+# Name "pjmedia_test - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\test\jbuf_test.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\rtp_test.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\sdptest.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\test\session_test.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Source File
+
+SOURCE=.\JBTEST.DAT
+# End Source File
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_test.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_test.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/build/pjmedia_test.vcproj	(revision 1753)
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjmedia_test"
+	ProjectGUID="{692B42C4-6888-4BF8-9613-E48A2F138005}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjmedia_test_vc7_Debug"
+			IntermediateDirectory=".\output\pjmedia_test_vc7_Debug"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../../pjlib/src,../src,../../pjsdp/src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjmedia_test_vc7_Debug/pjmedia_test.pch"
+				AssemblerListingLocation=".\output\pjmedia_test_vc7_Debug/"
+				ObjectFile=".\output\pjmedia_test_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjmedia_test_vc7_Debug/"
+				BrowseInformation="1"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib"
+				OutputFile="../bin/pjmedia_test_vc7d.exe"
+				LinkIncremental="2"
+				SuppressStartupBanner="TRUE"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile=".\output\pjmedia_test_vc7_Debug/pjmedia_test.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\output\pjmedia_test_vc7_Debug/pjmedia_test.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjmedia_test_vc7_Release"
+			IntermediateDirectory=".\output\pjmedia_test_vc7_Release"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../../pjlib/src,../src,../../pjsdp/src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjmedia_test_vc7_Release/pjmedia_test.pch"
+				AssemblerListingLocation=".\output\pjmedia_test_vc7_Release/"
+				ObjectFile=".\output\pjmedia_test_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjmedia_test_vc7_Release/"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib"
+				OutputFile="..\bin\pjmedia_test_vc7.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="TRUE"
+				ProgramDatabaseFile=".\output\pjmedia_test_vc7_Release/pjmedia_test.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\output\pjmedia_test_vc7_Release/pjmedia_test.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\test\jbuf_test.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\main.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\rtp_test.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\test\sdptest.c">
+			</File>
+			<File
+				RelativePath="..\src\test\session_test.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
+		</Filter>
+		<File
+			RelativePath="JBTEST.DAT">
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/docs/doxygen.cfg
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/docs/doxygen.cfg	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/docs/doxygen.cfg	(revision 1753)
@@ -0,0 +1,1042 @@
+# Doxyfile 1.3-rc3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           =  PJMEDIA
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = docs
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, 
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en 
+# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, 
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these class will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH        = ""
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower case letters. If set to YES upper case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments  will behave just like the Qt-style comments (thus requiring an 
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consist of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  =  src/pjmedia
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS          = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = *_i.h
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
+INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output dir.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non empty doxygen will try to run 
+# the html help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimised for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assigments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_XML           = NO
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed.
+
+PREDEFINED             = PJ_DECL(x)=x PJ_DEF(x)=x PJ_IDECL(x)=x \
+			 PJ_IDEF(x)=x PJ_INLINE(x)=x
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS         = NO
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermedate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
+
+# The CGI_NAME tag should be the name of the CGI script that 
+# starts the search engine (doxysearch) with the correct parameters. 
+# A script with this name will be generated by doxygen.
+
+CGI_NAME               = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the 
+# cgi binaries are located. See the documentation of your http daemon for 
+# details.
+
+CGI_URL                = 
+
+# The DOC_URL tag should be the absolute URL to the directory where the 
+# documentation is located. If left blank the absolute path to the 
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL                = 
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the 
+# documentation is located. If left blank the directory on the local machine 
+# will be used.
+
+DOC_ABSPATH            = 
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary 
+# is installed.
+
+BIN_ABSPATH            = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to 
+# documentation generated for other projects. This allows doxysearch to search 
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS          = 
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia.h	(revision 1753)
@@ -0,0 +1,36 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia.h 2     5/15/05 11:51a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __PJMEDIA_H__
+#define __PJMEDIA_H__
+
+#include <pjmedia/codec.h>
+#include <pjmedia/jbuf.h>
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/rtp.h>
+#include <pjmedia/session.h>
+#include <pjmedia/sound.h>
+#include <pjmedia/sdp.h>
+
+#endif	/* __PJMEDIA_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/codec.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/codec.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/codec.c	(revision 1753)
@@ -0,0 +1,110 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/codec.c 3     4/17/05 11:59a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjmedia/codec.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/log.h>
+
+#define THIS_FILE   "codec.c"
+
+static void enum_all_codecs (pj_codec_mgr *cm)
+{
+    pj_codec_factory *cf;
+
+    cf = cm->factory_list.next;
+    cm->codec_cnt = 0;
+    while (cf != &cm->factory_list) {
+	pj_codec_id temp[PJ_CODEC_MGR_MAX_CODECS];
+	int i, cnt;
+
+	cnt = cf->op->enum_codecs (cf, PJ_CODEC_MGR_MAX_CODECS, temp);
+	if (cnt > PJ_CODEC_MGR_MAX_CODECS) {
+	    pj_assert(0);
+	    PJ_LOG(4, (THIS_FILE, "Too many codecs reported by factory"));
+	    cnt = PJ_CODEC_MGR_MAX_CODECS;
+	}
+
+	for (i=0; i<cnt && cm->codec_cnt < PJ_CODEC_MGR_MAX_CODECS; ++i) {
+	    cm->codecs[cm->codec_cnt++] = temp[i];
+	}
+
+	cf = cf->next;
+    }
+}
+
+PJ_DEF(pj_status_t) pj_codec_mgr_init (pj_codec_mgr *mgr)
+{
+    pj_list_init (&mgr->factory_list);
+    mgr->codec_cnt = 0;
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pj_codec_mgr_register_factory (pj_codec_mgr *mgr,
+						   pj_codec_factory *factory)
+{
+    pj_list_insert_before (&mgr->factory_list, factory);
+    enum_all_codecs (mgr);
+    return 0;
+}
+
+PJ_DEF(void) pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory)
+{
+    PJ_UNUSED_ARG(mgr)
+    pj_list_erase(factory);
+    enum_all_codecs (mgr);
+}
+
+PJ_DEF(unsigned)
+pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[])
+{
+    unsigned i;
+
+    if (count > mgr->codec_cnt)
+	count = mgr->codec_cnt;
+
+    for (i=0; i<count; ++i)
+	codecs[i] = &mgr->codecs[i];
+
+    return mgr->codec_cnt;
+}
+
+PJ_DEF(pj_codec*) pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id)
+{
+    pj_codec_factory *factory = mgr->factory_list.next;
+    while (factory != &mgr->factory_list) {
+	if ( (*factory->op->match_id)(factory, id) == 0 ) {
+	    pj_codec *codec = (*factory->op->alloc_codec)(factory, id);
+	    if (codec != NULL)
+		return codec;
+	}
+	factory = factory->next;
+    }
+    return NULL;
+}
+
+PJ_DEF(void) pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec)
+{
+    PJ_UNUSED_ARG(mgr)
+    (*codec->factory->op->dealloc_codec)(codec->factory, codec);
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/codec.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/codec.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/codec.h	(revision 1753)
@@ -0,0 +1,358 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/codec.h 7     8/24/05 10:29a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJMEDIA_CODEC_H__
+#define __PJMEDIA_CODEC_H__
+
+
+/**
+ * @file codec.h
+ * @brief Codec framework.
+ */
+
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_CODEC Codec framework.
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/** Top most media type. */
+typedef enum pj_media_type
+{
+    /** No type. */
+    PJ_MEDIA_TYPE_NONE = 0,
+
+    /** The media is audio */
+    PJ_MEDIA_TYPE_AUDIO = 1,
+
+    /** The media is video. */
+    PJ_MEDIA_TYPE_VIDEO = 2,
+
+    /** Unknown media type, in this case the name will be specified in 
+     *  encoding_name.
+     */
+    PJ_MEDIA_TYPE_UNKNOWN = 3,
+
+} pj_media_type;
+
+
+/** Media direction. */
+typedef enum pj_media_dir_t
+{
+    /** None */
+    PJ_MEDIA_DIR_NONE = 0,
+
+    /** Encoding (outgoing to network) stream */
+    PJ_MEDIA_DIR_ENCODING = 1,
+
+    /** Decoding (incoming from network) stream. */
+    PJ_MEDIA_DIR_DECODING = 2,
+
+    /** Incoming and outgoing stream. */
+    PJ_MEDIA_DIR_ENCODING_DECODING = 3,
+
+} pj_media_dir_t;
+
+
+/** Standard RTP paylist types. */
+typedef enum pj_rtp_pt
+{
+    PJ_RTP_PT_PCMU = 0,		/* audio PCMU */
+    PJ_RTP_PT_GSM  = 3,		/* audio GSM */
+    PJ_RTP_PT_G723 = 4,		/* audio G723 */
+    PJ_RTP_PT_DVI4_8K = 5,	/* audio DVI4 8KHz */
+    PJ_RTP_PT_DVI4_16K = 6,	/* audio DVI4 16Khz */
+    PJ_RTP_PT_LPC = 7,		/* audio LPC */
+    PJ_RTP_PT_PCMA = 8,		/* audio PCMA */
+    PJ_RTP_PT_G722 = 9,		/* audio G722 */
+    PJ_RTP_PT_L16_2 = 10,	/* audio 16bit linear 44.1KHz stereo */
+    PJ_RTP_PT_L16_1 = 11,	/* audio 16bit linear 44.1KHz mono */
+    PJ_RTP_PT_QCELP = 12,	/* audio QCELP */
+    PJ_RTP_PT_CN = 13,		/* audio Comfort Noise */
+    PJ_RTP_PT_MPA = 14,		/* audio MPEG1 or MPEG2 as elementary streams */
+    PJ_RTP_PT_G728 = 15,	/* audio G728 */
+    PJ_RTP_PT_DVI4_11K = 16,	/* audio DVI4 11.025KHz mono */
+    PJ_RTP_PT_DVI4_22K = 17,	/* audio DVI4 22.050KHz mono */
+    PJ_RTP_PT_G729 = 18,	/* audio G729 */
+    PJ_RTP_PT_CELB = 25,	/* video/comb Cell-B by Sun Microsystems (RFC 2029) */
+    PJ_RTP_PT_JPEG = 26,	/* video JPEG */
+    PJ_RTP_PT_NV = 28,		/* video NV implemented by nv program by Xerox */
+    PJ_RTP_PT_H261 = 31,	/* video H261 */
+    PJ_RTP_PT_MPV = 32,		/* video MPEG1 or MPEG2 elementary streams */
+    PJ_RTP_PT_MP2T = 33,	/* video MPEG2 transport */
+    PJ_RTP_PT_H263 = 34,	/* video H263 */
+
+    PJ_RTP_PT_DYNAMIC = 96,	/* start of dynamic RTP payload */
+} pj_rtp_pt;
+
+
+/** Identification used to search for codec factory that supports specific 
+ *  codec specification. 
+ */
+typedef struct pj_codec_id
+{
+    /** Media type. */
+    pj_media_type   type;
+
+    /** Payload type (can be dynamic). */
+    unsigned	    pt;
+
+    /** Encoding name, must be present if the payload type is dynamic. */
+    pj_str_t	    encoding_name;
+
+    /** Sampling rate. */
+    unsigned	    sample_rate;
+} pj_codec_id;
+
+
+/** Detailed codec attributes used both to configure a codec and to query
+ *  the capability of codec factories.
+ */
+typedef struct pj_codec_attr
+{
+    pj_uint32_t	sample_rate;	    /* Sampling rate in Hz */
+    pj_uint32_t	avg_bps;	    /* Average bandwidth in bits per second */
+
+    pj_uint8_t	pcm_bits_per_sample;/* Bits per sample in the PCM side */
+    pj_uint16_t	ptime;		    /* Packet time in miliseconds */
+
+    unsigned	pt:8;		    /* Payload type. */
+    unsigned    vad_enabled:1;	    /* Voice Activity Detector. */
+    unsigned    cng_enabled:1;	    /* Comfort Noise Generator. */
+    unsigned    lpf_enabled:1;	    /* Low pass filter */
+    unsigned    hpf_enabled:1;	    /* High pass filter */
+    unsigned    penh_enabled:1;	    /* Perceptual Enhancement */
+    unsigned    concl_enabled:1;    /* Packet loss concealment */
+    unsigned    reserved_bit:1;
+
+} pj_codec_attr;
+
+/** Types of audio frame. */
+typedef enum pj_audio_frame_type
+{
+    /** The frame is a silence audio frame. */
+    PJ_AUDIO_FRAME_SILENCE,
+
+    /** The frame is a non-silence audio frame. */
+    PJ_AUDIO_FRAME_AUDIO,
+
+} pj_audio_frame_type;
+
+typedef struct pj_codec pj_codec;
+typedef struct pj_codec_factory pj_codec_factory;
+
+
+/** This structure describes an audio frame. */
+struct pj_audio_frame
+{
+    /** Type: silence or non-silence. */
+    pj_audio_frame_type type;
+
+    /** Pointer to buffer. */
+    void	*buf;
+
+    /** Frame size in bytes. */
+    unsigned	 size;
+};
+
+/**
+ * Operations that must be supported by the codec.
+ */
+typedef struct pj_codec_op
+{
+    /** Get default attributes. */
+    pj_status_t (*default_attr) (pj_codec *codec, pj_codec_attr *attr);
+
+    /** Open and initialize codec using the specified attribute.
+     *  @return zero on success.
+     */
+    pj_status_t	(*init)( pj_codec *codec, pj_pool_t *pool );
+
+    /** Close and shutdown codec.
+     */
+    pj_status_t	(*open)( pj_codec *codec, pj_codec_attr *attr );
+
+    /** Close and shutdown codec.
+     */
+    pj_status_t (*close)( pj_codec *codec );
+
+    /** Encode frame.
+     */
+    pj_status_t (*encode)( pj_codec *codec, const struct pj_audio_frame *input,
+			   unsigned output_buf_len, struct pj_audio_frame *output);
+
+    /** Decode frame.
+     */
+    pj_status_t (*decode)( pj_codec *codec, const struct pj_audio_frame *input,
+			   unsigned output_buf_len, struct pj_audio_frame *output);
+
+} pj_codec_op;
+
+/**
+ * A codec describes an instance to encode or decode media frames. 
+ */
+struct pj_codec
+{
+    /** Entries to put this codec instance in codec factory's list. */
+    PJ_DECL_LIST_MEMBER(struct pj_codec)
+
+    /** Codec's private data. */
+    void	*codec_data;
+
+    /** Codec factory where this codec was allocated. */
+    pj_codec_factory *factory;
+
+    /** Operations to codec. */
+    pj_codec_op	*op;
+};
+
+/**
+ * This structure describes operations that must be supported by codec factories.
+ */
+typedef struct pj_codec_factory_op
+{
+    /** Check whether the factory can create codec with the specified ID.
+     *  @param factory The codec factory.
+     *  @param id  The codec ID.
+     *  @return zero it matches.
+     */
+    pj_status_t	(*match_id)( pj_codec_factory *factory, const pj_codec_id *id );
+
+    /** Create default attributes for the specified codec ID. This function can
+     *  be called by application to get the capability of the codec.
+     *  @param factory The codec factory.
+     *  @param id  The codec ID.
+     *  @param attr The attribute to be initialized.
+     *  @return zero if success.
+     */
+    pj_status_t (*default_attr)( pj_codec_factory *factory, const pj_codec_id *id,
+				 pj_codec_attr *attr );
+
+    /** Enumerate supported codecs.
+     *  @param factory The codec factory.
+     *  @param count Number of entries in the array.
+     *  @param codecs The codec array.
+     *  @return the total number of supported codecs, which can be less or 
+     *          greater than requested.
+     */
+    unsigned (*enum_codecs) (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
+
+    /** This function is called by codec manager to instantiate one codec
+     *  instance.
+     *  @param factory The codec factory.
+     *  @param id  The codec ID.
+     *  @return the instance of the codec, or NULL if codec can not be created.
+     */
+    pj_codec* (*alloc_codec)( pj_codec_factory *factory, const pj_codec_id *id);
+
+    /** This function is called by codec manager to return a particular instance
+     *  of codec back to the codec factory.
+     *  @param factory The codec factory.
+     *  @param codec The codec instance to be returned.
+     */
+    void (*dealloc_codec)( pj_codec_factory *factory, pj_codec *codec );
+
+} pj_codec_factory_op;
+
+/**
+ * Codec factory describes a module that is able to create codec with specific
+ * capabilities. These capabilities can be queried by codec manager to create
+ * instances of codec.
+ */
+struct pj_codec_factory
+{
+    /** Entries to put this structure in the codec manager list. */
+    PJ_DECL_LIST_MEMBER(struct pj_codec_factory)
+
+    /** The factory's private data. */
+    void		*factory_data;
+
+    /** Operations to the factory. */
+    pj_codec_factory_op *op;
+
+};
+
+/**
+ * Declare maximum codecs
+ */
+#define PJ_CODEC_MGR_MAX_CODECS	    32
+
+/**
+ * Codec manager maintains codec factory etc.
+ */
+typedef struct pj_codec_mgr
+{
+    pj_codec_factory factory_list;
+    unsigned	     codec_cnt;
+    pj_codec_id	     codecs[PJ_CODEC_MGR_MAX_CODECS];
+} pj_codec_mgr;
+
+/**
+ * Init codec manager.
+ */
+PJ_DECL(pj_status_t) 
+pj_codec_mgr_init (pj_codec_mgr *mgr);
+
+/** 
+ * Register codec to codec manager. 
+ */
+PJ_DECL(pj_status_t) 
+pj_codec_mgr_register_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
+
+/**
+ * Unregister codec.
+ */
+PJ_DECL(void) 
+pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
+
+/**
+ * Enumerate codecs.
+ */
+PJ_DECL(unsigned)
+pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]);
+
+/**
+ * Open codec.
+ */
+PJ_DECL(pj_codec*) 
+pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id);
+
+/**
+ * Close codec.
+ */
+PJ_DECL(void) 
+pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_CODEC_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/config.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/config.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/config.h	(revision 1753)
@@ -0,0 +1,32 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/config.h 3     2/24/05 10:40a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJMED_CONFIG_H__
+#define __PJMED_CONFIG_H__
+
+/**
+ * @defgroup PJMEDIA Media Stack
+ */
+
+
+#endif	/* __PJMED_CONFIG_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/dsound.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/dsound.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/dsound.c	(revision 1753)
@@ -0,0 +1,882 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/dsound.c 6     6/14/05 12:54a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifdef _MSC_VER
+//# pragma warning(disable: 4201)   // non-standard extension: nameless struct/union
+# pragma warning(push, 3)
+#endif
+#include <pj/config.h>
+#include <pj/os.h>
+#include <pj/log.h>
+
+#include <dsound.h>
+#include <stdio.h>
+#include <assert.h>
+#include <pjmedia/sound.h>
+
+#define THIS_FILE   "dsound.c"
+
+/*
+ * Constants
+ */
+#define PACKET_BUFFER_COUNT 4
+
+typedef struct PJ_Direct_Sound_Device PJ_Direct_Sound_Device;
+
+
+/*
+ * DirectSound Factory Operations
+ */
+static pj_status_t dsound_init(void);
+static const char *dsound_get_name(void);
+static pj_status_t dsound_destroy(void);
+static pj_status_t dsound_enum_devices(int *count, char *dev_names[]);
+static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev);
+static pj_status_t dsound_destroy_dev(pj_snd_dev *dev);
+
+
+/*
+ * DirectSound Device Operations
+ */
+static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
+static pj_status_t dsound_dev_close( pj_snd_dev *dev );
+static pj_status_t dsound_dev_play( pj_snd_dev *dev );
+static pj_status_t dsound_dev_record( pj_snd_dev *dev );
+
+/*
+ * Utils.
+ */
+static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev );
+
+
+static pj_snd_dev_factory dsound_factory = 
+{
+    &dsound_init,
+    &dsound_get_name,
+    &dsound_destroy,
+    &dsound_enum_devices,
+    &dsound_create_dev,
+    &dsound_destroy_dev
+};
+
+static struct pj_snd_dev_op dsound_dev_op = 
+{
+    &dsound_dev_open,
+    &dsound_dev_close,
+    &dsound_dev_play,
+    &dsound_dev_record
+};
+
+#define DSOUND_TYPE_PLAYER	1
+#define DSOUND_TYPE_RECORDER	2
+
+typedef struct Direct_Sound_Descriptor
+{
+    int			type;
+
+    LPDIRECTSOUND	lpDsPlay;
+    LPDIRECTSOUNDBUFFER	lpDsPlayBuffer;
+
+    LPDIRECTSOUNDCAPTURE	lpDsCapture;
+    LPDIRECTSOUNDCAPTUREBUFFER	lpDsCaptureBuffer;
+
+    LPDIRECTSOUNDNOTIFY	lpDsNotify;
+    HANDLE		hEvent;
+    HANDLE		hThread;
+    HANDLE		hStartEvent;
+    DWORD		dwThreadQuitFlag;
+    pj_thread_desc	thread_desc;
+    pj_thread_t	       *thread;
+} Direct_Sound_Descriptor;
+
+struct PJ_Direct_Sound_Device
+{
+    Direct_Sound_Descriptor playDesc;
+    Direct_Sound_Descriptor recDesc;
+};
+
+struct Thread_Param
+{
+    pj_snd_dev	    *dev;
+    Direct_Sound_Descriptor *desc;
+};
+
+PJ_DEF(pj_snd_dev_factory*) pj_dsound_get_factory()
+{
+    return &dsound_factory;
+}
+
+/*
+ * Init DirectSound.
+ */
+static pj_status_t dsound_init(void)
+{
+    /* Nothing to do. */
+    return 0;
+}
+
+/*
+ * Get the name of the factory.
+ */
+static const char *dsound_get_name(void)
+{
+    return "DirectSound";
+}
+
+/*
+ * Destroy DirectSound.
+ */
+static pj_status_t dsound_destroy(void)
+{
+    /* TODO: clean up devices in case application haven't done it. */
+    return 0;
+}
+
+/*
+ * Enum devices in the system.
+ */
+static pj_status_t dsound_enum_devices(int *count, char *dev_names[])
+{
+    dev_names[0] = "DirectSound Default Device";
+    *count = 1;
+    return 0;
+}
+
+/*
+ * Create DirectSound device.
+ */
+static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev)
+{
+    PJ_Direct_Sound_Device *dsDev;
+
+    /* TODO: create based on the name. */
+    PJ_TODO(DSOUND_CREATE_DEVICE_BY_NAME);
+    
+    /* Create DirectSound structure. */
+    dsDev = malloc(sizeof(*dsDev));
+    if (!dsDev) {
+	PJ_LOG(1,(THIS_FILE, "No memory to allocate device!"));
+	return -1;
+    }
+    memset(dsDev, 0, sizeof(*dsDev));
+
+    /* Associate DirectSound device with device. */
+    dev->device = dsDev;
+    dev->op = &dsound_dev_op;
+
+    return 0;
+}
+
+/*
+ * Destroy DirectSound device.
+ */
+static pj_status_t dsound_destroy_dev( pj_snd_dev *dev )
+{
+    if (dev->device) {
+	free(dev->device);
+	dev->device = NULL;
+    }
+    return 0;
+}
+
+static void dsound_release_descriptor(Direct_Sound_Descriptor *desc)
+{
+    if (desc->lpDsNotify)
+	IDirectSoundNotify_Release( desc->lpDsNotify );
+    
+    if (desc->hEvent)
+	CloseHandle(desc->hEvent);
+
+    if (desc->lpDsPlayBuffer)
+	IDirectSoundBuffer_Release( desc->lpDsPlayBuffer );
+
+    if (desc->lpDsPlay)
+	IDirectSound_Release( desc->lpDsPlay );
+
+    if (desc->lpDsCaptureBuffer)
+	IDirectSoundCaptureBuffer_Release(desc->lpDsCaptureBuffer);
+
+    if (desc->lpDsCapture)
+	IDirectSoundCapture_Release(desc->lpDsCapture);
+}
+
+/*
+ * Destroy DirectSound resources.
+ */
+static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev )
+{
+    dsound_release_descriptor( &dsDev->playDesc );
+    dsound_release_descriptor( &dsDev->recDesc );
+    memset(dsDev, 0, sizeof(*dsDev));
+    return 0;
+}
+
+static void init_waveformatex (PCMWAVEFORMAT *pcmwf, pj_snd_dev *dev)
+{
+    memset(pcmwf, 0, sizeof(PCMWAVEFORMAT)); 
+    pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM; 
+    pcmwf->wf.nChannels = 1;
+    pcmwf->wf.nSamplesPerSec = dev->param.samples_per_sec;
+    pcmwf->wf.nBlockAlign = dev->param.bytes_per_frame;
+    pcmwf->wf.nAvgBytesPerSec = 
+	dev->param.samples_per_sec * dev->param.bytes_per_frame;
+    pcmwf->wBitsPerSample = dev->param.bits_per_sample;
+}
+
+/*
+ * Initialize DirectSound player device.
+ */
+static pj_status_t dsound_init_player (pj_snd_dev *dev)
+{
+    HRESULT hr;
+    HWND hwnd;
+    PCMWAVEFORMAT pcmwf; 
+    DSBUFFERDESC dsbdesc;
+    DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+    unsigned i;
+    PJ_Direct_Sound_Device *dsDev = dev->device;
+
+    /*
+     * Check parameters.
+     */
+    if (dev->play_cb == NULL) {
+	assert(0);
+	return -1;
+    }
+    if (dev->device == NULL) {
+	assert(0);
+	return -1;
+    }
+
+    PJ_LOG(4,(THIS_FILE, "Creating DirectSound player device"));
+
+    /*
+     * Create DirectSound device.
+     */
+    hr = DirectSoundCreate(NULL, &dsDev->playDesc.lpDsPlay, NULL);
+    if (FAILED(hr))
+	goto on_error;
+
+    hwnd = GetForegroundWindow();
+    if (hwnd == NULL) {
+	hwnd = GetDesktopWindow();
+    }    
+    hr = IDirectSound_SetCooperativeLevel( dsDev->playDesc.lpDsPlay, hwnd, 
+					   DSSCL_PRIORITY);
+    if FAILED(hr)
+	goto on_error;
+    
+    /*
+     * Create DirectSound play buffer.
+     */    
+    // Set up wave format structure. 
+    init_waveformatex (&pcmwf, dev);
+
+    // Set up DSBUFFERDESC structure. 
+    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
+    dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
+    dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY |
+		      DSBCAPS_GETCURRENTPOSITION2;
+
+    dsbdesc.dwBufferBytes = 
+	(PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * 
+	 dev->param.frames_per_packet); 
+    dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; 
+
+    // Create buffer. 
+    hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc, 
+					&dsDev->playDesc.lpDsPlayBuffer, NULL); 
+    if (FAILED(hr) )
+	goto on_error;
+
+    /*
+     * Create event for play notification.
+     */
+    dsDev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+    if (dsDev->playDesc.hEvent == NULL)
+	goto on_error;
+
+    /*
+     * Setup notification for play.
+     */
+    hr = IDirectSoundBuffer_QueryInterface( dsDev->playDesc.lpDsPlayBuffer, 
+					    &IID_IDirectSoundNotify, 
+					    (LPVOID *)&dsDev->playDesc.lpDsNotify); 
+    if (FAILED(hr))
+	goto on_error;
+
+    
+    for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
+	dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame * 
+				  dev->param.frames_per_packet;
+	dsPosNotify[i].hEventNotify = dsDev->playDesc.hEvent;
+    }
+    
+    hr = IDirectSoundNotify_SetNotificationPositions( dsDev->playDesc.lpDsNotify, 
+						      PACKET_BUFFER_COUNT, 
+						      dsPosNotify);
+    if (FAILED(hr))
+	goto on_error;
+
+    /* Done setting up play device. */
+    PJ_LOG(4,(THIS_FILE, "DirectSound player device created"));
+
+    return 0;
+
+on_error:
+    PJ_LOG(2,(THIS_FILE, "Error creating player device, hresult=0x%x", hr));
+    dsound_destroy_dsound_dev(dsDev);
+    return -1;
+}
+
+/*
+ * Initialize DirectSound recorder device
+ */
+static pj_status_t dsound_init_recorder (pj_snd_dev *dev)
+{
+    HRESULT hr;
+    PCMWAVEFORMAT pcmwf; 
+    DSCBUFFERDESC dscbdesc;
+    DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+    unsigned i;
+    PJ_Direct_Sound_Device *dsDev = dev->device;
+
+    /*
+     * Check parameters.
+     */
+    if (dev->rec_cb == NULL) {
+	assert(0);
+	return -1;
+    }
+    if (dev->device == NULL) {
+	assert(0);
+	return -1;
+    }
+
+    PJ_LOG(4,(THIS_FILE, "Creating DirectSound recorder device"));
+
+    /*
+     * Creating recorder device.
+     */
+    hr = DirectSoundCaptureCreate(NULL, &dsDev->recDesc.lpDsCapture, NULL);
+    if (FAILED(hr))
+	goto on_error;
+
+    /* Init wave format */
+    init_waveformatex (&pcmwf, dev);
+
+    /* 
+     * Setup capture buffer using sound buffer structure that was passed
+     * to play buffer creation earlier.
+     */
+    memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
+    dscbdesc.dwSize = sizeof(DSCBUFFERDESC); 
+    dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ;
+    dscbdesc.dwBufferBytes = 
+	(PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * 
+	 dev->param.frames_per_packet); 
+    dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; 
+
+    hr = IDirectSoundCapture_CreateCaptureBuffer( dsDev->recDesc.lpDsCapture,
+						  &dscbdesc, 
+						  &dsDev->recDesc.lpDsCaptureBuffer, 
+						  NULL);
+    if (FAILED(hr))
+	goto on_error;
+
+    /*
+     * Create event for play notification.
+     */
+    dsDev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+    if (dsDev->recDesc.hEvent == NULL)
+	goto on_error;
+
+    /*
+     * Setup notifications for recording.
+     */
+    hr = IDirectSoundCaptureBuffer_QueryInterface( dsDev->recDesc.lpDsCaptureBuffer, 
+						   &IID_IDirectSoundNotify, 
+						   (LPVOID *)&dsDev->recDesc.lpDsNotify); 
+    if (FAILED(hr))
+	goto on_error;
+
+    
+    for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
+	dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame * 
+				  dev->param.frames_per_packet;
+	dsPosNotify[i].hEventNotify = dsDev->recDesc.hEvent;
+    }
+    
+    hr = IDirectSoundNotify_SetNotificationPositions( dsDev->recDesc.lpDsNotify, 
+						      PACKET_BUFFER_COUNT, 
+						      dsPosNotify);
+    if (FAILED(hr))
+	goto on_error;
+
+    /* Done setting up recorder device. */
+    PJ_LOG(4,(THIS_FILE, "DirectSound recorder device created"));
+
+    return 0;
+
+on_error:
+    PJ_LOG(4,(THIS_FILE, "Error creating device, hresult=%d", hr));
+    dsound_destroy_dsound_dev(dsDev);
+    return -1;
+}
+
+/*
+ * Initialize DirectSound device.
+ */
+static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
+{
+    PJ_Direct_Sound_Device *dsDev = dev->device;
+    pj_status_t status;
+
+    dsDev->playDesc.type = DSOUND_TYPE_PLAYER;
+    dsDev->recDesc.type = DSOUND_TYPE_RECORDER;
+
+    if (role & PJ_SOUND_PLAYER) {
+	status = dsound_init_player (dev);
+	if (status != 0)
+	    return status;
+    }
+
+    if (role & PJ_SOUND_RECORDER) {
+	status = dsound_init_recorder (dev);
+	if (status != 0)
+	    return status;
+    }
+
+    return 0;
+}
+
+/*
+ * Close DirectSound device.
+ */
+static pj_status_t dsound_dev_close( pj_snd_dev *dev )
+{
+    PJ_LOG(4,(THIS_FILE, "Closing DirectSound device"));
+
+    if (dev->device) {
+	PJ_Direct_Sound_Device *dsDev = dev->device;
+
+	if (dsDev->playDesc.hThread) {
+	    PJ_LOG(4,(THIS_FILE, "Stopping DirectSound player"));
+	    dsDev->playDesc.dwThreadQuitFlag = 1;
+	    SetEvent(dsDev->playDesc.hEvent);
+	    if (WaitForSingleObject(dsDev->playDesc.hThread, 1000) != WAIT_OBJECT_0) {
+		PJ_LOG(4,(THIS_FILE, "Timed out waiting player thread to quit"));
+		TerminateThread(dsDev->playDesc.hThread, -1);
+		IDirectSoundBuffer_Stop( dsDev->playDesc.lpDsPlayBuffer );
+	    }
+	    
+	    pj_thread_destroy (dsDev->playDesc.thread);
+	}
+
+	if (dsDev->recDesc.hThread) {
+	    PJ_LOG(4,(THIS_FILE, "Stopping DirectSound recorder"));
+	    dsDev->recDesc.dwThreadQuitFlag = 1;
+	    SetEvent(dsDev->recDesc.hEvent);
+	    if (WaitForSingleObject(dsDev->recDesc.hThread, 1000) != WAIT_OBJECT_0) {
+		PJ_LOG(4,(THIS_FILE, "Timed out waiting recorder thread to quit"));
+		TerminateThread(dsDev->recDesc.hThread, -1);
+		IDirectSoundCaptureBuffer_Stop( dsDev->recDesc.lpDsCaptureBuffer );
+	    }
+	    
+	    pj_thread_destroy (dsDev->recDesc.thread);
+	}
+
+	dsound_destroy_dsound_dev( dev->device );
+	dev->op = NULL;
+    }
+
+    PJ_LOG(4,(THIS_FILE, "DirectSound device closed"));
+    return 0;
+}
+
+static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer.
+				  DWORD dwOffset,		    // Our own write cursor.
+				  LPBYTE lpbSoundData,		    // Start of our data.
+				  DWORD dwSoundBytes)		    // Size of block to copy.
+{ 
+    LPVOID  lpvPtr1; 
+    DWORD dwBytes1; 
+    LPVOID  lpvPtr2; 
+    DWORD dwBytes2; 
+    HRESULT hr; 
+    
+    // Obtain memory address of write block. This will be in two parts
+    // if the block wraps around.
+    
+    hr = IDirectSoundCaptureBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1, 
+					 &dwBytes1, &lpvPtr2, &dwBytes2, 0); 
+    
+    if SUCCEEDED(hr) { 
+	// Read from pointers. 
+	CopyMemory(lpbSoundData, lpvPtr1, dwBytes1); 
+	if (lpvPtr2 != NULL)
+	    CopyMemory(lpbSoundData+dwBytes1, lpvPtr2, dwBytes2); 
+	
+	// Release the data back to DirectSound. 
+	hr = IDirectSoundCaptureBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); 
+	if SUCCEEDED(hr)
+	    return TRUE; 
+    } 
+    
+    // Lock, Unlock, or Restore failed. 
+    return FALSE; 
+}
+
+
+static BOOL AppWriteDataToBuffer(LPDIRECTSOUNDBUFFER lpDsb,  // The buffer.
+				 DWORD dwOffset,	      // Our own write cursor.
+				 LPBYTE lpbSoundData,	      // Start of our data.
+				 DWORD dwSoundBytes)	      // Size of block to copy.
+{ 
+    LPVOID  lpvPtr1; 
+    DWORD dwBytes1; 
+    LPVOID  lpvPtr2; 
+    DWORD dwBytes2; 
+    HRESULT hr; 
+    
+    // Obtain memory address of write block. This will be in two parts
+    // if the block wraps around.
+    
+    hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1, 
+				  &dwBytes1, &lpvPtr2, &dwBytes2, 0); 
+    
+    // If the buffer was lost, restore and retry lock. 
+    if (DSERR_BUFFERLOST == hr) { 
+	IDirectSoundBuffer_Restore(lpDsb); 
+	hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, 
+				      &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); 
+    } 
+    if SUCCEEDED(hr) { 
+	CopyMemory(lpvPtr1, lpbSoundData, dwBytes1); 
+	if (NULL != lpvPtr2) 
+	    CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2); 
+	
+	hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); 
+	if SUCCEEDED(hr)
+	    return TRUE; 
+    } 
+    
+    return FALSE; 
+}
+
+
+/*
+ * Player thread.
+ */
+static DWORD WINAPI dsound_dev_thread(void *arg)
+{
+    struct Thread_Param *param = arg;
+    pj_snd_dev *dev = param->dev;
+    Direct_Sound_Descriptor *desc = param->desc;
+    unsigned bytes_per_pkt = dev->param.bytes_per_frame *
+			     dev->param.frames_per_packet;
+    unsigned samples_per_pkt = dev->param.samples_per_frame *
+			       dev->param.frames_per_packet;
+    void *buffer = NULL;
+    DWORD size;
+    pj_status_t status;
+    DWORD sample_pos;
+    DWORD byte_pos;
+    DWORD buffer_size = 
+	(PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * 
+	 dev->param.frames_per_packet);
+    HRESULT hr;
+
+    PJ_LOG(4,(THIS_FILE, "DirectSound thread starting"));
+
+    /* Allocate buffer for sound data */
+    buffer = malloc(bytes_per_pkt);
+    if (!buffer) {
+	PJ_LOG(1,(THIS_FILE, "Unable to allocate packet buffer!"));
+	return (DWORD)-1;
+    }
+
+    desc->thread = pj_thread_register ("dsound", desc->thread_desc);
+    if (desc->thread == NULL)
+	return (DWORD)-1;
+
+    /*
+     * Start playing or recording!
+     */
+    if (desc->type == DSOUND_TYPE_PLAYER) {
+	hr = IDirectSoundBuffer_SetCurrentPosition( desc->lpDsPlayBuffer, 0);
+	if (FAILED(hr)) {
+	    PJ_LOG(1,(THIS_FILE, "DirectSound play: SetCurrentPosition() error %d", hr));
+	    goto on_error;
+	}
+
+	hr = IDirectSoundBuffer_Play(desc->lpDsPlayBuffer, 0, 0, DSBPLAY_LOOPING);
+	if (FAILED(hr)) {
+	    PJ_LOG(1,(THIS_FILE, "DirectSound: Play() error %d", hr));
+	    goto on_error;
+	}
+    } else {
+	hr = IDirectSoundCaptureBuffer_Start(desc->lpDsCaptureBuffer, DSCBSTART_LOOPING );
+	if (FAILED(hr)) {
+	    PJ_LOG(1,(THIS_FILE, "DirectSound: Record() error %d", hr));
+	    goto on_error;
+	}
+    }
+
+    /*
+     * Reset initial positions.
+     */
+    byte_pos = 0xFFFFFFFF;
+    sample_pos = 0;
+
+    /*
+     * Wait to get the first notification.
+     */
+    if (WaitForSingleObject(desc->hEvent, 100) != WAIT_OBJECT_0) {
+	PJ_LOG(1,(THIS_FILE, "DirectSound: error getting notification"));
+	goto on_error;
+    }
+
+	
+    /* Get initial byte position. */
+    if (desc->type == DSOUND_TYPE_PLAYER) {
+	hr = IDirectSoundBuffer_GetCurrentPosition( desc->lpDsPlayBuffer, 
+						    NULL, &byte_pos );
+	if (FAILED(hr)) {
+	    PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
+				 "position, err %d", hr));
+	    goto on_error;
+	}
+    } else {
+	hr = IDirectSoundCaptureBuffer_GetCurrentPosition( desc->lpDsCaptureBuffer, 
+							   NULL, &byte_pos );
+	if (FAILED(hr)) {
+	    PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
+				 "position, err %d", hr));
+	    goto on_error;
+	}
+    }
+
+    /* Signal main thread that we're running. */
+    assert( desc->hStartEvent );
+    SetEvent( desc->hStartEvent );
+
+    /*
+     * Loop while not signalled to quit, wait for event object to be signalled
+     * by DirectSound play buffer, then request for sound data from the 
+     * application and write it to DirectSound buffer.
+     */
+    do {
+	
+	/* Call callback to get sound data */
+	if (desc->type == DSOUND_TYPE_PLAYER) {
+	    size = bytes_per_pkt;
+	    status = (*dev->play_cb)(dev, sample_pos, buffer, &size);
+
+	    /* Quit thread on error. */
+	    if (status != 0)
+		break;
+
+	    /* Write zeroes when we've got nothing from application. */
+	    if (size == 0) {
+		memset(buffer, 0, bytes_per_pkt);
+		size = bytes_per_pkt;
+	    }
+
+	    /* Write to DirectSound buffer. */
+	    AppWriteDataToBuffer( desc->lpDsPlayBuffer, byte_pos,
+				  (LPBYTE)buffer, size);
+
+	} else {
+	    /* Capture from DirectSound buffer. */
+	    size = bytes_per_pkt;
+	    if (AppReadDataFromBuffer( desc->lpDsCaptureBuffer, byte_pos,
+				       (LPBYTE)buffer, size)) {
+
+	    } else {
+		memset(buffer, 0, size);
+	    }
+
+	    /* Call callback */
+	    status = (*dev->rec_cb)(dev, sample_pos, buffer, size);
+
+	    /* Quit thread on error. */
+	    if (status != 0)
+		break;
+	}
+
+	/* Increment position. */
+	byte_pos += size;
+	if (byte_pos >= buffer_size)
+	    byte_pos -= buffer_size;
+	sample_pos += samples_per_pkt;
+
+	while (WaitForSingleObject(desc->hEvent, 500) != WAIT_OBJECT_0 &&
+	       (!desc->dwThreadQuitFlag)) 
+	{
+	    Sleep(1);
+	}
+    } while (!desc->dwThreadQuitFlag);
+
+
+    PJ_LOG(4,(THIS_FILE, "DirectSound: stopping.."));
+
+    free(buffer);
+    if (desc->type == DSOUND_TYPE_PLAYER) {
+	IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
+    } else {
+	IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
+    }
+    return 0;
+
+on_error:
+    PJ_LOG(4,(THIS_FILE, "DirectSound play stopping"));
+
+    if (buffer) 
+	free(buffer);
+    if (desc->type == DSOUND_TYPE_PLAYER) {
+	IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
+    } else {
+	IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
+    }
+    desc->dwThreadQuitFlag = 1;
+
+    /* Signal main thread that we failed to initialize */
+    assert( desc->hStartEvent );
+    SetEvent( desc->hStartEvent );
+    return -1;
+}
+
+
+/*
+ * Generic starter for play/record.
+ */
+static pj_status_t dsound_dev_play_record( pj_snd_dev *dev,
+					   Direct_Sound_Descriptor *desc )
+{
+    DWORD threadId;
+    int op_type = desc->type;
+    const char *op_name = (op_type == DSOUND_TYPE_PLAYER) ? "play" : "record";
+    struct Thread_Param param;
+
+    PJ_LOG(4,(THIS_FILE, "DirectSound %s()", op_name));
+
+    /*
+     * Create event for the thread to signal us that it is starting or
+     * quitting during startup.
+     */
+    desc->hStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (desc->hStartEvent == NULL) {
+	PJ_LOG(1,(THIS_FILE, "DirectSound %s: unable to create event", op_name));
+	return -1;
+    }
+
+    param.dev = dev;
+    param.desc = desc;
+
+    /*
+     * Create thread to handle feeding up data to player/recorder.
+     */
+    desc->hThread = NULL;
+    desc->dwThreadQuitFlag = 0;
+    desc->hThread = CreateThread( NULL, 0, &dsound_dev_thread, &param, 
+					    CREATE_SUSPENDED, &threadId);
+    if (!desc->hThread) {
+	PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to create thread", op_name));
+	return -1;
+    }
+
+    SetThreadPriority( desc->hThread, THREAD_PRIORITY_HIGHEST);
+
+    /*
+     * Resume thread.
+     */
+    if (ResumeThread(desc->hThread) == (DWORD)-1) {
+	PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to resume thread", op_name));
+	goto on_error;
+    }
+
+    /*
+     * Wait until we've got signal from the thread that it has successfully
+     * started, or when it is quitting.
+     */
+    WaitForSingleObject( desc->hStartEvent, INFINITE);
+
+    /* We can destroy the event now. */
+    CloseHandle( desc->hStartEvent );
+    desc->hStartEvent = NULL;
+
+    /* Examine thread status. */
+    if (desc->dwThreadQuitFlag != 0) {
+	/* Thread failed to initialize */
+	WaitForSingleObject(desc->hThread, INFINITE);
+	CloseHandle(desc->hThread);
+	desc->hThread = NULL;
+	return -1;
+    }
+
+    return 0;
+
+on_error:
+    TerminateThread(desc->hThread, -1);
+    CloseHandle(desc->hThread);
+    desc->hThread = NULL;
+    return -1;
+}
+
+/*
+ * Start playing.
+ */
+static pj_status_t dsound_dev_play( pj_snd_dev *dev )
+{
+    PJ_Direct_Sound_Device *dsDev = dev->device;
+
+    assert(dsDev);
+    if (!dsDev) {
+	assert(0);
+	return -1;
+    }
+
+    return dsound_dev_play_record( dev, &dsDev->playDesc );
+}
+
+/*
+ * Start recording.
+ */
+static pj_status_t dsound_dev_record( pj_snd_dev *dev )
+{
+    PJ_Direct_Sound_Device *dsDev = dev->device;
+
+    assert(dsDev);
+    if (!dsDev) {
+	assert(0);
+	return -1;
+    }
+
+    return dsound_dev_play_record( dev, &dsDev->recDesc );
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+# pragma warning(disable: 4514)	// unreferenced inline function has been removed
+#endif
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/g711.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/g711.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/g711.c	(revision 1753)
@@ -0,0 +1,621 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/g711.c 3     4/17/05 11:59a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+/* This file contains file from Sun Microsystems, Inc, with the complete 
+ * copyright notice in the second half of this file.
+ */
+#include <pjmedia/codec.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <string.h>	/* memset */
+
+#define G711_BPS	64000
+#define G711_CODEC_CNT	0	/* number of codec to preallocate in memory */
+
+/* These are the only public functions exported to applications */
+PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
+PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
+
+/* Algorithm prototypes. */
+static unsigned char linear2alaw(int		pcm_val);   /* 2's complement (16-bit range) */
+static int	     alaw2linear(unsigned char	a_val);
+static unsigned char linear2ulaw(int		pcm_val);
+static int	     ulaw2linear(unsigned char	u_val);
+
+/* Prototypes for G711 factory */
+static pj_status_t  g711_match_id( pj_codec_factory *factory, const pj_codec_id *id );
+static pj_status_t  g711_default_attr( pj_codec_factory *factory, const pj_codec_id *id, pj_codec_attr *attr );
+static unsigned	    g711_enum_codecs (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
+static pj_codec*    g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id);
+static void	    g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec );
+
+/* Prototypes for G711 implementation. */
+static pj_status_t  g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr);
+static pj_status_t  g711_init( pj_codec *codec, pj_pool_t *pool );
+static pj_status_t  g711_open( pj_codec *codec, pj_codec_attr *attr );
+static pj_status_t  g711_close( pj_codec *codec );
+static pj_status_t  g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
+				 unsigned output_buf_len, struct pj_audio_frame *output);
+static pj_status_t  g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
+				 unsigned output_buf_len, struct pj_audio_frame *output);
+
+/* Definition for G711 codec operations. */
+static pj_codec_op g711_op = 
+{
+    &g711_codec_default_attr ,
+    &g711_init,
+    &g711_open,
+    &g711_close,
+    &g711_encode,
+    &g711_decode
+};
+
+/* Definition for G711 codec factory operations. */
+static pj_codec_factory_op g711_factory_op =
+{
+    &g711_match_id,
+    &g711_default_attr,
+    &g711_enum_codecs,
+    &g711_alloc_codec,
+    &g711_dealloc_codec
+};
+
+/* G711 factory private data */
+struct g711_factory_private
+{
+    pj_pool_t  *pool;
+    pj_codec	codec_list;
+};
+
+/* G711 codec private data. */
+struct g711_private
+{
+    unsigned pt;
+};
+
+
+PJ_DEF(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool)
+{
+    struct g711_factory_private *priv;
+    //enum { CODEC_MEM_SIZE = sizeof(pj_codec) + sizeof(struct g711_private) + 4 };
+
+    /* Create pool. */
+    /*
+    pool = pj_pool_pool_create_pool(pp, "g711ftry", 
+					G711_CODEC_CNT*CODEC_MEM_SIZE + 
+					sizeof(struct g711_factory_private),
+				        CODEC_MEM_SIZE, NULL);
+    if (!pool)
+	return -1;
+    */
+
+    priv = pj_pool_alloc(pool, sizeof(struct g711_factory_private));
+    if (!priv)
+	return -1;
+
+    factory->factory_data = priv;
+    factory->op = &g711_factory_op;
+
+    priv->pool = pool;
+    pj_list_init(&priv->codec_list);
+    return 0;
+}
+
+PJ_DEF(pj_status_t) g711_deinit_factory (pj_codec_factory *factory)
+{
+    struct g711_factory_private *priv = factory->factory_data;
+
+    /* Invalidate member to help detect errors */
+    priv->pool = NULL;
+    priv->codec_list.next = priv->codec_list.prev = NULL;
+    return 0;
+}
+
+static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id )
+{
+    PJ_UNUSED_ARG(factory)
+
+    /* It's sufficient to check payload type only. */
+    return (id->pt==PJ_RTP_PT_PCMU || id->pt==PJ_RTP_PT_PCMA) ? 0 : -1;
+}
+
+static pj_status_t g711_default_attr (pj_codec_factory *factory, 
+				      const pj_codec_id *id, 
+				      pj_codec_attr *attr )
+{
+    PJ_UNUSED_ARG(factory)
+
+    memset(attr, 0, sizeof(pj_codec_attr));
+    attr->sample_rate = 8000;
+    attr->avg_bps = G711_BPS;
+    attr->pcm_bits_per_sample = 16;
+    attr->ptime = 20;
+    attr->pt = id->pt;
+
+    /* Default all flag bits disabled. */
+
+    return PJ_OK;
+}
+
+static unsigned	g711_enum_codecs (pj_codec_factory *factory, 
+				  unsigned count, pj_codec_id codecs[])
+{
+    PJ_UNUSED_ARG(factory)
+
+    if (count > 0) {
+	codecs[0].type = PJ_MEDIA_TYPE_AUDIO;
+	codecs[0].pt = PJ_RTP_PT_PCMU;
+	codecs[0].encoding_name = pj_str("PCMU");
+	codecs[0].sample_rate = 8000;
+    }
+    if (count > 1) {
+	codecs[1].type = PJ_MEDIA_TYPE_AUDIO;
+	codecs[1].pt = PJ_RTP_PT_PCMA;
+	codecs[1].encoding_name = pj_str("PCMA");
+	codecs[1].sample_rate = 8000;
+    }
+
+    return 2;
+}
+
+static pj_codec *g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id)
+{
+    struct g711_factory_private *priv = factory->factory_data;
+    pj_codec *codec = NULL;
+
+    /* Allocate new codec if no more is available */
+    if (pj_list_empty(&priv->codec_list)) {
+	struct g711_private *codec_priv;
+
+	codec = pj_pool_alloc(priv->pool, sizeof(pj_codec));
+	codec_priv = pj_pool_alloc(priv->pool, sizeof(struct g711_private));
+	if (!codec || !codec_priv)
+	    return NULL;
+
+	codec_priv->pt = id->pt;
+
+	codec->factory = factory;
+	codec->op = &g711_op;
+	codec->codec_data = codec_priv;
+    } else {
+	codec = priv->codec_list.next;
+	pj_list_erase(codec);
+    }
+
+    /* Zero the list, for error detection in g711_dealloc_codec */
+    codec->next = codec->prev = NULL;
+
+    return codec;
+}
+
+static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec )
+{
+    struct g711_factory_private *priv = factory->factory_data;
+
+    /* Check that this node has not been deallocated before */
+    pj_assert (codec->next==NULL && codec->prev==NULL);
+    if (codec->next!=NULL || codec->prev!=NULL) {
+	return;
+    }
+
+    /* Insert at the back of the list */
+    pj_list_insert_before(&priv->codec_list, codec);
+}
+
+static pj_status_t g711_codec_default_attr  (pj_codec *codec, pj_codec_attr *attr)
+{
+    struct g711_private *priv = codec->codec_data;
+    pj_codec_id id;
+
+    id.pt = priv->pt;
+    return g711_default_attr (NULL, &id, attr);
+}
+
+static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool )
+{
+    /* There's nothing to do here really */
+    PJ_UNUSED_ARG(codec)
+    PJ_UNUSED_ARG(pool)
+
+    return PJ_OK;
+}
+
+static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr )
+{
+    struct g711_private *priv = codec->codec_data;
+    priv->pt = attr->pt;
+    return PJ_OK;
+}
+
+static pj_status_t g711_close( pj_codec *codec )
+{
+    PJ_UNUSED_ARG(codec);
+    /* Nothing to do */
+    return PJ_OK;
+}
+
+static pj_status_t  g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
+				 unsigned output_buf_len, struct pj_audio_frame *output)
+{
+    pj_int16_t *samples = (pj_int16_t*) input->buf;
+    struct g711_private *priv = codec->codec_data;
+
+    /* Check output buffer length */
+    if (output_buf_len < input->size / 2)
+	return -1;
+
+    /* Encode */
+    if (priv->pt == PJ_RTP_PT_PCMA) {
+	unsigned i;
+	pj_uint8_t *dst = output->buf;
+
+	for (i=0; i!=input->size/2; ++i, ++dst) {
+	    *dst = linear2alaw(samples[i]);
+	}
+    } else if (priv->pt == PJ_RTP_PT_PCMU) {
+	unsigned i;
+	pj_uint8_t *dst = output->buf;
+
+	for (i=0; i!=input->size/2; ++i, ++dst) {
+	    *dst = linear2ulaw(samples[i]);
+	}
+
+    } else {
+	return -1;
+    }
+
+    output->type = PJ_AUDIO_FRAME_AUDIO;
+    output->size = input->size / 2;
+
+    return 0;
+}
+
+static pj_status_t  g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
+				 unsigned output_buf_len, struct pj_audio_frame *output)
+{
+    struct g711_private *priv = codec->codec_data;
+
+    /* Check output buffer length */
+    if (output_buf_len < input->size * 2)
+	return -1;
+
+    /* Decode */
+    if (priv->pt == PJ_RTP_PT_PCMA) {
+	unsigned i;
+	pj_uint8_t *src = input->buf;
+	pj_uint16_t *dst = output->buf;
+
+	for (i=0; i!=input->size; ++i) {
+	    *dst++ = (pj_uint16_t) alaw2linear(*src++);
+	}
+    } else if (priv->pt == PJ_RTP_PT_PCMU) {
+	unsigned i;
+	pj_uint8_t *src = input->buf;
+	pj_uint16_t *dst = output->buf;
+
+	for (i=0; i!=input->size; ++i) {
+	    *dst++ = (pj_uint16_t) ulaw2linear(*src++);
+	}
+
+    } else {
+	return -1;
+    }
+
+    output->type = PJ_AUDIO_FRAME_AUDIO;
+    output->size = input->size * 2;
+
+    return 0;
+}
+
+
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+
+
+#ifdef _MSC_VER
+#  pragma warning ( disable: 4244 ) /* Conversion from int to char etc */
+#endif
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+#define	SIGN_BIT	(0x80)		/* Sign bit for a A-law byte. */
+#define	QUANT_MASK	(0xf)		/* Quantization field mask. */
+#define	NSEGS		(8)		/* Number of A-law segments. */
+#define	SEG_SHIFT	(4)		/* Left shift for segment number. */
+#define	SEG_MASK	(0x70)		/* Segment field mask. */
+
+static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
+			    0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
+
+/* copy from CCITT G.711 specifications */
+static unsigned char _u2a[128] = {		/* u- to A-law conversions */
+	1,	1,	2,	2,	3,	3,	4,	4,
+	5,	5,	6,	6,	7,	7,	8,	8,
+	9,	10,	11,	12,	13,	14,	15,	16,
+	17,	18,	19,	20,	21,	22,	23,	24,
+	25,	27,	29,	31,	33,	34,	35,	36,
+	37,	38,	39,	40,	41,	42,	43,	44,
+	46,	48,	49,	50,	51,	52,	53,	54,
+	55,	56,	57,	58,	59,	60,	61,	62,
+	64,	65,	66,	67,	68,	69,	70,	71,
+	72,	73,	74,	75,	76,	77,	78,	79,
+	81,	82,	83,	84,	85,	86,	87,	88,
+	89,	90,	91,	92,	93,	94,	95,	96,
+	97,	98,	99,	100,	101,	102,	103,	104,
+	105,	106,	107,	108,	109,	110,	111,	112,
+	113,	114,	115,	116,	117,	118,	119,	120,
+	121,	122,	123,	124,	125,	126,	127,	128};
+
+static unsigned char _a2u[128] = {		/* A- to u-law conversions */
+	1,	3,	5,	7,	9,	11,	13,	15,
+	16,	17,	18,	19,	20,	21,	22,	23,
+	24,	25,	26,	27,	28,	29,	30,	31,
+	32,	32,	33,	33,	34,	34,	35,	35,
+	36,	37,	38,	39,	40,	41,	42,	43,
+	44,	45,	46,	47,	48,	48,	49,	49,
+	50,	51,	52,	53,	54,	55,	56,	57,
+	58,	59,	60,	61,	62,	63,	64,	64,
+	65,	66,	67,	68,	69,	70,	71,	72,
+	73,	74,	75,	76,	77,	78,	79,	79,
+	80,	81,	82,	83,	84,	85,	86,	87,
+	88,	89,	90,	91,	92,	93,	94,	95,
+	96,	97,	98,	99,	100,	101,	102,	103,
+	104,	105,	106,	107,	108,	109,	110,	111,
+	112,	113,	114,	115,	116,	117,	118,	119,
+	120,	121,	122,	123,	124,	125,	126,	127};
+
+static int
+search(
+	int		val,
+	short		*table,
+	int		size)
+{
+	int		i;
+
+	for (i = 0; i < size; i++) {
+		if (val <= *table++)
+			return (i);
+	}
+	return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ *		Linear Input Code	Compressed Code
+ *	------------------------	---------------
+ *	0000000wxyza			000wxyz
+ *	0000001wxyza			001wxyz
+ *	000001wxyzab			010wxyz
+ *	00001wxyzabc			011wxyz
+ *	0001wxyzabcd			100wxyz
+ *	001wxyzabcde			101wxyz
+ *	01wxyzabcdef			110wxyz
+ *	1wxyzabcdefg			111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+static unsigned char
+linear2alaw(
+	int		pcm_val)	/* 2's complement (16-bit range) */
+{
+	int		mask;
+	int		seg;
+	unsigned char	aval;
+
+	if (pcm_val >= 0) {
+		mask = 0xD5;		/* sign (7th) bit = 1 */
+	} else {
+		mask = 0x55;		/* sign bit = 0 */
+		pcm_val = -pcm_val - 8;
+	}
+
+	/* Convert the scaled magnitude to segment number. */
+	seg = search(pcm_val, seg_end, 8);
+
+	/* Combine the sign, segment, and quantization bits. */
+
+	if (seg >= 8)		/* out of range, return maximum value. */
+		return (0x7F ^ mask);
+	else {
+		aval = seg << SEG_SHIFT;
+		if (seg < 2)
+			aval |= (pcm_val >> 4) & QUANT_MASK;
+		else
+			aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
+		return (aval ^ mask);
+	}
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+static int
+alaw2linear(
+	unsigned char	a_val)
+{
+	int		t;
+	int		seg;
+
+	a_val ^= 0x55;
+
+	t = (a_val & QUANT_MASK) << 4;
+	seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+	switch (seg) {
+	case 0:
+		t += 8;
+		break;
+	case 1:
+		t += 0x108;
+		break;
+	default:
+		t += 0x108;
+		t <<= seg - 1;
+	}
+	return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#define	BIAS		(0x84)		/* Bias for linear code. */
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ *	Biased Linear Input Code	Compressed Code
+ *	------------------------	---------------
+ *	00000001wxyza			000wxyz
+ *	0000001wxyzab			001wxyz
+ *	000001wxyzabc			010wxyz
+ *	00001wxyzabcd			011wxyz
+ *	0001wxyzabcde			100wxyz
+ *	001wxyzabcdef			101wxyz
+ *	01wxyzabcdefg			110wxyz
+ *	1wxyzabcdefgh			111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz.  * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+static unsigned char
+linear2ulaw(
+	int		pcm_val)	/* 2's complement (16-bit range) */
+{
+	int		mask;
+	int		seg;
+	unsigned char	uval;
+
+	/* Get the sign and the magnitude of the value. */
+	if (pcm_val < 0) {
+		pcm_val = BIAS - pcm_val;
+		mask = 0x7F;
+	} else {
+		pcm_val += BIAS;
+		mask = 0xFF;
+	}
+
+	/* Convert the scaled magnitude to segment number. */
+	seg = search(pcm_val, seg_end, 8);
+
+	/*
+	 * Combine the sign, segment, quantization bits;
+	 * and complement the code word.
+	 */
+	if (seg >= 8)		/* out of range, return maximum value. */
+		return (0x7F ^ mask);
+	else {
+		uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
+		return (uval ^ mask);
+	}
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+static int
+ulaw2linear(
+	unsigned char	u_val)
+{
+	int		t;
+
+	/* Complement to obtain normal u-law value. */
+	u_val = ~u_val;
+
+	/*
+	 * Extract and bias the quantization bits. Then
+	 * shift up by the segment number and subtract out the bias.
+	 */
+	t = ((u_val & QUANT_MASK) << 3) + BIAS;
+	t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+	return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/* A-law to u-law conversion */
+unsigned char
+alaw2ulaw(
+	unsigned char	aval)
+{
+	aval &= 0xff;
+	return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+	    (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char
+ulaw2alaw(
+	unsigned char	uval)
+{
+	uval &= 0xff;
+	return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+	    (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/jbuf.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/jbuf.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/jbuf.c	(revision 1753)
@@ -0,0 +1,425 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/jbuf.c 6     6/24/05 11:12p Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#include <pjmedia/jbuf.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <string.h>	/* memset() */
+
+/*
+ * At the current state, this is basicly an ugly jitter buffer.
+ * It worked before by observing level, bit it doesn't work.
+ * Then I used the size, which makes the level obsolete.
+ * That's why it's ugly!
+ */
+
+#define MAX_SEQ_RANGE	1000	/* Range in which sequence is considered still within session */
+#define UPDATE_DURATION	20	/* Number of frames retrieved before jitter is updated */
+
+#define THIS_FILE   "jbuf"
+
+/* Individual frame in the frame list. */ 
+struct pj_jbframe
+{
+    pj_uint32_t  extseq;
+    void	*buf;
+};
+
+
+/* Jitter buffer state. */ 
+typedef enum jb_state_t
+{
+    JB_PREFETCH,
+    JB_NORMAL,
+} jb_state_t;
+
+
+/* Jitter buffer last operation. */ 
+typedef enum jb_op_t
+{
+    JB_PUT,
+    JB_GET,
+} jb_op_t;
+
+
+/* Short name for convenience. */ 
+typedef struct pj_jitter_buffer JB;
+
+
+/* Initialize framelist. */ 
+static pj_status_t
+pj_framelist_init( pj_jbframelist *lst, pj_pool_t *pool, unsigned maxcount )
+{
+    PJ_LOG(5, (THIS_FILE, "..pj_frame_list_init [lst=%p], maxcount=%d", lst, maxcount));
+
+    memset(lst, 0, sizeof(*lst));
+    lst->maxcount = maxcount;
+    lst->frames = pj_pool_calloc( pool, maxcount, sizeof(*lst->frames) );
+    if (lst->frames == NULL) {
+	PJ_LOG(1,(THIS_FILE, "Unable to allocate frame list!"));
+	return -1;
+    }
+    return 0;    
+}
+
+/* Reset framelist. */
+static void 
+pj_framelist_reset( pj_jbframelist *lst )
+{
+    PJ_LOG(6, (THIS_FILE, "..pj_frame_list_reset [lst=%p]", lst));
+
+    lst->count = 0;
+    lst->head = 0;
+    lst->frames[0].extseq = 0;
+}
+
+/* Put a buffer with the specified sequence into the ordered list. */ 
+static int 
+pj_framelist_put( pj_jbframelist *lst, pj_uint32_t extseq, void *buf )
+{
+    unsigned pos = (unsigned)-1;
+    pj_uint32_t startseq = lst->frames[lst->head].extseq;
+
+    if (lst->count == 0) {
+	/* Empty list. Initialize frame list. */
+	PJ_LOG(6, (THIS_FILE, "    ..pj_frame_list_put [lst=%p], empty, seq=%u@pos=%d", 
+		   lst, extseq, lst->head));
+
+	lst->head = 0;
+	lst->count = 1;
+	lst->frames[0].buf = buf;
+	lst->frames[0].extseq = extseq;
+	return 0;
+	
+    } else if (extseq < startseq) {
+	/* The sequence number is lower than our oldest packet. This can mean
+	   two things:
+	    - old packet has been receieved, or
+	    - the sequence number has wrapped around.
+	 */  
+	if (startseq + lst->maxcount <= extseq) {
+	    /* The sequence number has wrapped around, but it is beyond
+	       the capacity of the list (i.e. too soon).
+	     */
+	    PJ_LOG(5, (THIS_FILE, "    ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", 
+		       lst, extseq, startseq));
+	    return PJ_JB_STATUS_TOO_SOON;
+
+	} else if (startseq-extseq > lst->maxcount && startseq+lst->maxcount > extseq) {
+	    /* The sequence number has wrapped around, and it is still inside
+	       the 'window' of the framelist.
+	     */
+	    pos = extseq - startseq;
+	} else {
+	    /* The new frame is too old. */
+	    PJ_LOG(5, (THIS_FILE, "    ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", 
+		       lst, extseq, startseq));
+	    return PJ_JB_STATUS_TOO_OLD;
+	}
+	
+    } else if (extseq > startseq + lst->maxcount) {
+	/* Two possibilities here. Either:
+	    - packet is really too soon, or
+	    - sequence number of startseq has just wrapped around, and old packet
+	      which hasn't wrapped is received.
+	 */
+	if (extseq < MAX_SEQ_RANGE /*approx 20 seconds with 50 fps*/) {
+	    PJ_LOG(5, (THIS_FILE, "    ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", 
+		       lst, extseq, startseq));
+	    return PJ_JB_STATUS_TOO_SOON;
+	} else {
+	    PJ_LOG(5, (THIS_FILE, "    ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", 
+		       lst, extseq, startseq));
+	    return PJ_JB_STATUS_TOO_OLD;
+	}
+    } 
+
+    /* The new frame is within the framelist capacity.
+       Calculate position where to put it in the list.
+     */
+    if (pos == (unsigned)-1)
+	pos = ((extseq - startseq) + lst->head) % lst->maxcount;
+
+    pj_assert(pos < lst->maxcount);
+    
+    /* Update count only if we're not overwriting existing frame. */
+    if (lst->frames[pos].buf == NULL)
+        ++lst->count;
+
+    lst->frames[pos].buf = buf;
+    lst->frames[pos].extseq = extseq;
+
+    PJ_LOG(6, (THIS_FILE, "    ..pj_frame_list_put [lst=%p] seq=%u, startseq=%d, head=%d, pos=%d", 
+	       lst, extseq, startseq, lst->head, pos));
+    return 0;
+}
+
+/* Get the first element of the list. */ 
+static int 
+pj_framelist_get( pj_jbframelist *lst, pj_uint32_t *extseq, void **buf )
+{
+    if (lst->count == 0) {
+	/* Empty. */
+	*buf = NULL;
+	*extseq = 0;
+	PJ_LOG(6, (THIS_FILE, "    ..pj_frame_list_get [lst=%p], empty!", lst));
+	return -1;
+	
+    } else {
+	*buf = lst->frames[lst->head].buf;
+	*extseq = lst->frames[lst->head].extseq;
+	lst->frames[lst->head].buf = NULL;
+
+	PJ_LOG(6, (THIS_FILE, "    ..pj_frame_list_get [lst=%p] seq=%u, head=%d", 
+		   lst, *extseq, lst->head));
+
+	lst->head = (lst->head + 1) % lst->maxcount;
+	--lst->count;
+	return 0;
+    }
+}
+
+
+/*****************************************************************************
+ * Reset jitter buffer. 
+ ****************************************************************************
+*/
+PJ_DEF(void) pj_jb_reset(JB *jb)
+{
+    PJ_LOG(6, (THIS_FILE, "pj_jb_reset [jb=%p]", jb));
+
+    jb->level = jb->max_level = 1;
+    jb->prefetch = jb->min;
+    jb->get_cnt = 0;
+    jb->lastseq = 0;
+    jb->state = JB_PREFETCH;
+    jb->upd_count = 0;
+    jb->last_op = -1;
+    pj_framelist_reset( &jb->lst );
+}
+
+
+/*****************************************************************************
+ * Create jitter buffer.
+ *****************************************************************************
+ */ 
+PJ_DEF(pj_status_t) pj_jb_init( pj_jitter_buffer *jb, pj_pool_t *pool, 
+			        unsigned min, unsigned max, unsigned maxcount)
+{
+    pj_status_t status;
+
+    if (maxcount <= max) {
+	maxcount = max * 5 / 4;
+	PJ_LOG(3,(THIS_FILE, "Jitter buffer maximum count was adjusted."));
+    }
+
+    jb->min = min;
+    jb->max = max;
+
+    status = pj_framelist_init( &jb->lst, pool, maxcount );
+    if (status != PJ_OK)
+	return status;
+
+    pj_jb_reset(jb);
+
+    PJ_LOG(4, (THIS_FILE, "pj_jb_init success [jb=%p], min=%d, max=%d, maxcount=%d", 
+			  jb, min, max, maxcount));
+    return PJ_OK;
+}
+
+
+/*****************************************************************************
+ * Put a packet to the jitter buffer.
+ *****************************************************************************
+ */ 
+PJ_DEF(pj_status_t) pj_jb_put( JB *jb, pj_uint32_t extseq, void *buf )
+{
+    unsigned distance;
+    int status;
+    
+    PJ_LOG(6, (THIS_FILE, "==> pj_jb_put [jb=%p], seq=%u, buf=%p", jb, extseq, buf));
+
+    if (jb->lastseq == 0)
+	jb->lastseq = extseq - 1;
+
+    /* Calculate distance between this packet and last received packet
+       to detect long jump (indicating probably remote has just been
+       restarted.
+     */
+    distance = (extseq > jb->lastseq) ? extseq - jb->lastseq : jb->lastseq - extseq;
+    if (distance > MAX_SEQ_RANGE) {
+	/* Distance is out of range, reset jitter while maintaining current jitter
+	   level.
+	 */
+	int old_level = jb->level;
+	int old_prefetch = jb->prefetch;
+
+	PJ_LOG(4, (THIS_FILE, "    ..[jb=%p] distance out of range, resetting", jb));
+
+	pj_jb_reset(jb);
+	jb->level = old_level;
+	jb->prefetch = old_prefetch;
+	distance = 1;
+	jb->lastseq = extseq - 1;
+    }
+    
+    jb->lastseq = extseq;
+
+    status = pj_framelist_put( &jb->lst, extseq, buf );
+    if (status == PJ_JB_STATUS_TOO_OLD)
+	return -1;
+
+    if (status == PJ_JB_STATUS_TOO_SOON) {
+	/* TODO: discard old packets.. */
+	/* No, don't do it without putting a way to inform application so that
+	   it can free the memory */
+    }
+
+
+    if (jb->last_op != JB_PUT) {
+	if (jb->state != JB_PREFETCH)
+	    jb->level--;
+    } else {
+	jb->level++;
+    }
+
+    if (jb->lst.count > jb->max_level)
+	jb->max_level++;
+
+    jb->last_op = JB_PUT;
+    return 0;
+}
+
+
+/*
+ * Update jitter buffer algorithm.
+ */
+static void jb_update(JB *jb, int apply, int log_info)
+{
+    unsigned abs_level = jb->max_level > 0 ? jb->max_level : -jb->max_level;
+    unsigned new_prefetch;
+
+    /* Update prefetch count */
+    if (abs_level > jb->prefetch)
+	new_prefetch = (jb->prefetch + abs_level*9 + 1) / 10;
+    else {
+	new_prefetch = (jb->prefetch*4 + abs_level) / 5;
+	pj_assert(new_prefetch <= jb->prefetch);
+    }
+
+    if (log_info) {
+	PJ_LOG(5, (THIS_FILE, "    ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", 
+			      jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
+    } else {
+	PJ_LOG(6, (THIS_FILE, "    ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", 
+			      jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
+    }
+
+    if (new_prefetch < jb->min) new_prefetch = jb->min;
+    if (new_prefetch > jb->max) new_prefetch = jb->max;
+
+    /* If jitter buffer is empty, set state to JB_PREFETCH, taking care of the
+       new prefetch setting.
+     */
+    if (jb->lst.count == 0) {
+	jb->state = JB_PREFETCH;
+	jb->get_cnt = 0;
+    } else {
+	/* Check if delay is too long, which in this case probably better to
+	   discard some frames..
+	 */
+	/* No, don't do it without putting a way to inform application so that
+	   it can free the memory */
+    }
+
+
+    if (apply) {
+	jb->prefetch = new_prefetch;
+	if (jb->max_level > 0)
+	    jb->max_level--;
+    } else {
+	jb->level = new_prefetch;
+    }
+}
+
+
+/*****************************************************************************
+ * Get the oldest frame from jitter buffer.
+ *****************************************************************************
+ */ 
+PJ_DEF(pj_status_t) pj_jb_get( JB *jb, pj_uint32_t *extseq, void **buf )
+{
+    pj_status_t status;
+    
+    PJ_LOG(6, (THIS_FILE, "<== pj_jb_get [jb=%p]", jb));
+
+    /*
+     * Check whether we're ready to give frame. When we're in JB_PREFETCH state,
+     * only give frames only when:
+     *	- the buffer has enough frames in it (jb->list.count > jb->prefetch), OR
+     *	- after 'prefetch' attempts, there's still no frame, which in this
+     *	  case PJ_JB_STATUS_FRAME_NULL will be returned by the next check.
+     */
+    if (jb->state == JB_PREFETCH && jb->lst.count <= jb->prefetch && jb->get_cnt < jb->prefetch) {
+	jb->get_cnt++;   
+	jb->last_op = JB_GET;
+	PJ_LOG(5, (THIS_FILE, "    ..[jb=%p] bufferring...", jb));
+	return PJ_JB_STATUS_FRAME_NULL;
+    }
+
+    /* Attempt to get one frame from the list. */
+    status = pj_framelist_get( &jb->lst, extseq, buf );
+    if (status != 0) {
+	PJ_LOG(6, (THIS_FILE, "    ..[jb=%p] no packet!", jb));
+	status = jb->lst.count ? PJ_JB_STATUS_FRAME_MISSING : PJ_JB_STATUS_FRAME_NULL;
+	jb_update(jb, 1, 0);
+	return status;
+    }
+
+    /* Force state to NORMAL */
+    jb->state = JB_NORMAL;
+
+    /* Increase level only when last operation is GET.
+     * This is to prevent level from increasing during silence period, which
+     * no packets is receieved.
+     */
+    if (jb->last_op != JB_GET) {
+	int apply;
+
+	//jb->level++;
+	jb->last_op = JB_GET;
+
+	apply = (++jb->upd_count > UPDATE_DURATION);
+	if (apply)
+	    jb->upd_count = 0;
+
+	jb_update(jb, apply, apply);
+    }
+
+    PJ_LOG(6, (THIS_FILE, "    ..[jb=%p] seq=%u, level=%d, prefetch=%d, size=%u, delay=%d", 
+			  jb, *extseq, jb->level, jb->prefetch, jb->lst.count,
+			  jb->lastseq - *extseq));
+    return 0;
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/jbuf.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/jbuf.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/jbuf.h	(revision 1753)
@@ -0,0 +1,147 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/jbuf.h 5     6/24/05 11:12p Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJMEDIA_JBUF_H__
+#define __PJMEDIA_JBUF_H__
+
+
+/**
+ * @file jbuf.h
+ * @brief Adaptive jitter buffer implementation.
+ */
+/**
+ * @defgroup PJMED_JBUF Adaptive jitter buffer
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+#include <pj/types.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Opaque declaration of internal frame type used by jitter buffer. 
+ */
+struct pj_jbframe;
+
+
+/**
+ * Miscelaneous operation result/status. 
+ */ 
+typedef enum jb_op_status
+{
+    PJ_JB_STATUS_TOO_OLD = -2,		/** The packet is too old. */
+    PJ_JB_STATUS_TOO_SOON = -3,		/** The packet is too soon. */
+    PJ_JB_STATUS_FRAME_NULL = -4,	/** No packet can be retrieved */
+    PJ_JB_STATUS_FRAME_MISSING = -5,	/** The specified packet is missing/lost */
+} jb_op_status;
+
+
+/*
+ * Frame list, container abstraction for ordered list with fixed maximum
+ * size. It is used internally by the jitter buffer.
+ */  
+typedef struct pj_jbframelist
+{
+    unsigned		head, count, maxcount;
+    struct pj_jbframe  *frames;
+} pj_jbframelist;
+
+
+/**
+ * Jitter buffer implementation.
+ */ 
+typedef struct pj_jitter_buffer
+{
+    pj_jbframelist  lst;	    /** The frame list. */
+    int		    level;	    /** Current, real-time jitter level. */
+    int		    max_level;	    /** Maximum level for the period.	 */
+    unsigned	    prefetch;	    /** Prefetch count currently used. */
+    unsigned	    get_cnt;	    /** Number of get operation during prefetch state. */
+    unsigned	    min;	    /** Minimum jitter size, in packets. */
+    unsigned	    max;	    /** Maximum jitter size, in packets. */
+    pj_uint32_t	    lastseq;	    /** Last sequence number put to jitter buffer. */
+    unsigned	    upd_count;	    /** Internal counter to manage update interval. */
+    int		    state;	    /** Jitter buffer state (1==operational) */
+    int		    last_op;	    /** Last jitter buffer operation. */
+} pj_jitter_buffer;
+
+
+/**
+ * Initialize jitter buffer with the specified parameters.
+ * This function will allocate internal frame buffer from the specified pool.
+ * @param jb The jitter buffer to be initialized.
+ * @param pool Pool where memory will be allocated for the frame buffer.
+ * @param min The minimum value of jitter buffer, in packets.
+ * @param max The maximum value of jitter buffer, in packets.
+ * @param maxcount The maximum number of delay, in packets, which must be
+ *		   greater than max.
+ * @return PJ_OK on success.
+ */
+PJ_DECL(pj_status_t) pj_jb_init(pj_jitter_buffer *jb, pj_pool_t *pool, 
+				unsigned min, unsigned max, unsigned maxcount);
+
+/**
+ * Reset jitter buffer according to the parameters specified when the jitter
+ * buffer was initialized. Any packets currently in the buffer will be 
+ * discarded.
+ */
+PJ_DECL(void) pj_jb_reset(pj_jitter_buffer *jb);
+
+/**
+ * Put a pointer to the buffer with the specified sequence number. The pointer
+ * normally points to a buffer held by application, and this pointer will be
+ * returned later on when pj_jb_get() is called. The jitter buffer will not try
+ * to interpret the content of this pointer.
+ * @return:
+ *   - PJ_OK on success.
+ *   - PJ_JB_STATUS_TOO_OLD when the packet is too old.
+ *   - PJ_JB_STATUS_TOO_SOON when the packet is too soon.
+ */
+PJ_DECL(pj_status_t) pj_jb_put( pj_jitter_buffer *jb, pj_uint32_t extseq, void *buf );
+
+/**
+ * Get the earliest data from the jitter buffer. ONLY when the operation succeeds,
+ * the function returns both sequence number and a pointer in the parameters.
+ * This returned data corresponds to sequence number and pointer that were
+ * given to jitter buffer in the previous pj_jb_put operation.
+ * @return 
+ *  - PJ_OK on success
+ *  - PJ_JB_STATUS_FRAME_NULL when there is no frames to be returned.
+ *  - PJ_JB_STATUS_FRAME_MISSING if the jitter buffer detects that the packet 
+ *     is lost. Application may run packet concealment algorithm when it 
+ *     receives this status.
+ */
+PJ_DECL(pj_status_t) pj_jb_get( pj_jitter_buffer *jb, pj_uint32_t *extseq, void **buf );
+
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif	/* __PJMEDIA_JBUF_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/mediamgr.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/mediamgr.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/mediamgr.c	(revision 1753)
@@ -0,0 +1,116 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/mediamgr.c 8     6/24/05 1:00a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjmedia/mediamgr.h>
+#include <pj/sock.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
+PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
+
+/** Concrete declaration of media manager. */
+struct pj_med_mgr_t
+{
+    /** Pool. */
+    pj_pool_t		 *pool;
+
+    /** Pool factory. */
+    pj_pool_factory	 *pf;
+
+    /** Codec manager. */
+    pj_codec_mgr	  codec_mgr;
+};
+
+/**
+ * Initialize and get the instance of media manager.
+ */
+PJ_DEF(pj_med_mgr_t*) pj_med_mgr_create ( pj_pool_factory *pf)
+{
+    pj_pool_t *pool;
+    pj_med_mgr_t *mm;
+    pj_codec_factory *cf;
+    pj_status_t status;
+
+    pool = pj_pool_create(pf, "mediamgr", 512, 512, NULL);
+    if (!pool)
+	return NULL;
+
+    mm = pj_pool_calloc(pool, 1, sizeof(struct pj_med_mgr_t));
+    mm->pool = pool;
+    mm->pf = pf;
+
+    /* Sound */
+    pj_snd_init(pf);
+
+    /* Init codec manager. */
+    status = pj_codec_mgr_init(&mm->codec_mgr);
+    if (status != 0) {
+	pj_snd_deinit();
+	goto on_error;
+    }
+
+    /* Init and register G.711 codec. */
+    cf = pj_pool_alloc (mm->pool, sizeof(pj_codec_factory));
+
+    status = g711_init_factory (cf, mm->pool);
+    if (status != 0) {
+	pj_snd_deinit();
+	return NULL;
+    }
+
+    status = pj_codec_mgr_register_factory (&mm->codec_mgr, cf);
+    if (status != 0) 
+	return NULL;
+
+    return mm;
+
+on_error:
+    pj_pool_release(pool);
+    return NULL;
+}
+
+/**
+ * Get the codec manager instance.
+ */
+PJ_DEF(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr)
+{
+    return &mgr->codec_mgr;
+}
+
+/**
+ * Deinitialize media manager.
+ */
+PJ_DEF(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr)
+{
+    pj_snd_deinit();
+    pj_pool_release (mgr->pool);
+    return 0;
+}
+
+/**
+ * Get pool factory.
+ */
+PJ_DEF(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr)
+{
+    return mgr->pf;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/mediamgr.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/mediamgr.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/mediamgr.h	(revision 1753)
@@ -0,0 +1,105 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/mediamgr.h 7     8/24/05 10:29a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJMEDIA_MEDIAMGR_H__
+#define __PJMEDIA_MEDIAMGR_H__
+
+
+/**
+ * @file mediamgr.h
+ * @brief Media Manager.
+ */
+/**
+ * @defgroup PJMED_MGR Media Manager
+ * @ingroup PJMEDIA
+ * @{
+ *
+ * The media manager acts as placeholder for endpoint capabilities. Each 
+ * media manager will have a codec manager to manage list of codecs installed
+ * in the endpoint and a sound device factory.
+ *
+ * A reference to media manager instance is required when application wants
+ * to create a media session (#pj_media_session_create or 
+ * #pj_media_session_create_from_sdp).
+ */
+
+#include <pjmedia/sound.h>
+#include <pjmedia/codec.h>
+
+
+PJ_BEGIN_DECL
+
+
+/** Opague declaration of media manager. */
+typedef struct pj_med_mgr_t pj_med_mgr_t;
+
+/**
+ * Create an instance of media manager.
+ *
+ * @param pf		Pool factory.
+ * @param conn_addr	Connection address to be used by this media manager.
+ *
+ * @return A new instance of media manager, or NULL if failed.
+ */
+PJ_DECL(pj_med_mgr_t*) pj_med_mgr_create (pj_pool_factory *pf);
+
+/**
+ * Destroy media manager instance.
+ *
+ * @param mgr		Media manager instance.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr);
+
+/**
+ * Get pool factory of the media manager as specified when the media
+ * manager was created.
+ *
+ * @param mgr		The media manager instance.
+ *
+ * @return Pool factory instance of the media manager.
+ */
+PJ_DECL(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr);
+
+/**
+ * Get the codec manager instance.
+ *
+ * @param mgr		The media manager instance.
+ *
+ * @return The instance of codec manager.
+ */
+PJ_DECL(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr);
+
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+
+
+#endif	/* __PJMEDIA_MEDIAMGR_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/nullsound.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/nullsound.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/nullsound.c	(revision 1753)
@@ -0,0 +1,131 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/nullsound.c 3     6/14/05 12:54a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjmedia/sound.h>
+
+/*
+ * Null Factory Operations
+ */
+static pj_status_t null_sound_init(void);
+static const char *null_sound_get_name(void);
+static pj_status_t null_sound_destroy(void);
+static pj_status_t null_sound_enum_devices(int *count, char *dev_names[]);
+static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev);
+static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev);
+
+
+/*
+ * Null Device Operations
+ */
+static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
+static pj_status_t null_sound_dev_close( pj_snd_dev *dev );
+static pj_status_t null_sound_dev_play( pj_snd_dev *dev );
+static pj_status_t null_sound_dev_record( pj_snd_dev *dev );
+
+
+static pj_snd_dev_factory null_sound_factory = 
+{
+    &null_sound_init,
+    &null_sound_get_name,
+    &null_sound_destroy,
+    &null_sound_enum_devices,
+    &null_sound_create_dev,
+    &null_sound_destroy_dev
+};
+
+static struct pj_snd_dev_op null_sound_dev_op = 
+{
+    &null_sound_dev_open,
+    &null_sound_dev_close,
+    &null_sound_dev_play,
+    &null_sound_dev_record
+};
+
+PJ_DEF(pj_snd_dev_factory*) pj_nullsound_get_factory()
+{
+    return &null_sound_factory;
+}
+
+static pj_status_t null_sound_init(void)
+{
+    return 0;
+}
+
+static const char *null_sound_get_name(void)
+{
+    return "nullsound";
+}
+
+static pj_status_t null_sound_destroy(void)
+{
+    return 0;
+}
+
+static pj_status_t null_sound_enum_devices(int *count, char *dev_names[])
+{
+    *count = 1;
+    dev_names[0] = "nullsound";
+    return 0;
+}
+
+static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev)
+{
+    PJ_UNUSED_ARG(dev_name);
+    dev->op = &null_sound_dev_op;
+    return 0;
+}
+
+static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev)
+{
+    PJ_UNUSED_ARG(dev);
+    return 0;
+}
+
+
+/*
+ * Null Device Operations
+ */
+static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
+{
+    PJ_UNUSED_ARG(dev);
+    PJ_UNUSED_ARG(role);
+    return 0;
+}
+
+static pj_status_t null_sound_dev_close( pj_snd_dev *dev )
+{
+    PJ_UNUSED_ARG(dev);
+    return 0;
+}
+
+static pj_status_t null_sound_dev_play( pj_snd_dev *dev )
+{
+    PJ_UNUSED_ARG(dev);
+    return 0;
+}
+
+static pj_status_t null_sound_dev_record( pj_snd_dev *dev )
+{
+    PJ_UNUSED_ARG(dev);
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/pasound.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/pasound.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/pasound.c	(revision 1753)
@@ -0,0 +1,411 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/pasound.c 3     6/24/05 11:13p Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjmedia/sound.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <portaudio.h>
+
+#define THIS_FILE	"pasound.c"
+
+static struct snd_mgr
+{
+    pj_pool_factory *factory;
+} snd_mgr;
+
+struct pj_snd_stream
+{
+    pj_pool_t	    *pool;
+    PaStream	    *stream;
+    int		     dev_index;
+    int		     bytes_per_sample;
+    pj_uint32_t	     samples_per_sec;
+    pj_uint32_t	     timestamp;
+    pj_uint32_t	     underflow;
+    pj_uint32_t	     overflow;
+    void	    *user_data;
+    pj_snd_rec_cb    rec_cb;
+    pj_snd_play_cb   play_cb;
+    pj_bool_t	     quit_flag;
+    pj_bool_t	     thread_has_exited;
+    pj_bool_t	     thread_initialized;
+    pj_thread_desc   thread_desc;
+    pj_thread_t	    *thread;
+};
+
+
+static int PaRecorderCallback(const void *input, 
+			      void *output,
+			      unsigned long frameCount,
+			      const PaStreamCallbackTimeInfo* timeInfo,
+			      PaStreamCallbackFlags statusFlags,
+			      void *userData )
+{
+    pj_snd_stream *stream = userData;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(output)
+    PJ_UNUSED_ARG(timeInfo)
+
+    if (stream->quit_flag)
+	goto on_break;
+
+    if (stream->thread_initialized == 0) {
+	stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
+	stream->thread_initialized = 1;
+    }
+
+    if (statusFlags & paInputUnderflow)
+	++stream->underflow;
+    if (statusFlags & paInputOverflow)
+	++stream->overflow;
+
+    stream->timestamp += frameCount;
+
+    status = (*stream->rec_cb)(stream->user_data, stream->timestamp, 
+			       input, frameCount * stream->bytes_per_sample);
+    
+    if (status==0) 
+	return paContinue;
+
+on_break:
+    stream->thread_has_exited = 1;
+    return paAbort;
+}
+
+static int PaPlayerCallback( const void *input, 
+			     void *output,
+			     unsigned long frameCount,
+			     const PaStreamCallbackTimeInfo* timeInfo,
+			     PaStreamCallbackFlags statusFlags,
+			     void *userData )
+{
+    pj_snd_stream *stream = userData;
+    pj_status_t status;
+    unsigned size = frameCount * stream->bytes_per_sample;
+
+    PJ_UNUSED_ARG(input)
+    PJ_UNUSED_ARG(timeInfo)
+
+    if (stream->quit_flag)
+	goto on_break;
+
+    if (stream->thread_initialized == 0) {
+	stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
+	stream->thread_initialized = 1;
+    }
+
+    if (statusFlags & paInputUnderflow)
+	++stream->underflow;
+    if (statusFlags & paInputOverflow)
+	++stream->overflow;
+
+    stream->timestamp += frameCount;
+
+    status = (*stream->play_cb)(stream->user_data, stream->timestamp, 
+			        output, size);
+    
+    if (status==0) 
+	return paContinue;
+
+on_break:
+    stream->thread_has_exited = 1;
+    return paAbort;
+}
+
+
+/*
+ * Init sound library.
+ */
+PJ_DEF(pj_status_t) pj_snd_init(pj_pool_factory *factory)
+{
+    int err;
+
+    snd_mgr.factory = factory;
+    err = Pa_Initialize();
+
+    PJ_LOG(4,(THIS_FILE, "PortAudio sound library initialized, status=%d", err));
+
+    if (err == 0) {
+	PJ_LOG(4,(THIS_FILE, " Device count=%d", pj_snd_get_dev_count()));
+    }
+    return err;
+}
+
+
+/*
+ * Get device count.
+ */
+PJ_DEF(int) pj_snd_get_dev_count(void)
+{
+    return Pa_GetDeviceCount();
+}
+
+
+/*
+ * Get device info.
+ */
+PJ_DEF(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index)
+{
+    static pj_snd_dev_info info;
+    const PaDeviceInfo *pa_info;
+
+    pa_info = Pa_GetDeviceInfo(index);
+    if (!pa_info)
+	return NULL;
+
+    pj_memset(&info, 0, sizeof(info));
+    strncpy(info.name, pa_info->name, sizeof(info.name));
+    info.name[sizeof(info.name)-1] = '\0';
+    info.input_count = pa_info->maxInputChannels;
+    info.output_count = pa_info->maxOutputChannels;
+    info.default_samples_per_sec = (unsigned)pa_info->defaultSampleRate;
+
+    return &info;
+}
+
+
+/*
+ * Open stream.
+ */
+PJ_DEF(pj_snd_stream*) pj_snd_open_recorder( int index,
+					     const pj_snd_stream_info *param,
+					     pj_snd_rec_cb rec_cb,
+					     void *user_data)
+{
+    pj_pool_t *pool;
+    pj_snd_stream *stream;
+    PaStreamParameters inputParam;
+    int sampleFormat;
+    const PaDeviceInfo *paDevInfo = NULL;
+    PaError err;
+
+    if (index == -1) {
+	int count = Pa_GetDeviceCount();
+	for (index=0; index<count; ++index) {
+	    paDevInfo = Pa_GetDeviceInfo(index);
+	    if (paDevInfo->maxInputChannels > 0)
+		break;
+	}
+	if (index == count) {
+	    PJ_LOG(2,(THIS_FILE, "Error: unable to find recorder device"));
+	    return NULL;
+	}
+    } else {
+	paDevInfo = Pa_GetDeviceInfo(index);
+	if (!paDevInfo)
+	    return NULL;
+    }
+
+    if (param->bits_per_sample == 8)
+	sampleFormat = paUInt8;
+    else if (param->bits_per_sample == 16)
+	sampleFormat = paInt16;
+    else if (param->bits_per_sample == 32)
+	sampleFormat = paInt32;
+    else
+	return NULL;
+    
+    pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
+    if (!pool)
+	return NULL;
+
+    stream = pj_pool_calloc(pool, 1, sizeof(*stream));
+    stream->pool = pool;
+    stream->user_data = user_data;
+    stream->dev_index = index;
+    stream->samples_per_sec = param->samples_per_frame;
+    stream->bytes_per_sample = param->bits_per_sample / 8;
+    stream->rec_cb = rec_cb;
+
+    pj_memset(&inputParam, 0, sizeof(inputParam));
+    inputParam.device = index;
+    inputParam.channelCount = 1;
+    inputParam.hostApiSpecificStreamInfo = NULL;
+    inputParam.sampleFormat = sampleFormat;
+    inputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
+
+    err = Pa_OpenStream( &stream->stream, &inputParam, NULL,
+			 param->samples_per_sec, 
+			 param->samples_per_frame * param->frames_per_packet, 
+			 0,
+			 &PaRecorderCallback, stream );
+    if (err != paNoError) {
+	pj_pool_release(pool);
+	PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
+	return NULL;
+    }
+
+    PJ_LOG(4,(THIS_FILE, "%s opening device %s for recording, sample rate=%d, "
+			 "%d bits per sample, %d samples per buffer",
+			 (err==0 ? "Success" : "Error"),
+			 paDevInfo->name, param->samples_per_sec, 
+			 param->bits_per_sample,
+			 param->samples_per_frame * param->frames_per_packet));
+
+    return stream;
+}
+
+
+PJ_DEF(pj_snd_stream*) pj_snd_open_player(int index,
+					   const pj_snd_stream_info *param,
+					   pj_snd_play_cb play_cb,
+					   void *user_data)
+{
+    pj_pool_t *pool;
+    pj_snd_stream *stream;
+    PaStreamParameters outputParam;
+    int sampleFormat;
+    const PaDeviceInfo *paDevInfo = NULL;
+    PaError err;
+
+    if (index == -1) {
+	int count = Pa_GetDeviceCount();
+	for (index=0; index<count; ++index) {
+	    paDevInfo = Pa_GetDeviceInfo(index);
+	    if (paDevInfo->maxOutputChannels > 0)
+		break;
+	}
+	if (index == count) {
+	    PJ_LOG(2,(THIS_FILE, "Error: unable to find player device"));
+	    return NULL;
+	}
+    } else {
+	paDevInfo = Pa_GetDeviceInfo(index);
+	if (!paDevInfo)
+	    return NULL;
+    }
+
+    if (param->bits_per_sample == 8)
+	sampleFormat = paUInt8;
+    else if (param->bits_per_sample == 16)
+	sampleFormat = paInt16;
+    else if (param->bits_per_sample == 32)
+	sampleFormat = paInt32;
+    else
+	return NULL;
+    
+    pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
+    if (!pool)
+	return NULL;
+
+    stream = pj_pool_calloc(pool, 1, sizeof(*stream));
+    stream->pool = pool;
+    stream->user_data = user_data;
+    stream->samples_per_sec = param->samples_per_frame;
+    stream->bytes_per_sample = param->bits_per_sample / 8;
+    stream->dev_index = index;
+    stream->play_cb = play_cb;
+
+    pj_memset(&outputParam, 0, sizeof(outputParam));
+    outputParam.device = index;
+    outputParam.channelCount = 1;
+    outputParam.hostApiSpecificStreamInfo = NULL;
+    outputParam.sampleFormat = sampleFormat;
+    outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
+
+    err = Pa_OpenStream( &stream->stream, NULL, &outputParam,
+			 param->samples_per_sec, 
+			 param->samples_per_frame * param->frames_per_packet, 
+			 0,
+			 &PaPlayerCallback, stream );
+    if (err != paNoError) {
+	pj_pool_release(pool);
+	PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
+	return NULL;
+    }
+
+    PJ_LOG(4,(THIS_FILE, "%s opening device %s for playing, sample rate=%d, "
+			 "%d bits per sample, %d samples per buffer",
+			 (err==0 ? "Success" : "Error"),
+			 paDevInfo->name, param->samples_per_sec, 
+		 	 param->bits_per_sample,
+			 param->samples_per_frame * param->frames_per_packet));
+
+    return stream;
+}
+
+
+/*
+ * Start stream.
+ */
+PJ_DEF(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream)
+{
+    PJ_LOG(4,(THIS_FILE, "Starting stream.."));
+    return Pa_StartStream(stream->stream);
+}
+
+/*
+ * Stop stream.
+ */
+PJ_DEF(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream)
+{
+    int i, err;
+
+    stream->quit_flag = 1;
+    for (i=0; !stream->thread_has_exited && i<100; ++i)
+	pj_thread_sleep(10);
+
+    pj_thread_sleep(1);
+
+    PJ_LOG(4,(THIS_FILE, "Stopping stream.."));
+
+    err = Pa_StopStream(stream->stream);
+    return err;
+}
+
+/*
+ * Destroy stream.
+ */
+PJ_DEF(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream)
+{
+    int i, err;
+    const PaDeviceInfo *paDevInfo;
+
+    stream->quit_flag = 1;
+    for (i=0; !stream->thread_has_exited && i<100; ++i)
+	pj_thread_sleep(10);
+
+    pj_thread_sleep(1);
+
+    paDevInfo = Pa_GetDeviceInfo(stream->dev_index);
+
+    PJ_LOG(4,(THIS_FILE, "Closing %s: %lu underflow, %lu overflow",
+			 paDevInfo->name,
+			 stream->underflow, stream->overflow));
+
+    err = Pa_CloseStream(stream->stream);
+    pj_pool_release(stream->pool);
+    return err;
+}
+
+/*
+ * Deinitialize sound library.
+ */
+PJ_DEF(pj_status_t) pj_snd_deinit(void)
+{
+    PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down.."));
+
+    return Pa_Terminate();
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/LICENSE.txt
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/LICENSE.txt	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/LICENSE.txt	(revision 1753)
@@ -0,0 +1,65 @@
+Portable header file to contain:
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+ 
+
+Implementation files to contain:
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest version at: http://www.audiomulch.com/portaudio/
+ * <platform> Implementation
+ * Copyright (c) 1999-2000 <author(s)>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/README.txt
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/README.txt	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/README.txt	(revision 1753)
@@ -0,0 +1,81 @@
+README for PortAudio
+Implementations for PC DirectSound and Mac SoundManager
+
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com//
+ *
+ * Copyright (c) 1999-2000 Phil Burk and Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+PortAudio is a portable audio I/O library designed for cross-platform
+support of audio. It uses a callback mechanism to request audio processing.
+Audio can be generated in various formats, including 32 bit floating point,
+and will be converted to the native format internally.
+
+Documentation:
+	See "pa_common/portaudio.h" for API spec.
+	See docs folder for a tutorial.
+	Also see http://www.portaudio.com/docs/
+	And see "pa_tests/patest_saw.c" for an example.
+
+For information on compiling programs with PortAudio, please see the
+tutorial at:
+
+  http://www.portaudio.com/docs/pa_tutorial.html
+  
+Important Files and Folders:
+	pa_common/              = platform independant code
+	pa_common/portaudio.h   = header file for PortAudio API. Specifies API.
+	pa_common/pa_lib.c      = host independant code for all implementations.
+
+    pablio                  = simple blocking read/write interface
+    
+Platform Implementations
+    pa_asio                 = ASIO for Windows and Macintosh
+    pa_beos                 = BeOS
+    pa_mac_sm               = Macintosh Sound Manager for OS 8,9 and Carbon
+    pa_mac_core             = Macintosh Core Audio for OS X
+    pa_sgi                  = Silicon Graphics AL
+    pa_unix_oss             = OSS implementation for various Unixes
+    pa_win_ds               = Windows Direct Sound
+    pa_win_wmme             = Windows MME (most widely supported)
+    
+Test Programs
+	pa_tests/pa_fuzz.c = guitar fuzz box
+	pa_tests/pa_devs.c = print a list of available devices
+	pa_tests/pa_minlat.c = determine minimum latency for your machine
+	pa_tests/paqa_devs.c = self test that opens all devices
+	pa_tests/paqa_errs.c = test error detection and reporting
+	pa_tests/patest_clip.c = hear a sine wave clipped and unclipped
+	pa_tests/patest_dither.c = hear effects of dithering (extremely subtle)
+	pa_tests/patest_pink.c = fun with pink noise
+	pa_tests/patest_record.c = record and playback some audio
+	pa_tests/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad().
+	pa_tests/patest_sine.c = output a sine wave in a simple PA app
+	pa_tests/patest_sync.c = test syncronization of audio and video
+	pa_tests/patest_wire.c = pass input to output, wire simulator
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/V19-devel-readme.txt
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/V19-devel-readme.txt	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/V19-devel-readme.txt	(revision 1753)
@@ -0,0 +1,222 @@
+STATUS:
+
+MME, DirectSound and ASIO versions are more-or-less working. See FIXMEs @todos
+and the proposals matrix at portaudio.com for further status.
+
+The pa_tests directory contains tests. pa_tests/README.txt notes which tests
+currently build.  
+
+The PaUtil support code is finished enough for other implementations to be
+ported. No changes are expected to be made to the definition of the PaUtil
+functions.
+
+Note that it's not yet 100% clear how the current support functions
+will interact with blocking read/write streams.
+
+BUILD INSTRUCTIONS
+
+to build tests/patest_sine.c you will need to compile and link the following
+files (MME)
+pa_common\pa_process.c
+pa_common\pa_skeleton.c
+pa_common\pa_stream.c
+pa_common\pa_trace.c
+pa_common\pa_converters.c
+pa_common\pa_cpuload.c
+pa_common\pa_dither.c
+pa_common\pa_front.c
+pa_common\pa_allocation.h
+pa_win\pa_win_util.c
+pa_win\pa_win_hostapis.c
+pa_win_wmme\pa_win_wmme.c
+
+see below for a description of these files.
+               
+
+FILES:
+
+portaudio.h
+    public api header file
+
+pa_front.c
+    implements the interface defined in portaudio.h. manages multiple host apis.
+    validates function parameters before calling through to host apis. tracks
+    open streams and closes them at Pa_Terminate().
+
+pa_util.h 
+    declares utility functions for use my implementations. including utility
+    functions which must be implemented separately for each platform.
+
+pa_hostapi.h
+    hostapi representation structure used to interface between pa_front.c
+    and implementations
+    
+pa_stream.c/h
+    stream interface and representation structures and helper functions
+    used to interface between pa_front.c and implementations
+
+pa_cpuload.c/h
+    source and header for cpu load calculation facility
+
+pa_trace.c/h
+    source and header for debug trace log facility
+
+pa_converters.c/h
+    sample buffer conversion facility
+
+pa_dither.c/h
+    dither noise generator
+
+pa_process.c/h
+    callback buffer processing facility including interleave and block adaption
+
+pa_allocation.c/h
+    allocation context for tracking groups of allocations
+
+pa_skeleton.c
+    an skeleton implementation showing how the common code can be used.
+
+pa_win_util.c
+    Win32 implementation of platform specific PaUtil functions (memory allocation,
+    usec clock, Pa_Sleep().)  The file will be used with all Win32 host APIs.
+    
+pa_win_hostapis.c
+    contains the paHostApiInitializers array and an implementation of
+    Pa_GetDefaultHostApi() for win32 builds.
+
+pa_win_wmme.c
+    Win32 host api implementation for the windows multimedia extensions audio API.
+
+pa_win_wmme.h
+    public header file containing interfaces to mme-specific functions and the
+    deviceInfo data structure.
+
+
+CODING GUIDELINES:
+
+naming conventions:
+    #defines begin with PA_
+    #defines local to a file end with _
+    global utility variables begin with paUtil
+    global utility types begin with PaUtil  (including function types)
+    global utility functions begin with PaUtil_
+    static variables end with _
+    static constants begin with const and end with _
+    static funtions have no special prefix/suffix
+
+In general, implementations should declare all of their members static,
+except for their initializer which should be exported. All exported names
+should be preceeded by Pa<MN>_ where MN is the module name, for example
+the windows mme initializer should be named PaWinWmme_Initialize().
+
+Every host api should define an initializer which returns an error code
+and a PaHostApiInterface*. The initializer should only return an error other
+than paNoError if it encounters an unexpected and fatal error (memory allocation
+error for example). In general, there may be conditions under which it returns
+a NULL interface pointer and also returns paNoError. For example, if the ASIO
+implementation detects that ASIO is not installed, it should return a
+NULL interface, and paNoError.
+
+Platform-specific shared functions should begin with Pa<PN>_ where PN is the
+platform name. eg. PaWin_ for windows, PaUnix_ for unix.
+
+The above two conventions should also be followed whenever it is necessary to
+share functions accross multiple source files.
+
+Two utilities for debug messages are provided. The PA_DEBUG macro defined in
+pa_implementation.h provides a simple way to print debug messages to stderr.
+Due to real-time performance issues, PA_DEBUG may not be suitable for use
+within the portaudio processing callback, or in other threads. In such cases
+the event tracing facility provided in pa_trace.h may be more appropriate.
+
+If PA_LOG_API_CALLS is defined, all calls to the public PortAudio API
+will be logged to stderr along with parameter and return values.
+
+
+TODO:
+    (this list is totally out of date)
+    
+    finish coding converter functions in pa_converters.c (anyone?)
+
+    implement block adaption in pa_process.c (phil?)
+
+    fix all current tests to work with new code. this should mostly involve
+    changing PortAudioStream to PaStream, and GetDefaultDeviceID to GetDefaultDevice etc.
+
+    write some new tests to exercise the multi-api functions
+
+    write (doxygen) documentation for pa_trace (phil?)
+
+    remove unused typeids from PaHostAPITypeID
+
+    create a global configuration file which documents which PA_ defines can be
+    used for configuration
+
+    need a coding standard for comment formatting
+
+    migrate directx (phil)
+
+    migrate asio (ross?, stephane?)
+
+    see top of pa_win_wmme.c for MME todo items (ross)
+
+    write style guide document (ross)
+    
+    
+DESIGN ISSUES:
+    (this list is totally out of date)
+    
+    consider removing Pa_ConvertHostApiDeviceIndexToGlobalDeviceIndex() from the API
+
+    switch to new latency parameter mechanism now (?)
+    
+    question: if input or outputDriverInfo structures are passed for a different
+    hostApi from the one being called, do we return an error or just ignore
+    them? (i think return error)
+
+    consider renaming PortAudioCallback to PaStreamCallback
+
+    consider renaming PaError, PaResult
+    
+
+ASSORTED DISORGANISED NOTES:
+
+    NOTE:
+        pa_lib.c performs the following validations for Pa_OpenStream() which we do not currently do:
+        - checks the device info to make sure that the device supports the requested sample rate,
+            it may also change the sample rate to the "closest available" sample rate if it
+            is within a particular error margin
+
+    rationale for breaking up internalPortAudioStream:
+        each implementation has its own requirements and behavior, and should be
+        able to choose the best way to operate without being limited by the
+        constraints imposed by a common infrastructure. in other words the
+        implementations should be able to pick and choose services from the
+        common infrastructure. currently identified services include:
+
+        - cpu load tracking
+        - buffering and conversion service (same code works for input and output)
+            - should support buffer multiplexing (non-integer length input and output buffers)
+            - in-place conversion where possible (only for callback, read/write always copies)
+            - should manage allocation of temporary buffers if necessary
+        - instrumentation (should be able to be disabled): callback count, framesProcessed
+        - common data: magic, streamInterface, callback, userdata
+
+
+- conversion functions: 
+	- should handle temp buffer allocation
+	- dithering (random number state per-stream)
+	- buffer size mismatches
+	- with new buffer slip rules, temp buffers may always be needed
+	- we should aim for in-place conversion wherever possible
+	- does phil's code support in-place conversion?  (yes)              
+
+- dicuss relationship between user and host buffer sizes
+	- completely independent.. individual implementations may constrain
+    host buffer sizes if necessary
+
+
+- discuss device capabilities:
+	- i'd like to be able to request certain information:
+	- channel count for example
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c	(revision 1753)
@@ -0,0 +1,637 @@
+/*
+ * $Id: dsound_wrapper.c,v 1.1.1.1.2.11 2003/09/07 13:04:53 rossbencina Exp $
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "dsound_wrapper.h"
+#include "pa_trace.h"
+
+/*
+    Rather than linking with dxguid.a or using "#define INITGUID" to force a
+    header file to instantiate the required GUID(s), we define them directly
+    below.
+*/
+#include <initguid.h> // needed for the DEFINE_GUID macro
+DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
+
+
+/************************************************************************************/
+DSoundEntryPoints dswDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 };
+/************************************************************************************/
+static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
+{
+    (void)lpcGuidDevice; /* unused parameter */
+    (void)ppDS; /* unused parameter */
+    (void)pUnkOuter; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundEnumerateW(LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext)
+{
+    (void)lpDSEnumCallback; /* unused parameter */
+    (void)lpContext; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundEnumerateA(LPDSENUMCALLBACKA lpDSEnumCallback, LPVOID lpContext)
+{
+    (void)lpDSEnumCallback; /* unused parameter */
+    (void)lpContext; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureCreate(LPGUID lpcGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter)
+{
+    (void)lpcGUID; /* unused parameter */
+    (void)lplpDSC; /* unused parameter */
+    (void)pUnkOuter; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW lpDSCEnumCallback, LPVOID lpContext)
+{
+    (void)lpDSCEnumCallback; /* unused parameter */
+    (void)lpContext; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCEnumCallback, LPVOID lpContext)
+{
+    (void)lpDSCEnumCallback; /* unused parameter */
+    (void)lpContext; /* unused parameter */
+    return E_NOTIMPL;
+}
+/************************************************************************************/
+void DSW_InitializeDSoundEntryPoints(void)
+{
+    dswDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll");
+    if( dswDSoundEntryPoints.hInstance_ != NULL )
+    {
+        dswDSoundEntryPoints.DirectSoundCreate =
+                (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCreate" );
+        if( dswDSoundEntryPoints.DirectSoundCreate == NULL )
+            dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
+
+        dswDSoundEntryPoints.DirectSoundEnumerateW =
+                (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" );
+        if( dswDSoundEntryPoints.DirectSoundEnumerateW == NULL )
+            dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
+
+        dswDSoundEntryPoints.DirectSoundEnumerateA =
+                (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" );
+        if( dswDSoundEntryPoints.DirectSoundEnumerateA == NULL )
+            dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
+
+        dswDSoundEntryPoints.DirectSoundCaptureCreate =
+                (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" );
+        if( dswDSoundEntryPoints.DirectSoundCaptureCreate == NULL )
+            dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
+
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateW =
+                (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" );
+        if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL )
+            dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
+
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateA =
+                (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" );
+        if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL )
+            dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
+    }
+    else
+    {
+        /* initialize with dummy entry points to make live easy when ds isn't present */
+        dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
+        dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
+        dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
+        dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
+    }
+}
+/************************************************************************************/
+void DSW_TerminateDSoundEntryPoints(void)
+{
+    if( dswDSoundEntryPoints.hInstance_ != NULL )
+    {
+        FreeLibrary( dswDSoundEntryPoints.hInstance_ );
+        dswDSoundEntryPoints.hInstance_ = NULL;
+        /* ensure that we crash reliably if the entry points arent initialised */
+        dswDSoundEntryPoints.DirectSoundCreate = 0;
+        dswDSoundEntryPoints.DirectSoundEnumerateW = 0;
+        dswDSoundEntryPoints.DirectSoundEnumerateA = 0;
+        dswDSoundEntryPoints.DirectSoundCaptureCreate = 0;
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = 0;
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = 0;
+    }
+}
+/************************************************************************************/
+void DSW_Term( DSoundWrapper *dsw )
+{
+    // Cleanup the sound buffers
+    if (dsw->dsw_OutputBuffer)
+    {
+        IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+        IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer );
+        dsw->dsw_OutputBuffer = NULL;
+    }
+
+    if (dsw->dsw_InputBuffer)
+    {
+        IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+        IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer );
+        dsw->dsw_InputBuffer = NULL;
+    }
+
+    if (dsw->dsw_pDirectSoundCapture)
+    {
+        IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture );
+        dsw->dsw_pDirectSoundCapture = NULL;
+    }
+
+    if (dsw->dsw_pDirectSound)
+    {
+        IDirectSound_Release( dsw->dsw_pDirectSound );
+        dsw->dsw_pDirectSound = NULL;
+    }
+}
+/************************************************************************************/
+HRESULT DSW_Init( DSoundWrapper *dsw )
+{
+    memset( dsw, 0, sizeof(DSoundWrapper) );
+    return 0;
+}
+/************************************************************************************/
+HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+    // Create the DS object
+    HRESULT hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
+    if( hr != DS_OK ) return hr;
+    return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
+{
+    DWORD          dwDataLen;
+    DWORD          playCursor;
+    HRESULT        result;
+    LPDIRECTSOUNDBUFFER pPrimaryBuffer;
+    HWND           hWnd;
+    HRESULT        hr;
+    WAVEFORMATEX   wfFormat;
+    DSBUFFERDESC   primaryDesc;
+    DSBUFFERDESC   secondaryDesc;
+    unsigned char* pDSBuffData;
+    LARGE_INTEGER  counterFrequency;
+
+    dsw->dsw_OutputSize = bytesPerBuffer;
+    dsw->dsw_OutputRunning = FALSE;
+    dsw->dsw_OutputUnderflows = 0;
+    dsw->dsw_FramesWritten = 0;
+    dsw->dsw_BytesPerOutputFrame = nChannels * sizeof(short);
+
+    // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
+    // applications's window. Also if that window is closed before the Buffer is closed
+    // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
+    // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
+    // hWnd = GetForegroundWindow();
+    //
+    //  FIXME: The example code I have on the net creates a hidden window that
+    //      is managed by our code - I think we should do that - one hidden
+    //      window for the whole of Pa_DS
+    //
+    hWnd = GetDesktopWindow();
+
+    // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
+    // Exclusize also prevents unexpected sounds from other apps during a performance.
+    if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
+              hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
+    {
+        return hr;
+    }
+
+    // -----------------------------------------------------------------------
+    // Create primary buffer and set format just so we can specify our custom format.
+    // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
+    // Setup the primary buffer description
+    ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
+    primaryDesc.dwSize        = sizeof(DSBUFFERDESC);
+    primaryDesc.dwFlags       = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
+    primaryDesc.dwBufferBytes = 0;
+    primaryDesc.lpwfxFormat   = NULL;
+    // Create the buffer
+    if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+                  &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
+    // Define the buffer format
+    wfFormat.wFormatTag = WAVE_FORMAT_PCM;
+    wfFormat.nChannels = nChannels;
+    wfFormat.nSamplesPerSec = nFrameRate;
+    wfFormat.wBitsPerSample = 8 * sizeof(short);
+    wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
+    wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+    wfFormat.cbSize = 0;  /* No extended format info. */
+    // Set the primary buffer's format
+    if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
+
+    // ----------------------------------------------------------------------
+    // Setup the secondary buffer description
+    ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
+    secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
+    secondaryDesc.dwFlags =  DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+    secondaryDesc.dwBufferBytes = bytesPerBuffer;
+    secondaryDesc.lpwfxFormat = &wfFormat;
+    // Create the secondary buffer
+    if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+                  &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
+    // Lock the DS buffer
+    if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
+                                           &dwDataLen, NULL, 0, 0)) != DS_OK) return result;
+    // Zero the DS buffer
+    ZeroMemory(pDSBuffData, dwDataLen);
+    // Unlock the DS buffer
+    if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
+    if( QueryPerformanceFrequency( &counterFrequency ) )
+    {
+        int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
+        dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
+    }
+    else
+    {
+        dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
+    }
+    // Let DSound set the starting write position because if we set it to zero, it looks like the
+    // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
+    hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerOutputFrame;
+    /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
+    return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartOutput( DSoundWrapper *dsw )
+{
+    HRESULT        hr;
+    QueryPerformanceCounter( &dsw->dsw_LastPlayTime );
+    dsw->dsw_LastPlayCursor = 0;
+    dsw->dsw_FramesPlayed = 0;
+    hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    // Start the buffer playback in a loop.
+    if( dsw->dsw_OutputBuffer != NULL )
+    {
+        hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING );
+        if( hr != DS_OK )
+        {
+            return hr;
+        }
+        dsw->dsw_OutputRunning = TRUE;
+    }
+
+    return 0;
+}
+/************************************************************************************/
+HRESULT DSW_StopOutput( DSoundWrapper *dsw )
+{
+    // Stop the buffer playback
+    if( dsw->dsw_OutputBuffer != NULL )
+    {
+        dsw->dsw_OutputRunning = FALSE;
+        return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+    }
+    else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilledPtr )
+{
+    HRESULT hr;
+    DWORD   playCursor;
+    DWORD   writeCursor;
+    long    bytesFilled;
+    // Query to see where play position is.
+    // We don't need the writeCursor but sometimes DirectSound doesn't handle NULLS correctly
+    // so let's pass a pointer just to be safe.
+    hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    bytesFilled = dsw->dsw_WriteOffset - playCursor;
+    if( bytesFilled < 0 ) bytesFilled += dsw->dsw_OutputSize; // unwrap offset
+    *bytesFilledPtr = bytesFilled;
+    return hr;
+}
+
+/************************************************************************************
+ * Determine how much space can be safely written to in DS buffer.
+ * Detect underflows and overflows.
+ * Does not allow writing into safety gap maintained by DirectSound.
+ */
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
+{
+    HRESULT hr;
+    DWORD   playCursor;
+    DWORD   writeCursor;
+    long    numBytesEmpty;
+    long    playWriteGap;
+    // Query to see how much room is in buffer.
+    hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    // Determine size of gap between playIndex and WriteIndex that we cannot write into.
+    playWriteGap = writeCursor - playCursor;
+    if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap
+    /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
+    /* Attempt to detect playCursor wrap-around and correct it. */
+    if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
+    {
+        /* How much time has elapsed since last check. */
+        LARGE_INTEGER   currentTime;
+        LARGE_INTEGER   elapsedTime;
+        long            bytesPlayed;
+        long            bytesExpected;
+        long            buffersWrapped;
+        QueryPerformanceCounter( &currentTime );
+        elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
+        dsw->dsw_LastPlayTime = currentTime;
+        /* How many bytes does DirectSound say have been played. */
+        bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
+        if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
+        dsw->dsw_LastPlayCursor = playCursor;
+        /* Calculate how many bytes we would have expected to been played by now. */
+        bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
+        buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
+        if( buffersWrapped > 0 )
+        {
+            playCursor += (buffersWrapped * dsw->dsw_OutputSize);
+            bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
+        }
+        /* Maintain frame output cursor. */
+        dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerOutputFrame);
+    }
+    numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
+    if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
+    /* Have we underflowed? */
+    if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
+    {
+        if( dsw->dsw_OutputRunning )
+        {
+            dsw->dsw_OutputUnderflows += 1;
+        }
+        dsw->dsw_WriteOffset = writeCursor;
+        numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
+    }
+    *bytesEmpty = numBytesEmpty;
+    return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw )
+{
+    HRESULT hr;
+    LPBYTE lpbuf1 = NULL;
+    LPBYTE lpbuf2 = NULL;
+    DWORD dwsize1 = 0;
+    DWORD dwsize2 = 0;
+    long  bytesEmpty;
+    hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed
+    if (hr != DS_OK) return hr;
+    if( bytesEmpty == 0 ) return DS_OK;
+    // Lock free space in the DS
+    hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1,
+                                  (void **) &lpbuf2, &dwsize2, 0);
+    if (hr == DS_OK)
+    {
+        // Copy the buffer into the DS
+        ZeroMemory(lpbuf1, dwsize1);
+        if(lpbuf2 != NULL)
+        {
+            ZeroMemory(lpbuf2, dwsize2);
+        }
+        // Update our buffer offset and unlock sound buffer
+        dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+        IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+        dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerOutputFrame;
+    }
+    return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+    HRESULT hr;
+    LPBYTE lpbuf1 = NULL;
+    LPBYTE lpbuf2 = NULL;
+    DWORD dwsize1 = 0;
+    DWORD dwsize2 = 0;
+    // Lock free space in the DS
+    hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+                                  (void **) &lpbuf2, &dwsize2, 0);
+    if (hr == DS_OK)
+    {
+        // Copy the buffer into the DS
+        CopyMemory(lpbuf1, buf, dwsize1);
+        if(lpbuf2 != NULL)
+        {
+            CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
+        }
+        // Update our buffer offset and unlock sound buffer
+        dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+        IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+        dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerOutputFrame;
+    }
+    return hr;
+}
+
+/************************************************************************************/
+DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
+{
+    DWORD status;
+    if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
+        return( DSERR_INVALIDPARAM );
+    else
+        return( status );
+}
+
+/* These routines are used to support audio input.
+ * Do NOT compile these calls when using NT4 because it does
+ * not support the entry points.
+ */
+/************************************************************************************/
+HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+    HRESULT hr = dswDSoundEntryPoints.DirectSoundCaptureCreate(  lpGUID, &dsw->dsw_pDirectSoundCapture,   NULL );
+    if( hr != DS_OK ) return hr;
+    return hr;
+}
+/************************************************************************************/
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
+{
+    DSCBUFFERDESC  captureDesc;
+    WAVEFORMATEX   wfFormat;
+    HRESULT        result;
+    
+    dsw->dsw_BytesPerInputFrame = nChannels * sizeof(short);
+
+    // Define the buffer format
+    wfFormat.wFormatTag      = WAVE_FORMAT_PCM;
+    wfFormat.nChannels       = nChannels;
+    wfFormat.nSamplesPerSec  = nFrameRate;
+    wfFormat.wBitsPerSample  = 8 * sizeof(short);
+    wfFormat.nBlockAlign     = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
+    wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+    wfFormat.cbSize          = 0;   /* No extended format info. */
+    dsw->dsw_InputSize = bytesPerBuffer;
+    // ----------------------------------------------------------------------
+    // Setup the secondary buffer description
+    ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
+    captureDesc.dwSize = sizeof(DSCBUFFERDESC);
+    captureDesc.dwFlags =  0;
+    captureDesc.dwBufferBytes = bytesPerBuffer;
+    captureDesc.lpwfxFormat = &wfFormat;
+    // Create the capture buffer
+    if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture,
+                  &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result;
+    dsw->dsw_ReadOffset = 0;  // reset last read position to start of buffer
+    return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartInput( DSoundWrapper *dsw )
+{
+    // Start the buffer playback
+    if( dsw->dsw_InputBuffer != NULL )
+    {
+        return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING );
+    }
+    else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_StopInput( DSoundWrapper *dsw )
+{
+    // Stop the buffer playback
+    if( dsw->dsw_InputBuffer != NULL )
+    {
+        return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+    }
+    else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled )
+{
+    HRESULT hr;
+    DWORD capturePos;
+    DWORD readPos;
+    long  filled;
+    // Query to see how much data is in buffer.
+    // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
+    // so let's pass a pointer just to be safe.
+    hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    filled = readPos - dsw->dsw_ReadOffset;
+    if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset
+    *bytesFilled = filled;
+    return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+    HRESULT hr;
+    LPBYTE lpbuf1 = NULL;
+    LPBYTE lpbuf2 = NULL;
+    DWORD dwsize1 = 0;
+    DWORD dwsize2 = 0;
+    // Lock free space in the DS
+    hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+                                          (void **) &lpbuf2, &dwsize2, 0);
+    if (hr == DS_OK)
+    {
+        // Copy from DS to the buffer
+        CopyMemory( buf, lpbuf1, dwsize1);
+        if(lpbuf2 != NULL)
+        {
+            CopyMemory( buf+dwsize1, lpbuf2, dwsize2);
+        }
+        // Update our buffer offset and unlock sound buffer
+        dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize;
+        IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+    }
+    return hr;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h	(revision 1753)
@@ -0,0 +1,151 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef __DSOUND_WRAPPER_H
+#define __DSOUND_WRAPPER_H
+/*
+ * $Id: dsound_wrapper.h,v 1.1.1.1.2.8 2005/01/16 20:48:37 rossbencina Exp $
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * For PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* on Borland compilers, WIN32 doesn't seem to be defined by default, which
+    breaks DSound.h. Adding the define here fixes the problem. - rossb. */
+#ifdef __BORLANDC__
+#if !defined(WIN32)
+#define WIN32
+#endif
+#endif
+
+/*
+  We are only using DX3 in here, no need to polute the namespace - davidv
+*/
+#define DIRECTSOUND_VERSION 0x0300
+
+#include <DSound.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct
+{
+    HINSTANCE hInstance_;
+    
+    HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
+    HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
+    HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
+
+    HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
+    HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
+    HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
+}DSoundEntryPoints;
+
+extern DSoundEntryPoints dswDSoundEntryPoints;
+
+void DSW_InitializeDSoundEntryPoints(void);
+void DSW_TerminateDSoundEntryPoints(void);
+
+#define DSW_NUM_POSITIONS     (4)
+#define DSW_NUM_EVENTS        (5)
+#define DSW_TERMINATION_EVENT     (DSW_NUM_POSITIONS)
+
+typedef struct
+{
+/* Output */
+    LPDIRECTSOUND        dsw_pDirectSound;
+    LPDIRECTSOUNDBUFFER  dsw_OutputBuffer;
+    DWORD                dsw_WriteOffset;     /* last write position */
+    INT                  dsw_OutputSize;
+    INT                  dsw_BytesPerOutputFrame;
+    /* Try to detect play buffer underflows. */
+    LARGE_INTEGER        dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
+    LARGE_INTEGER        dsw_LastPlayTime;
+    UINT                 dsw_LastPlayCursor;
+    UINT                 dsw_OutputUnderflows;
+    BOOL                 dsw_OutputRunning;
+    /* use double which lets us can play for several thousand years with enough precision */
+    double               dsw_FramesWritten;
+    double               dsw_FramesPlayed;
+/* Input */
+    INT                  dsw_BytesPerInputFrame;
+    LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture;
+    LPDIRECTSOUNDCAPTUREBUFFER   dsw_InputBuffer;
+    UINT                 dsw_ReadOffset;      /* last read position */
+    UINT                 dsw_InputSize;
+} DSoundWrapper;
+
+HRESULT DSW_Init( DSoundWrapper *dsw );
+void DSW_Term( DSoundWrapper *dsw );
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+                              WORD nChannels, int bufSize );
+HRESULT DSW_StartOutput( DSoundWrapper *dsw );
+HRESULT DSW_StopOutput( DSoundWrapper *dsw );
+DWORD   DSW_GetOutputStatus( DSoundWrapper *dsw );
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw );
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty );
+HRESULT DSW_Enumerate( DSoundWrapper *dsw );
+
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+                             WORD nChannels, int bufSize );
+HRESULT DSW_StartInput( DSoundWrapper *dsw );
+HRESULT DSW_StopInput( DSoundWrapper *dsw );
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled );
+HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilled );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif  /* __DSOUND_WRAPPER_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_allocation.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_allocation.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_allocation.c	(revision 1753)
@@ -0,0 +1,255 @@
+/*
+ * $Id: pa_allocation.c,v 1.1.2.6 2004/12/20 12:07:51 rossbencina Exp $
+ * Portable Audio I/O Library allocation group implementation
+ * memory allocation group for tracking allocation groups
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+ @brief Allocation Group implementation.
+*/
+
+
+#include "pa_allocation.h"
+#include "pa_util.h"
+
+
+/*
+    Maintain 3 singly linked lists...
+    linkBlocks: the buffers used to allocate the links
+    spareLinks: links available for use in the allocations list
+    allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory()
+
+    Link block size is doubled every time new links are allocated.
+*/
+
+
+#define PA_INITIAL_LINK_COUNT_    16
+
+struct PaUtilAllocationGroupLink
+{
+    struct PaUtilAllocationGroupLink *next;
+    void *buffer;
+};
+
+/*
+    Allocate a block of links. The first link will have it's buffer member
+    pointing to the block, and it's next member set to <nextBlock>. The remaining
+    links will have NULL buffer members, and each link will point to
+    the next link except the last, which will point to <nextSpare>
+*/
+static struct PaUtilAllocationGroupLink *AllocateLinks( long count,
+        struct PaUtilAllocationGroupLink *nextBlock,
+        struct PaUtilAllocationGroupLink *nextSpare )
+{
+    struct PaUtilAllocationGroupLink *result;
+    int i;
+    
+    result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory(
+            sizeof(struct PaUtilAllocationGroupLink) * count );
+    if( result )
+    {
+        /* the block link */
+        result[0].buffer = result;
+        result[0].next = nextBlock;
+
+        /* the spare links */
+        for( i=1; i<count; ++i )
+        {
+            result[i].buffer = 0;
+            result[i].next = &result[i+1];
+        }
+        result[count-1].next = nextSpare;
+    }
+    
+    return result;
+}
+
+
+PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void )
+{
+    PaUtilAllocationGroup* result = 0;
+    struct PaUtilAllocationGroupLink *links;
+
+
+    links = AllocateLinks( PA_INITIAL_LINK_COUNT_, 0, 0 );
+    if( links != 0 )
+    {
+        result = (PaUtilAllocationGroup*)PaUtil_AllocateMemory( sizeof(PaUtilAllocationGroup) );
+        if( result )
+        {
+            result->linkCount = PA_INITIAL_LINK_COUNT_;
+            result->linkBlocks = &links[0];
+            result->spareLinks = &links[1];
+            result->allocations = 0;
+        }
+        else
+        {
+            PaUtil_FreeMemory( links );
+        }
+    }
+
+    return result;
+}
+
+
+void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group )
+{
+    struct PaUtilAllocationGroupLink *current = group->linkBlocks;
+    struct PaUtilAllocationGroupLink *next;
+
+    while( current )
+    {
+        next = current->next;
+        PaUtil_FreeMemory( current->buffer );
+        current = next;
+    }
+
+    PaUtil_FreeMemory( group );
+}
+
+
+void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size )
+{
+    struct PaUtilAllocationGroupLink *links, *link;
+    void *result = 0;
+    
+    /* allocate more links if necessary */
+    if( !group->spareLinks )
+    {
+        /* double the link count on each block allocation */
+        links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks );
+        if( links )
+        {
+            group->linkCount += group->linkCount;
+            group->linkBlocks = &links[0];
+            group->spareLinks = &links[1];
+        }
+    }
+
+    if( group->spareLinks )
+    {
+        result = PaUtil_AllocateMemory( size );
+        if( result )
+        {
+            link = group->spareLinks;
+            group->spareLinks = link->next;
+
+            link->buffer = result;
+            link->next = group->allocations;
+
+            group->allocations = link;
+        }
+    }
+
+    return result;    
+}
+
+
+void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer )
+{
+    struct PaUtilAllocationGroupLink *current = group->allocations;
+    struct PaUtilAllocationGroupLink *previous = 0;
+
+    if( buffer == 0 )
+        return;
+
+    /* find the right link and remove it */
+    while( current )
+    {
+        if( current->buffer == buffer )
+        {
+            if( previous )
+            {
+                previous->next = current->next;
+            }
+            else
+            {
+                group->allocations = current->next;
+            }
+
+            current->buffer = 0;
+            current->next = group->spareLinks;
+            group->spareLinks = current;
+
+            break;
+        }
+        
+        previous = current;
+        current = current->next;
+    }
+
+    PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */
+}
+
+
+void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group )
+{
+    struct PaUtilAllocationGroupLink *current = group->allocations;
+    struct PaUtilAllocationGroupLink *previous = 0;
+
+    /* free all buffers in the allocations list */
+    while( current )
+    {
+        PaUtil_FreeMemory( current->buffer );
+        current->buffer = 0;
+
+        previous = current;
+        current = current->next;
+    }
+
+    /* link the former allocations list onto the front of the spareLinks list */
+    if( previous )
+    {
+        previous->next = group->spareLinks;
+        group->spareLinks = group->allocations;
+        group->allocations = 0;
+    }
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_allocation.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_allocation.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_allocation.h	(revision 1753)
@@ -0,0 +1,116 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_ALLOCATION_H
+#define PA_ALLOCATION_H
+/*
+ * $Id: pa_allocation.h,v 1.1.2.4 2003/09/20 21:04:44 rossbencina Exp $
+ * Portable Audio I/O Library allocation context header
+ * memory allocation context for tracking allocation groups
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Allocation Group prototypes. An Allocation Group makes it easy to
+ allocate multiple blocks of memory and free them all simultanously.
+ 
+ An allocation group is useful for keeping track of multiple blocks
+ of memory which are allocated at the same time (such as during initialization)
+ and need to be deallocated at the same time. The allocation group maintains
+ a list of allocated blocks, and can deallocate them all simultaneously which
+ can be usefull for cleaning up after a partially initialized object fails.
+
+ The allocation group implementation is built on top of the lower
+ level allocation functions defined in pa_util.h
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct
+{
+    long linkCount;
+    struct PaUtilAllocationGroupLink *linkBlocks;
+    struct PaUtilAllocationGroupLink *spareLinks;
+    struct PaUtilAllocationGroupLink *allocations;
+}PaUtilAllocationGroup;
+
+
+
+/** Create an allocation group.
+*/
+PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void );
+
+/** Destroy an allocation group, but not the memory allocated through the group.
+*/
+void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group );
+
+/** Allocate a block of memory though an allocation group.
+*/
+void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size );
+
+/** Free a block of memory that was previously allocated though an allocation
+ group. Calling this function is a relatively time consuming operation.
+ Under normal circumstances clients should call PaUtil_FreeAllAllocations to
+ free all allocated blocks simultaneously.
+ @see PaUtil_FreeAllAllocations
+*/
+void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer );
+
+/** Free all blocks of memory which have been allocated through the allocation
+ group. This function doesn't destroy the group itself.
+*/
+void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_ALLOCATION_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_converters.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_converters.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_converters.c	(revision 1753)
@@ -0,0 +1,1947 @@
+/*
+ * $Id: pa_converters.c,v 1.1.2.26 2004/12/11 16:32:38 aknudsen Exp $
+ * Portable Audio I/O Library sample conversion mechanism
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+ @brief Conversion functions implementations.
+ 
+ If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it
+
+ @todo Consider whether functions which dither but don't clip should exist,
+ V18 automatically enabled clipping whenever dithering was selected. Perhaps
+ we should do the same.
+
+ @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither,
+ Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither,
+ Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither, 
+ Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither,
+
+ @todo review the converters marked REVIEW: Float32_To_Int32,
+ Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip,
+ Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32
+*/
+
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+#include "pa_endianness.h"
+#include "pa_types.h"
+
+
+PaSampleFormat PaUtil_SelectClosestAvailableFormat(
+        PaSampleFormat availableFormats, PaSampleFormat format )
+{
+    PaSampleFormat result;
+
+    format &= ~paNonInterleaved;
+    availableFormats &= ~paNonInterleaved;
+    
+    if( (format & availableFormats) == 0 )
+    {
+        /* NOTE: this code depends on the sample format constants being in
+            descending order of quality - ie best quality is 0
+            FIXME: should write an assert which checks that all of the
+            known constants conform to that requirement.
+        */
+
+        if( format != 0x01 )
+        {
+            /* scan for better formats */
+            result = format;
+            do
+            {
+                result >>= 1;
+            }
+            while( (result & availableFormats) == 0 && result != 0 );
+        }
+        else
+        {
+            result = 0;
+        }
+        
+        if( result == 0 ){
+            /* scan for worse formats */
+            result = format;
+            do
+            {
+                result <<= 1;
+            }
+            while( (result & availableFormats) == 0 && result != paCustomFormat );
+
+            if( (result & availableFormats) == 0 )
+                result = paSampleFormatNotSupported;
+        }
+        
+    }else{
+        result = format;
+    }
+
+    return result;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_FORMAT_( format, float32, int32, int24, int16, int8, uint8 ) \
+    switch( format & ~paNonInterleaved ){                                      \
+    case paFloat32:                                                            \
+        float32                                                                \
+    case paInt32:                                                              \
+        int32                                                                  \
+    case paInt24:                                                              \
+        int24                                                                  \
+    case paInt16:                                                              \
+        int16                                                                  \
+    case paInt8:                                                               \
+        int8                                                                   \
+    case paUInt8:                                                              \
+        uint8                                                                  \
+    default: return 0;                                                         \
+    }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_CLIP_( flags, source, destination )         \
+    if( flags & paClipOff ){ /* no clip */                                     \
+        if( flags & paDitherOff ){ /* no dither */                             \
+            return paConverters. source ## _To_ ## destination;                \
+        }else{ /* dither */                                                    \
+            return paConverters. source ## _To_ ## destination ## _Dither;     \
+        }                                                                      \
+    }else{ /* clip */                                                          \
+        if( flags & paDitherOff ){ /* no dither */                             \
+            return paConverters. source ## _To_ ## destination ## _Clip;       \
+        }else{ /* dither */                                                    \
+            return paConverters. source ## _To_ ## destination ## _DitherClip; \
+        }                                                                      \
+    }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_( flags, source, destination )              \
+    if( flags & paDitherOff ){ /* no dither */                                 \
+        return paConverters. source ## _To_ ## destination;                    \
+    }else{ /* dither */                                                        \
+        return paConverters. source ## _To_ ## destination ## _Dither;         \
+    }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_USE_CONVERTER_( source, destination )\
+    return paConverters. source ## _To_ ## destination;
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_UNITY_CONVERSION_( wordlength )\
+    return paConverters. Copy_ ## wordlength ## _To_ ## wordlength;
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
+        PaSampleFormat destinationFormat, PaStreamFlags flags )
+{
+    PA_SELECT_FORMAT_( sourceFormat,
+                       /* paFloat32: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_UNITY_CONVERSION_( 32 ),
+                                          /* paInt32: */          PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int32 ),
+                                          /* paInt24: */          PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int24 ),
+                                          /* paInt16: */          PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int16 ),
+                                          /* paInt8: */           PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int8 ),
+                                          /* paUInt8: */          PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, UInt8 )
+                                        ),
+                       /* paInt32: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( Int32, Float32 ),
+                                          /* paInt32: */          PA_UNITY_CONVERSION_( 32 ),
+                                          /* paInt24: */          PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int24 ),
+                                          /* paInt16: */          PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int16 ),
+                                          /* paInt8: */           PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int8 ),
+                                          /* paUInt8: */          PA_SELECT_CONVERTER_DITHER_( flags, Int32, UInt8 )
+                                        ),
+                       /* paInt24: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( Int24, Float32 ),
+                                          /* paInt32: */          PA_USE_CONVERTER_( Int24, Int32 ),
+                                          /* paInt24: */          PA_UNITY_CONVERSION_( 24 ),
+                                          /* paInt16: */          PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int16 ),
+                                          /* paInt8: */           PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int8 ),
+                                          /* paUInt8: */          PA_SELECT_CONVERTER_DITHER_( flags, Int24, UInt8 )
+                                        ),
+                       /* paInt16: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( Int16, Float32 ),
+                                          /* paInt32: */          PA_USE_CONVERTER_( Int16, Int32 ),
+                                          /* paInt24: */          PA_USE_CONVERTER_( Int16, Int24 ),
+                                          /* paInt16: */          PA_UNITY_CONVERSION_( 16 ),
+                                          /* paInt8: */           PA_SELECT_CONVERTER_DITHER_( flags, Int16, Int8 ),
+                                          /* paUInt8: */          PA_SELECT_CONVERTER_DITHER_( flags, Int16, UInt8 )
+                                        ),
+                       /* paInt8: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( Int8, Float32 ),
+                                          /* paInt32: */          PA_USE_CONVERTER_( Int8, Int32 ),
+                                          /* paInt24: */          PA_USE_CONVERTER_( Int8, Int24 ),
+                                          /* paInt16: */          PA_USE_CONVERTER_( Int8, Int16 ),
+                                          /* paInt8: */           PA_UNITY_CONVERSION_( 8 ),
+                                          /* paUInt8: */          PA_USE_CONVERTER_( Int8, UInt8 )
+                                        ),
+                       /* paUInt8: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( UInt8, Float32 ),
+                                          /* paInt32: */          PA_USE_CONVERTER_( UInt8, Int32 ),
+                                          /* paInt24: */          PA_USE_CONVERTER_( UInt8, Int24 ),
+                                          /* paInt16: */          PA_USE_CONVERTER_( UInt8, Int16 ),
+                                          /* paInt8: */           PA_USE_CONVERTER_( UInt8, Int8 ),
+                                          /* paUInt8: */          PA_UNITY_CONVERSION_( 8 )
+                                        )
+                     )
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_CONVERTERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+    0, /* PaUtilConverter *Float32_To_Int32; */
+    0, /* PaUtilConverter *Float32_To_Int32_Dither; */
+    0, /* PaUtilConverter *Float32_To_Int32_Clip; */
+    0, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+    0, /* PaUtilConverter *Float32_To_Int24; */
+    0, /* PaUtilConverter *Float32_To_Int24_Dither; */
+    0, /* PaUtilConverter *Float32_To_Int24_Clip; */
+    0, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+
+    0, /* PaUtilConverter *Float32_To_Int16; */
+    0, /* PaUtilConverter *Float32_To_Int16_Dither; */
+    0, /* PaUtilConverter *Float32_To_Int16_Clip; */
+    0, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+    0, /* PaUtilConverter *Float32_To_Int8; */
+    0, /* PaUtilConverter *Float32_To_Int8_Dither; */
+    0, /* PaUtilConverter *Float32_To_Int8_Clip; */
+    0, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+    0, /* PaUtilConverter *Float32_To_UInt8; */
+    0, /* PaUtilConverter *Float32_To_UInt8_Dither; */
+    0, /* PaUtilConverter *Float32_To_UInt8_Clip; */
+    0, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+    0, /* PaUtilConverter *Int32_To_Float32; */
+    0, /* PaUtilConverter *Int32_To_Int24; */
+    0, /* PaUtilConverter *Int32_To_Int24_Dither; */
+    0, /* PaUtilConverter *Int32_To_Int16; */
+    0, /* PaUtilConverter *Int32_To_Int16_Dither; */
+    0, /* PaUtilConverter *Int32_To_Int8; */
+    0, /* PaUtilConverter *Int32_To_Int8_Dither; */
+    0, /* PaUtilConverter *Int32_To_UInt8; */
+    0, /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+    0, /* PaUtilConverter *Int24_To_Float32; */
+    0, /* PaUtilConverter *Int24_To_Int32; */
+    0, /* PaUtilConverter *Int24_To_Int16; */
+    0, /* PaUtilConverter *Int24_To_Int16_Dither; */
+    0, /* PaUtilConverter *Int24_To_Int8; */
+    0, /* PaUtilConverter *Int24_To_Int8_Dither; */
+    0, /* PaUtilConverter *Int24_To_UInt8; */
+    0, /* PaUtilConverter *Int24_To_UInt8_Dither; */
+    
+    0, /* PaUtilConverter *Int16_To_Float32; */
+    0, /* PaUtilConverter *Int16_To_Int32; */
+    0, /* PaUtilConverter *Int16_To_Int24; */
+    0, /* PaUtilConverter *Int16_To_Int8; */
+    0, /* PaUtilConverter *Int16_To_Int8_Dither; */
+    0, /* PaUtilConverter *Int16_To_UInt8; */
+    0, /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+    0, /* PaUtilConverter *Int8_To_Float32; */
+    0, /* PaUtilConverter *Int8_To_Int32; */
+    0, /* PaUtilConverter *Int8_To_Int24 */
+    0, /* PaUtilConverter *Int8_To_Int16; */
+    0, /* PaUtilConverter *Int8_To_UInt8; */
+
+    0, /* PaUtilConverter *UInt8_To_Float32; */
+    0, /* PaUtilConverter *UInt8_To_Int32; */
+    0, /* PaUtilConverter *UInt8_To_Int24; */
+    0, /* PaUtilConverter *UInt8_To_Int16; */
+    0, /* PaUtilConverter *UInt8_To_Int8; */
+
+    0, /* PaUtilConverter *Copy_8_To_8; */
+    0, /* PaUtilConverter *Copy_16_To_16; */
+    0, /* PaUtilConverter *Copy_24_To_24; */
+    0  /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_CONVERTERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_CLIP_( val, min, max )\
+    { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+
+
+static const float const_1_div_128_ = 1.0f / 128.0f;  /* 8 bit multiplier */
+
+static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */
+
+static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+        float scaled = *src * 0x7FFFFFFF;
+        *dest = lrintf(scaled-0.5f);
+#else
+        double scaled = *src * 0x7FFFFFFF;
+        *dest = (signed long) scaled;        
+#endif
+        
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+
+    while( count-- )
+    {
+        /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = ((float)*src * (2147483646.0f)) + dither;
+        *dest = lrintf(dithered - 0.5f);
+#else
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        *dest = (signed long) dithered;
+#endif
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+        float scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648.f, 2147483647.f  );
+        *dest = lrintf(scaled-0.5f);
+#else
+        double scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648., 2147483647.  );
+        *dest = (signed long) scaled;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+
+    while( count-- )
+    {
+        /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = ((float)*src * (2147483646.0f)) + dither;
+        PA_CLIP_( dithered, -2147483648.f, 2147483647.f  );
+        *dest = lrintf(dithered-0.5f);
+#else
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        PA_CLIP_( dithered, -2147483648., 2147483647.  );
+        *dest = (signed long) dithered;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        /* convert to 32 bit and drop the low 8 bits */
+        double scaled = *src * 0x7FFFFFFF;
+        temp = (signed long) scaled;
+        
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 24);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    while( count-- )
+    {
+        /* convert to 32 bit and drop the low 8 bits */
+
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        
+        temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 24);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        /* convert to 32 bit and drop the low 8 bits */
+        double scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648., 2147483647.  );
+        temp = (signed long) scaled;
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 24);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+    
+    while( count-- )
+    {
+        /* convert to 32 bit and drop the low 8 bits */
+        
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        PA_CLIP_( dithered, -2147483648., 2147483647.  );
+        
+        temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 24);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+#ifdef PA_USE_C99_LRINTF
+        float tempf = (*src * (32767.0f)) ;
+        *dest = lrintf(tempf-0.5f);
+#else
+        short samp = (short) (*src * (32767.0f));
+        *dest = samp;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed short *dest = (signed short*)destinationBuffer;
+
+    while( count-- )
+    {
+
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = (*src * (32766.0f)) + dither;
+
+#ifdef PA_USE_C99_LRINTF
+        *dest = lrintf(dithered-0.5f);
+#else
+        *dest = (signed short) dithered;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+#ifdef PA_USE_C99_LRINTF
+        long samp = lrintf((*src * (32767.0f)) -0.5f);
+#else
+        long samp = (signed long) (*src * (32767.0f));
+#endif
+        PA_CLIP_( samp, -0x8000, 0x7FFF );
+        *dest = (signed short) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = (*src * (32766.0f)) + dither;
+        signed long samp = (signed long) dithered;
+        PA_CLIP_( samp, -0x8000, 0x7FFF );
+#ifdef PA_USE_C99_LRINTF
+        *dest = lrintf(samp-0.5f);
+#else
+        *dest = (signed short) samp;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        signed char samp = (signed char) (*src * (127.0f));
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = (*src * (126.0f)) + dither;
+        signed long samp = (signed long) dithered;
+        *dest = (signed char) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        signed long samp = (signed long)(*src * (127.0f));
+        PA_CLIP_( samp, -0x80, 0x7F );
+        *dest = (signed char) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = (*src * (126.0f)) + dither;
+        signed long samp = (signed long) dithered;
+        PA_CLIP_( samp, -0x80, 0x7F );
+        *dest = (signed char) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        unsigned char samp = (unsigned char)(128 + ((unsigned char) (*src * (127.0f))));
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    float *dest =  (float*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        *dest = (float) ((double)*src * const_1_div_2147483648_);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src    = (signed long*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    (void) ditherGenerator; /* unused parameter */
+    
+	while( count-- )
+    {
+		/* REVIEW */
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(*src >> 8);
+        dest[1] = (unsigned char)(*src >> 16);
+        dest[2] = (unsigned char)(*src >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(*src >> 24);
+        dest[1] = (unsigned char)(*src >> 16);
+        dest[2] = (unsigned char)(*src >> 8);
+#endif
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    (void) destinationBuffer; /* unused parameters */
+    (void) destinationStride; /* unused parameters */
+    (void) sourceBuffer; /* unused parameters */
+    (void) sourceStride; /* unused parameters */
+    (void) count; /* unused parameters */
+    (void) ditherGenerator; /* unused parameters */
+    /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        *dest = (signed short) ((*src) >> 16);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    signed long dither;
+
+    while( count-- )
+    {
+        /* REVIEW */
+        dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+        *dest = (signed short) ((((*src)>>1) + dither) >> 15);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        *dest = (signed char) ((*src) >> 24);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    signed long dither;
+
+    while( count-- )
+    {
+        /* REVIEW */
+        dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+        *dest = (signed char) ((((*src)>>1) + dither) >> 23);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (unsigned char)(((*src) >> 24) + 128); 
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    float *dest = (float*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+
+#if defined(PA_LITTLE_ENDIAN)
+        temp = (((long)src[0]) << 8);  
+        temp = temp | (((long)src[1]) << 16);
+        temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+        temp = (((long)src[0]) << 24);
+        temp = temp | (((long)src[1]) << 16);
+        temp = temp | (((long)src[2]) << 8);
+#endif
+
+        *dest = (float) ((double)temp * const_1_div_2147483648_);
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src  = (unsigned char*)sourceBuffer;
+    signed long   *dest = (signed long*)  destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+
+#if defined(PA_LITTLE_ENDIAN)
+        temp = (((long)src[0]) << 8);  
+        temp = temp | (((long)src[1]) << 16);
+        temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+        temp = (((long)src[0]) << 24);
+        temp = temp | (((long)src[1]) << 16);
+        temp = temp | (((long)src[2]) << 8);
+#endif
+
+        *dest = temp;
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed short *dest = (signed short*)destinationBuffer;
+    
+	signed short temp;
+
+    (void) ditherGenerator; /* unused parameter */
+        
+    while( count-- )
+    {
+		
+#if defined(PA_LITTLE_ENDIAN)
+		/* src[0] is discarded */
+        temp = (((signed short)src[1]));
+        temp = temp | (signed short)(((signed short)src[2]) << 8);
+#elif defined(PA_BIG_ENDIAN)
+		/* src[2] is discarded */
+        temp = (signed short)(((signed short)src[0]) << 8);
+        temp = temp | (((signed short)src[1]));
+#endif
+
+        *dest = temp;
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    (void) destinationBuffer; /* unused parameters */
+    (void) destinationStride; /* unused parameters */
+    (void) sourceBuffer; /* unused parameters */
+    (void) sourceStride; /* unused parameters */
+    (void) count; /* unused parameters */
+    (void) ditherGenerator; /* unused parameters */
+    /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed char  *dest = (signed char*)destinationBuffer;
+    
+    (void) ditherGenerator; /* unused parameter */
+        
+    while( count-- )
+    {	
+	
+#if defined(PA_LITTLE_ENDIAN)
+		/* src[0] is discarded */
+		/* src[1] is discarded */
+        *dest = src[2];
+#elif defined(PA_BIG_ENDIAN)
+		/* src[2] is discarded */
+		/* src[1] is discarded */
+		*dest = src[0];
+#endif
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    (void) destinationBuffer; /* unused parameters */
+    (void) destinationStride; /* unused parameters */
+    (void) sourceBuffer; /* unused parameters */
+    (void) sourceStride; /* unused parameters */
+    (void) count; /* unused parameters */
+    (void) ditherGenerator; /* unused parameters */
+    /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    
+    (void) ditherGenerator; /* unused parameter */
+        
+    while( count-- )
+    {
+		
+#if defined(PA_LITTLE_ENDIAN)
+		/* src[0] is discarded */
+		/* src[1] is discarded */
+        *dest = (unsigned char)(src[2] + 128);
+#elif defined(PA_BIG_ENDIAN)
+        *dest = (unsigned char)(src[0] + 128);
+		/* src[1] is discarded */
+		/* src[2] is discarded */		
+#endif
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    (void) destinationBuffer; /* unused parameters */
+    (void) destinationStride; /* unused parameters */
+    (void) sourceBuffer; /* unused parameters */
+    (void) sourceStride; /* unused parameters */
+    (void) count; /* unused parameters */
+    (void) ditherGenerator; /* unused parameters */
+    /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    float *dest =  (float*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float samp = *src * const_1_div_32768_; /* FIXME: i'm concerned about this being asymetrical with float->int16 -rb */
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* REVIEW: we should consider something like
+            (*src << 16) | (*src & 0xFFFF)
+        */
+        
+        *dest = *src << 16;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src   = (signed short*) sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed short temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        temp = *src;
+        
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = 0;
+        dest[1] = (unsigned char)(temp);
+        dest[2] = (unsigned char)(temp >> 8);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp);
+        dest[2] = 0;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        (*dest) = (signed char)((*src) >> 8);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (unsigned char)(((*src) >> 8) + 128); 
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    float *dest =  (float*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float samp = *src * const_1_div_128_;
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (*src) << 24;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = 0;
+        dest[1] = 0;
+        dest[2] = (*src);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (*src);
+        dest[1] = 0;
+        dest[2] = 0;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (signed short)((*src) << 8);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        (*dest) = (unsigned char)(*src + 128);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    float *dest =  (float*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float samp = (*src - 128) * const_1_div_128_;
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (*src - 128) << 24;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+	unsigned char *src  = (unsigned char*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    (void) ditherGenerator; /* unused parameters */
+    
+	while( count-- )
+    {
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = 0;
+        dest[1] = 0;
+        dest[2] = (unsigned char)(*src - 128);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(*src - 128);
+        dest[1] = 0;
+        dest[2] = 0;
+#endif
+		
+        src += sourceStride;
+        dest += destinationStride * 3;    
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (signed short)((*src - 128) << 8);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed char  *dest = (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        (*dest) = (signed char)(*src - 128);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_8_To_8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+                                                      
+    (void) ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        *dest = *src;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_16_To_16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    PaUint16 *src = (PaUint16 *)sourceBuffer;
+    PaUint16 *dest = (PaUint16 *)destinationBuffer;
+                                                        
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        *dest = *src;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_24_To_24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        dest[0] = src[0];
+        dest[1] = src[1];
+        dest[2] = src[2];
+
+        src += sourceStride * 3;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_32_To_32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    PaUint32 *dest = (PaUint32 *)destinationBuffer;
+    PaUint32 *src = (PaUint32 *)sourceBuffer;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        *dest = *src;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+    Float32_To_Int32,              /* PaUtilConverter *Float32_To_Int32; */
+    Float32_To_Int32_Dither,       /* PaUtilConverter *Float32_To_Int32_Dither; */
+    Float32_To_Int32_Clip,         /* PaUtilConverter *Float32_To_Int32_Clip; */
+    Float32_To_Int32_DitherClip,   /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+    Float32_To_Int24,              /* PaUtilConverter *Float32_To_Int24; */
+    Float32_To_Int24_Dither,       /* PaUtilConverter *Float32_To_Int24_Dither; */
+    Float32_To_Int24_Clip,         /* PaUtilConverter *Float32_To_Int24_Clip; */
+    Float32_To_Int24_DitherClip,   /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+    
+    Float32_To_Int16,              /* PaUtilConverter *Float32_To_Int16; */
+    Float32_To_Int16_Dither,       /* PaUtilConverter *Float32_To_Int16_Dither; */
+    Float32_To_Int16_Clip,         /* PaUtilConverter *Float32_To_Int16_Clip; */
+    Float32_To_Int16_DitherClip,   /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+    Float32_To_Int8,               /* PaUtilConverter *Float32_To_Int8; */
+    Float32_To_Int8_Dither,        /* PaUtilConverter *Float32_To_Int8_Dither; */
+    Float32_To_Int8_Clip,          /* PaUtilConverter *Float32_To_Int8_Clip; */
+    Float32_To_Int8_DitherClip,    /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+    Float32_To_UInt8,              /* PaUtilConverter *Float32_To_UInt8; */
+    Float32_To_UInt8_Dither,       /* PaUtilConverter *Float32_To_UInt8_Dither; */
+    Float32_To_UInt8_Clip,         /* PaUtilConverter *Float32_To_UInt8_Clip; */
+    Float32_To_UInt8_DitherClip,   /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+    Int32_To_Float32,              /* PaUtilConverter *Int32_To_Float32; */
+    Int32_To_Int24,                /* PaUtilConverter *Int32_To_Int24; */
+    Int32_To_Int24_Dither,         /* PaUtilConverter *Int32_To_Int24_Dither; */
+    Int32_To_Int16,                /* PaUtilConverter *Int32_To_Int16; */
+    Int32_To_Int16_Dither,         /* PaUtilConverter *Int32_To_Int16_Dither; */
+    Int32_To_Int8,                 /* PaUtilConverter *Int32_To_Int8; */
+    Int32_To_Int8_Dither,          /* PaUtilConverter *Int32_To_Int8_Dither; */
+    Int32_To_UInt8,                /* PaUtilConverter *Int32_To_UInt8; */
+    Int32_To_UInt8_Dither,         /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+    Int24_To_Float32,              /* PaUtilConverter *Int24_To_Float32; */
+    Int24_To_Int32,                /* PaUtilConverter *Int24_To_Int32; */
+    Int24_To_Int16,                /* PaUtilConverter *Int24_To_Int16; */
+    Int24_To_Int16_Dither,         /* PaUtilConverter *Int24_To_Int16_Dither; */
+    Int24_To_Int8,                 /* PaUtilConverter *Int24_To_Int8; */
+    Int24_To_Int8_Dither,          /* PaUtilConverter *Int24_To_Int8_Dither; */
+    Int24_To_UInt8,                /* PaUtilConverter *Int24_To_UInt8; */
+    Int24_To_UInt8_Dither,         /* PaUtilConverter *Int24_To_UInt8_Dither; */
+
+    Int16_To_Float32,              /* PaUtilConverter *Int16_To_Float32; */
+    Int16_To_Int32,                /* PaUtilConverter *Int16_To_Int32; */
+    Int16_To_Int24,                /* PaUtilConverter *Int16_To_Int24; */
+    Int16_To_Int8,                 /* PaUtilConverter *Int16_To_Int8; */
+    Int16_To_Int8_Dither,          /* PaUtilConverter *Int16_To_Int8_Dither; */
+    Int16_To_UInt8,                /* PaUtilConverter *Int16_To_UInt8; */
+    Int16_To_UInt8_Dither,         /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+    Int8_To_Float32,               /* PaUtilConverter *Int8_To_Float32; */
+    Int8_To_Int32,                 /* PaUtilConverter *Int8_To_Int32; */
+    Int8_To_Int24,                 /* PaUtilConverter *Int8_To_Int24 */
+    Int8_To_Int16,                 /* PaUtilConverter *Int8_To_Int16; */
+    Int8_To_UInt8,                 /* PaUtilConverter *Int8_To_UInt8; */
+
+    UInt8_To_Float32,              /* PaUtilConverter *UInt8_To_Float32; */
+    UInt8_To_Int32,                /* PaUtilConverter *UInt8_To_Int32; */
+    UInt8_To_Int24,                /* PaUtilConverter *UInt8_To_Int24; */
+    UInt8_To_Int16,                /* PaUtilConverter *UInt8_To_Int16; */
+    UInt8_To_Int8,                 /* PaUtilConverter *UInt8_To_Int8; */
+
+    Copy_8_To_8,                   /* PaUtilConverter *Copy_8_To_8; */
+    Copy_16_To_16,                 /* PaUtilConverter *Copy_16_To_16; */
+    Copy_24_To_24,                 /* PaUtilConverter *Copy_24_To_24; */
+    Copy_32_To_32                  /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_CONVERTERS */
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat )
+{
+    switch( destinationFormat & ~paNonInterleaved ){
+    case paFloat32:
+        return paZeroers.Zero32;
+    case paInt32:
+        return paZeroers.Zero32;
+    case paInt24:
+        return paZeroers.Zero24;
+    case paInt16:
+        return paZeroers.Zero16;
+    case paInt8:
+        return paZeroers.Zero8;
+    case paUInt8:
+        return paZeroers.ZeroU8;
+    default: return 0;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_ZEROERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+    0,  /* PaUtilZeroer *ZeroU8; */
+    0,  /* PaUtilZeroer *Zero8; */
+    0,  /* PaUtilZeroer *Zero16; */
+    0,  /* PaUtilZeroer *Zero24; */
+    0,  /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_ZEROERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+static void ZeroU8( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+
+    while( count-- )
+    {
+        *dest = 128;
+
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero8( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+
+    while( count-- )
+    {
+        *dest = 0;
+
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero16( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    PaUint16 *dest = (PaUint16 *)destinationBuffer;
+
+    while( count-- )
+    {
+        *dest = 0;
+
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero24( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+
+    while( count-- )
+    {
+        dest[0] = 0;
+        dest[1] = 0;
+        dest[2] = 0;
+
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero32( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    PaUint32 *dest = (PaUint32 *)destinationBuffer;
+
+    while( count-- )
+    {
+        *dest = 0;
+
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+    ZeroU8,  /* PaUtilZeroer *ZeroU8; */
+    Zero8,  /* PaUtilZeroer *Zero8; */
+    Zero16,  /* PaUtilZeroer *Zero16; */
+    Zero24,  /* PaUtilZeroer *Zero24; */
+    Zero32,  /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_ZEROERS */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_converters.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_converters.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_converters.h	(revision 1753)
@@ -0,0 +1,275 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_CONVERTERS_H
+#define PA_CONVERTERS_H
+/*
+ * $Id: pa_converters.h,v 1.1.2.9 2003/09/20 21:05:14 rossbencina Exp $
+ * Portable Audio I/O Library sample conversion mechanism
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Conversion functions used to convert buffers of samples from one
+ format to another.
+*/
+
+
+#include "portaudio.h"  /* for PaSampleFormat */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+struct PaUtilTriangularDitherGenerator;
+
+
+/** Choose an available sample format which is most appropriate for
+ representing the requested format. If the requested format is not available
+ higher quality formats are considered before lower quality formates.
+ @param availableFormats A variable containing the logical OR of all available
+ formats.
+ @param format The desired format.
+ @return The most appropriate available format for representing the requested
+ format.
+*/
+PaSampleFormat PaUtil_SelectClosestAvailableFormat(
+        PaSampleFormat availableFormats, PaSampleFormat format );
+
+
+/* high level conversions functions for use by implementations */
+
+
+/** The generic sample converter prototype. Sample converters convert count
+    samples from sourceBuffer to destinationBuffer. The actual type of the data
+    pointed to by these parameters varys for different converter functions.
+    @param destinationBuffer A pointer to the first sample of the destination.
+    @param destinationStride An offset between successive destination samples
+    expressed in samples (not bytes.) It may be negative.
+    @param sourceBuffer A pointer to the first sample of the source.
+    @param sourceStride An offset between successive source samples
+    expressed in samples (not bytes.) It may be negative.
+    @param count The number of samples to convert.
+    @param ditherState State information used to calculate dither. Converters
+    that do not perform dithering will ignore this parameter, in which case
+    NULL or invalid dither state may be passed.
+*/
+typedef void PaUtilConverter(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator );
+
+
+/** Find a sample converter function for the given source and destinations
+    formats and flags (clip and dither.)
+    @return
+    A pointer to a PaUtilConverter which will perform the requested
+    conversion, or NULL if the given format conversion is not supported.
+    For conversions where clipping or dithering is not necessary, the
+    clip and dither flags are ignored and a non-clipping or dithering
+    version is returned.
+    If the source and destination formats are the same, a function which
+    copies data of the appropriate size will be returned.
+*/
+PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
+        PaSampleFormat destinationFormat, PaStreamFlags flags );
+
+
+/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to
+    destinationBuffer. The actual type of the data pointed to varys for
+    different zeroer functions.
+    @param destinationBuffer A pointer to the first sample of the destination.
+    @param destinationStride An offset between successive destination samples
+    expressed in samples (not bytes.) It may be negative.
+    @param count The number of samples to zero.
+*/
+typedef void PaUtilZeroer(
+    void *destinationBuffer, signed int destinationStride, unsigned int count );
+
+    
+/** Find a buffer zeroer function for the given destination format.
+    @return
+    A pointer to a PaUtilZeroer which will perform the requested
+    zeroing.
+*/
+PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat );
+
+/*----------------------------------------------------------------------------*/
+/* low level functions and data structures which may be used for
+    substituting conversion functions */
+
+
+/** The type used to store all sample conversion functions.
+    @see paConverters;
+*/
+typedef struct{
+    PaUtilConverter *Float32_To_Int32;
+    PaUtilConverter *Float32_To_Int32_Dither;
+    PaUtilConverter *Float32_To_Int32_Clip;
+    PaUtilConverter *Float32_To_Int32_DitherClip;
+
+    PaUtilConverter *Float32_To_Int24;
+    PaUtilConverter *Float32_To_Int24_Dither;
+    PaUtilConverter *Float32_To_Int24_Clip;
+    PaUtilConverter *Float32_To_Int24_DitherClip;
+    
+    PaUtilConverter *Float32_To_Int16;
+    PaUtilConverter *Float32_To_Int16_Dither;
+    PaUtilConverter *Float32_To_Int16_Clip;
+    PaUtilConverter *Float32_To_Int16_DitherClip;
+
+    PaUtilConverter *Float32_To_Int8;
+    PaUtilConverter *Float32_To_Int8_Dither;
+    PaUtilConverter *Float32_To_Int8_Clip;
+    PaUtilConverter *Float32_To_Int8_DitherClip;
+
+    PaUtilConverter *Float32_To_UInt8;
+    PaUtilConverter *Float32_To_UInt8_Dither;
+    PaUtilConverter *Float32_To_UInt8_Clip;
+    PaUtilConverter *Float32_To_UInt8_DitherClip;
+
+    PaUtilConverter *Int32_To_Float32;
+    PaUtilConverter *Int32_To_Int24;
+    PaUtilConverter *Int32_To_Int24_Dither;
+    PaUtilConverter *Int32_To_Int16;
+    PaUtilConverter *Int32_To_Int16_Dither;
+    PaUtilConverter *Int32_To_Int8;
+    PaUtilConverter *Int32_To_Int8_Dither;
+    PaUtilConverter *Int32_To_UInt8;
+    PaUtilConverter *Int32_To_UInt8_Dither;
+
+    PaUtilConverter *Int24_To_Float32;
+    PaUtilConverter *Int24_To_Int32;
+    PaUtilConverter *Int24_To_Int16;
+    PaUtilConverter *Int24_To_Int16_Dither;
+    PaUtilConverter *Int24_To_Int8;
+    PaUtilConverter *Int24_To_Int8_Dither;
+    PaUtilConverter *Int24_To_UInt8;
+    PaUtilConverter *Int24_To_UInt8_Dither;
+
+    PaUtilConverter *Int16_To_Float32;
+    PaUtilConverter *Int16_To_Int32;
+    PaUtilConverter *Int16_To_Int24;
+    PaUtilConverter *Int16_To_Int8;
+    PaUtilConverter *Int16_To_Int8_Dither;
+    PaUtilConverter *Int16_To_UInt8;
+    PaUtilConverter *Int16_To_UInt8_Dither;
+
+    PaUtilConverter *Int8_To_Float32;
+    PaUtilConverter *Int8_To_Int32;
+    PaUtilConverter *Int8_To_Int24;
+    PaUtilConverter *Int8_To_Int16;
+    PaUtilConverter *Int8_To_UInt8;
+    
+    PaUtilConverter *UInt8_To_Float32;
+    PaUtilConverter *UInt8_To_Int32;
+    PaUtilConverter *UInt8_To_Int24;
+    PaUtilConverter *UInt8_To_Int16;
+    PaUtilConverter *UInt8_To_Int8;
+
+    PaUtilConverter *Copy_8_To_8;       /* copy without any conversion */
+    PaUtilConverter *Copy_16_To_16;     /* copy without any conversion */
+    PaUtilConverter *Copy_24_To_24;     /* copy without any conversion */
+    PaUtilConverter *Copy_32_To_32;     /* copy without any conversion */
+} PaUtilConverterTable;
+
+
+/** A table of pointers to all required converter functions.
+    PaUtil_SelectConverter() uses this table to lookup the appropriate
+    conversion functions. The fields of this structure are initialized
+    with default conversion functions. Fields may be NULL, indicating that
+    no conversion function is available. User code may substitue optimised
+    conversion functions by assigning different function pointers to
+    these fields.
+
+    @note
+    If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined,
+    PortAudio's standard converters will not be compiled, and all fields
+    of this structure will be initialized to NULL. In such cases, users
+    should supply their own conversion functions if the require PortAudio
+    to open a stream that requires sample conversion.
+
+    @see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter
+*/
+extern PaUtilConverterTable paConverters;
+
+
+/** The type used to store all buffer zeroing functions.
+    @see paZeroers;
+*/
+typedef struct{
+    PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */
+    PaUtilZeroer *Zero8;
+    PaUtilZeroer *Zero16;
+    PaUtilZeroer *Zero24;
+    PaUtilZeroer *Zero32;
+} PaUtilZeroerTable;
+
+
+/** A table of pointers to all required zeroer functions.
+    PaUtil_SelectZeroer() uses this table to lookup the appropriate
+    conversion functions. The fields of this structure are initialized
+    with default conversion functions. User code may substitue optimised
+    conversion functions by assigning different function pointers to
+    these fields.
+
+    @note
+    If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined,
+    PortAudio's standard zeroers will not be compiled, and all fields
+    of this structure will be initialized to NULL. In such cases, users
+    should supply their own zeroing functions for the sample sizes which
+    they intend to use.
+
+    @see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer
+*/
+extern PaUtilZeroerTable paZeroers;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_CONVERTERS_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_cpuload.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_cpuload.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_cpuload.c	(revision 1753)
@@ -0,0 +1,117 @@
+/*
+ * $Id: pa_cpuload.c,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
+ * Portable Audio I/O Library CPU Load measurement functions
+ * Portable CPU load measurement facility.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+ @brief Functions to assist in measuring the CPU utilization of a callback
+ stream. Used to implement the Pa_GetStreamCpuLoad() function.
+
+ @todo Dynamically calculate the coefficients used to smooth the CPU Load
+ Measurements over time to provide a uniform characterisation of CPU Load
+ independent of rate at which PaUtil_BeginCpuLoadMeasurement /
+ PaUtil_EndCpuLoadMeasurement are called.
+*/
+
+
+#include "pa_cpuload.h"
+
+#include <assert.h>
+
+#include "pa_util.h"   /* for PaUtil_GetTime() */
+
+
+void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate )
+{
+    assert( sampleRate > 0 );
+
+    measurer->samplingPeriod = 1. / sampleRate;
+    measurer->averageLoad = 0.;
+}
+
+void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer )
+{
+    measurer->averageLoad = 0.;
+}
+
+void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer )
+{
+    measurer->measurementStartTime = PaUtil_GetTime();
+}
+
+
+void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed )
+{
+    double measurementEndTime, secondsFor100Percent, measuredLoad;
+
+    if( framesProcessed > 0 ){
+        measurementEndTime = PaUtil_GetTime();
+
+        assert( framesProcessed > 0 );
+        secondsFor100Percent = framesProcessed * measurer->samplingPeriod;
+
+        measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent;
+
+        /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */
+        /** FIXME @todo these coefficients shouldn't be hardwired */
+#define LOWPASS_COEFFICIENT_0   (0.9)
+#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
+
+        measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) +
+                               (LOWPASS_COEFFICIENT_1 * measuredLoad);
+    }
+}
+
+
+double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer )
+{
+    return measurer->averageLoad;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_cpuload.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_cpuload.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_cpuload.h	(revision 1753)
@@ -0,0 +1,84 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_CPULOAD_H
+#define PA_CPULOAD_H
+/*
+ * $Id: pa_cpuload.h,v 1.1.2.10 2004/01/08 22:01:12 rossbencina Exp $
+ * Portable Audio I/O Library CPU Load measurement functions
+ * Portable CPU load measurement facility.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Functions to assist in measuring the CPU utilization of a callback
+ stream. Used to implement the Pa_GetStreamCpuLoad() function.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct {
+    double samplingPeriod;
+    double measurementStartTime;
+    double averageLoad;
+} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */
+
+void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate );
+void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer );
+void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed );
+void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer );
+double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */     
+#endif /* PA_CPULOAD_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_dither.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_dither.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_dither.c	(revision 1753)
@@ -0,0 +1,225 @@
+/*
+ * $Id: pa_dither.c,v 1.1.2.6 2005/05/28 22:49:02 rossbencina Exp $
+ * Portable Audio I/O Library triangular dither generator
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+ @brief Functions for generating dither noise
+*/
+
+
+#include "pa_dither.h"
+#include "pa_types.h"
+
+#define PA_DITHER_BITS_   (15)
+
+
+void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state )
+{
+    state->previous = 0;
+    state->randSeed1 = 22222;
+    state->randSeed2 = 5555555;
+}
+
+
+signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state )
+{
+    signed long current, highPass;
+
+    /* Generate two random numbers. */
+    state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
+    state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
+
+    /* Generate triangular distribution about 0.
+     * Shift before adding to prevent overflow which would skew the distribution.
+     * Also shift an extra bit for the high pass filter. 
+     */
+#define DITHER_SHIFT_  ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
+    current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
+              (((signed long)state->randSeed2)>>DITHER_SHIFT_);
+
+    /* High pass filter to reduce audibility. */
+    highPass = current - state->previous;
+    state->previous = current;
+    return highPass;
+}
+
+
+/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
+#define PA_FLOAT_DITHER_SCALE_  (1.0f / ((1<<PA_DITHER_BITS_)-1))
+static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
+
+float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *state )
+{
+    signed long current, highPass;
+
+    /* Generate two random numbers. */
+    state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
+    state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
+
+    /* Generate triangular distribution about 0.
+     * Shift before adding to prevent overflow which would skew the distribution.
+     * Also shift an extra bit for the high pass filter. 
+     */
+#define DITHER_SHIFT_  ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
+    current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
+              (((signed long)state->randSeed2)>>DITHER_SHIFT_);
+
+    /* High pass filter to reduce audibility. */
+    highPass = current - state->previous;
+    state->previous = current;
+    return ((float)highPass) * const_float_dither_scale_;
+}
+
+
+/*
+The following alternate dither algorithms (from musicdsp.org) could be
+considered
+*/
+
+/*Noise shaped dither  (March 2000)
+-------------------
+
+This is a simple implementation of highpass triangular-PDF dither with
+2nd-order noise shaping, for use when truncating floating point audio
+data to fixed point.
+
+The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz
+sample rate) compared to triangular-PDF dither. The code below assumes
+input data is in the range +1 to -1 and doesn't check for overloads!
+
+To save time when generating dither for multiple channels you can do
+things like this:  r3=(r1 & 0x7F)<<8; instead of calling rand() again.
+
+
+
+  int   r1, r2;                //rectangular-PDF random numbers
+  float s1, s2;                //error feedback buffers
+  float s = 0.5f;              //set to 0.0f for no noise shaping
+  float w = pow(2.0,bits-1);   //word length (usually bits=16)
+  float wi= 1.0f/w;            
+  float d = wi / RAND_MAX;     //dither amplitude (2 lsb)
+  float o = wi * 0.5f;         //remove dc offset
+  float in, tmp;
+  int   out;
+
+
+//for each sample...
+
+  r2=r1;                               //can make HP-TRI dither by
+  r1=rand();                           //subtracting previous rand()
+    
+  in += s * (s1 + s1 - s2);            //error feedback
+  tmp = in + o + d * (float)(r1 - r2); //dc offset and dither 
+  
+  out = (int)(w * tmp);                //truncate downwards
+  if(tmp<0.0f) out--;                  //this is faster than floor()
+
+  s2 = s1;                            
+  s1 = in - wi * (float)out;           //error
+
+
+
+-- 
+paul.kellett@maxim.abel.co.uk
+http://www.maxim.abel.co.uk
+*/
+
+
+/*
+16-to-8-bit first-order dither
+
+Type : First order error feedforward dithering code
+References : Posted by Jon Watte
+
+Notes : 
+This is about as simple a dithering algorithm as you can implement, but it's
+likely to sound better than just truncating to N bits.
+
+Note that you might not want to carry forward the full difference for infinity.
+It's probably likely that the worst performance hit comes from the saturation
+conditionals, which can be avoided with appropriate instructions on many DSPs
+and integer SIMD type instructions, or CMOV.
+
+Last, if sound quality is paramount (such as when going from > 16 bits to 16
+bits) you probably want to use a higher-order dither function found elsewhere
+on this site. 
+
+
+Code : 
+// This code will down-convert and dither a 16-bit signed short 
+// mono signal into an 8-bit unsigned char signal, using a first 
+// order forward-feeding error term dither. 
+
+#define uchar unsigned char 
+
+void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory ) 
+{ 
+  int m = *memory; 
+  while( count-- > 0 ) { 
+    int i = *input++; 
+    i += m; 
+    int j = i + 32768 - 128; 
+    uchar o; 
+    if( j < 0 ) { 
+      o = 0; 
+    } 
+    else if( j > 65535 ) { 
+      o = 255; 
+    } 
+    else { 
+      o = (uchar)((j>>8)&0xff); 
+    } 
+    m = ((j-32768+128)-i); 
+    *output++ = o; 
+  } 
+  *memory = m; 
+} 
+*/
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_dither.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_dither.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_dither.h	(revision 1753)
@@ -0,0 +1,112 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_DITHER_H
+#define PA_DITHER_H
+/*
+ * $Id: pa_dither.h,v 1.1.2.4 2003/09/20 21:06:19 rossbencina Exp $
+ * Portable Audio I/O Library triangular dither generator
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Functions for generating dither noise
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** @brief State needed to generate a dither signal */
+typedef struct PaUtilTriangularDitherGenerator{
+    unsigned long previous;
+    unsigned long randSeed1;
+    unsigned long randSeed2;
+} PaUtilTriangularDitherGenerator;
+
+
+/** @brief Initialize dither state */
+void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState );
+
+
+/**
+ @brief Calculate 2 LSB dither signal with a triangular distribution.
+ Ranged for adding to a 1 bit right-shifted 32 bit integer
+ prior to >>15. eg:
+<pre>
+    signed long in = *
+    signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
+    signed short out = (signed short)(((in>>1) + dither) >> 15);
+</pre>
+ @return
+ A signed long with a range of +32767 to -32768
+*/
+signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
+
+
+/**
+ @brief Calculate 2 LSB dither signal with a triangular distribution.
+ Ranged for adding to a pre-scaled float.
+<pre>
+    float in = *
+    float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
+    // use smaller scaler to prevent overflow when we add the dither
+    signed short out = (signed short)(in*(32766.0f) + dither );
+</pre>
+ @return
+ A float with a range of -2.0 to +1.99999.
+*/
+float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_DITHER_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_endianness.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_endianness.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_endianness.h	(revision 1753)
@@ -0,0 +1,132 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_ENDIANNESS_H
+#define PA_ENDIANNESS_H
+/*
+ * $Id: pa_endianness.h,v 1.1.2.3 2003/09/20 21:06:19 rossbencina Exp $
+ * Portable Audio I/O Library current platform endianness macros
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Configure endianness symbols for the target processor.
+
+ Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols
+ to be defined. The one that is defined reflects the endianness of the target
+ platform and may be used to implement conditional compilation of byte-order
+ dependent code.
+
+ If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt
+ is made to override that setting. This may be useful if you have a better way
+ of determining the platform's endianness. The autoconf mechanism uses this for
+ example.
+
+ A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time
+ and runtime endiannes and raise an assertion if they don't match.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN)
+    /* endianness define has been set externally, such as by autoconf */
+
+    #if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN)
+    #error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please
+    #endif
+
+#else
+    /* endianness define has not been set externally */
+
+    /* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */
+
+    #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
+
+    #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */
+
+    #else
+
+#endif
+
+    #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN)
+    /*
+     If the following error is raised, you either need to modify the code above
+     to automatically determine the endianness from other symbols defined on your
+     platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally.
+    */
+    #error pa_endianness.h was unable to automatically determine the endianness of the target platform
+    #endif
+    
+#endif
+
+/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness,
+ and raises an assertion if they don't match. <assert.h> must be included in
+ the context in which this macro is used.
+*/
+#if defined(PA_LITTLE_ENDIAN)
+    #define PA_VALIDATE_ENDIANNESS \
+    { \
+        const long nativeOne = 1; \
+        assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \
+    }
+#elif defined(PA_BIG_ENDIAN)
+    #define PA_VALIDATE_ENDIANNESS \
+    { \
+        const long nativeOne = 1; \
+        assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \
+    }
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_ENDIANNESS_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_front.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_front.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_front.c	(revision 1753)
@@ -0,0 +1,1997 @@
+/*
+ * $Id: pa_front.c,v 1.1.2.51 2005/02/05 15:52:12 rossbencina Exp $
+ * Portable Audio I/O Library Multi-Host API front end
+ * Validate function parameters and manage multiple host APIs.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/* doxygen index page */
+/** @mainpage
+
+PortAudio is an open-source cross-platform C library for audio input
+and output. It is designed to simplify the porting of audio applications
+between various platforms, and also to simplify the development of audio
+software in general by hiding the complexities of device interfacing.
+
+See the PortAudio website for further information http://www.portaudio.com/
+
+This documentation pertains to PortAudio V19, API version 2.0 which is
+currently under development. API version 2.0 differs in a number of ways from
+previous versions, please consult the enhancement proposals for further details:
+http://www.portaudio.com/docs/proposals/index.html
+
+This documentation is under construction. Things you might be interested in
+include:
+
+- The PortAudio API 2.0, as documented in portaudio.h
+
+- The <a href="todo.html">TODO List</a>
+
+Feel free to pick an item off TODO list and fix/implement it. You may want to
+enquire about status on the PortAudio mailing list first.
+*/
+
+
+/** @file
+ @brief Implements public PortAudio API, checks some errors, forwards to
+ host API implementations.
+ 
+ Implements the functions defined in the PortAudio API, checks for
+ some parameter and state inconsistencies and forwards API requests to
+ specific Host API implementations (via the interface declared in
+ pa_hostapi.h), and Streams (via the interface declared in pa_stream.h).
+
+ This file handles initialization and termination of Host API
+ implementations via initializers stored in the paHostApiInitializers
+ global variable.
+
+ Some utility functions declared in pa_util.h are implemented in this file.
+
+ All PortAudio API functions can be conditionally compiled with logging code.
+ To compile with logging, define the PA_LOG_API_CALLS precompiler symbol.
+
+    @todo Consider adding host API specific error text in Pa_GetErrorText() for
+    paUnanticipatedHostError
+
+    @todo Consider adding a new error code for when (inputParameters == NULL)
+    && (outputParameters == NULL)
+
+    @todo review whether Pa_CloseStream() should call the interface's
+    CloseStream function if aborting the stream returns an error code.
+
+    @todo Create new error codes if a NULL buffer pointer, or a
+    zero frame count is passed to Pa_ReadStream or Pa_WriteStream.
+*/
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <memory.h>
+#include <string.h>
+#include <assert.h> /* needed by PA_VALIDATE_ENDIANNESS */
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_endianness.h"
+#include "pa_types.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+
+#include "pa_trace.h"
+
+
+#define PA_VERSION_  1899
+#define PA_VERSION_TEXT_ "PortAudio V19-devel"
+
+
+
+/* #define PA_LOG_API_CALLS */
+
+/*
+    The basic format for log messages is described below. If you need to
+    add any log messages, please follow this format.
+ 
+    Function entry (void function):
+ 
+        "FunctionName called.\n"
+ 
+    Function entry (non void function):
+ 
+        "FunctionName called:\n"
+        "\tParam1Type param1: param1Value\n"
+        "\tParam2Type param2: param2Value\n"      (etc...)
+ 
+ 
+    Function exit (no return value):
+ 
+        "FunctionName returned.\n"
+ 
+    Function exit (simple return value):
+ 
+        "FunctionName returned:\n"
+        "\tReturnType: returnValue\n\n"
+ 
+    If the return type is an error code, the error text is displayed in ()
+ 
+    If the return type is not an error code, but has taken a special value
+    because an error occurred, then the reason for the error is shown in []
+ 
+    If the return type is a struct ptr, the struct is dumped.
+ 
+    See the code below for examples
+*/
+
+
+int Pa_GetVersion( void )
+{
+    return PA_VERSION_;
+}
+
+
+const char* Pa_GetVersionText( void )
+{
+    return PA_VERSION_TEXT_;
+}
+
+
+
+#define PA_LAST_HOST_ERROR_TEXT_LENGTH_  1024
+
+static char lastHostErrorText_[ PA_LAST_HOST_ERROR_TEXT_LENGTH_ + 1 ] = {0};
+
+static PaHostErrorInfo lastHostErrorInfo_ = { (PaHostApiTypeId)-1, 0, lastHostErrorText_ };
+
+
+void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
+        const char *errorText )
+{
+    lastHostErrorInfo_.hostApiType = hostApiType;
+    lastHostErrorInfo_.errorCode = errorCode;
+
+    strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ );
+}
+
+
+void PaUtil_DebugPrint( const char *format, ... )
+{
+    va_list ap;
+
+    va_start( ap, format );
+    vfprintf( stderr, format, ap );
+    va_end( ap );
+
+    fflush( stderr );
+}
+
+
+static PaUtilHostApiRepresentation **hostApis_ = 0;
+static int hostApisCount_ = 0;
+static int initializationCount_ = 0;
+static int deviceCount_ = 0;
+
+PaUtilStreamRepresentation *firstOpenStream_ = NULL;
+
+
+#define PA_IS_INITIALISED_ (initializationCount_ != 0)
+
+
+static int CountHostApiInitializers( void )
+{
+    int result = 0;
+
+    while( paHostApiInitializers[ result ] != 0 )
+        ++result;
+    return result;
+}
+
+
+static void TerminateHostApis( void )
+{
+    /* terminate in reverse order from initialization */
+
+    while( hostApisCount_ > 0 )
+    {
+        --hostApisCount_;
+        hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] );
+    }
+    hostApisCount_ = 0;
+    deviceCount_ = 0;
+
+    if( hostApis_ != 0 )
+        PaUtil_FreeMemory( hostApis_ );
+    hostApis_ = 0;
+}
+
+
+static PaError InitializeHostApis( void )
+{
+    PaError result = paNoError;
+    int i, initializerCount, baseDeviceIndex;
+
+    initializerCount = CountHostApiInitializers();
+
+    hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory(
+            sizeof(PaUtilHostApiRepresentation*) * initializerCount );
+    if( !hostApis_ )
+    {
+        result = paInsufficientMemory;
+        goto error; 
+    }
+
+    hostApisCount_ = 0;
+    deviceCount_ = 0;
+    baseDeviceIndex = 0;
+
+    for( i=0; i< initializerCount; ++i )
+    {
+        hostApis_[hostApisCount_] = NULL;
+        result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ );
+        if( result != paNoError )
+            goto error;
+
+        if( hostApis_[hostApisCount_] )
+        {
+
+            hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
+
+            if( hostApis_[hostApisCount_]->info.defaultInputDevice != paNoDevice )
+                hostApis_[hostApisCount_]->info.defaultInputDevice += baseDeviceIndex;
+
+            if( hostApis_[hostApisCount_]->info.defaultOutputDevice != paNoDevice )
+                hostApis_[hostApisCount_]->info.defaultOutputDevice += baseDeviceIndex;
+
+            baseDeviceIndex += hostApis_[hostApisCount_]->info.deviceCount;
+            deviceCount_ += hostApis_[hostApisCount_]->info.deviceCount;
+
+            ++hostApisCount_;
+        }
+    }
+
+    return result;
+
+error:
+    TerminateHostApis();
+    return result;
+}
+
+
+/*
+    FindHostApi() finds the index of the host api to which
+    <device> belongs and returns it. if <hostSpecificDeviceIndex> is
+    non-null, the host specific device index is returned in it.
+    returns -1 if <device> is out of range.
+ 
+*/
+static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex )
+{
+    int i=0;
+
+    if( !PA_IS_INITIALISED_ )
+        return -1;
+
+    if( device < 0 )
+        return -1;
+
+    while( i < hostApisCount_
+            && device >= hostApis_[i]->info.deviceCount )
+    {
+
+        device -= hostApis_[i]->info.deviceCount;
+        ++i;
+    }
+
+    if( i >= hostApisCount_ )
+        return -1;
+
+    if( hostSpecificDeviceIndex )
+        *hostSpecificDeviceIndex = device;
+
+    return i;
+}
+
+
+static void AddOpenStream( PaStream* stream )
+{
+    ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_;
+    firstOpenStream_ = (PaUtilStreamRepresentation*)stream;
+}
+
+
+static void RemoveOpenStream( PaStream* stream )
+{
+    PaUtilStreamRepresentation *previous = NULL;
+    PaUtilStreamRepresentation *current = firstOpenStream_;
+
+    while( current != NULL )
+    {
+        if( ((PaStream*)current) == stream )
+        {
+            if( previous == NULL )
+            {
+                firstOpenStream_ = current->nextOpenStream;
+            }
+            else
+            {
+                previous->nextOpenStream = current->nextOpenStream;
+            }
+            return;
+        }
+        else
+        {
+            previous = current;
+            current = current->nextOpenStream;
+        }
+    }
+}
+
+
+static void CloseOpenStreams( void )
+{
+    /* we call Pa_CloseStream() here to ensure that the same destruction
+        logic is used for automatically closed streams */
+
+    while( firstOpenStream_ != NULL )
+        Pa_CloseStream( firstOpenStream_ );
+}
+
+
+PaError Pa_Initialize( void )
+{
+    PaError result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint( "Pa_Initialize called.\n" );
+#endif
+
+    if( PA_IS_INITIALISED_ )
+    {
+        ++initializationCount_;
+        result = paNoError;
+    }
+    else
+    {
+        PA_VALIDATE_TYPE_SIZES;
+        PA_VALIDATE_ENDIANNESS;
+        
+        PaUtil_InitializeClock();
+        PaUtil_ResetTraceMessages();
+
+        result = InitializeHostApis();
+        if( result == paNoError )
+            ++initializationCount_;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint( "Pa_Initialize returned:\n" );
+    PaUtil_DebugPrint( "\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_Terminate( void )
+{
+    PaError result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_Terminate called.\n" );
+#endif
+
+    if( PA_IS_INITIALISED_ )
+    {
+        if( --initializationCount_ == 0 )
+        {
+            CloseOpenStreams();
+
+            TerminateHostApis();
+
+            PaUtil_DumpTraceMessages();
+        }
+        result = paNoError;
+    }
+    else
+    {
+        result=  paNotInitialized;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_Terminate returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void )
+{
+    return &lastHostErrorInfo_;
+}
+
+
+const char *Pa_GetErrorText( PaError errorCode )
+{
+    const char *result;
+
+    switch( errorCode )
+    {
+    case paNoError:                  result = "Success"; break;
+    case paNotInitialized:           result = "PortAudio not initialized"; break;
+    /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */
+    case paUnanticipatedHostError:   result = "Unanticipated host error"; break;
+    case paInvalidChannelCount:      result = "Invalid number of channels"; break;
+    case paInvalidSampleRate:        result = "Invalid sample rate"; break;
+    case paInvalidDevice:            result = "Invalid device"; break;
+    case paInvalidFlag:              result = "Invalid flag"; break;
+    case paSampleFormatNotSupported: result = "Sample format not supported"; break;
+    case paBadIODeviceCombination:   result = "Illegal combination of I/O devices"; break;
+    case paInsufficientMemory:       result = "Insufficient memory"; break;
+    case paBufferTooBig:             result = "Buffer too big"; break;
+    case paBufferTooSmall:           result = "Buffer too small"; break;
+    case paNullCallback:             result = "No callback routine specified"; break;
+    case paBadStreamPtr:             result = "Invalid stream pointer"; break;
+    case paTimedOut:                 result = "Wait timed out"; break;
+    case paInternalError:            result = "Internal PortAudio error"; break;
+    case paDeviceUnavailable:        result = "Device unavailable"; break;
+    case paIncompatibleHostApiSpecificStreamInfo:   result = "Incompatible host API specific stream info"; break;
+    case paStreamIsStopped:          result = "Stream is stopped"; break;
+    case paStreamIsNotStopped:       result = "Stream is not stopped"; break;
+    case paInputOverflowed:          result = "Input overflowed"; break;
+    case paOutputUnderflowed:        result = "Output underflowed"; break;
+    case paHostApiNotFound:          result = "Host API not found"; break;
+    case paInvalidHostApi:           result = "Invalid host API"; break;
+    case paCanNotReadFromACallbackStream:       result = "Can't read from a callback stream"; break;
+    case paCanNotWriteToACallbackStream:        result = "Can't write to a callback stream"; break;
+    case paCanNotReadFromAnOutputOnlyStream:    result = "Can't read from an output only stream"; break;
+    case paCanNotWriteToAnInputOnlyStream:      result = "Can't write to an input only stream"; break;
+    default:                         result = "Illegal error number"; break;
+    }
+    return result;
+}
+
+
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type )
+{
+    PaHostApiIndex result;
+    int i;
+    
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex called:\n" );
+    PaUtil_DebugPrint("\tPaHostApiTypeId type: %d\n", type );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = paHostApiNotFound;
+        
+        for( i=0; i < hostApisCount_; ++i )
+        {
+            if( hostApis_[i]->info.type == type )
+            {
+                result = i;
+                break;
+            }         
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaHostApiIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
+        PaHostApiTypeId type )
+{
+    PaError result;
+    int i;
+    
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = paHostApiNotFound;
+                
+        for( i=0; i < hostApisCount_; ++i )
+        {
+            if( hostApis_[i]->info.type == type )
+            {
+                *hostApi = hostApis_[i];
+                result = paNoError;
+                break;
+            }
+        }
+    }
+
+    return result;
+}
+
+
+PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
+        PaDeviceIndex *hostApiDevice, PaDeviceIndex device, struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaError result;
+    PaDeviceIndex x;
+    
+    x = device - hostApi->privatePaFrontInfo.baseDeviceIndex;
+
+    if( x < 0 || x >= hostApi->info.deviceCount )
+    {
+        result = paInvalidDevice;
+    }
+    else
+    {
+        *hostApiDevice = x;
+        result = paNoError;
+    }
+
+    return result;
+}
+
+
+PaHostApiIndex Pa_GetHostApiCount( void )
+{
+    int result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetHostApiCount called.\n" );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = hostApisCount_;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetHostApiCount returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaHostApiIndex Pa_GetDefaultHostApi( void )
+{
+    int result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = paDefaultHostApiIndex;
+
+        /* internal consistency check: make sure that the default host api
+         index is within range */
+
+        if( result < 0 || result >= hostApisCount_ )
+        {
+            result = paInternalError;
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi )
+{
+    PaHostApiInfo *info;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetHostApiInfo called:\n" );
+    PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        info = NULL;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n\n" );
+#endif
+
+    }
+    else if( hostApi < 0 || hostApi >= hostApisCount_ )
+    {
+        info = NULL;
+        
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ hostApi out of range ]\n\n" );
+#endif
+
+    }
+    else
+    {
+        info = &hostApis_[hostApi]->info;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaHostApiInfo*: 0x%p\n", info );
+        PaUtil_DebugPrint("\t{" );
+        PaUtil_DebugPrint("\t\tint structVersion: %d\n", info->structVersion );
+        PaUtil_DebugPrint("\t\tPaHostApiTypeId type: %d\n", info->type );
+        PaUtil_DebugPrint("\t\tconst char *name: %s\n\n", info->name );
+        PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+    }
+
+     return info;
+}
+
+
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex )
+{
+    PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex called:\n" );
+    PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
+    PaUtil_DebugPrint("\tint hostApiDeviceIndex: %d\n", hostApiDeviceIndex );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        if( hostApi < 0 || hostApi >= hostApisCount_ )
+        {
+            result = paInvalidHostApi;
+        }
+        else
+        {
+            if( hostApiDeviceIndex < 0 ||
+                    hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount )
+            {
+                result = paInvalidDevice;
+            }
+            else
+            {
+                result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex;
+            }
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaDeviceIndex Pa_GetDeviceCount( void )
+{
+    PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDeviceCount called.\n" );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = deviceCount_;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDeviceCount returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaDeviceIndex Pa_GetDefaultInputDevice( void )
+{
+    PaHostApiIndex hostApi;
+    PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultInputDevice called.\n" );
+#endif
+
+    hostApi = Pa_GetDefaultHostApi();
+    if( hostApi < 0 )
+    {
+        result = paNoDevice;
+    }
+    else
+    {
+        result = hostApis_[hostApi]->info.defaultInputDevice;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultInputDevice returned:\n" );
+    PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaDeviceIndex Pa_GetDefaultOutputDevice( void )
+{
+    PaHostApiIndex hostApi;
+    PaDeviceIndex result;
+    
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultOutputDevice called.\n" );
+#endif
+
+    hostApi = Pa_GetDefaultHostApi();
+    if( hostApi < 0 )
+    {
+        result = paNoDevice;
+    }
+    else
+    {
+        result = hostApis_[hostApi]->info.defaultOutputDevice;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultOutputDevice returned:\n" );
+    PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
+{
+    int hostSpecificDeviceIndex;
+    int hostApiIndex = FindHostApi( device, &hostSpecificDeviceIndex );
+    PaDeviceInfo *result;
+
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDeviceInfo called:\n" );
+    PaUtil_DebugPrint("\tPaDeviceIndex device: %d\n", device );
+#endif
+
+    if( hostApiIndex < 0 )
+    {
+        result = NULL;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaDeviceInfo* NULL [ invalid device index ]\n\n" );
+#endif
+
+    }
+    else
+    {
+        result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ];
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaDeviceInfo*: 0x%p:\n", result );
+        PaUtil_DebugPrint("\t{\n" );
+
+        PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
+        PaUtil_DebugPrint("\t\tconst char *name: %s\n", result->name );
+        PaUtil_DebugPrint("\t\tPaHostApiIndex hostApi: %d\n", result->hostApi );
+        PaUtil_DebugPrint("\t\tint maxInputChannels: %d\n", result->maxInputChannels );
+        PaUtil_DebugPrint("\t\tint maxOutputChannels: %d\n", result->maxOutputChannels );
+        PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+    }
+
+    return result;
+}
+
+
+/*
+    SampleFormatIsValid() returns 1 if sampleFormat is a sample format
+    defined in portaudio.h, or 0 otherwise.
+*/
+static int SampleFormatIsValid( PaSampleFormat format )
+{
+    switch( format & ~paNonInterleaved )
+    {
+    case paFloat32: return 1;
+    case paInt16: return 1;
+    case paInt32: return 1;
+    case paInt24: return 1;
+    case paInt8: return 1;
+    case paUInt8: return 1;
+    case paCustomFormat: return 1;
+    default: return 0;
+    }
+}
+
+/*
+    NOTE: make sure this validation list is kept syncronised with the one in
+            pa_hostapi.h
+
+    ValidateOpenStreamParameters() checks that parameters to Pa_OpenStream()
+    conform to the expected values as described below. This function is
+    also designed to be used with the proposed Pa_IsFormatSupported() function.
+    
+    There are basically two types of validation that could be performed:
+    Generic conformance validation, and device capability mismatch
+    validation. This function performs only generic conformance validation.
+    Validation that would require knowledge of device capabilities is
+    not performed because of potentially complex relationships between
+    combinations of parameters - for example, even if the sampleRate
+    seems ok, it might not be for a duplex stream - we have no way of
+    checking this in an API-neutral way, so we don't try.
+ 
+    On success the function returns PaNoError and fills in hostApi,
+    hostApiInputDeviceID, and hostApiOutputDeviceID fields. On failure
+    the function returns an error code indicating the first encountered
+    parameter error.
+ 
+ 
+    If ValidateOpenStreamParameters() returns paNoError, the following
+    assertions are guaranteed to be true.
+ 
+    - at least one of inputParameters & outputParmeters is valid (not NULL)
+
+    - if inputParameters & outputParmeters are both valid, that
+        inputParameters->device & outputParmeters->device  both use the same host api
+ 
+    PaDeviceIndex inputParameters->device
+        - is within range (0 to Pa_GetDeviceCount-1) Or:
+        - is paUseHostApiSpecificDeviceSpecification and
+            inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
+            to a valid host api
+
+    int inputParameters->channelCount
+        - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, channelCount is > 0
+        - upper bound is NOT validated against device capabilities
+ 
+    PaSampleFormat inputParameters->sampleFormat
+        - is one of the sample formats defined in portaudio.h
+
+    void *inputParameters->hostApiSpecificStreamInfo
+        - if supplied its hostApi field matches the input device's host Api
+ 
+    PaDeviceIndex outputParmeters->device
+        - is within range (0 to Pa_GetDeviceCount-1)
+ 
+    int outputParmeters->channelCount
+        - if inputDevice is valid, channelCount is > 0
+        - upper bound is NOT validated against device capabilities
+ 
+    PaSampleFormat outputParmeters->sampleFormat
+        - is one of the sample formats defined in portaudio.h
+        
+    void *outputParmeters->hostApiSpecificStreamInfo
+        - if supplied its hostApi field matches the output device's host Api
+ 
+    double sampleRate
+        - is not an 'absurd' rate (less than 1000. or greater than 200000.)
+        - sampleRate is NOT validated against device capabilities
+ 
+    PaStreamFlags streamFlags
+        - unused platform neutral flags are zero
+        - paNeverDropInput is only used for full-duplex callback streams with
+            variable buffer size (paFramesPerBufferUnspecified)
+*/
+static PaError ValidateOpenStreamParameters(
+    const PaStreamParameters *inputParameters,
+    const PaStreamParameters *outputParameters,
+    double sampleRate,
+    unsigned long framesPerBuffer,
+    PaStreamFlags streamFlags,
+    PaStreamCallback *streamCallback,
+    PaUtilHostApiRepresentation **hostApi,
+    PaDeviceIndex *hostApiInputDevice,
+    PaDeviceIndex *hostApiOutputDevice )
+{
+    int inputHostApiIndex  = -1, /* Surpress uninitialised var warnings: compiler does */
+        outputHostApiIndex = -1; /* not see that if inputParameters and outputParame-  */
+                                 /* ters are both nonzero, these indices are set.      */
+
+    if( (inputParameters == NULL) && (outputParameters == NULL) )
+    {
+        return paInvalidDevice; /** @todo should be a new error code "invalid device parameters" or something */
+    }
+    else
+    {
+        if( inputParameters == NULL )
+        {
+            *hostApiInputDevice = paNoDevice;
+        }
+        else if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+        {
+            if( inputParameters->hostApiSpecificStreamInfo )
+            {
+                inputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
+                        ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType );
+
+                if( inputHostApiIndex != -1 )
+                {
+                    *hostApiInputDevice = paUseHostApiSpecificDeviceSpecification;
+                    *hostApi = hostApis_[inputHostApiIndex];
+                }
+                else
+                {
+                    return paInvalidDevice;
+                }
+            }
+            else
+            {
+                return paInvalidDevice;
+            }
+        }
+        else
+        {
+            if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ )
+                return paInvalidDevice;
+
+            inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice );
+            if( inputHostApiIndex < 0 )
+                return paInternalError;
+
+            *hostApi = hostApis_[inputHostApiIndex];
+
+            if( inputParameters->channelCount <= 0 )
+                return paInvalidChannelCount;
+
+            if( !SampleFormatIsValid( inputParameters->sampleFormat ) )
+                return paSampleFormatNotSupported;
+
+            if( inputParameters->hostApiSpecificStreamInfo != NULL )
+            {
+                if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType
+                        != (*hostApi)->info.type )
+                    return paIncompatibleHostApiSpecificStreamInfo;
+            }
+        }
+
+        if( outputParameters == NULL )
+        {
+            *hostApiOutputDevice = paNoDevice;
+        }
+        else if( outputParameters->device == paUseHostApiSpecificDeviceSpecification  )
+        {
+            if( outputParameters->hostApiSpecificStreamInfo )
+            {
+                outputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
+                        ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType );
+
+                if( outputHostApiIndex != -1 )
+                {
+                    *hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification;
+                    *hostApi = hostApis_[outputHostApiIndex];
+                }
+                else
+                {
+                    return paInvalidDevice;
+                }
+            }
+            else
+            {
+                return paInvalidDevice;
+            }
+        }
+        else
+        {
+            if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ )
+                return paInvalidDevice;
+
+            outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice );
+            if( outputHostApiIndex < 0 )
+                return paInternalError;
+
+            *hostApi = hostApis_[outputHostApiIndex];
+
+            if( outputParameters->channelCount <= 0 )
+                return paInvalidChannelCount;
+
+            if( !SampleFormatIsValid( outputParameters->sampleFormat ) )
+                return paSampleFormatNotSupported;
+
+            if( outputParameters->hostApiSpecificStreamInfo != NULL )
+            {
+                if( ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType
+                        != (*hostApi)->info.type )
+                    return paIncompatibleHostApiSpecificStreamInfo;
+            }
+        }   
+
+        if( (inputParameters != NULL) && (outputParameters != NULL) )
+        {
+            /* ensure that both devices use the same API */
+            if( inputHostApiIndex != outputHostApiIndex )
+                return paBadIODeviceCombination;
+        }
+    }
+    
+    
+    /* Check for absurd sample rates. */
+    if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
+        return paInvalidSampleRate;
+
+    if( ((streamFlags & ~paPlatformSpecificFlags) & ~(paClipOff | paDitherOff | paNeverDropInput | paPrimeOutputBuffersUsingStreamCallback ) ) != 0 )
+        return paInvalidFlag;
+
+    if( streamFlags & paNeverDropInput )
+    {
+        /* must be a callback stream */
+        if( !streamCallback )
+             return paInvalidFlag;
+
+        /* must be a full duplex stream */
+        if( (inputParameters == NULL) || (outputParameters == NULL) )
+            return paInvalidFlag;
+
+        /* must use paFramesPerBufferUnspecified */
+        if( framesPerBuffer != paFramesPerBufferUnspecified )
+            return paInvalidFlag;
+    }
+    
+    return paNoError;
+}
+
+
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+                              const PaStreamParameters *outputParameters,
+                              double sampleRate )
+{
+    PaError result;
+    PaUtilHostApiRepresentation *hostApi;
+    PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
+    PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+    PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsFormatSupported called:\n" );
+
+    if( inputParameters == NULL ){
+        PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
+    }else{
+        PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
+        PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
+        PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
+        PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
+        PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
+        PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
+    }
+
+    if( outputParameters == NULL ){
+        PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
+    }else{
+        PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
+        PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
+        PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
+        PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
+        PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
+        PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
+    }
+    
+    PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+
+    result = ValidateOpenStreamParameters( inputParameters,
+                                           outputParameters,
+                                           sampleRate, 0, paNoFlag, 0,
+                                           &hostApi,
+                                           &hostApiInputDevice,
+                                           &hostApiOutputDevice );
+    if( result != paNoError )
+    {
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+    
+
+    if( inputParameters )
+    {
+        hostApiInputParameters.device = hostApiInputDevice;
+        hostApiInputParameters.channelCount = inputParameters->channelCount;
+        hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
+        hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
+        hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+        hostApiInputParametersPtr = &hostApiInputParameters;
+    }
+    else
+    {
+        hostApiInputParametersPtr = NULL;
+    }
+
+    if( outputParameters )
+    {
+        hostApiOutputParameters.device = hostApiOutputDevice;
+        hostApiOutputParameters.channelCount = outputParameters->channelCount;
+        hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
+        hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
+        hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+        hostApiOutputParametersPtr = &hostApiOutputParameters;
+    }
+    else
+    {
+        hostApiOutputParametersPtr = NULL;
+    }
+
+    result = hostApi->IsFormatSupported( hostApi,
+                                  hostApiInputParametersPtr, hostApiOutputParametersPtr,
+                                  sampleRate );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+    if( result == paFormatIsSupported )
+        PaUtil_DebugPrint("\tPaError: 0 [ paFormatIsSupported ]\n\n" );
+    else
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_OpenStream( PaStream** stream,
+                       const PaStreamParameters *inputParameters,
+                       const PaStreamParameters *outputParameters,
+                       double sampleRate,
+                       unsigned long framesPerBuffer,
+                       PaStreamFlags streamFlags,
+                       PaStreamCallback *streamCallback,
+                       void *userData )
+{
+    PaError result;
+    PaUtilHostApiRepresentation *hostApi;
+    PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
+    PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+    PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
+
+    if( inputParameters == NULL ){
+        PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
+    }else{
+        PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
+        PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
+        PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
+        PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
+        PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
+        PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
+    }
+
+    if( outputParameters == NULL ){
+        PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
+    }else{
+        PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
+        PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
+        PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
+        PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
+        PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
+        PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
+    }
+    
+    PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+    PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
+    PaUtil_DebugPrint("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags );
+    PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
+    PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+        PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+
+    /* Check for parameter errors.
+        NOTE: make sure this validation list is kept syncronised with the one
+        in pa_hostapi.h
+    */
+
+    if( stream == NULL )
+    {
+        result = paBadStreamPtr;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+        PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+
+    result = ValidateOpenStreamParameters( inputParameters,
+                                           outputParameters,
+                                           sampleRate, framesPerBuffer,
+                                           streamFlags, streamCallback,
+                                           &hostApi,
+                                           &hostApiInputDevice,
+                                           &hostApiOutputDevice );
+    if( result != paNoError )
+    {
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+        PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+    
+
+    if( inputParameters )
+    {
+        hostApiInputParameters.device = hostApiInputDevice;
+        hostApiInputParameters.channelCount = inputParameters->channelCount;
+        hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
+        hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
+        hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+        hostApiInputParametersPtr = &hostApiInputParameters;
+    }
+    else
+    {
+        hostApiInputParametersPtr = NULL;
+    }
+
+    if( outputParameters )
+    {
+        hostApiOutputParameters.device = hostApiOutputDevice;
+        hostApiOutputParameters.channelCount = outputParameters->channelCount;
+        hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
+        hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
+        hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+        hostApiOutputParametersPtr = &hostApiOutputParameters;
+    }
+    else
+    {
+        hostApiOutputParametersPtr = NULL;
+    }
+
+    result = hostApi->OpenStream( hostApi, stream,
+                                  hostApiInputParametersPtr, hostApiOutputParametersPtr,
+                                  sampleRate, framesPerBuffer, streamFlags, streamCallback, userData );
+
+    if( result == paNoError )
+        AddOpenStream( *stream );
+
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+    PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p\n", *stream );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_OpenDefaultStream( PaStream** stream,
+                              int inputChannelCount,
+                              int outputChannelCount,
+                              PaSampleFormat sampleFormat,
+                              double sampleRate,
+                              unsigned long framesPerBuffer,
+                              PaStreamCallback *streamCallback,
+                              void *userData )
+{
+    PaError result;
+    PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+    PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenDefaultStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
+    PaUtil_DebugPrint("\tint inputChannelCount: %d\n", inputChannelCount );
+    PaUtil_DebugPrint("\tint outputChannelCount: %d\n", outputChannelCount );
+    PaUtil_DebugPrint("\tPaSampleFormat sampleFormat: %d\n", sampleFormat );
+    PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+    PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
+    PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
+    PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
+#endif
+
+
+    if( inputChannelCount > 0 )
+    {
+        hostApiInputParameters.device = Pa_GetDefaultInputDevice();
+        hostApiInputParameters.channelCount = inputChannelCount;
+        hostApiInputParameters.sampleFormat = sampleFormat;
+        /* defaultHighInputLatency is used below instead of
+           defaultLowInputLatency because it is more important for the default
+           stream to work reliably than it is for it to work with the lowest
+           latency.
+         */
+        hostApiInputParameters.suggestedLatency = 
+             Pa_GetDeviceInfo( hostApiInputParameters.device )->defaultHighInputLatency;
+        hostApiInputParameters.hostApiSpecificStreamInfo = NULL;
+        hostApiInputParametersPtr = &hostApiInputParameters;
+    }
+    else
+    {
+        hostApiInputParametersPtr = NULL;
+    }
+
+    if( outputChannelCount > 0 )
+    {
+        hostApiOutputParameters.device = Pa_GetDefaultOutputDevice();
+        hostApiOutputParameters.channelCount = outputChannelCount;
+        hostApiOutputParameters.sampleFormat = sampleFormat;
+        /* defaultHighOutputLatency is used below instead of
+           defaultLowOutputLatency because it is more important for the default
+           stream to work reliably than it is for it to work with the lowest
+           latency.
+         */
+        hostApiOutputParameters.suggestedLatency =
+             Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency;
+        hostApiOutputParameters.hostApiSpecificStreamInfo = NULL;
+        hostApiOutputParametersPtr = &hostApiOutputParameters;
+    }
+    else
+    {
+        hostApiOutputParametersPtr = NULL;
+    }
+
+
+    result = Pa_OpenStream(
+                 stream, hostApiInputParametersPtr, hostApiOutputParametersPtr,
+                 sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenDefaultStream returned:\n" );
+    PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p", *stream );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError PaUtil_ValidateStreamPointer( PaStream* stream )
+{
+    if( !PA_IS_INITIALISED_ ) return paNotInitialized;
+
+    if( stream == NULL ) return paBadStreamPtr;
+
+    if( ((PaUtilStreamRepresentation*)stream)->magic != PA_STREAM_MAGIC )
+        return paBadStreamPtr;
+
+    return paNoError;
+}
+
+
+PaError Pa_CloseStream( PaStream* stream )
+{
+    PaUtilStreamInterface *interface;
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_CloseStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    /* always remove the open stream from our list, even if this function
+        eventually returns an error. Otherwise CloseOpenStreams() will
+        get stuck in an infinite loop */
+    RemoveOpenStream( stream ); /* be sure to call this _before_ closing the stream */
+
+    if( result == paNoError )
+    {
+        interface = PA_STREAM_INTERFACE(stream);
+
+        /* abort the stream if it isn't stopped */
+        result = interface->IsStopped( stream );
+        if( result == 1 )
+            result = paNoError;
+        else if( result == 0 )
+            result = interface->Abort( stream );
+
+        if( result == paNoError )                 /** @todo REVIEW: shouldn't we close anyway? */
+            result = interface->Close( stream );
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_CloseStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_SetStreamFinishedCallback called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+    PaUtil_DebugPrint("\tPaStreamFinishedCallback* streamFinishedCallback: 0x%p\n", streamFinishedCallback );
+#endif
+
+    if( result == paNoError )
+    {
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+        if( result == 0 )
+        {
+            result = paStreamIsNotStopped ;
+        }
+        if( result == 1 )
+        {
+            PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback;
+            result = paNoError;
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_SetStreamFinishedCallback returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+
+}
+
+
+PaError Pa_StartStream( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_StartStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+        if( result == 0 )
+        {
+            result = paStreamIsNotStopped ;
+        }
+        else if( result == 1 )
+        {
+            result = PA_STREAM_INTERFACE(stream)->Start( stream );
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_StartStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_StopStream( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_StopStream called\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+        if( result == 0 )
+        {
+            result = PA_STREAM_INTERFACE(stream)->Stop( stream );
+        }
+        else if( result == 1 )
+        {
+            result = paStreamIsStopped;
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_StopStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_AbortStream( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_AbortStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+        if( result == 0 )
+        {
+            result = PA_STREAM_INTERFACE(stream)->Abort( stream );
+        }
+        else if( result == 1 )
+        {
+            result = paStreamIsStopped;
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_AbortStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_IsStreamStopped( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsStreamStopped called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsStreamStopped returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_IsStreamActive( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsStreamActive called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+        result = PA_STREAM_INTERFACE(stream)->IsActive( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsStreamActive returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    const PaStreamInfo *result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamInfo called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+        result = 0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
+        PaUtil_DebugPrint("\tconst PaStreamInfo*: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = &PA_STREAM_REP( stream )->streamInfo;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
+        PaUtil_DebugPrint("\tconst PaStreamInfo*: 0x%p:\n", result );
+        PaUtil_DebugPrint("\t{" );
+
+        PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
+        PaUtil_DebugPrint("\t\tPaTime inputLatency: %f\n", result->inputLatency );
+        PaUtil_DebugPrint("\t\tPaTime outputLatency: %f\n", result->outputLatency );
+        PaUtil_DebugPrint("\t\tdouble sampleRate: %f\n", result->sampleRate );
+        PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+    }
+
+    return result;
+}
+
+
+PaTime Pa_GetStreamTime( PaStream *stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    PaTime result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamTime called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+        result = 0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
+        PaUtil_DebugPrint("\tPaTime: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = PA_STREAM_INTERFACE(stream)->GetTime( stream );
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
+        PaUtil_DebugPrint("\tPaTime: %g\n\n", result );
+#endif
+
+    }
+
+    return result;
+}
+
+
+double Pa_GetStreamCpuLoad( PaStream* stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    double result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamCpuLoad called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+
+        result = 0.0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
+        PaUtil_DebugPrint("\tdouble: 0.0 [PaError error: %d ( %s )]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = PA_STREAM_INTERFACE(stream)->GetCpuLoad( stream );
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
+        PaUtil_DebugPrint("\tdouble: %g\n\n", result );
+#endif
+
+    }
+
+    return result;
+}
+
+
+PaError Pa_ReadStream( PaStream* stream,
+                       void *buffer,
+                       unsigned long frames )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_ReadStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        if( frames == 0 )
+        {
+            result = paInternalError; /** @todo should return a different error code */
+        }
+        else if( buffer == 0 )
+        {
+            result = paInternalError; /** @todo should return a different error code */
+        }
+        else
+        {
+            result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+            if( result == 0 )
+            {
+                result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames );
+            }
+            else if( result == 1 )
+            {
+                result = paStreamIsStopped;
+            }
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_ReadStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_WriteStream( PaStream* stream,
+                        const void *buffer,
+                        unsigned long frames )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_WriteStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        if( frames == 0 )
+        {
+            result = paInternalError; /** @todo should return a different error code */
+        }
+        else if( buffer == 0 )
+        {
+            result = paInternalError; /** @todo should return a different error code */
+        }
+        else
+        {
+            result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+            if( result == 0 )
+            {
+                result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames );
+            }
+            else if( result == 1 )
+            {
+                result = paStreamIsStopped;
+            }  
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_WriteStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+signed long Pa_GetStreamReadAvailable( PaStream* stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    signed long result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamReadAvailable called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+        result = 0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
+        PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = PA_STREAM_INTERFACE(stream)->GetReadAvailable( stream );
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    }
+
+    return result;
+}
+
+
+signed long Pa_GetStreamWriteAvailable( PaStream* stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    signed long result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamWriteAvailable called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+        result = 0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
+        PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = PA_STREAM_INTERFACE(stream)->GetWriteAvailable( stream );
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    }
+
+    return result;
+}
+
+
+PaError Pa_GetSampleSize( PaSampleFormat format )
+{
+    int result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetSampleSize called:\n" );
+    PaUtil_DebugPrint("\tPaSampleFormat format: %d\n", format );
+#endif
+
+    switch( format & ~paNonInterleaved )
+    {
+
+    case paUInt8:
+    case paInt8:
+        result = 1;
+        break;
+
+    case paInt16:
+        result = 2;
+        break;
+
+    case paInt24:
+        result = 3;
+        break;
+
+    case paFloat32:
+    case paInt32:
+        result = 4;
+        break;
+
+    default:
+        result = paSampleFormatNotSupported;
+        break;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetSampleSize returned:\n" );
+    if( result > 0 )
+        PaUtil_DebugPrint("\tint: %d\n\n", result );
+    else
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return (PaError) result;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_hostapi.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_hostapi.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_hostapi.h	(revision 1753)
@@ -0,0 +1,265 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_HOSTAPI_H
+#define PA_HOSTAPI_H
+/*
+ * $Id: pa_hostapi.h,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
+ * Portable Audio I/O Library
+ * host api representation
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ host APIs.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** **FOR THE USE OF pa_front.c ONLY**
+    Do NOT use fields in this structure, they my change at any time.
+    Use functions defined in pa_util.h if you think you need functionality
+    which can be derived from here.
+*/
+typedef struct PaUtilPrivatePaFrontHostApiInfo {
+
+
+    unsigned long baseDeviceIndex;
+}PaUtilPrivatePaFrontHostApiInfo;
+
+
+/** The common header for all data structures whose pointers are passed through
+ the hostApiSpecificStreamInfo field of the PaStreamParameters structure.
+ Note that in order to keep the public PortAudio interface clean, this structure
+ is not used explicitly when declaring hostApiSpecificStreamInfo data structures.
+ However, some code in pa_front depends on the first 3 members being equivalent
+ with this structure.
+ @see PaStreamParameters
+*/
+typedef struct PaUtilHostApiSpecificStreamInfoHeader
+{
+    unsigned long size;             /**< size of whole structure including this header */
+    PaHostApiTypeId hostApiType;    /**< host API for which this data is intended */
+    unsigned long version;          /**< structure version */
+} PaUtilHostApiSpecificStreamInfoHeader;
+
+
+
+/** A structure representing the interface to a host API. Contains both
+ concrete data and pointers to functions which implement the interface.
+*/
+typedef struct PaUtilHostApiRepresentation {
+    PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo;
+
+    /** The host api implementation should populate the info field. In the
+        case of info.defaultInputDevice and info.defaultOutputDevice the
+        values stored should be 0 based indices within the host api's own
+        device index range (0 to deviceCount). These values will be converted
+        to global device indices by pa_front after PaUtilHostApiInitializer()
+        returns.
+    */
+    PaHostApiInfo info;
+
+    PaDeviceInfo** deviceInfos;
+
+    /**
+        (*Terminate)() is guaranteed to be called with a valid <hostApi>
+        parameter, which was previously returned from the same implementation's
+        initializer.
+    */
+    void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi );
+
+    /**
+        The inputParameters and outputParameters pointers should not be saved
+        as they will not remain valid after OpenStream is called.
+
+        
+        The following guarantees are made about parameters to (*OpenStream)():
+
+            [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be
+                kept in sync with the one for ValidateOpenStreamParameters and
+                Pa_OpenStream in pa_front.c]
+                
+            PaHostApiRepresentation *hostApi
+                - is valid for this implementation
+
+            PaStream** stream
+                - is non-null
+
+            - at least one of inputParameters & outputParmeters is valid (not NULL)
+
+            - if inputParameters & outputParmeters are both valid, that
+                inputParameters->device & outputParmeters->device  both use the same host api
+ 
+            PaDeviceIndex inputParameters->device
+                - is within range (0 to Pa_CountDevices-1) Or:
+                - is paUseHostApiSpecificDeviceSpecification and
+                    inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
+                    to a valid host api
+
+            int inputParameters->numChannels
+                - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0
+                - upper bound is NOT validated against device capabilities
+ 
+            PaSampleFormat inputParameters->sampleFormat
+                - is one of the sample formats defined in portaudio.h
+
+            void *inputParameters->hostApiSpecificStreamInfo
+                - if supplied its hostApi field matches the input device's host Api
+ 
+            PaDeviceIndex outputParmeters->device
+                - is within range (0 to Pa_CountDevices-1)
+ 
+            int outputParmeters->numChannels
+                - if inputDevice is valid, numInputChannels is > 0
+                - upper bound is NOT validated against device capabilities
+ 
+            PaSampleFormat outputParmeters->sampleFormat
+                - is one of the sample formats defined in portaudio.h
+        
+            void *outputParmeters->hostApiSpecificStreamInfo
+                - if supplied its hostApi field matches the output device's host Api
+ 
+            double sampleRate
+                - is not an 'absurd' rate (less than 1000. or greater than 200000.)
+                - sampleRate is NOT validated against device capabilities
+ 
+            PaStreamFlags streamFlags
+                - unused platform neutral flags are zero
+                - paNeverDropInput is only used for full-duplex callback streams
+                    with variable buffer size (paFramesPerBufferUnspecified)
+
+            [*END PA FRONT VALIDATIONS*]
+
+
+        The following validations MUST be performed by (*OpenStream)():
+
+            - check that input device can support numInputChannels
+            
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - if inputStreamInfo is supplied, validate its contents,
+                or return an error if no inputStreamInfo is expected
+
+            - check that output device can support numOutputChannels
+            
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - if outputStreamInfo is supplied, validate its contents,
+                or return an error if no outputStreamInfo is expected
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+
+            - check that the device supports sampleRate
+
+            - alter sampleRate to a close allowable rate if necessary
+
+            - validate inputLatency and outputLatency
+
+            - validate any platform specific flags, if flags are supplied they
+                must be valid.
+    */
+    PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** stream,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerCallback,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+
+
+    PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+} PaUtilHostApiRepresentation;
+
+
+/** Prototype for the initialization function which must be implemented by every
+ host API.
+ 
+ @see paHostApiInitializers
+*/
+typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex );
+
+
+/** paHostApiInitializers is a NULL-terminated array of host API initialization
+ functions. These functions are called by pa_front to initialize the host APIs
+ when the client calls Pa_Initialize().
+
+ There is a platform specific file which defines paHostApiInitializers for that
+ platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example.
+*/
+extern PaUtilHostApiInitializer *paHostApiInitializers[];
+
+
+/** The index of the default host API in the paHostApiInitializers array.
+ 
+ There is a platform specific file which defines paDefaultHostApiIndex for that
+ platform, see pa_win/pa_win_hostapis.c for example.
+*/
+extern int paDefaultHostApiIndex;
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_HOSTAPI_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c	(revision 1753)
@@ -0,0 +1,3293 @@
+/*
+ * $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * ALSA implementation by Joshua Haberman and Arve Knudsen
+ *
+ * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
+ * Copyright (c) 2005 Arve Knudsen <aknuds-1@broadpark.no>
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+#undef ALSA_PCM_NEW_HW_PARAMS_API
+#undef ALSA_PCM_NEW_SW_PARAMS_API
+
+#include <sys/poll.h>
+#include <string.h> /* strlen() */
+#include <limits.h>
+#include <math.h>
+#include <pthread.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <signal.h> /* For sig_atomic_t */
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_unix_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "pa_linux_alsa.h"
+
+/* Check return value of ALSA function, and map it to PaError */
+#define ENSURE_(expr, code) \
+    do { \
+        if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \
+        { \
+            /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+            if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \
+            { \
+                PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
+            } \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = (code); \
+            goto error; \
+        } \
+    } while( 0 );
+
+#define ASSERT_CALL_(expr, success) \
+    aErr_ = (expr); \
+    assert( aErr_ == success );
+
+static int aErr_;               /* Used with ENSURE_ */
+static pthread_t callbackThread_;
+
+typedef enum
+{
+    StreamDirection_In,
+    StreamDirection_Out
+} StreamDirection;
+
+/* Threading utility struct */
+typedef struct PaAlsaThreading
+{
+    pthread_t watchdogThread;
+    pthread_t callbackThread;
+    int watchdogRunning;
+    int rtSched;
+    int rtPrio;
+    int useWatchdog;
+    unsigned long throttledSleepTime;
+    volatile PaTime callbackTime;
+    volatile PaTime callbackCpuTime;
+    PaUtilCpuLoadMeasurer *cpuLoadMeasurer;
+} PaAlsaThreading;
+
+typedef struct
+{
+    PaSampleFormat hostSampleFormat;
+    unsigned long framesPerBuffer;
+    int numUserChannels, numHostChannels;
+    int userInterleaved, hostInterleaved;
+
+    snd_pcm_t *pcm;
+    snd_pcm_uframes_t bufferSize;
+    snd_pcm_format_t nativeFormat;
+    unsigned int nfds;
+    int ready;  /* Marked ready from poll */
+    void **userBuffers;
+    snd_pcm_uframes_t offset;
+    StreamDirection streamDir;
+
+    snd_pcm_channel_area_t *channelAreas;  /* Needed for channel adaption */
+} PaAlsaStreamComponent;
+
+/* Implementation specific stream structure */
+typedef struct PaAlsaStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+    PaAlsaThreading threading;
+
+    unsigned long framesPerUserBuffer;
+
+    int primeBuffers;
+    int callbackMode;              /* bool: are we running in callback mode? */
+    int pcmsSynced;	            /* Have we successfully synced pcms */
+
+    /* the callback thread uses these to poll the sound device(s), waiting
+     * for data to be ready/available */
+    struct pollfd *pfds;
+    int pollTimeout;
+
+    /* Used in communication between threads */
+    volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */
+    volatile sig_atomic_t callbackAbort;    /* Drop frames? */
+    volatile sig_atomic_t callbackStop;     /* Signal a stop */
+    volatile sig_atomic_t isActive;         /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */
+    pthread_mutex_t stateMtx;               /* Used to synchronize access to stream state */
+    pthread_mutex_t startMtx;               /* Used to synchronize stream start in callback mode */
+    pthread_cond_t startCond;               /* Wait untill audio is started in callback thread */
+
+    int neverDropInput;
+
+    PaTime underrun;
+    PaTime overrun;
+
+    PaAlsaStreamComponent capture, playback;
+}
+PaAlsaStream;
+
+/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct PaAlsaHostApiRepresentation
+{
+    PaUtilHostApiRepresentation commonHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+
+    PaHostApiIndex hostApiIndex;
+}
+PaAlsaHostApiRepresentation;
+
+typedef struct PaAlsaDeviceInfo
+{
+    PaDeviceInfo commonDeviceInfo;
+    char *alsaName;
+    int isPlug;
+    int minInputChannels;
+    int minOutputChannels;
+}
+PaAlsaDeviceInfo;
+
+/* Threading utilities */
+
+static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm )
+{
+    th->watchdogRunning = 0;
+    th->rtSched = 0;
+    th->callbackTime = 0;
+    th->callbackCpuTime = 0;
+    th->useWatchdog = 1;
+    th->throttledSleepTime = 0;
+    th->cpuLoadMeasurer = clm;
+
+    th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2
+            + sched_get_priority_min( SCHED_FIFO );
+}
+
+static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult )
+{
+    PaError result = paNoError;
+    void *pret;
+
+    if( exitResult )
+        *exitResult = paNoError;
+    if( watchdogExitResult )
+        *watchdogExitResult = paNoError;
+
+    if( th->watchdogRunning )
+    {
+        pthread_cancel( th->watchdogThread );
+        ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 );
+
+        if( pret && pret != PTHREAD_CANCELED )
+        {
+            if( watchdogExitResult )
+                *watchdogExitResult = *(PaError *) pret;
+            free( pret );
+        }
+    }
+
+    /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
+    /* TODO: Make join time out */
+    if( !wait )
+        pthread_cancel( th->callbackThread );   /* XXX: Safe to call this if the thread has exited on its own? */
+    ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 );
+
+    if( pret && pret != PTHREAD_CANCELED )
+    {
+        if( exitResult )
+            *exitResult = *(PaError *) pret;
+        free( pret );
+    }
+
+    return result;
+}
+
+static void OnWatchdogExit( void *userData )
+{
+    PaAlsaThreading *th = (PaAlsaThreading *) userData;
+    struct sched_param spm = { 0 };
+    assert( th );
+
+    ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 );    /* Lower before exiting */
+    PA_DEBUG(( "Watchdog exiting\n" ));
+}
+
+static PaError BoostPriority( PaAlsaThreading *th )
+{
+    PaError result = paNoError;
+    struct sched_param spm = { 0 };
+    spm.sched_priority = th->rtPrio;
+
+    assert( th );
+
+    if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
+    {
+        PA_UNLESS( errno == EPERM, paInternalError );  /* Lack permission to raise priority */
+        PA_DEBUG(( "Failed bumping priority\n" ));
+        result = 0;
+    }
+    else
+        result = 1; /* Success */
+error:
+    return result;
+}
+
+static void *WatchdogFunc( void *userData )
+{
+    PaError result = paNoError, *pres = NULL;
+    int err;
+    PaAlsaThreading *th = (PaAlsaThreading *) userData;
+    unsigned intervalMsec = 500;
+    const PaTime maxSeconds = 3.;   /* Max seconds between callbacks */
+    PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
+    double cpuLoad, avgCpuLoad = 0.;
+    int throttled = 0;
+
+    assert( th );
+
+    pthread_cleanup_push( &OnWatchdogExit, th );	/* Execute OnWatchdogExit when exiting */
+
+    /* Boost priority of callback thread */
+    PA_ENSURE( result = BoostPriority( th ) );
+    if( !result )
+    {
+        pthread_exit( NULL );   /* Boost failed, might as well exit */
+    }
+
+    cpuTimeThen = th->callbackCpuTime;
+    {
+        int policy;
+        struct sched_param spm = { 0 };
+        pthread_getschedparam( pthread_self(), &policy, &spm );
+        PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
+    }
+
+    while( 1 )
+    {
+        double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
+        
+        /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
+        pthread_testcancel();
+        Pa_Sleep( intervalMsec );
+        pthread_testcancel();
+
+        if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
+        {
+            PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
+            /* Tell thread to terminate */
+            err = pthread_kill( th->callbackThread, SIGKILL );
+            pthread_exit( NULL );
+        }
+
+        PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
+
+        /* Check if we should throttle, or unthrottle :P */
+        cpuTimeNow = th->callbackCpuTime;
+        cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
+        cpuTimeThen = cpuTimeNow;
+
+        timeNow = PaUtil_GetTime();
+        timeElapsed = timeNow - timeThen;
+        timeThen = timeNow;
+        cpuLoad = cpuTimeElapsed / timeElapsed;
+        avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
+        /*
+        if( throttled )
+            PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
+            */
+        if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
+        {
+            static int policy;
+            static struct sched_param spm = { 0 };
+            static const struct sched_param defaultSpm = { 0 };
+            PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
+
+            pthread_getschedparam( th->callbackThread, &policy, &spm );
+            if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
+            {
+                throttled = 1;
+            }
+            else
+                PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
+
+            /* Give other processes a go, before raising priority again */
+            PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
+            Pa_Sleep( th->throttledSleepTime );
+
+            /* Reset callback priority */
+            if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
+            {
+                PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
+            }
+
+            if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
+                intervalMsec = 50;
+            else
+                intervalMsec = 100;
+
+            /*
+            lowpassCoeff = .97;
+            lowpassCoeff1 = .99999 - lowpassCoeff;
+            */
+        }
+        else if( throttled && avgCpuLoad < .8 )
+        {
+            intervalMsec = 500;
+            throttled = 0;
+
+            /*
+            lowpassCoeff = .9;
+            lowpassCoeff1 = .99999 - lowpassCoeff;
+            */
+        }
+    }
+
+    pthread_cleanup_pop( 1 );   /* Execute cleanup on exit */
+
+error:
+    /* Shouldn't get here in the normal case */
+
+    /* Pass on error code */
+    pres = malloc( sizeof (PaError) );
+    *pres = result;
+    
+    pthread_exit( pres );
+}
+
+static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s )
+{
+    PaError result = paNoError;
+    pthread_attr_t attr;
+    int started = 0;
+
+#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
+    if( th->rtSched )
+    {
+        if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
+        {
+            int savedErrno = errno;             /* In case errno gets overwritten */
+            assert( savedErrno != EINVAL );     /* Most likely a programmer error */
+            PA_UNLESS( (savedErrno == EPERM), paInternalError );
+            PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
+        }
+        else
+            PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
+    }
+#endif
+
+    PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
+    /* Priority relative to other processes */
+    PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );   
+
+    PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError );
+    started = 1;
+
+    if( th->rtSched )
+    {
+        if( th->useWatchdog )
+        {
+            int err;
+            struct sched_param wdSpm = { 0 };
+            /* Launch watchdog, watchdog sets callback thread priority */
+            int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
+            wdSpm.sched_priority = prio;
+
+            PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
+            PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
+            PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
+            PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
+            PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
+            if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) )
+            {
+                PA_UNLESS( err == EPERM, paInternalError );
+                /* Permission error, go on without realtime privileges */
+                PA_DEBUG(( "Failed bumping priority\n" ));
+            }
+            else
+            {
+                int policy;
+                th->watchdogRunning = 1;
+                ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 );
+                /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
+                if( wdSpm.sched_priority != prio )
+                {
+                    PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
+                    PA_ENSURE( paInternalError );
+                }
+            }
+        }
+        else
+            PA_ENSURE( BoostPriority( th ) );
+    }
+
+end:
+    return result;
+error:
+    if( started )
+        KillCallbackThread( th, 0, NULL, NULL );
+
+    goto end;
+}
+
+static void CallbackUpdate( PaAlsaThreading *th )
+{
+    th->callbackTime = PaUtil_GetTime();
+    th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
+}
+
+/* prototypes for functions declared in this file */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *callback,
+                           void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
+static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
+static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
+
+/* Callback prototypes */
+static void *CallbackThreadFunc( void *userData );
+
+/* Blocking prototypes */
+static signed long GetStreamReadAvailable( PaStream* s );
+static signed long GetStreamWriteAvailable( PaStream* s );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+
+
+static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device )
+{
+    return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
+}
+
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    PaAlsaHostApiRepresentation *alsaHostApi = NULL;
+
+    PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory(
+                sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
+    PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+    alsaHostApi->hostApiIndex = hostApiIndex;
+
+    *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paALSA;
+    (*hostApi)->info.name = "ALSA";
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PA_ENSURE( BuildDeviceList( alsaHostApi ) );
+
+    PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface,
+                                      CloseStream, StartStream,
+                                      StopStream, AbortStream,
+                                      IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable,
+                                      PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface,
+                                      CloseStream, StartStream,
+                                      StopStream, AbortStream,
+                                      IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream,
+                                      GetStreamReadAvailable,
+                                      GetStreamWriteAvailable );
+
+    return result;
+
+error:
+    if( alsaHostApi )
+    {
+        if( alsaHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( alsaHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
+        }
+
+        PaUtil_FreeMemory( alsaHostApi );
+    }
+
+    return result;
+}
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+
+    assert( hostApi );
+
+    if( alsaHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( alsaHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( alsaHostApi );
+    snd_config_update_free_global();
+}
+
+/*! Determine max channels and default latencies.
+ *
+ * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for 
+ * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero,
+ * and a suitable result returned. The device is closed before returning.
+ */
+static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency,
+        double *defaultHighLatency, double *defaultSampleRate, int isPlug )
+{
+    PaError result = paNoError;
+    snd_pcm_hw_params_t *hwParams;
+    snd_pcm_uframes_t lowLatency = 512, highLatency = 2048;
+    unsigned int minChans, maxChans;
+    double defaultSr = *defaultSampleRate;
+
+    assert( pcm );
+
+    ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError );
+
+    snd_pcm_hw_params_alloca( &hwParams );
+    snd_pcm_hw_params_any( pcm, hwParams );
+
+    if( defaultSr >= 0 )
+    {
+        /* Could be that the device opened in one mode supports samplerates that the other mode wont have,
+         * so try again .. */
+        if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 )
+        {
+            defaultSr = -1.;
+            PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ ));
+        }
+    }
+
+    if( defaultSr < 0. )           /* Default sample rate not set */
+    {
+        unsigned int sampleRate = 44100;        /* Will contain approximate rate returned by alsa-lib */
+        ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError );
+        ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError );
+    }
+
+    ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError );
+    assert( maxChans <= INT_MAX );
+    assert( maxChans > 0 );    /* Weird linking issue could cause wrong version of ALSA symbols to be called,
+                                   resulting in zeroed values */
+
+    /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */
+    if( isPlug && maxChans > 128 )
+    {
+        maxChans = 128;
+        PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans ));
+    }
+
+    /* TWEAKME:
+     *
+     * Giving values for default min and max latency is not
+     * straightforward.  Here are our objectives:
+     *
+     *         * for low latency, we want to give the lowest value
+     *         that will work reliably.  This varies based on the
+     *         sound card, kernel, CPU, etc.  I think it is better
+     *         to give sub-optimal latency than to give a number
+     *         too low and cause dropouts.  My conservative
+     *         estimate at this point is to base it on 4096-sample
+     *         latency at 44.1 kHz, which gives a latency of 23ms.
+     *         * for high latency we want to give a large enough
+     *         value that dropouts are basically impossible.  This
+     *         doesn't really require as much tweaking, since
+     *         providing too large a number will just cause us to
+     *         select the nearest setting that will work at stream
+     *         config time.
+     */
+    ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError );
+
+    /* Have to reset hwParams, to set new buffer size */
+    ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); 
+    ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError );
+
+    *minChannels = (int)minChans;
+    *maxChannels = (int)maxChans;
+    *defaultSampleRate = defaultSr;
+    *defaultLowLatency = (double) lowLatency / *defaultSampleRate;
+    *defaultHighLatency = (double) highLatency / *defaultSampleRate;
+
+end:
+    snd_pcm_close( pcm );
+    return result;
+
+error:
+    goto end;
+}
+
+/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate
+ * wether input/output is available) */
+static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
+{
+    deviceInfo->structVersion = -1;
+    deviceInfo->name = NULL;
+    deviceInfo->hostApi = -1;
+    deviceInfo->maxInputChannels = 0;
+    deviceInfo->maxOutputChannels = 0;
+    deviceInfo->defaultLowInputLatency = -1.;
+    deviceInfo->defaultLowOutputLatency = -1.;
+    deviceInfo->defaultHighInputLatency = -1.;
+    deviceInfo->defaultHighOutputLatency = -1.;
+    deviceInfo->defaultSampleRate = -1.;
+}
+
+/* Helper struct */
+typedef struct
+{
+    char *alsaName;
+    char *name;
+    int isPlug;
+    int hasPlayback;
+    int hasCapture;
+} DeviceNames;
+
+static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi,
+        char **dst,
+        const char *src)
+{
+    PaError result = paNoError;
+    int len = strlen( src ) + 1;
+
+    /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */
+
+    PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
+            paInsufficientMemory );
+    strncpy( *dst, src, len );
+
+error:
+    return result;
+}
+
+/* Disregard standard plugins
+ * XXX: Might want to make the "default" plugin available, if we can make it work
+ */
+static int IgnorePlugin( const char *pluginId )
+{
+#define numIgnored 10
+    static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee",
+        "file", "null", "shm"};
+    int i;
+
+    for( i = 0; i < numIgnored; ++i )
+    {
+        if( !strcmp( pluginId, ignoredPlugins[i] ) )
+        {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
+{
+    PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep;
+    PaAlsaDeviceInfo *deviceInfoArray;
+    int cardIdx = -1, devIdx = 0;
+    snd_ctl_card_info_t *cardInfo;
+    PaError result = paNoError;
+    size_t numDeviceNames = 0, maxDeviceNames = 1, i;
+    DeviceNames *deviceNames = NULL;
+    snd_config_t *topNode = NULL;
+    snd_pcm_info_t *pcmInfo;
+    int res;
+    int blocking = SND_PCM_NONBLOCK;
+    char alsaCardName[50];
+    if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
+        blocking = 0;
+
+    /* These two will be set to the first working input and output device, respectively */
+    commonApi->info.defaultInputDevice = paNoDevice;
+    commonApi->info.defaultOutputDevice = paNoDevice;
+
+    /* count the devices by enumerating all the card numbers */
+
+    /* snd_card_next() modifies the integer passed to it to be:
+     *      the index of the first card if the parameter is -1
+     *      the index of the next card if the parameter is the index of a card
+     *      -1 if there are no more cards
+     *
+     * The function itself returns 0 if it succeeded. */
+    cardIdx = -1;
+    snd_ctl_card_info_alloca( &cardInfo );
+    snd_pcm_info_alloca( &pcmInfo );
+    while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 )
+    {
+        char *cardName;
+        int devIdx = -1;
+        snd_ctl_t *ctl;
+        char buf[50];
+
+        snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx );
+
+        /* Acquire name of card */
+        if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 )
+            continue;   /* Unable to open card :( */
+        snd_ctl_card_info( ctl, cardInfo );
+
+        PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) );
+
+        while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 )
+        {
+            char *alsaDeviceName, *deviceName;
+            size_t len;
+            int hasPlayback = 0, hasCapture = 0;
+            snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx );
+
+            /* Obtain info about this particular device */
+            snd_pcm_info_set_device( pcmInfo, devIdx );
+            snd_pcm_info_set_subdevice( pcmInfo, 0 );
+            snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE );
+            if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+                hasCapture = 1;
+            
+            snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK );
+            if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+                hasPlayback = 1;
+
+            if( !hasPlayback && !hasCapture )
+            {
+                continue;   /* Error */
+            }
+
+            /* The length of the string written by snprintf plus terminating 0 */
+            len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1;
+            PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
+                    paInsufficientMemory );
+            snprintf( deviceName, len, "%s: %s (%s)", cardName,
+                    snd_pcm_info_get_name( pcmInfo ), buf );
+
+            ++numDeviceNames;
+            if( !deviceNames || numDeviceNames > maxDeviceNames )
+            {
+                maxDeviceNames *= 2;
+                PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
+                        paInsufficientMemory );
+            }
+
+            PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) );
+
+            deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName;
+            deviceNames[ numDeviceNames - 1 ].name = deviceName;
+            deviceNames[ numDeviceNames - 1 ].isPlug = 0;
+            deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback;
+            deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture;
+        }
+        snd_ctl_close( ctl );
+    }
+
+    /* Iterate over plugin devices */
+    snd_config_update();
+    if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 )
+    {
+        snd_config_iterator_t i, next;
+
+        snd_config_for_each( i, next, topNode )
+        {
+            const char *tpStr = NULL, *idStr = NULL;
+            char *alsaDeviceName, *deviceName;
+            snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL;
+            if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND )
+                continue;
+
+            ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError );
+            ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
+
+            ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError );
+            if( IgnorePlugin( idStr ) )
+            {
+                PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr ));
+                continue;
+            }
+
+            PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr ));
+
+            PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
+                                                            strlen(idStr) + 6 ), paInsufficientMemory );
+            strcpy( alsaDeviceName, idStr );
+            PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
+                                                            strlen(idStr) + 1 ), paInsufficientMemory );
+            strcpy( deviceName, idStr );
+
+            ++numDeviceNames;
+            if( !deviceNames || numDeviceNames > maxDeviceNames )
+            {
+                maxDeviceNames *= 2;
+                PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
+                        paInsufficientMemory );
+            }
+
+            deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName;
+            deviceNames[numDeviceNames - 1].name = deviceName;
+            deviceNames[numDeviceNames - 1].isPlug = 1;
+            deviceNames[numDeviceNames - 1].hasPlayback = 1;
+            deviceNames[numDeviceNames - 1].hasCapture = 1;
+        }
+    }
+    else
+        PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) ));
+
+    /* allocate deviceInfo memory based on the number of devices */
+    PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+            alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory );
+
+    /* allocate all device info structs in a contiguous block */
+    PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory(
+            alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory );
+
+    /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name),
+     * it's ignored.
+     */
+    /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */
+    for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
+    {
+        snd_pcm_t *pcm;
+        PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx];
+        PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo;
+
+        /* Zero fields */
+        InitializeDeviceInfo( commonDeviceInfo );
+
+        /* to determine device capabilities, we must open the device and query the
+         * hardware parameter configuration space */
+
+        /* Query capture */
+        if( deviceNames[i].hasCapture &&
+                snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 )
+        {
+            if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels,
+                        &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency,
+                        &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
+                continue;   /* Error */
+        }
+
+        /* Query playback */
+        if( deviceNames[i].hasPlayback &&
+                snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 )
+        {
+            if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels,
+                        &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency,
+                        &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
+                continue;   /* Error */
+        }
+
+        commonDeviceInfo->structVersion = 2;
+        commonDeviceInfo->hostApi = alsaApi->hostApiIndex;
+        commonDeviceInfo->name = deviceNames[i].name;
+        deviceInfo->alsaName = deviceNames[i].alsaName;
+        deviceInfo->isPlug = deviceNames[i].isPlug;
+
+        /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object.
+         * Should now be safe to add device info, unless the device supports neither capture nor playback
+         */
+        if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 )
+        {
+            if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 )
+                commonApi->info.defaultInputDevice = devIdx;
+            if(  commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 )
+                commonApi->info.defaultOutputDevice = devIdx;
+
+            commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo;
+        }
+    }
+    free( deviceNames );
+
+    commonApi->info.deviceCount = devIdx;   /* Number of successfully queried devices */
+
+end:
+    return result;
+
+error:
+    goto end;   /* No particular action */
+}
+
+/* Check against known device capabilities */
+static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode )
+{
+    PaError result = paNoError;
+    int maxChans;
+    const PaAlsaDeviceInfo *deviceInfo = NULL;
+    assert( parameters );
+
+    if( parameters->device != paUseHostApiSpecificDeviceSpecification )
+    {
+        assert( parameters->device < hostApi->info.deviceCount );
+        PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination );
+        deviceInfo = GetDeviceInfo( hostApi, parameters->device );
+    }
+    else
+    {
+        const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo;
+
+        PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice );
+        PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1,
+                paIncompatibleHostApiSpecificStreamInfo );
+
+        return paNoError;   /* Skip further checking */
+    }
+
+    assert( deviceInfo );
+    assert( parameters->hostApiSpecificStreamInfo == NULL );
+    maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels :
+        deviceInfo->commonDeviceInfo.maxOutputChannels);
+    PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount );
+
+error:
+    return result;
+}
+
+/* Given an open stream, what sample formats are available? */
+
+static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
+{
+    PaSampleFormat available = 0;
+    snd_pcm_hw_params_t *hwParams;
+    snd_pcm_hw_params_alloca( &hwParams );
+
+    snd_pcm_hw_params_any( pcm, hwParams );
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0)
+        available |= paFloat32;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0)
+        available |= paInt32;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0)
+        available |= paInt24;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0)
+        available |= paInt16;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0)
+        available |= paUInt8;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0)
+        available |= paInt8;
+
+    return available;
+}
+
+static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat )
+{
+    switch( paFormat )
+    {
+        case paFloat32:
+            return SND_PCM_FORMAT_FLOAT;
+
+        case paInt16:
+            return SND_PCM_FORMAT_S16;
+
+        case paInt24:
+            return SND_PCM_FORMAT_S24;
+
+        case paInt32:
+            return SND_PCM_FORMAT_S32;
+
+        case paInt8:
+            return SND_PCM_FORMAT_S8;
+
+        case paUInt8:
+            return SND_PCM_FORMAT_U8;
+
+        default:
+            return SND_PCM_FORMAT_UNKNOWN;
+    }
+}
+
+/** Open an ALSA pcm handle.
+ * 
+ * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a
+ * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin
+ * device.
+ */
+static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection
+        streamDir, snd_pcm_t **pcm )
+{
+    PaError result = paNoError;
+    int ret;
+    const char *deviceName = alloca( 50 );
+    const PaAlsaDeviceInfo *deviceInfo = NULL;
+    PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo;
+
+    if( !streamInfo )
+    {
+        int usePlug = 0;
+        deviceInfo = GetDeviceInfo( hostApi, params->device );
+        
+        /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */
+        if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) )
+            usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) );
+        if( usePlug )
+            snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName );
+        else
+            deviceName = deviceInfo->alsaName;
+    }
+    else
+        deviceName = streamInfo->deviceString;
+
+    if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
+                    SND_PCM_NONBLOCK )) < 0 )
+    {
+        *pcm = NULL;     /* Not to be closed */
+        ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination );
+    }
+    ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
+
+end:
+    return result;
+
+error:
+    goto end;
+}
+
+static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters,
+        double sampleRate, StreamDirection streamDir )
+{
+    PaError result = paNoError;
+    snd_pcm_t *pcm = NULL;
+    PaSampleFormat availableFormats;
+    /* We are able to adapt to a number of channels less than what the device supports */
+    unsigned int numHostChannels;
+    PaSampleFormat hostFormat;
+    snd_pcm_hw_params_t *hwParams;
+    snd_pcm_hw_params_alloca( &hwParams );
+    
+    if( !parameters->hostApiSpecificStreamInfo )
+    {
+        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device );
+        numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ?
+                devInfo->minInputChannels : devInfo->minOutputChannels );
+    }
+    else
+        numHostChannels = parameters->channelCount;
+
+    PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) );
+
+    snd_pcm_hw_params_any( pcm, hwParams );
+
+    if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 )
+    {
+        result = paInvalidSampleRate;
+        goto error;
+    }
+
+    if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 )
+    {
+        result = paInvalidChannelCount;
+        goto error;
+    }
+
+    /* See if we can find a best possible match */
+    availableFormats = GetAvailableFormats( pcm );
+    PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) );
+    ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError );
+
+    ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+
+end:
+    if( pcm )
+        snd_pcm_close( pcm );
+    return result;
+
+error:
+    goto end;
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    int inputChannelCount = 0, outputChannelCount = 0;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaError result = paFormatIsSupported;
+
+    if( inputParameters )
+    {
+        PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
+
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+    }
+
+    if( outputParameters )
+    {
+        PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
+
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+    }
+
+    if( inputChannelCount )
+    {
+        if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In ))
+                != paNoError )
+            goto error;
+    }
+    if ( outputChannelCount )
+    {
+        if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out ))
+                != paNoError )
+            goto error;
+    }
+
+    return paFormatIsSupported;
+
+error:
+    return result;
+}
+
+static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
+        const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
+{
+    PaError result = paNoError;
+    PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat;
+    assert( params->channelCount > 0 );
+
+    /* Make sure things have an initial value */
+    memset( self, 0, sizeof (PaAlsaStreamComponent) );
+
+    if( NULL == params->hostApiSpecificStreamInfo )
+    {
+        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device );
+        self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
+                : devInfo->minOutputChannels );
+    }
+    else
+    {
+        /* We're blissfully unaware of the minimum channelCount */
+        self->numHostChannels = params->channelCount;
+    }
+
+    PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) );
+    self->nfds = snd_pcm_poll_descriptors_count( self->pcm );
+    hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat );
+
+    self->hostSampleFormat = hostSampleFormat;
+    self->nativeFormat = Pa2AlsaFormat( hostSampleFormat );
+    self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
+    self->numUserChannels = params->channelCount;
+    self->streamDir = streamDir;
+
+    if( !callbackMode && !self->userInterleaved )
+    {
+        /* Pre-allocate non-interleaved user provided buffers */
+        PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ),
+                paInsufficientMemory );
+    }
+
+error:
+    return result;
+}
+
+static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self )
+{
+    snd_pcm_close( self->pcm );
+    if( self->userBuffers )
+        PaUtil_FreeMemory( self->userBuffers );
+}
+
+/** Configure the associated ALSA pcm.
+ *
+ */
+static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, const PaStreamParameters *params, unsigned long
+        framesPerHostBuffer, int primeBuffers, int callbackMode, double *sampleRate, PaTime *returnedLatency )
+{
+    /*
+    int numPeriods;
+
+    if( getenv("PA_NUMPERIODS") != NULL )
+        numPeriods = atoi( getenv("PA_NUMPERIODS") );
+    else
+        numPeriods = ( (latency * sampleRate) / *framesPerBuffer ) + 1;
+
+    PA_DEBUG(( "latency: %f, rate: %f, framesPerBuffer: %d\n", latency, sampleRate, *framesPerBuffer ));
+    if( numPeriods <= 1 )
+        numPeriods = 2;
+    */
+
+    /* Configuration consists of setting all of ALSA's parameters.
+     * These parameters come in two flavors: hardware parameters
+     * and software paramters.  Hardware parameters will affect
+     * the way the device is initialized, software parameters
+     * affect the way ALSA interacts with me, the user-level client.
+     */
+
+    snd_pcm_hw_params_t *hwParams;
+    snd_pcm_sw_params_t *swParams;
+    PaError result = paNoError;
+    snd_pcm_access_t accessMode, alternateAccessMode;
+    unsigned int numPeriods, minPeriods = 2;
+    int dir = 0;
+    snd_pcm_t *pcm = self->pcm;
+    PaTime latency = params->suggestedLatency;
+    double sr = *sampleRate;
+    *returnedLatency = -1.;
+
+    snd_pcm_hw_params_alloca( &hwParams );
+    snd_pcm_sw_params_alloca( &swParams );
+
+    self->framesPerBuffer = framesPerHostBuffer;
+
+    /* ... fill up the configuration space with all possibile
+     * combinations of parameters this device will accept */
+    ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+
+    ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
+
+    if( self->userInterleaved )
+    {
+        accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+        alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+    }
+    else
+    {
+        accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+        alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+    }
+
+    /* If requested access mode fails, try alternate mode */
+    if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
+    {
+        ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError );
+        /* Flip mode */
+        self->hostInterleaved = !self->userInterleaved;
+    }
+
+    ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
+
+    ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
+    ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
+    /* reject if there's no sample rate within 1% of the one requested */
+    if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
+    {
+        PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));                 
+        PA_ENSURE( paInvalidSampleRate );
+    }
+
+    ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
+
+    /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
+    dir = 0;
+    ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
+    dir = 0;
+    ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError );
+    
+    /* Find an acceptable number of periods */
+    numPeriods = (latency * sr) / self->framesPerBuffer + 1;
+    dir = 0;
+    ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError );
+    /* Minimum of periods should already be 2 */
+    PA_UNLESS( numPeriods >= 2, paInternalError );
+
+    /* Set the parameters! */
+    ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
+
+    /* Latency in seconds, one period is not counted as latency */
+    latency = (numPeriods - 1) * self->framesPerBuffer / sr;
+
+    /* Now software parameters... */
+    ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError );
+
+    ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
+
+    /* Silence buffer in the case of underrun */
+    if( !primeBuffers ) /* XXX: Make sense? */
+    {
+        snd_pcm_uframes_t boundary;
+        ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError );
+    }
+        
+    ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
+
+    /* Set the parameters! */
+    ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError );
+
+    *sampleRate = sr;
+    *returnedLatency = latency;
+
+end:
+    return result;
+
+error:
+    goto end;   /* No particular action */
+}
+
+static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
+        const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
+        PaStreamFlags streamFlags, void *userData )
+{
+    PaError result = paNoError;
+    assert( self );
+
+    memset( self, 0, sizeof (PaAlsaStream) );
+
+    if( NULL != callback )
+    {
+        PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
+                                               &alsaApi->callbackStreamInterface,
+                                               callback, userData );
+        self->callbackMode = 1;
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
+                                               &alsaApi->blockingStreamInterface,
+                                               NULL, userData );
+    }
+
+    self->framesPerUserBuffer = framesPerUserBuffer;
+    self->neverDropInput = streamFlags & paNeverDropInput;
+    /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
+    /*
+    if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
+        self->primeBuffers = 1;
+        */
+    memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
+    memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
+    if( inParams )
+        PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
+    if( outParams )
+        PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );
+
+    assert( self->capture.nfds || self->playback.nfds );
+
+    PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
+                    self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );
+
+    PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
+    InitializeThreading( &self->threading, &self->cpuLoadMeasurer );
+    ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 );
+    ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 );
+    ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 );
+
+error:
+    return result;
+}
+
+/** Free resources associated with stream, and eventually stream itself.
+ *
+ * Frees allocated memory, and terminates individual StreamComponents.
+ */
+static void PaAlsaStream_Terminate( PaAlsaStream *self )
+{
+    assert( self );
+
+    if( self->capture.pcm )
+    {
+        PaAlsaStreamComponent_Terminate( &self->capture );
+    }
+    if( self->playback.pcm )
+    {
+        PaAlsaStreamComponent_Terminate( &self->playback );
+    }
+
+    PaUtil_FreeMemory( self->pfds );
+    ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 );
+    ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 );
+    ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 );
+
+    PaUtil_FreeMemory( self );
+}
+
+/** Calculate polling timeout
+ *
+ * @param frames Time to wait
+ * @return Polling timeout in milliseconds
+ */
+static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
+{
+    assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
+    /* Period in msecs, rounded up */
+    return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
+}
+
+/** Set up ALSA stream parameters.
+ *
+ */
+static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters
+        *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency,
+        unsigned long *maxHostBufferSize )
+{
+    PaError result = paNoError;
+    double realSr = sampleRate;
+
+    if( self->capture.pcm )
+        PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers,
+                    self->callbackMode, &realSr, inputLatency ) );
+    if( self->playback.pcm )
+        PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers,
+                    self->callbackMode, &realSr, outputLatency ) );
+
+    /* Should be exact now */
+    self->streamRepresentation.streamInfo.sampleRate = realSr;
+
+    /* this will cause the two streams to automatically start/stop/prepare in sync.
+     * We only need to execute these operations on one of the pair.
+     * A: We don't want to do this on a blocking stream.
+     */
+    if( self->callbackMode && self->capture.pcm && self->playback.pcm )
+    {
+        int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
+        if( err >= 0 )
+            self->pcmsSynced = 1;
+        else
+            PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
+    }
+
+    /* Frames per host buffer for the stream is set as a compromise between the two directions */
+    framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
+            self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
+    self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer );    /* Period in msecs, rounded up */
+
+    *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0,
+            self->playback.pcm ? self->playback.bufferSize : 0 );
+
+    /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
+    self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000);
+
+    if( self->callbackMode )
+    {
+        /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
+         * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
+         * of host buffer frames with what the user specified */
+        if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
+        {
+            /* self->alignFrames = 1; */
+
+            /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
+             * on block adaption */
+        /*
+            if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
+                        self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
+                self->useBlockAdaption = 1;
+            else
+                self->alignFrames = 1;
+        */
+        }
+    }
+
+error:
+    return result;
+}
+
+/* We need to determine how many frames per host buffer to use.  Our
+ * goals are to provide the best possible performance, but also to
+ * most closely honor the requested latency settings.  Therefore this
+ * decision is based on:
+ *
+ *   - the period sizes that playback and/or capture support.  The
+ *     host buffer size has to be one of these.
+ *   - the number of periods that playback and/or capture support.
+ *
+ * We want to make period_size*(num_periods-1) to be as close as possible
+ * to latency*rate for both playback and capture.
+ *
+ * This is one of those blocks of code that will just take a lot of
+ * refinement to be any good.
+ *
+ * In the full-duplex case it is possible that the routine was unable
+ * to find a number of frames per buffer acceptable to both devices
+ * TODO: Implement an algorithm to find the value closest to acceptance
+ * by both devices, to minimize difference between period sizes?
+ */
+static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters,
+        const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi )
+{
+    PaError result = paNoError;
+    unsigned long framesPerBuffer = 0;
+    int numHostInputChannels = 0, numHostOutputChannels = 0;
+
+    /* XXX: Clean this up */
+    if( stream->capture.pcm )
+    {
+        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device );
+        numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels );
+    }
+    if( stream->playback.pcm )
+    {
+        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device );
+        numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels );
+    }
+
+    if( stream->capture.pcm && stream->playback.pcm )
+    {
+        snd_pcm_uframes_t desiredLatency, e;
+        snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture,
+                          optimalPeriodSize, periodSize;
+        int dir = 0;
+        unsigned int minPeriods = 2;
+
+        snd_pcm_t *pcm;
+        snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture;
+
+        snd_pcm_hw_params_alloca( &hwParamsPlayback );
+        snd_pcm_hw_params_alloca( &hwParamsCapture );
+
+        /* Come up with a common desired latency */
+        pcm = stream->playback.pcm;
+        snd_pcm_hw_params_any( pcm, hwParamsPlayback );
+        ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate );
+        ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ),
+                paBadIODeviceCombination );
+
+        ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
+
+        pcm = stream->capture.pcm;
+        ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError );
+        ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination );
+        ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ),
+                paBadIODeviceCombination );
+
+        ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
+
+        minPeriodSize = PA_MAX( minPlayback, minCapture );
+        maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
+
+        desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
+                * sampleRate);
+        /* Clamp desiredLatency */
+        {
+            snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX;
+            ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError );
+            ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError );
+            maxBufferSize = PA_MIN( maxBufferSize, tmp );
+
+            desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
+        }
+
+        /* Find the closest power of 2 */
+        e = ilogb( minPeriodSize );
+        if( minPeriodSize & (minPeriodSize - 1) )
+            e += 1;
+        periodSize = (snd_pcm_uframes_t) pow( 2, e );
+
+        while( periodSize <= maxPeriodSize )
+        {
+            if( snd_pcm_hw_params_test_period_size( stream->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
+                    snd_pcm_hw_params_test_period_size( stream->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
+                break;  /* Ok! */
+
+            periodSize *= 2;
+        }
+
+        /* 4 periods considered optimal */
+        optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+        optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+
+        /* Find the closest power of 2 */
+        e = ilogb( optimalPeriodSize );
+        if( optimalPeriodSize & (optimalPeriodSize - 1) )
+            e += 1;
+        optimalPeriodSize = (snd_pcm_uframes_t) pow( 2, e );
+
+        while( optimalPeriodSize >= periodSize )
+        {
+            pcm = stream->playback.pcm;
+            if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 )
+                continue;
+
+            pcm = stream->capture.pcm;
+            if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 )
+                break;
+
+            optimalPeriodSize /= 2;
+        }
+
+        if( optimalPeriodSize > periodSize )
+            periodSize = optimalPeriodSize;
+
+        if( periodSize <= maxPeriodSize )
+        {
+            /* Looks good */
+            framesPerBuffer = periodSize;
+        }
+        else
+        {
+            /* Unable to find a common period size, oh well */
+            optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+            optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+
+            /* ConfigureStream should find individual period sizes acceptable for each device */
+            framesPerBuffer = optimalPeriodSize;
+            /* PA_ENSURE( paBadIODeviceCombination ); */
+        }
+    }
+    else    /* half-duplex is a slightly simpler case */
+    {
+        unsigned long bufferSize, channels;
+        snd_pcm_t *pcm;
+        snd_pcm_hw_params_t *hwParams;
+
+        snd_pcm_hw_params_alloca( &hwParams );
+
+        if( stream->capture.pcm )
+        {
+            pcm = stream->capture.pcm;
+            bufferSize = inputParameters->suggestedLatency * sampleRate;
+            channels = numHostInputChannels;
+        }
+        else
+        {
+            pcm = stream->playback.pcm;
+            bufferSize = outputParameters->suggestedLatency * sampleRate;
+            channels = numHostOutputChannels;
+        }
+
+        ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+        ENSURE_( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paInvalidSampleRate );
+        ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination );
+
+        ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
+
+        /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
+           finding a combination of period/buffer size which best fits these constraints */
+        framesPerBuffer = bufferSize / 4;
+        bufferSize += framesPerBuffer;   /* One period doesn't count as latency */
+        ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &bufferSize ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerBuffer, NULL ), paUnanticipatedHostError );
+    }
+
+    PA_UNLESS( framesPerBuffer != 0, paInternalError );
+    *determinedFrames = framesPerBuffer;
+
+error:
+    return result;
+}
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *callback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+    PaAlsaStream *stream = NULL;
+    PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
+    PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
+    int numInputChannels = 0, numOutputChannels = 0;
+    PaTime inputLatency, outputLatency;
+    unsigned long framesPerHostBuffer;
+    PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilBoundedHostBufferSize;
+    unsigned long maxHostBufferSize;    /* Upper bound of host buffer size */
+
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag;
+
+    if( inputParameters )
+    {
+        PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
+
+        numInputChannels = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+    }
+    if( outputParameters )
+    {
+        PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
+
+        numOutputChannels = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+    }
+
+    /* XXX: Why do we support this anyway? */
+    if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL )
+    {
+        PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ ));
+        framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") );
+    }
+    framesPerHostBuffer = framesPerBuffer;
+
+    PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory );
+    PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate,
+                framesPerBuffer, callback, streamFlags, userData ) );
+
+    /* If the number of frames per buffer is unspecified, we have to come up with
+     * one. This is both a blessing and a curse: a blessing because we can optimize
+     * the number to best meet the requirements, but a curse because that's really
+     * hard to do well. For this reason we also support an interface where the user
+     * specifies these by setting environment variables. */
+    if( framesPerBuffer == paFramesPerBufferUnspecified )
+    {
+        PA_ENSURE( DetermineFramesPerBuffer( stream, sampleRate, inputParameters, outputParameters, &framesPerHostBuffer,
+                    hostApi ) );
+    }
+
+    PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerHostBuffer,
+                &inputLatency, &outputLatency, &maxHostBufferSize ) );
+    hostInputSampleFormat = stream->capture.hostSampleFormat;
+    hostOutputSampleFormat = stream->playback.hostSampleFormat;
+
+    if( framesPerHostBuffer != framesPerBuffer )
+    {
+        PA_DEBUG(( "%s: Number of frames per user and host buffer differs\n", __FUNCTION__ ));
+    }
+
+    PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+                    numInputChannels, inputSampleFormat, hostInputSampleFormat,
+                    numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
+                    sampleRate, streamFlags, framesPerBuffer, maxHostBufferSize,
+                    hostBufferSizeMode, callback, userData ) );
+
+    /* Ok, buffer processor is initialized, now we can deduce it's latency */
+    if( numInputChannels > 0 )
+        stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency(
+                &stream->bufferProcessor );
+    if( numOutputChannels > 0 )
+        stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency(
+                &stream->bufferProcessor );
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( stream )
+        PaAlsaStream_Terminate( stream );
+
+    return result;
+}
+
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+    PaAlsaStream_Terminate( stream );
+
+    return result;
+}
+
+static void SilenceBuffer( PaAlsaStream *stream )
+{
+    const snd_pcm_channel_area_t *areas;
+    snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset;
+
+    snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames );
+    snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat );
+    snd_pcm_mmap_commit( stream->playback.pcm, offset, frames );
+}
+
+/** Start/prepare pcm(s) for streaming.
+ *
+ * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply
+ * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and
+ * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will
+ * be started automatically as the user writes to output. 
+ *
+ * The capture pcm, however, will simply be prepared and started.
+ *
+ * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode)
+ */
+static PaError AlsaStart( PaAlsaStream *stream, int priming )
+{
+    PaError result = paNoError;
+
+    if( stream->playback.pcm )
+    {
+        if( stream->callbackMode )
+        {
+            if( !priming )
+            {
+                /* Buffer isn't primed, so prepare and silence */
+                ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+                SilenceBuffer( stream );
+            }
+            ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+        }
+        else
+            ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+    }
+    if( stream->capture.pcm && !stream->pcmsSynced )
+    {
+        ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
+        /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */
+        ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
+    }
+
+end:
+    return result;
+error:
+    goto end;
+}
+
+/** Utility function for determining if pcms are in running state.
+ *
+ */
+static int IsRunning( PaAlsaStream *stream )
+{
+    int result = 0;
+
+    ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); /* Synchronize access to pcm state */
+    if( stream->capture.pcm )
+    {
+        snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm );
+
+        if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN
+                || capture_state == SND_PCM_STATE_DRAINING )
+        {
+            result = 1;
+            goto end;
+        }
+    }
+
+    if( stream->playback.pcm )
+    {
+        snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm );
+
+        if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN
+                || playback_state == SND_PCM_STATE_DRAINING )
+        {
+            result = 1;
+            goto end;
+        }
+    }
+
+end:
+    ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+
+    return result;
+}
+
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    int streamStarted = 0;  /* So we can know wether we need to take the stream down */
+
+    /* Ready the processor */
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+    /* Set now, so we can test for activity further down */
+    stream->isActive = 1;
+
+    if( stream->callbackMode )
+    {
+        int res = 0;
+        PaTime pt = PaUtil_GetTime();
+        struct timespec ts;
+
+        PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) );
+        streamStarted = 1;
+
+        /* Wait for stream to be started */
+        ts.tv_sec = (time_t) floor( pt + 1 );
+        ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000);
+
+        /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking
+         * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely
+         * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */
+        ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
+
+        /* Due to possible spurious wakeups, we enclose in a loop */
+        while( !IsRunning( stream ) && IsStreamActive( s ) && !res )
+        {
+            res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts );
+        }
+        ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+
+        PA_UNLESS( !res || res == ETIMEDOUT, paInternalError );
+        PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt ));
+
+        if( res == ETIMEDOUT )
+        {
+            PA_ENSURE( paTimedOut );
+        }
+    }
+    else
+    {
+        PA_ENSURE( AlsaStart( stream, 0 ) );
+        streamStarted = 1;
+    }
+
+end:
+    return result;
+error:
+    if( streamStarted )
+        AbortStream( stream );
+    stream->isActive = 0;
+    
+    goto end;
+}
+
+static PaError AlsaStop( PaAlsaStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    if( abort )
+    {
+        if( stream->playback.pcm )
+            ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError );
+        if( stream->capture.pcm && !stream->pcmsSynced )
+            ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError );
+
+        PA_DEBUG(( "Dropped frames\n" ));
+    }
+    else
+    {
+        if( stream->playback.pcm )
+            ENSURE_( snd_pcm_drain( stream->playback.pcm ), paUnanticipatedHostError );
+        if( stream->capture.pcm && !stream->pcmsSynced )
+            ENSURE_( snd_pcm_drain( stream->capture.pcm ), paUnanticipatedHostError );
+    }
+
+end:
+    return result;
+error:
+    goto end;
+}
+
+/** Stop or abort stream.
+ *
+ * If a stream is in callback mode we will have to inspect wether the background thread has
+ * finished, or we will have to take it out. In either case we join the thread before
+ * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish
+ * buffers (drain)
+ *
+ * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function
+ */
+static PaError RealStop( PaAlsaStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    /* First deal with the callback thread, cancelling and/or joining
+     * it if necessary
+     */
+    if( stream->callbackMode )
+    {
+        PaError threadRes, watchdogRes;
+        stream->callbackAbort = abort;
+
+        if( !abort )
+        {
+            PA_DEBUG(( "Stopping callback\n" ));
+            stream->callbackStop = 1;
+        }
+        PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) );
+        if( threadRes != paNoError )
+            PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
+        if( watchdogRes != paNoError )
+            PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes ));
+
+        stream->callbackStop = 0;   /* The deed is done */
+        stream->callback_finished = 0;
+    }
+    else
+    {
+        PA_ENSURE( AlsaStop( stream, abort ) );
+    }
+
+    stream->isActive = 0;
+
+end:
+    return result;
+
+error:
+    goto end;
+}
+
+static PaError StopStream( PaStream *s )
+{
+    return RealStop( (PaAlsaStream *) s, 0 );
+}
+
+static PaError AbortStream( PaStream *s )
+{
+    return RealStop( (PaAlsaStream * ) s, 1 );
+}
+
+/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback
+ * returning !paContinue is not considered)
+ *
+ */
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaAlsaStream *stream = (PaAlsaStream *)s;
+
+    /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */
+    return !IsStreamActive( s ) && !stream->callback_finished;
+}
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    return stream->isActive;
+}
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+
+    snd_timestamp_t timestamp;
+    snd_pcm_status_t *status;
+    snd_pcm_status_alloca( &status );
+
+    /* TODO: what if we have both?  does it really matter? */
+
+    /* TODO: if running in callback mode, this will mean
+     * libasound routines are being called from multiple threads.
+     * need to verify that libasound is thread-safe. */
+
+    if( stream->capture.pcm )
+    {
+        snd_pcm_status( stream->capture.pcm, status );
+    }
+    else if( stream->playback.pcm )
+    {
+        snd_pcm_status( stream->playback.pcm, status );
+    }
+
+    snd_pcm_status_get_tstamp( status, &timestamp );
+    return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0;
+}
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate )
+{
+    unsigned long approx = (unsigned long) sampleRate;
+    int dir = 0;
+    double fraction = sampleRate - approx;
+
+    assert( pcm && hwParams );
+
+    if( fraction > 0.0 )
+    {
+        if( fraction > 0.5 )
+        {
+            ++approx;
+            dir = -1;
+        }
+        else
+            dir = 1;
+    }
+
+    return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir );
+}
+
+/* Return exact sample rate in param sampleRate */
+static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate )
+{
+    unsigned int num, den;
+    int err; 
+
+    assert( hwParams );
+
+    err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den );
+    *sampleRate = (double) num / den;
+
+    return err;
+}
+
+/* Utility functions for blocking/callback interfaces */
+
+/* Atomic restart of stream (we don't want the intermediate state visible) */
+static PaError AlsaRestart( PaAlsaStream *stream )
+{
+    PaError result = paNoError;
+
+    ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 );
+    PA_ENSURE( AlsaStop( stream, 0 ) );
+    PA_ENSURE( AlsaStart( stream, 0 ) );
+
+    PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ ));
+
+error:
+    ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+    return result;
+}
+
+/** Recover from xrun state.
+ *
+ */
+static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
+{
+    PaError result = paNoError;
+    snd_pcm_status_t *st;
+    PaTime now = PaUtil_GetTime();
+    snd_timestamp_t t;
+
+    snd_pcm_status_alloca( &st );
+
+    if( self->playback.pcm )
+    {
+        snd_pcm_status( self->playback.pcm, st );
+        if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
+        {
+            snd_pcm_status_get_trigger_tstamp( st, &t );
+            self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+        }
+    }
+    if( self->capture.pcm )
+    {
+        snd_pcm_status( self->capture.pcm, st );
+        if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
+        {
+            snd_pcm_status_get_trigger_tstamp( st, &t );
+            self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+        }
+    }
+
+    PA_ENSURE( AlsaRestart( self ) );
+
+end:
+    return result;
+error:
+    goto end;
+}
+
+/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout.
+ * 
+ */
+static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll )
+{
+    PaError result = paNoError;
+    snd_pcm_sframes_t delay, margin;
+    int err;
+    const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL;
+
+    *continuePoll = 1;
+
+    if( StreamDirection_In == streamDir )
+    {
+        component = &stream->capture;
+        otherComponent = &stream->playback;
+    }
+    else
+    {
+        component = &stream->playback;
+        otherComponent = &stream->capture;
+    }
+
+    /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */
+    if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 )
+    {
+        if( err == -EPIPE )
+        {
+            /* Xrun */
+            *continuePoll = 0;
+            goto error;
+        }
+
+        ENSURE_( err, paUnanticipatedHostError );
+    }
+
+    if( StreamDirection_Out == streamDir )
+    {
+        /* Number of eligible frames before capture overrun */
+        delay = otherComponent->bufferSize - delay;
+    }
+    margin = delay - otherComponent->framesPerBuffer / 2;
+
+    if( margin < 0 )
+    {
+        PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" ));
+        *continuePoll = 0;
+    }
+    else if( margin < otherComponent->framesPerBuffer )
+    {
+        *pollTimeout = CalculatePollTimeout( stream, margin );
+        PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n",
+                    __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout ));
+    }
+
+error:
+    return result;
+}
+
+/* Callback interface */
+
+static void OnExit( void *data )
+{
+    PaAlsaStream *stream = (PaAlsaStream *) data;
+
+    assert( data );
+
+    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+    stream->callback_finished = 1;  /* Let the outside world know stream was stopped in callback */
+    AlsaStop( stream, stream->callbackAbort );
+    stream->callbackAbort = 0;      /* Clear state */
+    
+    PA_DEBUG(( "OnExit: Stoppage\n" ));
+
+    /* Eventually notify user all buffers have played */
+    if( stream->streamRepresentation.streamFinishedCallback )
+        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    stream->isActive = 0;
+}
+
+static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo )
+{
+    snd_pcm_status_t *capture_status, *playback_status;
+    snd_timestamp_t capture_timestamp, playback_timestamp;
+    PaTime capture_time = 0., playback_time = 0.;
+
+    snd_pcm_status_alloca( &capture_status );
+    snd_pcm_status_alloca( &playback_status );
+
+    if( stream->capture.pcm )
+    {
+        snd_pcm_sframes_t capture_delay;
+
+        snd_pcm_status( stream->capture.pcm, capture_status );
+        snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
+
+        capture_time = capture_timestamp.tv_sec +
+            ((PaTime)capture_timestamp.tv_usec / 1000000.0);
+        timeInfo->currentTime = capture_time;
+
+        capture_delay = snd_pcm_status_get_delay( capture_status );
+        timeInfo->inputBufferAdcTime = timeInfo->currentTime -
+            (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
+    }
+    if( stream->playback.pcm )
+    {
+        snd_pcm_sframes_t playback_delay;
+
+        snd_pcm_status( stream->playback.pcm, playback_status );
+        snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
+
+        playback_time = playback_timestamp.tv_sec +
+            ((PaTime)playback_timestamp.tv_usec / 1000000.0);
+
+        if( stream->capture.pcm ) /* Full duplex */
+        {
+            /* Hmm, we have both a playback and a capture timestamp.
+             * Hopefully they are the same... */
+            if( fabs( capture_time - playback_time ) > 0.01 )
+                PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time)));
+        }
+        else
+            timeInfo->currentTime = playback_time;
+
+        playback_delay = snd_pcm_status_get_delay( playback_status );
+        timeInfo->outputBufferDacTime = timeInfo->currentTime +
+            (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
+    }
+}
+
+/** Called after buffer processing is finished.
+ *
+ * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime.
+ *
+ * @param numFrames The number of frames that has been processed
+ * @param xrun Return whether an xrun has occurred
+ */
+static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
+{
+    PaError result = paNoError;
+    int res;
+
+    /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
+     * afterwards
+     */
+    if( !self->ready )
+        goto end;
+
+    res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+    if( res == -EPIPE || res == -ESTRPIPE )
+    {
+        *xrun = 1;
+    }
+    else
+    {
+        ENSURE_( res, paUnanticipatedHostError );
+    }
+
+end:
+error:
+    return result;
+}
+
+/* Extract buffer from channel area */
+static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset )
+{
+    return (unsigned char *) area->addr + (area->first + offset * area->step) / 8;
+}
+
+/** Do necessary adaption between user and host channels.
+ *
+    @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and
+    duplicating mono information if host outputs come in pairs.
+ */
+static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames )
+{
+    PaError result = paNoError;
+    unsigned char *p;
+    int i;
+    int unusedChans = self->numHostChannels - self->numUserChannels;
+    unsigned char *src, *dst;
+    int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0;
+
+    assert( StreamDirection_Out == self->streamDir );
+
+    if( self->hostInterleaved )
+    {
+        int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
+        unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset );
+
+        /* Start after the last user channel */
+        p = buffer + self->numUserChannels * swidth;
+
+        if( convertMono )
+        {
+            /* Convert the last user channel into stereo pair */
+            src = buffer + (self->numUserChannels - 1) * swidth;
+            for( i = 0; i < numFrames; ++i )
+            {
+                dst = src + swidth;
+                memcpy( dst, src, swidth );
+                src += self->numHostChannels * swidth;
+            }
+
+            /* Don't touch the channel we just wrote to */
+            p += swidth;
+            --unusedChans;
+        }
+
+        if( unusedChans > 0 )
+        {
+            /* Silence unused output channels */
+            for( i = 0; i < numFrames; ++i )
+            {
+                memset( p, 0, swidth * unusedChans );
+                p += self->numHostChannels * swidth;
+            }
+        }
+    }
+    else
+    {
+        /* We extract the last user channel */
+        if( convertMono )
+        {
+            ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas +
+                    (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError );
+            --unusedChans;
+        }
+        if( unusedChans > 0 )
+        {
+            snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames,
+                    self->nativeFormat );
+        }
+    }
+
+error:
+    return result;
+}
+
+static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    int xrun = 0;
+
+    if( self->capture.pcm )
+    {
+        PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) );
+    }
+    if( self->playback.pcm )
+    {
+        if( self->playback.numHostChannels > self->playback.numUserChannels )
+            PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) );
+        PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) );
+    }
+
+error:
+    *xrunOccurred = xrun;
+    return result;
+}
+
+/** Update the number of available frames.
+ *
+ */
+static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm );
+    *xrunOccurred = 0;
+
+    if( -EPIPE == framesAvail )
+    {
+        *xrunOccurred = 1;
+        framesAvail = 0;
+    }
+    else
+        ENSURE_( framesAvail, paUnanticipatedHostError );
+
+    *numFrames = framesAvail;
+
+error:
+    return result;
+}
+
+/** Fill in pollfd objects.
+ */
+static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self, struct pollfd *pfds )
+{
+    PaError result = paNoError;
+    int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds );
+    assert( ret == self->nfds );
+
+    self->ready = 0;
+
+    return result;
+}
+
+/** Examine results from poll().
+ *
+ * @param pfds pollfds to inspect
+ * @param shouldPoll Should we continue to poll
+ * @param xrun Has an xrun occurred
+ */
+static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent *self, struct pollfd *pfds, int *shouldPoll, int *xrun )
+{
+    PaError result = paNoError;
+    unsigned short revents;
+
+    ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError );
+    if( revents != 0 )
+    {
+        if( revents & POLLERR )
+        {
+            *xrun = 1;
+        }
+        else
+            self->ready = 1;
+
+        *shouldPoll = 0;
+    }
+
+error:
+    return result;
+}
+
+/** Return the number of available frames for this stream.
+ *
+ * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore
+ * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback.
+ *
+ * @param queryCapture Check available for capture
+ * @param queryPlayback Check available for playback
+ * @param available The returned number of frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long
+        *available, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    unsigned long captureFrames, playbackFrames;
+    *xrunOccurred = 0;
+
+    assert( queryCapture || queryPlayback );
+
+    if( queryCapture )
+    {
+        assert( self->capture.pcm );
+        PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) );
+        if( *xrunOccurred )
+            goto end;
+    }
+    if( queryPlayback )
+    {
+        assert( self->playback.pcm );
+        PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) );
+        if( *xrunOccurred )
+            goto end;
+    }
+
+    if( queryCapture && queryPlayback )
+    {
+        *available = PA_MIN( captureFrames, playbackFrames );
+    }
+    else if( queryCapture )
+    {
+        *available = captureFrames;
+    }
+    else
+    {
+        *available = playbackFrames;
+    }
+
+end:
+error:
+    return result;
+}
+
+/** Wait for and report available buffer space from ALSA.
+ *
+ * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more.
+ * Both of these operations can uncover xrun conditions.
+ *
+ * @concern Xruns Both polling and querying available frames can report an xrun condition.
+ *
+ * @param framesAvail Return the number of available frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */ 
+static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL;
+    int pollTimeout = self->pollTimeout;
+    int xrun = 0;
+
+    assert( self );
+    assert( framesAvail );
+
+    if( !self->callbackMode )
+    {
+        /* In blocking mode we will only wait if necessary */
+        PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL,
+                    framesAvail, &xrun ) );
+        if( xrun )
+        {
+            goto end;
+        }
+
+        if( *framesAvail > 0 )
+        {
+            /* Mark pcms ready from poll */
+            if( self->capture.pcm )
+                self->capture.ready = 1;
+            if( self->playback.pcm )
+                self->playback.ready = 1;
+
+            goto end;
+        }
+    }
+
+    while( pollPlayback || pollCapture )
+    {
+        int totalFds = 0;
+        struct pollfd *capturePfds = NULL, *playbackPfds = NULL;
+
+        pthread_testcancel();
+
+        if( pollCapture )
+        {
+            capturePfds = self->pfds;
+            PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) );
+            totalFds += self->capture.nfds;
+        }
+        if( pollPlayback )
+        {
+            playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0);
+            PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) );
+            totalFds += self->playback.nfds;
+        }
+
+        if( poll( self->pfds, totalFds, pollTimeout ) < 0 )
+        {
+            /*  XXX: Depend on preprocessor condition? */
+            if( errno == EINTR ) {  /* gdb */
+                continue;
+            }
+
+            /* TODO: Add macro for checking system calls */
+            PA_ENSURE( paInternalError );
+        }
+
+        /* check the return status of our pfds */
+        if( pollCapture )
+        {
+            PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) );
+        }
+        if( pollPlayback )
+        {
+            PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) );
+        }
+        if( xrun )
+        {
+            break;
+        }
+
+        /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two.
+         * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will
+         * stop polling.
+         */
+        if( self->capture.pcm && self->playback.pcm )
+        {
+            if( pollCapture && !pollPlayback )
+            {
+                PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) );
+            }
+            else if( pollPlayback && !pollCapture )
+            {
+                PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) );
+            }
+        }
+    }
+
+    if( !xrun )
+    {
+        /* Get the number of available frames for the pcms that are marked ready.
+         * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for
+         * the other direction is returned. This under the assumption that input is dropped earlier if paNeverDropInput
+         * is not specified.
+         */
+        int captureReady = self->capture.pcm ? self->capture.ready : 0,
+            playbackReady = self->playback.pcm ? self->playback.ready : 0;
+        PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) );
+
+        if( self->capture.pcm && self->playback.pcm )
+        {
+            if( !self->playback.ready && !self->neverDropInput )
+            {
+                /* TODO: Drop input */
+            }
+        }
+    }
+
+end:
+error:
+    if( xrun )
+    {
+        /* Recover from the xrun state */
+        PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
+        *framesAvail = 0;
+    }
+    *xrunOccurred = xrun;
+
+    return result;
+}
+
+/** Register per-channel ALSA buffer information with buffer processor.
+ *
+ * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the
+ * number of host and user channels is taken into account.
+ * 
+ * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames.
+ */
+static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp,
+        unsigned long *numFrames, int *xrun )
+{
+    PaError result = paNoError;
+    const snd_pcm_channel_area_t *areas, *area;
+    void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) =
+        StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel;
+    unsigned char *buffer, *p;
+    int i;
+    unsigned long framesAvail;
+
+    /* This _must_ be called before mmap_begin */
+    PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) );
+    if( *xrun )
+    {
+        *numFrames = 0;
+        goto end;
+    }
+
+    ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+
+    if( self->hostInterleaved )
+    {
+        int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
+
+        p = buffer = ExtractAddress( areas, self->offset );
+        for( i = 0; i < self->numUserChannels; ++i )
+        {
+            /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
+            setChannel( bp, i, p, self->numHostChannels );
+            p += swidth;
+        }
+    }
+    else
+    {
+        for( i = 0; i < self->numUserChannels; ++i )
+        {
+            area = areas + i;
+            buffer = ExtractAddress( area, self->offset );
+            setChannel( bp, i, buffer, 1 );
+        }
+    }
+
+    /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
+    self->channelAreas = (snd_pcm_channel_area_t *)areas;
+
+end:
+error:
+    return result;
+}
+
+/** Initiate buffer processing.
+ *
+ * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set.
+ *
+ * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is
+ * calculated.
+ *
+ * @param numFrames On entrance the number of available frames, on exit the number of received frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0;
+    int xrun = 0;
+
+    /* Extract per-channel ALSA buffer pointers and register them with the buffer processor.
+     * It is possible that a direction is not marked ready however, because it is out of sync with the other.
+     */
+    if( self->capture.pcm && self->capture.ready )
+    {
+        captureFrames = *numFrames;
+        PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames, 
+                    &xrun ) );
+    }
+    if( self->playback.pcm && self->playback.ready )
+    {
+        playbackFrames = *numFrames;
+        PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames, 
+                    &xrun ) );
+    }
+    if( xrun )
+    {
+        /* Nothing more to do */
+        assert( 0 == commonFrames );
+        goto end;
+    }
+
+    commonFrames = PA_MIN( captureFrames, playbackFrames );
+    assert( commonFrames <= *numFrames );
+
+    /* Inform PortAudio of the number of frames we got.
+     * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on
+     * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply
+     * discard the excess input or call the callback with paOutputOverflow flagged.
+     */
+    if( self->capture.pcm )
+    {
+        if( self->capture.ready )
+        {
+            PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames );
+        }
+        else
+        {
+            /* We have input underflow */
+            PaUtil_SetNoInput( &self->bufferProcessor );
+        }
+    }
+    if( self->playback.pcm )
+    {
+        if( self->playback.ready )
+        {
+            PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames );
+        }
+        else
+        {
+            /* We have output underflow, but keeping input data (paNeverDropInput) */
+            /* assert( self->neverDropInput ); */
+            PaUtil_SetNoOutput( &self->bufferProcessor );
+        }
+    }
+    
+end:
+    *numFrames = commonFrames;
+error:
+    if( xrun )
+    {
+        PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
+        *numFrames = 0;
+    }
+    *xrunOccurred = xrun;
+
+    return result;
+}
+
+/** Callback thread's function.
+ *
+ * Roughly, the workflow can be described in the following way: The number of available frames that can be processed
+ * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount
+ * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with
+ * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can
+ * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected).
+ */
+static void *CallbackThreadFunc( void *userData )
+{
+    PaError result = paNoError, *pres = NULL;
+    PaAlsaStream *stream = (PaAlsaStream*) userData;
+    PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
+    snd_pcm_sframes_t startThreshold = 0;
+    int callbackResult = paContinue;
+    PaStreamCallbackFlags cbFlags = 0;  /* We might want to keep state across iterations */
+    int streamStarted = 0;
+
+    assert( stream );
+
+    callbackThread_ = pthread_self();
+    /* Execute OnExit when exiting */
+    pthread_cleanup_push( &OnExit, stream );
+
+    /* Not implemented */
+    assert( !stream->primeBuffers );
+
+    /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the
+     * stream is started immediately. The latter involves signaling the waiting main thread.
+     */
+    if( stream->primeBuffers )
+    {
+        snd_pcm_sframes_t avail;
+        
+        if( stream->playback.pcm )
+            ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+        if( stream->capture.pcm && !stream->pcmsSynced )
+            ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
+
+        /* We can't be certain that the whole ring buffer is available for priming, but there should be
+         * at least one period */
+        avail = snd_pcm_avail_update( stream->playback.pcm );
+        startThreshold = avail - (avail % stream->playback.framesPerBuffer);
+        assert( startThreshold >= stream->playback.framesPerBuffer );
+    }
+    else
+    {
+        ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
+        PA_ENSURE( AlsaStart( stream, 0 ) );    /* Buffer will be zeroed */
+        ASSERT_CALL_( pthread_cond_signal( &stream->startCond ), 0 );
+        ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+
+        streamStarted = 1;
+    }
+
+    while( 1 )
+    {
+        unsigned long framesAvail, framesGot;
+        int xrun = 0;
+
+        pthread_testcancel();
+
+        /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively
+         * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output).
+         */
+        if( stream->callbackStop && paContinue == callbackResult )
+        {
+            PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
+            callbackResult = paComplete;
+        }
+
+        if( paContinue != callbackResult )
+        {
+            stream->callbackAbort = (paAbort == callbackResult);
+            if( stream->callbackAbort ||
+                    /** @concern BlockAdaption Go on if adaption buffers are empty */
+                    PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) 
+                goto end;
+
+            PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
+            /* There is still buffered output that needs to be processed */
+        }
+
+        /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have
+         * a number of available frames.
+         */
+        PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+        if( xrun )
+        {
+            assert( 0 == framesAvail );
+            continue;
+
+            /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due
+             * to constant xruns, it might be desirable to notify the user of this.
+             */
+        }
+
+        /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the
+         * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller
+         * portions at a time than is available as a whole. Therefore we should be prepared to process several
+         * chunks successively. The buffers are passed to the PA buffer processor.
+         */
+        while( framesAvail > 0 )
+        {
+            xrun = 0;
+
+            pthread_testcancel();
+
+            /** @concern Xruns Under/overflows are to be reported to the callback */
+            if( stream->underrun > 0.0 )
+            {
+                cbFlags |= paOutputUnderflow;
+                stream->underrun = 0.0;
+            }
+            if( stream->overrun > 0.0 )
+            {
+                cbFlags |= paInputOverflow;
+                stream->overrun = 0.0;
+            }
+            if( stream->capture.pcm && stream->playback.pcm )
+            {
+                /** @concern FullDuplex It's possible that only one direction is being processed to avoid an
+                 * under- or overflow, this should be reported correspondingly */
+                if( !stream->capture.ready )
+                {
+                    cbFlags |= paInputUnderflow;
+                    PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ ));
+                }
+                else if( !stream->playback.ready )
+                {
+                    cbFlags |= paOutputOverflow;
+                    PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ ));
+                }
+            }
+
+            CallbackUpdate( &stream->threading );
+            CalculateTimeInfo( stream, &timeInfo );
+            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
+            cbFlags = 0;
+
+            /* CPU load measurement should include processing activivity external to the stream callback */
+            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+            framesGot = framesAvail;
+            PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+            framesAvail -= framesGot;
+
+            if( framesGot > 0 )
+            {
+                assert( !xrun );
+
+                PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+                PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+            }
+            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
+
+            if( framesGot == 0 )
+            {
+                if( !xrun )
+                    PA_DEBUG(( "%s: Received less frames than reported from ALSA!\n", __FUNCTION__ ));
+
+                /* Go back to polling for more frames */
+                break;
+
+            }
+
+            if( paContinue != callbackResult )
+                break;
+        }
+    }
+
+    /* Match pthread_cleanup_push */
+    pthread_cleanup_pop( 1 );
+
+end:
+    pthread_exit( pres );
+
+error:
+    /* Pass on error code */
+    pres = malloc( sizeof (PaError) );
+    *pres = result;
+    
+    goto end;
+}
+
+/* Blocking interface */
+
+static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    unsigned long framesGot, framesAvail;
+    void *userBuffer;
+    snd_pcm_t *save = stream->playback.pcm;
+
+    assert( stream );
+
+    PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream );
+
+    /* Disregard playback */
+    stream->playback.pcm = NULL;
+
+    if( stream->overrun > 0. )
+    {
+        result = paInputOverflowed;
+        stream->overrun = 0.0;
+    }
+
+    if( stream->capture.userInterleaved )
+        userBuffer = buffer;
+    else
+    {
+        /* Copy channels into local array */
+        userBuffer = stream->capture.userBuffers;
+        memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels );
+    }
+
+    /* Start stream if in prepared state */
+    if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED )
+    {
+        ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
+    }
+
+    while( frames > 0 )
+    {
+        int xrun = 0;
+        PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+        framesGot = PA_MIN( framesAvail, frames );
+
+        PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+        if( framesGot > 0 )
+        {
+            framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
+            PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+            frames -= framesGot;
+        }
+    }
+
+end:
+    stream->playback.pcm = save;
+    return result;
+error:
+    goto end;
+}
+
+static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames )
+{
+    PaError result = paNoError;
+    signed long err;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    snd_pcm_uframes_t framesGot, framesAvail;
+    const void *userBuffer;
+    snd_pcm_t *save = stream->capture.pcm;
+    
+    assert( stream );
+
+    PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream );
+
+    /* Disregard capture */
+    stream->capture.pcm = NULL;
+
+    if( stream->underrun > 0. )
+    {
+        result = paOutputUnderflowed;
+        stream->underrun = 0.0;
+    }
+
+    if( stream->playback.userInterleaved )
+        userBuffer = buffer;
+    else /* Copy channels into local array */
+    {
+        userBuffer = stream->playback.userBuffers;
+        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels );
+    }
+
+    while( frames > 0 )
+    {
+        int xrun = 0;
+        snd_pcm_uframes_t hwAvail;
+
+        PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+        framesGot = PA_MIN( framesAvail, frames );
+
+        PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+        if( framesGot > 0 )
+        {
+            framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
+            PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+            frames -= framesGot;
+        }
+
+        /* Frames residing in buffer */
+        PA_ENSURE( err = GetStreamWriteAvailable( stream ) );
+        framesAvail = err;
+        hwAvail = stream->playback.bufferSize - framesAvail;
+
+        /* Start stream after one period of samples worth */
+        if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED &&
+                hwAvail >= stream->playback.framesPerBuffer )
+        {
+            ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+        }
+    }
+
+end:
+    stream->capture.pcm = save;
+    return result;
+error:
+    goto end;
+}
+
+/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    unsigned long avail;
+    int xrun;
+
+    PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
+    if( xrun )
+    {
+        PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
+        PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
+        if( xrun )
+            PA_ENSURE( paInputOverflowed );
+    }
+
+    return (signed long)avail;
+
+error:
+    return result;
+}
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    unsigned long avail;
+    int xrun;
+
+    PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) );
+    if( xrun )
+    {
+        snd_pcm_sframes_t savail;
+
+        PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
+        savail = snd_pcm_avail_update( stream->playback.pcm );
+
+        /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */
+        ENSURE_( savail, paUnanticipatedHostError );
+
+        avail = (unsigned long) savail;
+    }
+
+    return (signed long)avail;
+
+error:
+    return result;
+}
+
+/* Extensions */
+
+/* Initialize host api specific structure */
+void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
+{
+    info->size = sizeof (PaAlsaStreamInfo);
+    info->hostApiType = paALSA;
+    info->version = 1;
+    info->deviceString = NULL;
+}
+
+void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
+{
+    PaAlsaStream *stream = (PaAlsaStream *) s;
+    stream->threading.rtSched = enable;
+}
+
+void PaAlsa_EnableWatchdog( PaStream *s, int enable )
+{
+    PaAlsaStream *stream = (PaAlsaStream *) s;
+    stream->threading.useWatchdog = enable;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h	(revision 1753)
@@ -0,0 +1,85 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_LINUX_ALSA_H
+#define PA_LINUX_ALSA_H
+
+/*
+ * $Id: pa_linux_alsa.h,v 1.1.2.12 2004/09/25 14:15:25 aknudsen Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * ALSA-specific extensions
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/** @file
+ * ALSA-specific PortAudio API extension header file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct PaAlsaStreamInfo
+{
+    unsigned long size;
+    PaHostApiTypeId hostApiType;
+    unsigned long version;
+
+    const char *deviceString;
+}
+PaAlsaStreamInfo;
+
+void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info );
+
+void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable );
+
+void PaAlsa_EnableWatchdog( PaStream *s, int enable );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_process.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_process.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_process.c	(revision 1753)
@@ -0,0 +1,1777 @@
+/*
+ * $Id: pa_process.c,v 1.1.2.48 2004/12/13 09:48:43 rossbencina Exp $
+ * Portable Audio I/O Library
+ * streamCallback <-> host buffer processing adapter
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+ @brief Buffer Processor implementation.
+    
+ The code in this file is not optimised yet - although it's not clear that
+ it needs to be. there may appear to be redundancies
+ that could be factored into common functions, but the redundanceis are left
+ intentionally as each appearance may have different optimisation possibilities.
+
+ The optimisations which are planned involve only converting data in-place
+ where possible, rather than copying to the temp buffer(s).
+
+ Note that in the extreme case of being able to convert in-place, and there
+ being no conversion necessary there should be some code which short-circuits
+ the operation.
+
+    @todo Consider cache tilings for intereave<->deinterleave.
+
+    @todo implement timeInfo->currentTime int PaUtil_BeginBufferProcessing()
+
+    @todo specify and implement some kind of logical policy for handling the
+        underflow and overflow stream flags when the underflow/overflow overlaps
+        multiple user buffers/callbacks.
+
+	@todo provide support for priming the buffers with data from the callback.
+        The client interface is now implemented through PaUtil_SetNoInput()
+        which sets bp->hostInputChannels[0][0].data to zero. However this is
+        currently only implemented in NonAdaptingProcess(). It shouldn't be
+        needed for AdaptingInputOnlyProcess() (no priming should ever be
+        requested for AdaptingInputOnlyProcess()).
+        Not sure if additional work should be required to make it work with
+        AdaptingOutputOnlyProcess, but it definitely is required for
+        AdaptingProcess.
+
+    @todo implement PaUtil_SetNoOutput for AdaptingProcess
+
+    @todo don't allocate temp buffers for blocking streams unless they are
+        needed. At the moment they are needed, but perhaps for host APIs
+        where the implementation passes a buffer to the host they could be
+        used.
+*/
+
+
+#include <assert.h>
+#include <string.h> /* memset() */
+
+#include "pa_process.h"
+#include "pa_util.h"
+
+
+#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_    1024
+
+#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) )
+
+
+/* greatest common divisor - PGCD in French */
+static unsigned long GCD( unsigned long a, unsigned long b )
+{
+    return (b==0) ? a : GCD( b, a%b);
+}
+
+/* least common multiple - PPCM in French */
+static unsigned long LCM( unsigned long a, unsigned long b )
+{
+    return (a*b) / GCD(a,b);
+}
+
+#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b))
+
+static unsigned long CalculateFrameShift( unsigned long M, unsigned long N )
+{
+    unsigned long result = 0;
+    unsigned long i;
+    unsigned long lcm;
+
+    assert( M > 0 );
+    assert( N > 0 );
+
+    lcm = LCM( M, N );
+    for( i = M; i < lcm; i += M )
+        result = PA_MAX_( result, i % N );
+
+    return result;
+}
+
+
+PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp,
+        int inputChannelCount, PaSampleFormat userInputSampleFormat,
+        PaSampleFormat hostInputSampleFormat,
+        int outputChannelCount, PaSampleFormat userOutputSampleFormat,
+        PaSampleFormat hostOutputSampleFormat,
+        double sampleRate,
+        PaStreamFlags streamFlags,
+        unsigned long framesPerUserBuffer,
+        unsigned long framesPerHostBuffer,
+        PaUtilHostBufferSizeMode hostBufferSizeMode,
+        PaStreamCallback *streamCallback, void *userData )
+{
+    PaError result = paNoError;
+    PaError bytesPerSample;
+    unsigned long tempInputBufferSize, tempOutputBufferSize;
+
+    /* initialize buffer ptrs to zero so they can be freed if necessary in error */
+    bp->tempInputBuffer = 0;
+    bp->tempInputBufferPtrs = 0;
+    bp->tempOutputBuffer = 0;
+    bp->tempOutputBufferPtrs = 0;
+
+    bp->framesPerUserBuffer = framesPerUserBuffer;
+    bp->framesPerHostBuffer = framesPerHostBuffer;
+
+    bp->inputChannelCount = inputChannelCount;
+    bp->outputChannelCount = outputChannelCount;
+
+    bp->hostBufferSizeMode = hostBufferSizeMode;
+
+    bp->hostInputChannels[0] = bp->hostInputChannels[1] = 0;
+    bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0;
+
+    if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */
+    {
+        bp->useNonAdaptingProcess = 1;
+        bp->initialFramesInTempInputBuffer = 0;
+        bp->initialFramesInTempOutputBuffer = 0;
+
+        if( hostBufferSizeMode == paUtilFixedHostBufferSize
+                || hostBufferSizeMode == paUtilBoundedHostBufferSize )
+        {
+            bp->framesPerTempBuffer = framesPerHostBuffer;
+        }
+        else /* unknown host buffer size */
+        {
+             bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_;
+        }
+    }
+    else
+    {
+        bp->framesPerTempBuffer = framesPerUserBuffer;
+
+        if( hostBufferSizeMode == paUtilFixedHostBufferSize
+                && framesPerHostBuffer % framesPerUserBuffer == 0 )
+        {
+            bp->useNonAdaptingProcess = 1;
+            bp->initialFramesInTempInputBuffer = 0;
+            bp->initialFramesInTempOutputBuffer = 0;
+        }
+        else
+        {
+            bp->useNonAdaptingProcess = 0;
+
+            if( inputChannelCount > 0 && outputChannelCount > 0 )
+            {
+                /* full duplex */
+                if( hostBufferSizeMode == paUtilFixedHostBufferSize )
+                {
+                    unsigned long frameShift =
+                        CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer );
+
+                    if( framesPerUserBuffer > framesPerHostBuffer )
+                    {
+                        bp->initialFramesInTempInputBuffer = frameShift;
+                        bp->initialFramesInTempOutputBuffer = 0;
+                    }
+                    else
+                    {
+                        bp->initialFramesInTempInputBuffer = 0;
+                        bp->initialFramesInTempOutputBuffer = frameShift;
+                    }
+                }
+                else /* variable host buffer size, add framesPerUserBuffer latency */
+                {
+                    bp->initialFramesInTempInputBuffer = 0;
+                    bp->initialFramesInTempOutputBuffer = framesPerUserBuffer;
+                }
+            }
+            else
+            {
+                /* half duplex */
+                bp->initialFramesInTempInputBuffer = 0;
+                bp->initialFramesInTempOutputBuffer = 0;
+            }
+        }
+    }
+
+
+    bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
+    bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
+
+    
+    if( inputChannelCount > 0 )
+    {
+        bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat );
+        if( bytesPerSample > 0 )
+        {
+            bp->bytesPerHostInputSample = bytesPerSample;
+        }
+        else
+        {
+            result = bytesPerSample;
+            goto error;
+        }
+
+        bytesPerSample = Pa_GetSampleSize( userInputSampleFormat );
+        if( bytesPerSample > 0 )
+        {
+            bp->bytesPerUserInputSample = bytesPerSample;
+        }
+        else
+        {
+            result = bytesPerSample;
+            goto error;
+        }
+
+        bp->inputConverter =
+            PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags );
+
+        bp->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat );
+            
+        bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1;
+
+
+        tempInputBufferSize =
+            bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount;
+         
+        bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize );
+        if( bp->tempInputBuffer == 0 )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        
+        if( bp->framesInTempInputBuffer > 0 )
+            memset( bp->tempInputBuffer, 0, tempInputBufferSize );
+
+        if( userInputSampleFormat & paNonInterleaved )
+        {
+            bp->tempInputBufferPtrs =
+                (void **)PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount );
+            if( bp->tempInputBufferPtrs == 0 )
+            {
+                result = paInsufficientMemory;
+                goto error;
+            }
+        }
+
+        bp->hostInputChannels[0] = (PaUtilChannelDescriptor*)
+                PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2);
+        if( bp->hostInputChannels[0] == 0 )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount];
+    }
+
+    if( outputChannelCount > 0 )
+    {
+        bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat );
+        if( bytesPerSample > 0 )
+        {
+            bp->bytesPerHostOutputSample = bytesPerSample;
+        }
+        else
+        {
+            result = bytesPerSample;
+            goto error;
+        }
+
+        bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat );
+        if( bytesPerSample > 0 )
+        {
+            bp->bytesPerUserOutputSample = bytesPerSample;
+        }
+        else
+        {
+            result = bytesPerSample;
+            goto error;
+        }
+
+        bp->outputConverter =
+            PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags );
+
+        bp->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat );
+
+        bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1;
+
+        tempOutputBufferSize =
+                bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount;
+
+        bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize );
+        if( bp->tempOutputBuffer == 0 )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        if( bp->framesInTempOutputBuffer > 0 )
+            memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
+        
+        if( userOutputSampleFormat & paNonInterleaved )
+        {
+            bp->tempOutputBufferPtrs =
+                (void **)PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount );
+            if( bp->tempOutputBufferPtrs == 0 )
+            {
+                result = paInsufficientMemory;
+                goto error;
+            }
+        }
+
+        bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*)
+                PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 );
+        if( bp->hostOutputChannels[0] == 0 )
+        {                                                                     
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount];
+    }
+
+    PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator );
+
+    bp->samplePeriod = 1. / sampleRate;
+
+    bp->streamCallback = streamCallback;
+    bp->userData = userData;
+
+    return result;
+
+error:
+    if( bp->tempInputBuffer )
+        PaUtil_FreeMemory( bp->tempInputBuffer );
+
+    if( bp->tempInputBufferPtrs )
+        PaUtil_FreeMemory( bp->tempInputBufferPtrs );
+
+    if( bp->hostInputChannels[0] )
+        PaUtil_FreeMemory( bp->hostInputChannels[0] );
+
+    if( bp->tempOutputBuffer )
+        PaUtil_FreeMemory( bp->tempOutputBuffer );
+
+    if( bp->tempOutputBufferPtrs )
+        PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
+
+    if( bp->hostOutputChannels[0] )
+        PaUtil_FreeMemory( bp->hostOutputChannels[0] );
+
+    return result;
+}
+
+
+void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp )
+{
+    if( bp->tempInputBuffer )
+        PaUtil_FreeMemory( bp->tempInputBuffer );
+
+    if( bp->tempInputBufferPtrs )
+        PaUtil_FreeMemory( bp->tempInputBufferPtrs );
+
+    if( bp->hostInputChannels[0] )
+        PaUtil_FreeMemory( bp->hostInputChannels[0] );
+        
+    if( bp->tempOutputBuffer )
+        PaUtil_FreeMemory( bp->tempOutputBuffer );
+
+    if( bp->tempOutputBufferPtrs )
+        PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
+
+    if( bp->hostOutputChannels[0] )
+        PaUtil_FreeMemory( bp->hostOutputChannels[0] );
+}
+
+
+void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp )
+{
+    unsigned long tempInputBufferSize, tempOutputBufferSize;
+
+    bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
+    bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
+
+    if( bp->framesInTempInputBuffer > 0 )
+    {
+        tempInputBufferSize =
+            bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount;
+        memset( bp->tempInputBuffer, 0, tempInputBufferSize );
+    }
+
+    if( bp->framesInTempOutputBuffer > 0 )
+    {      
+        tempOutputBufferSize =
+            bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount;
+        memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
+    }
+}
+
+
+unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp )
+{
+    return bp->initialFramesInTempInputBuffer;
+}
+
+
+unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp )
+{
+    return bp->initialFramesInTempOutputBuffer;
+}
+
+
+void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp,
+        unsigned long frameCount )
+{
+    if( frameCount == 0 )
+        bp->hostInputFrameCount[0] = bp->framesPerHostBuffer;
+    else
+        bp->hostInputFrameCount[0] = frameCount;
+}
+        
+
+void PaUtil_SetNoInput( PaUtilBufferProcessor* bp )
+{
+    assert( bp->inputChannelCount > 0 );
+
+    bp->hostInputChannels[0][0].data = 0;
+}
+
+
+void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data, unsigned int stride )
+{
+    assert( channel < bp->inputChannelCount );
+    
+    bp->hostInputChannels[0][channel].data = data;
+    bp->hostInputChannels[0][channel].stride = stride;
+}
+
+
+void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp,
+        unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+    unsigned int i;
+    unsigned int channel = firstChannel;
+    unsigned char *p = (unsigned char*)data;
+
+    if( channelCount == 0 )
+        channelCount = bp->inputChannelCount;
+
+    assert( firstChannel < bp->inputChannelCount );
+    assert( firstChannel + channelCount <= bp->inputChannelCount );
+
+    for( i=0; i< channelCount; ++i )
+    {
+        bp->hostInputChannels[0][channel+i].data = p;
+        p += bp->bytesPerHostInputSample;
+        bp->hostInputChannels[0][channel+i].stride = channelCount;
+    }
+}
+
+
+void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data )
+{
+    assert( channel < bp->inputChannelCount );
+    
+    bp->hostInputChannels[0][channel].data = data;
+    bp->hostInputChannels[0][channel].stride = 1;
+}
+
+
+void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp,
+        unsigned long frameCount )
+{
+    bp->hostInputFrameCount[1] = frameCount;
+}
+
+
+void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data, unsigned int stride )
+{
+    assert( channel < bp->inputChannelCount );
+
+    bp->hostInputChannels[1][channel].data = data;
+    bp->hostInputChannels[1][channel].stride = stride;
+}
+
+
+void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp,
+        unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+    unsigned int i;
+    unsigned int channel = firstChannel;
+    unsigned char *p = (unsigned char*)data;
+
+    if( channelCount == 0 )
+        channelCount = bp->inputChannelCount;
+
+    assert( firstChannel < bp->inputChannelCount );
+    assert( firstChannel + channelCount <= bp->inputChannelCount );
+    
+    for( i=0; i< channelCount; ++i )
+    {
+        bp->hostInputChannels[1][channel+i].data = p;
+        p += bp->bytesPerHostInputSample;
+        bp->hostInputChannels[1][channel+i].stride = channelCount;
+    }
+}
+
+        
+void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data )
+{
+    assert( channel < bp->inputChannelCount );
+    
+    bp->hostInputChannels[1][channel].data = data;
+    bp->hostInputChannels[1][channel].stride = 1;
+}
+
+
+void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp,
+        unsigned long frameCount )
+{
+    if( frameCount == 0 )
+        bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer;
+    else
+        bp->hostOutputFrameCount[0] = frameCount;
+}
+
+
+void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp )
+{
+    assert( bp->outputChannelCount > 0 );
+
+    bp->hostOutputChannels[0][0].data = 0;
+}
+
+
+void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data, unsigned int stride )
+{
+    assert( channel < bp->outputChannelCount );
+    
+    bp->hostOutputChannels[0][channel].data = data;
+    bp->hostOutputChannels[0][channel].stride = stride;
+}
+
+
+void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp,
+        unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+    unsigned int i;
+    unsigned int channel = firstChannel;
+    unsigned char *p = (unsigned char*)data;
+
+    if( channelCount == 0 )
+        channelCount = bp->outputChannelCount;
+
+    assert( firstChannel < bp->outputChannelCount );
+    assert( firstChannel + channelCount <= bp->outputChannelCount );
+    
+    for( i=0; i< channelCount; ++i )
+    {
+        bp->hostOutputChannels[0][channel+i].data = p;
+        p += bp->bytesPerHostOutputSample;
+        bp->hostOutputChannels[0][channel+i].stride = channelCount;
+    }
+}
+
+
+void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data )
+{
+    assert( channel < bp->outputChannelCount );
+
+    bp->hostOutputChannels[0][channel].data = data;
+    bp->hostOutputChannels[0][channel].stride = 1;
+}
+
+
+void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp,
+        unsigned long frameCount )
+{
+    bp->hostOutputFrameCount[1] = frameCount;
+}
+
+
+void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data, unsigned int stride )
+{
+    assert( channel < bp->outputChannelCount );
+
+    bp->hostOutputChannels[1][channel].data = data;
+    bp->hostOutputChannels[1][channel].stride = stride;
+}
+
+
+void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp,
+        unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+    unsigned int i;
+    unsigned int channel = firstChannel;
+    unsigned char *p = (unsigned char*)data;
+
+    if( channelCount == 0 )
+        channelCount = bp->outputChannelCount;
+
+    assert( firstChannel < bp->outputChannelCount );
+    assert( firstChannel + channelCount <= bp->outputChannelCount );
+    
+    for( i=0; i< channelCount; ++i )
+    {
+        bp->hostOutputChannels[1][channel+i].data = p;
+        p += bp->bytesPerHostOutputSample;
+        bp->hostOutputChannels[1][channel+i].stride = channelCount;
+    }
+}
+
+        
+void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data )
+{
+    assert( channel < bp->outputChannelCount );
+    
+    bp->hostOutputChannels[1][channel].data = data;
+    bp->hostOutputChannels[1][channel].stride = 1;
+}
+
+
+void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp,
+        PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags )
+{
+    bp->timeInfo = timeInfo;
+
+    /* the first streamCallback will be called to process samples which are
+        currently in the input buffer before the ones starting at the timeInfo time */
+        
+    bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod;
+    
+    bp->timeInfo->currentTime = 0; /** FIXME: @todo time info currentTime not implemented */
+
+    /* the first streamCallback will be called to generate samples which will be
+        outputted after the frames currently in the output buffer have been
+        outputted. */
+    bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod;
+
+    bp->callbackStatusFlags = callbackStatusFlags;
+
+    bp->hostInputFrameCount[1] = 0;
+    bp->hostOutputFrameCount[1] = 0;
+}
+
+
+/*
+    NonAdaptingProcess() is a simple buffer copying adaptor that can handle
+    both full and half duplex copies. It processes framesToProcess frames,
+    broken into blocks bp->framesPerTempBuffer long.
+    This routine can be used when the streamCallback doesn't care what length
+    the buffers are, or when framesToProcess is an integer multiple of
+    bp->framesPerTempBuffer, in which case streamCallback will always be called
+    with bp->framesPerTempBuffer samples.
+*/
+static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp,
+        int *streamCallbackResult,
+        PaUtilChannelDescriptor *hostInputChannels,
+        PaUtilChannelDescriptor *hostOutputChannels,
+        unsigned long framesToProcess )
+{
+    void *userInput, *userOutput;
+    unsigned char *srcBytePtr, *destBytePtr;
+    unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+    unsigned long frameCount;
+    unsigned long framesToGo = framesToProcess;
+    unsigned long framesProcessed = 0;
+
+
+    if( *streamCallbackResult == paContinue )
+    {
+        do
+        {
+            frameCount = PA_MIN_( bp->framesPerTempBuffer, framesToGo );
+
+            /* configure user input buffer and convert input data (host -> user) */
+            if( bp->inputChannelCount == 0 )
+            {
+                /* no input */
+                userInput = 0;
+            }
+            else /* there are input channels */
+            {
+                /*
+                    could use more elaborate logic here and sometimes process
+                    buffers in-place.
+                */
+            
+                destBytePtr = (unsigned char *)bp->tempInputBuffer;
+
+                if( bp->userInputIsInterleaved )
+                {
+                    destSampleStrideSamples = bp->inputChannelCount;
+                    destChannelStrideBytes = bp->bytesPerUserInputSample;
+                    userInput = bp->tempInputBuffer;
+                }
+                else /* user input is not interleaved */
+                {
+                    destSampleStrideSamples = 1;
+                    destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample;
+
+                    /* setup non-interleaved ptrs */
+                    for( i=0; i<bp->inputChannelCount; ++i )
+                    {
+                        bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+                            i * bp->bytesPerUserInputSample * frameCount;
+                    }
+                
+                    userInput = bp->tempInputBufferPtrs;
+                }
+
+                if( !bp->hostInputChannels[0][0].data )
+                {
+                    /* no input was supplied (see PaUtil_SetNoInput), so
+                        zero the input buffer */
+
+                    for( i=0; i<bp->inputChannelCount; ++i )
+                    {
+                        bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount );
+                        destBytePtr += destChannelStrideBytes;  /* skip to next destination channel */
+                    }
+                }
+                else
+                {
+                    for( i=0; i<bp->inputChannelCount; ++i )
+                    {
+                        bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                                hostInputChannels[i].data,
+                                                hostInputChannels[i].stride,
+                                                frameCount, &bp->ditherGenerator );
+
+                        destBytePtr += destChannelStrideBytes;  /* skip to next destination channel */
+
+                        /* advance src ptr for next iteration */
+                        hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                                frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+                    }
+                }
+            }
+
+            /* configure user output buffer */
+            if( bp->outputChannelCount == 0 )
+            {
+                /* no output */
+                userOutput = 0;
+            }
+            else /* there are output channels */
+            {
+                if( bp->userOutputIsInterleaved )
+                {
+                    userOutput = bp->tempOutputBuffer;
+                }
+                else /* user output is not interleaved */
+                {
+                    for( i = 0; i < bp->outputChannelCount; ++i )
+                    {
+                        bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+                            i * bp->bytesPerUserOutputSample * frameCount;
+                    }
+
+                    userOutput = bp->tempOutputBufferPtrs;
+                }
+            }
+        
+            *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+                    frameCount, bp->timeInfo, bp->callbackStatusFlags, bp->userData );
+
+            if( *streamCallbackResult == paAbort )
+            {
+                /* callback returned paAbort, don't advance framesProcessed
+                        and framesToGo, they will be handled below */
+            }
+            else
+            {
+                bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
+                bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod;
+
+                /* convert output data (user -> host) */
+                
+                if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
+                {
+                    /*
+                        could use more elaborate logic here and sometimes process
+                        buffers in-place.
+                    */
+            
+                    srcBytePtr = (unsigned char *)bp->tempOutputBuffer;
+
+                    if( bp->userOutputIsInterleaved )
+                    {
+                        srcSampleStrideSamples = bp->outputChannelCount;
+                        srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+                    }
+                    else /* user output is not interleaved */
+                    {
+                        srcSampleStrideSamples = 1;
+                        srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample;
+                    }
+
+                    for( i=0; i<bp->outputChannelCount; ++i )
+                    {
+                        bp->outputConverter(    hostOutputChannels[i].data,
+                                                hostOutputChannels[i].stride,
+                                                srcBytePtr, srcSampleStrideSamples,
+                                                frameCount, &bp->ditherGenerator );
+
+                        srcBytePtr += srcChannelStrideBytes;  /* skip to next source channel */
+
+                        /* advance dest ptr for next iteration */
+                        hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                                frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+                    }
+                }
+             
+                framesProcessed += frameCount;
+
+                framesToGo -= frameCount;
+            }
+        }
+        while( framesToGo > 0  && *streamCallbackResult == paContinue );
+    }
+
+    if( framesToGo > 0 )
+    {
+        /* zero any remaining frames output. There will only be remaining frames
+            if the callback has returned paComplete or paAbort */
+
+        frameCount = framesToGo;
+
+        if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
+        {
+            for( i=0; i<bp->outputChannelCount; ++i )
+            {
+                bp->outputZeroer(   hostOutputChannels[i].data,
+                                    hostOutputChannels[i].stride,
+                                    frameCount );
+
+                /* advance dest ptr for next iteration */
+                hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                        frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+            }
+        }
+
+        framesProcessed += frameCount;
+    }
+
+    return framesProcessed;
+}
+
+
+/*
+    AdaptingInputOnlyProcess() is a half duplex input buffer processor. It
+    converts data from the input buffers into the temporary input buffer,
+    when the temporary input buffer is full, it calls the streamCallback.
+*/
+static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp,
+        int *streamCallbackResult,
+        PaUtilChannelDescriptor *hostInputChannels,
+        unsigned long framesToProcess )
+{
+    void *userInput, *userOutput;
+    unsigned char *destBytePtr;
+    unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+    unsigned long frameCount;
+    unsigned long framesToGo = framesToProcess;
+    unsigned long framesProcessed = 0;
+    
+    userOutput = 0;
+
+    do
+    {
+        frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer )
+                ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer )
+                : framesToGo;
+
+        /* convert frameCount samples into temp buffer */
+
+        if( bp->userInputIsInterleaved )
+        {
+            destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+                    bp->bytesPerUserInputSample * bp->inputChannelCount *
+                    bp->framesInTempInputBuffer;
+                      
+            destSampleStrideSamples = bp->inputChannelCount;
+            destChannelStrideBytes = bp->bytesPerUserInputSample;
+
+            userInput = bp->tempInputBuffer;
+        }
+        else /* user input is not interleaved */
+        {
+            destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+                    bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
+
+            destSampleStrideSamples = 1;
+            destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+
+            /* setup non-interleaved ptrs */
+            for( i=0; i<bp->inputChannelCount; ++i )
+            {
+                bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+                    i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer;
+            }
+                    
+            userInput = bp->tempInputBufferPtrs;
+        }
+
+        for( i=0; i<bp->inputChannelCount; ++i )
+        {
+            bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                    hostInputChannels[i].data,
+                                    hostInputChannels[i].stride,
+                                    frameCount, &bp->ditherGenerator );
+
+            destBytePtr += destChannelStrideBytes;  /* skip to next destination channel */
+
+            /* advance src ptr for next iteration */
+            hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                    frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+        }
+
+        bp->framesInTempInputBuffer += frameCount;
+
+        if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer )
+        {
+            /**
+            @todo (non-critical optimisation)
+            The conditional below implements the continue/complete/abort mechanism
+            simply by continuing on iterating through the input buffer, but not
+            passing the data to the callback. With care, the outer loop could be
+            terminated earlier, thus some unneeded conversion cycles would be
+            saved.
+            */
+            if( *streamCallbackResult == paContinue )
+            {
+                bp->timeInfo->outputBufferDacTime = 0;
+
+                *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+                        bp->framesPerUserBuffer, bp->timeInfo,
+                        bp->callbackStatusFlags, bp->userData );
+
+                bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
+            }
+            
+            bp->framesInTempInputBuffer = 0;
+        }
+
+        framesProcessed += frameCount;
+
+        framesToGo -= frameCount;
+    }while( framesToGo > 0 );
+
+    return framesProcessed;
+}
+
+
+/*
+    AdaptingOutputOnlyProcess() is a half duplex output buffer processor.
+    It converts data from the temporary output buffer, to the output buffers,
+    when the temporary output buffer is empty, it calls the streamCallback.
+*/
+static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp,
+        int *streamCallbackResult,
+        PaUtilChannelDescriptor *hostOutputChannels,
+        unsigned long framesToProcess )
+{
+    void *userInput, *userOutput;
+    unsigned char *srcBytePtr;
+    unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int srcChannelStrideBytes;  /* stride from one channel to the next, in bytes */
+    unsigned int i;
+    unsigned long frameCount;
+    unsigned long framesToGo = framesToProcess;
+    unsigned long framesProcessed = 0;
+
+    do
+    {
+        if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue )
+        {
+            userInput = 0;
+
+            /* setup userOutput */
+            if( bp->userOutputIsInterleaved )
+            {
+                userOutput = bp->tempOutputBuffer;
+            }
+            else /* user output is not interleaved */
+            {
+                for( i = 0; i < bp->outputChannelCount; ++i )
+                {
+                    bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+                            i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+                }
+
+                userOutput = bp->tempOutputBufferPtrs;
+            }
+
+            bp->timeInfo->inputBufferAdcTime = 0;
+            
+            *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+                    bp->framesPerUserBuffer, bp->timeInfo,
+                    bp->callbackStatusFlags, bp->userData );
+
+            if( *streamCallbackResult == paAbort )
+            {
+                /* if the callback returned paAbort, we disregard its output */
+            }
+            else
+            {
+                bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
+
+                bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
+            }
+        }
+
+        if( bp->framesInTempOutputBuffer > 0 )
+        {
+            /* convert frameCount frames from user buffer to host buffer */
+
+            frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo );
+
+            if( bp->userOutputIsInterleaved )
+            {
+                srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+                        bp->bytesPerUserOutputSample * bp->outputChannelCount *
+                        (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+                srcSampleStrideSamples = bp->outputChannelCount;
+                srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+            }
+            else /* user output is not interleaved */
+            {
+                srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+                        bp->bytesPerUserOutputSample *
+                        (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+                            
+                srcSampleStrideSamples = 1;
+                srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+            }
+
+            for( i=0; i<bp->outputChannelCount; ++i )
+            {
+                bp->outputConverter(    hostOutputChannels[i].data,
+                                        hostOutputChannels[i].stride,
+                                        srcBytePtr, srcSampleStrideSamples,
+                                        frameCount, &bp->ditherGenerator );
+
+                srcBytePtr += srcChannelStrideBytes;  /* skip to next source channel */
+
+                /* advance dest ptr for next iteration */
+                hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                        frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+            }
+
+            bp->framesInTempOutputBuffer -= frameCount;
+        }
+        else
+        {
+            /* no more user data is available because the callback has returned
+                paComplete or paAbort. Fill the remainder of the host buffer
+                with zeros.
+            */
+
+            frameCount = framesToGo;
+
+            for( i=0; i<bp->outputChannelCount; ++i )
+            {
+                bp->outputZeroer(   hostOutputChannels[i].data,
+                                    hostOutputChannels[i].stride,
+                                    frameCount );
+
+                /* advance dest ptr for next iteration */
+                hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                        frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+            }
+        }
+        
+        framesProcessed += frameCount;
+        
+        framesToGo -= frameCount;
+
+    }while( framesToGo > 0 );
+
+    return framesProcessed;
+}
+
+/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from
+	tempOutputBuffer to hostOutputChannels. This includes data conversion
+	and interleaving. 
+*/
+static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp)
+{
+    unsigned long maxFramesToCopy;
+    PaUtilChannelDescriptor *hostOutputChannels;
+    unsigned int frameCount;
+    unsigned char *srcBytePtr;
+    unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+
+     /* copy frames from user to host output buffers */
+     while( bp->framesInTempOutputBuffer > 0 &&
+             ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) )
+     {
+         maxFramesToCopy = bp->framesInTempOutputBuffer;
+
+         /* select the output buffer set (1st or 2nd) */
+         if( bp->hostOutputFrameCount[0] > 0 )
+         {
+             hostOutputChannels = bp->hostOutputChannels[0];
+             frameCount = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy );
+         }
+         else
+         {
+             hostOutputChannels = bp->hostOutputChannels[1];
+             frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy );
+         }
+
+         if( bp->userOutputIsInterleaved )
+         {
+             srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+                     bp->bytesPerUserOutputSample * bp->outputChannelCount *
+                     (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+                         
+             srcSampleStrideSamples = bp->outputChannelCount;
+             srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+         }
+         else /* user output is not interleaved */
+         {
+             srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+                     bp->bytesPerUserOutputSample *
+                     (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+             srcSampleStrideSamples = 1;
+             srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+         }
+
+         for( i=0; i<bp->outputChannelCount; ++i )
+         {
+             bp->outputConverter(    hostOutputChannels[i].data,
+                                     hostOutputChannels[i].stride,
+                                     srcBytePtr, srcSampleStrideSamples,
+                                     frameCount, &bp->ditherGenerator );
+
+             srcBytePtr += srcChannelStrideBytes;  /* skip to next source channel */
+
+             /* advance dest ptr for next iteration */
+             hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                     frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+         }
+
+         if( bp->hostOutputFrameCount[0] > 0 )
+             bp->hostOutputFrameCount[0] -= frameCount;
+         else
+             bp->hostOutputFrameCount[1] -= frameCount;
+
+         bp->framesInTempOutputBuffer -= frameCount;
+     }
+}
+
+/*
+    AdaptingProcess is a full duplex adapting buffer processor. It converts
+    data from the temporary output buffer into the host output buffers, then
+    from the host input buffers into the temporary input buffers. Calling the
+    streamCallback when necessary.
+    When processPartialUserBuffers is 0, all available input data will be
+    consumed and all available output space will be filled. When
+    processPartialUserBuffers is non-zero, as many full user buffers
+    as possible will be processed, but partial buffers will not be consumed.
+*/
+static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp,
+        int *streamCallbackResult, int processPartialUserBuffers )
+{
+    void *userInput, *userOutput;
+    unsigned long framesProcessed = 0;
+    unsigned long framesAvailable;
+    unsigned long endProcessingMinFrameCount;
+    unsigned long maxFramesToCopy;
+    PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels;
+    unsigned int frameCount;
+    unsigned char *destBytePtr;
+    unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i, j;
+ 
+
+    framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */
+
+    if( processPartialUserBuffers )
+        endProcessingMinFrameCount = 0;
+    else
+        endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1);
+
+    /* Fill host output with remaining frames in user output (tempOutputBuffer) */
+    CopyTempOutputBuffersToHostOutputBuffers( bp );		  	
+
+    while( framesAvailable > endProcessingMinFrameCount ) 
+    {
+
+        if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue )
+        {
+            /* the callback will not be called any more, so zero what remains
+                of the host output buffers */
+
+            for( i=0; i<2; ++i )
+            {
+                frameCount = bp->hostOutputFrameCount[i];
+                if( frameCount > 0 )
+                {
+                    hostOutputChannels = bp->hostOutputChannels[i];
+                    
+                    for( j=0; j<bp->outputChannelCount; ++j )
+                    {
+                        bp->outputZeroer(   hostOutputChannels[j].data,
+                                            hostOutputChannels[j].stride,
+                                            frameCount );
+
+                        /* advance dest ptr for next iteration  */
+                        hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) +
+                                frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample;
+                    }
+                    bp->hostOutputFrameCount[i] = 0;
+                }
+            }
+        }          
+
+
+        /* copy frames from host to user input buffers */
+        while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer &&
+                ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) )
+        {
+            maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer;
+
+            /* select the input buffer set (1st or 2nd) */
+            if( bp->hostInputFrameCount[0] > 0 )
+            {
+                hostInputChannels = bp->hostInputChannels[0];
+                frameCount = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy );
+            }
+            else
+            {
+                hostInputChannels = bp->hostInputChannels[1];
+                frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy );
+            }
+
+            /* configure conversion destination pointers */
+            if( bp->userInputIsInterleaved )
+            {
+                destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+                        bp->bytesPerUserInputSample * bp->inputChannelCount *
+                        bp->framesInTempInputBuffer;
+
+                destSampleStrideSamples = bp->inputChannelCount;
+                destChannelStrideBytes = bp->bytesPerUserInputSample;
+            }
+            else /* user input is not interleaved */
+            {
+                destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+                        bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
+
+                destSampleStrideSamples = 1;
+                destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+            }
+
+            for( i=0; i<bp->inputChannelCount; ++i )
+            {
+                bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                        hostInputChannels[i].data,
+                                        hostInputChannels[i].stride,
+                                        frameCount, &bp->ditherGenerator );
+
+                destBytePtr += destChannelStrideBytes;  /* skip to next destination channel */
+
+                /* advance src ptr for next iteration */
+                hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                        frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+            }
+
+            if( bp->hostInputFrameCount[0] > 0 )
+                bp->hostInputFrameCount[0] -= frameCount;
+            else
+                bp->hostInputFrameCount[1] -= frameCount;
+                
+            bp->framesInTempInputBuffer += frameCount;
+
+            /* update framesAvailable and framesProcessed based on input consumed
+                unless something is very wrong this will also correspond to the
+                amount of output generated */
+            framesAvailable -= frameCount;
+            framesProcessed += frameCount;
+        }
+
+        /* call streamCallback */
+        if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer &&
+            bp->framesInTempOutputBuffer == 0 )
+        {
+            if( *streamCallbackResult == paContinue )
+            {
+                /* setup userInput */
+                if( bp->userInputIsInterleaved )
+                {
+                    userInput = bp->tempInputBuffer;
+                }
+                else /* user input is not interleaved */
+                {
+                    for( i = 0; i < bp->inputChannelCount; ++i )
+                    {
+                        bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+                                i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+                    }
+
+                    userInput = bp->tempInputBufferPtrs;
+                }
+
+                /* setup userOutput */
+                if( bp->userOutputIsInterleaved )
+                {
+                    userOutput = bp->tempOutputBuffer;
+                }
+                else /* user output is not interleaved */
+                {
+                    for( i = 0; i < bp->outputChannelCount; ++i )
+                    {
+                        bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+                                i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+                    }
+
+                    userOutput = bp->tempOutputBufferPtrs;
+                }
+
+                /* call streamCallback */
+
+                *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+                        bp->framesPerUserBuffer, bp->timeInfo,
+                        bp->callbackStatusFlags, bp->userData );
+
+                bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod;
+                bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
+
+                bp->framesInTempInputBuffer = 0;
+
+                if( *streamCallbackResult == paAbort )
+                    bp->framesInTempOutputBuffer = 0;
+                else
+                    bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
+            }
+            else
+            {
+                /* paComplete or paAbort has already been called. */
+
+                bp->framesInTempInputBuffer = 0;
+            }
+        }
+
+        /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels) 
+           Means to process the user output provided by the callback. Has to be called after
+            each callback. */
+        CopyTempOutputBuffersToHostOutputBuffers( bp );		  	
+
+    }
+    
+    return framesProcessed;
+}
+
+
+unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult )
+{
+    unsigned long framesToProcess, framesToGo;
+    unsigned long framesProcessed = 0;
+    
+    if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0
+            && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */
+            && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ )
+    {
+        assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) ==
+                (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) );
+    }
+
+    assert( *streamCallbackResult == paContinue
+            || *streamCallbackResult == paComplete
+            || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */
+
+    if( bp->useNonAdaptingProcess )
+    {
+        if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
+        {
+            /* full duplex non-adapting process, splice buffers if they are
+                different lengths */
+
+            framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */
+
+            do{
+                unsigned long noInputInputFrameCount;
+                unsigned long *hostInputFrameCount;
+                PaUtilChannelDescriptor *hostInputChannels;
+                unsigned long noOutputOutputFrameCount;
+                unsigned long *hostOutputFrameCount;
+                PaUtilChannelDescriptor *hostOutputChannels;
+                unsigned long framesProcessedThisIteration;
+
+                if( !bp->hostInputChannels[0][0].data )
+                {
+                    /* no input was supplied (see PaUtil_SetNoInput)
+                        NonAdaptingProcess knows how to deal with this
+                    */
+                    noInputInputFrameCount = framesToGo;
+                    hostInputFrameCount = &noInputInputFrameCount;
+                    hostInputChannels = 0;
+                }
+                else if( bp->hostInputFrameCount[0] != 0 )
+                {
+                    hostInputFrameCount = &bp->hostInputFrameCount[0];
+                    hostInputChannels = bp->hostInputChannels[0];
+                }
+                else
+                {
+                    hostInputFrameCount = &bp->hostInputFrameCount[1];
+                    hostInputChannels = bp->hostInputChannels[1];
+                }
+
+                if( !bp->hostOutputChannels[0][0].data )
+                {
+                    /* no output was supplied (see PaUtil_SetNoOutput)
+                        NonAdaptingProcess knows how to deal with this
+                    */
+                    noOutputOutputFrameCount = framesToGo;
+                    hostOutputFrameCount = &noOutputOutputFrameCount;
+                    hostOutputChannels = 0;
+                }
+                if( bp->hostOutputFrameCount[0] != 0 )
+                {
+                    hostOutputFrameCount = &bp->hostOutputFrameCount[0];
+                    hostOutputChannels = bp->hostOutputChannels[0];
+                }
+                else
+                {
+                    hostOutputFrameCount = &bp->hostOutputFrameCount[1];
+                    hostOutputChannels = bp->hostOutputChannels[1];
+                }
+
+                framesToProcess = PA_MIN_( *hostInputFrameCount,
+                                       *hostOutputFrameCount );
+
+                assert( framesToProcess != 0 );
+                
+                framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult,
+                        hostInputChannels, hostOutputChannels,
+                        framesToProcess );                                       
+
+                *hostInputFrameCount -= framesProcessedThisIteration;
+                *hostOutputFrameCount -= framesProcessedThisIteration;
+
+                framesProcessed += framesProcessedThisIteration;
+                framesToGo -= framesProcessedThisIteration;
+                
+            }while( framesToGo > 0 );
+        }
+        else
+        {
+            /* half duplex non-adapting process, just process 1st and 2nd buffer */
+            /* process first buffer */
+
+            framesToProcess = (bp->inputChannelCount != 0)
+                            ? bp->hostInputFrameCount[0]
+                            : bp->hostOutputFrameCount[0];
+
+            framesProcessed = NonAdaptingProcess( bp, streamCallbackResult,
+                        bp->hostInputChannels[0], bp->hostOutputChannels[0],
+                        framesToProcess );
+
+            /* process second buffer if provided */
+    
+            framesToProcess = (bp->inputChannelCount != 0)
+                            ? bp->hostInputFrameCount[1]
+                            : bp->hostOutputFrameCount[1];
+            if( framesToProcess > 0 )
+            {
+                framesProcessed += NonAdaptingProcess( bp, streamCallbackResult,
+                    bp->hostInputChannels[1], bp->hostOutputChannels[1],
+                    framesToProcess );
+            }
+        }
+    }
+    else /* block adaption necessary*/
+    {
+
+        if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
+        {
+            /* full duplex */
+            
+            if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed  )
+            {
+                framesProcessed = AdaptingProcess( bp, streamCallbackResult,
+                        0 /* dont process partial user buffers */ );
+            }
+            else
+            {
+                framesProcessed = AdaptingProcess( bp, streamCallbackResult,
+                        1 /* process partial user buffers */ );
+            }
+        }
+        else if( bp->inputChannelCount != 0 )
+        {
+            /* input only */
+            framesToProcess = bp->hostInputFrameCount[0];
+
+            framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult,
+                        bp->hostInputChannels[0], framesToProcess );
+
+            framesToProcess = bp->hostInputFrameCount[1];
+            if( framesToProcess > 0 )
+            {
+                framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult,
+                        bp->hostInputChannels[1], framesToProcess );
+            }
+        }
+        else
+        {
+            /* output only */
+            framesToProcess = bp->hostOutputFrameCount[0];
+
+            framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult,
+                        bp->hostOutputChannels[0], framesToProcess );
+
+            framesToProcess = bp->hostOutputFrameCount[1];
+            if( framesToProcess > 0 )
+            {
+                framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult,
+                        bp->hostOutputChannels[1], framesToProcess );
+            }
+        }
+    }
+
+    return framesProcessed;
+}
+
+
+int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp )
+{
+    return (bp->framesInTempOutputBuffer) ? 0 : 1;
+} 
+
+
+unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp,
+        void **buffer, unsigned long frameCount )
+{
+    PaUtilChannelDescriptor *hostInputChannels;
+    unsigned int framesToCopy;
+    unsigned char *destBytePtr;
+    void **nonInterleavedDestPtrs;
+    unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+
+    hostInputChannels = bp->hostInputChannels[0];
+    framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount );
+
+    if( bp->userInputIsInterleaved )
+    {
+        destBytePtr = (unsigned char*)*buffer;
+        
+        destSampleStrideSamples = bp->inputChannelCount;
+        destChannelStrideBytes = bp->bytesPerUserInputSample;
+
+        for( i=0; i<bp->inputChannelCount; ++i )
+        {
+            bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                hostInputChannels[i].data,
+                                hostInputChannels[i].stride,
+                                framesToCopy, &bp->ditherGenerator );
+
+            destBytePtr += destChannelStrideBytes;  /* skip to next source channel */
+
+            /* advance dest ptr for next iteration */
+            hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                    framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+        }
+
+        /* advance callers dest pointer (buffer) */
+        *buffer = ((unsigned char *)*buffer) +
+                framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample;
+    }
+    else
+    {
+        /* user input is not interleaved */
+        
+        nonInterleavedDestPtrs = (void**)*buffer;
+
+        destSampleStrideSamples = 1;
+        
+        for( i=0; i<bp->inputChannelCount; ++i )
+        {
+            destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i];
+
+            bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                hostInputChannels[i].data,
+                                hostInputChannels[i].stride,
+                                framesToCopy, &bp->ditherGenerator );
+
+            /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */
+            destBytePtr += bp->bytesPerUserInputSample * framesToCopy;
+            nonInterleavedDestPtrs[i] = destBytePtr;
+            
+            /* advance dest ptr for next iteration */
+            hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                    framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+        }
+    }
+
+    bp->hostInputFrameCount[0] -= framesToCopy;
+    
+    return framesToCopy;
+}
+
+unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp,
+        const void ** buffer, unsigned long frameCount )
+{
+    PaUtilChannelDescriptor *hostOutputChannels;
+    unsigned int framesToCopy;
+    unsigned char *srcBytePtr;
+    void **nonInterleavedSrcPtrs;
+    unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+
+    hostOutputChannels = bp->hostOutputChannels[0];
+    framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
+
+    if( bp->userOutputIsInterleaved )
+    {
+        srcBytePtr = (unsigned char*)*buffer;
+        
+        srcSampleStrideSamples = bp->outputChannelCount;
+        srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+
+        for( i=0; i<bp->outputChannelCount; ++i )
+        {
+            bp->outputConverter(    hostOutputChannels[i].data,
+                                    hostOutputChannels[i].stride,
+                                    srcBytePtr, srcSampleStrideSamples,
+                                    framesToCopy, &bp->ditherGenerator );
+
+            srcBytePtr += srcChannelStrideBytes;  /* skip to next source channel */
+
+            /* advance dest ptr for next iteration */
+            hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                    framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+        }
+
+        /* advance callers source pointer (buffer) */
+        *buffer = ((unsigned char *)*buffer) +
+                framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample;
+
+    }
+    else
+    {
+        /* user output is not interleaved */
+        
+        nonInterleavedSrcPtrs = (void**)*buffer;
+
+        srcSampleStrideSamples = 1;
+        
+        for( i=0; i<bp->outputChannelCount; ++i )
+        {
+            srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i];
+            
+            bp->outputConverter(    hostOutputChannels[i].data,
+                                    hostOutputChannels[i].stride,
+                                    srcBytePtr, srcSampleStrideSamples,
+                                    framesToCopy, &bp->ditherGenerator );
+
+
+            /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */
+            srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy;
+            nonInterleavedSrcPtrs[i] = srcBytePtr;
+            
+            /* advance dest ptr for next iteration */
+            hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                    framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+        }
+    }
+
+    bp->hostOutputFrameCount[0] += framesToCopy;
+    
+    return framesToCopy;
+}
+
+
+unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount )
+{
+    PaUtilChannelDescriptor *hostOutputChannels;
+    unsigned int framesToZero;
+    unsigned int i;
+
+    hostOutputChannels = bp->hostOutputChannels[0];
+    framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
+
+    for( i=0; i<bp->outputChannelCount; ++i )
+    {
+        bp->outputZeroer(   hostOutputChannels[i].data,
+                            hostOutputChannels[i].stride,
+                            framesToZero );
+
+
+        /* advance dest ptr for next iteration */
+        hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+    }
+
+    bp->hostOutputFrameCount[0] += framesToZero;
+    
+    return framesToZero;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_process.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_process.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_process.h	(revision 1753)
@@ -0,0 +1,762 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_PROCESS_H
+#define PA_PROCESS_H
+/*
+ * $Id: pa_process.h,v 1.1.2.30 2004/12/13 09:48:44 rossbencina Exp $
+ * Portable Audio I/O Library callback buffer processing adapters
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+ 
+/** @file
+ @brief Buffer Processor prototypes. A Buffer Processor performs buffer length
+ adaption, coordinates sample format conversion, and interleaves/deinterleaves
+ channels.
+
+ <h3>Overview</h3>
+
+ The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio
+ data from host buffers to user buffers and back again. Where required, the
+ buffer processor takes care of converting between host and user sample formats,
+ interleaving and deinterleaving multichannel buffers, and adapting between host
+ and user buffers with different lengths. The buffer processor may be used with
+ full and half duplex streams, for both callback streams and blocking read/write
+ streams.
+
+ One of the important capabilities provided by the buffer processor is
+ the ability to adapt between user and host buffer sizes of different lengths
+ with minimum latency. Although this task is relatively easy to perform when
+ the host buffer size is an integer multiple of the user buffer size, the
+ problem is more complicated when this is not the case - especially for
+ full-duplex callback streams. Where necessary the adaption is implemented by
+ internally buffering some input and/or output data. The buffer adation
+ algorithm used by the buffer processor was originally implemented by
+ Stephan Letz for the ASIO version of PortAudio, and is described in his
+ Callback_adaption_.pdf which is included in the distribution.
+
+ The buffer processor performs sample conversion using the functions provided
+ by pa_converters.c.
+
+ The following sections provide an overview of how to use the buffer processor.
+ Interested readers are advised to consult the host API implementations for
+ examples of buffer processor usage.
+ 
+
+ <h4>Initialization, resetting and termination</h4>
+
+ When a stream is opened, the buffer processor should be initialized using
+ PaUtil_InitializeBufferProcessor. This function initializes internal state
+ and allocates temporary buffers as neccesary according to the supplied
+ configuration parameters. Some of the parameters correspond to those requested
+ by the user in their call to Pa_OpenStream(), others reflect the requirements
+ of the host API implementation - they indicate host buffer sizes, formats,
+ and the type of buffering which the Host API uses. The buffer processor should
+ be initialized for callback streams and blocking read/write streams.
+
+ Call PaUtil_ResetBufferProcessor to clear any sample data which is present
+ in the buffer processor before starting to use it (for example when
+ Pa_StartStream is called).
+
+ When the buffer processor is no longer used call
+ PaUtil_TerminateBufferProcessor.
+
+ 
+ <h4>Using the buffer processor for a callback stream</h4>
+
+ The buffer processor's role in a callback stream is to take host input buffers
+ process them with the stream callback, and fill host output buffers. For a
+ full duplex stream, the buffer processor handles input and output simultaneously
+ due to the requirements of the minimum-latency buffer adation algorithm.
+
+ When a host buffer becomes available, the implementation should call
+ the buffer processor to process the buffer. The buffer processor calls the
+ stream callback to consume and/or produce audio data as necessary. The buffer
+ processor will convert sample formats, interleave/deinterleave channels,
+ and slice or chunk the data to the appropriate buffer lengths according to
+ the requirements of the stream callback and the host API.
+
+ To process a host buffer (or a pair of host buffers for a full-duplex stream)
+ use the following calling sequence:
+
+ -# Call PaUtil_BeginBufferProcessing
+ -# For a stream which takes input:
+    - Call PaUtil_SetInputFrameCount with the number of frames in the host input
+        buffer.
+    - Call one of the following functions one or more times to tell the
+        buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
+        PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
+        Which function you call will depend on whether the host buffer(s) are
+        interleaved or not.
+    - If the available host data is split accross two buffers (for example a
+        data range at the end of a circular buffer and another range at the
+        beginning of the circular buffer), also call
+        PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel,
+        PaUtil_Set2ndInterleavedInputChannels,
+        PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer
+        processor about the second buffer.
+ -# For a stream which generates output:
+    - Call PaUtil_SetOutputFrameCount with the number of frames in the host
+        output buffer.
+    - Call one of the following functions one or more times to tell the
+        buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
+        PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
+        Which function you call will depend on whether the host buffer(s) are
+        interleaved or not.
+    - If the available host output buffer space is split accross two buffers
+        (for example a data range at the end of a circular buffer and another
+        range at the beginning of the circular buffer), call
+        PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel,
+        PaUtil_Set2ndInterleavedOutputChannels,
+        PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer
+        processor about the second buffer.
+ -# Call PaUtil_EndBufferProcessing, this function performs the actual data
+    conversion and processing.
+
+
+ <h4>Using the buffer processor for a blocking read/write stream</h4>
+
+ Blocking read/write streams use the buffer processor to convert and copy user
+ output data to a host buffer, and to convert and copy host input data to
+ the user's buffer. The buffer processor does not perform any buffer adaption.
+ When using the buffer processor in a blocking read/write stream the input and
+ output conversion are performed separately by the PaUtil_CopyInput and
+ PaUtil_CopyOutput functions.
+
+ To copy data from a host input buffer to the buffer(s) which the user supplies
+ to Pa_ReadStream, use the following calling sequence.
+
+ - Repeat the following three steps until the user buffer(s) have been filled
+    with samples from the host input buffers:
+     -# Call PaUtil_SetInputFrameCount with the number of frames in the host
+        input buffer.
+     -# Call one of the following functions one or more times to tell the
+        buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
+        PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
+        Which function you call will depend on whether the host buffer(s) are
+        interleaved or not.
+     -# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the
+        array of buffer pointers for a non-interleaved stream) passed to
+        Pa_ReadStream, along with the number of frames in the user buffer(s).
+        Be careful to pass a <i>copy</i> of the user buffer pointers to
+        PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to
+        the start of the next region to copy.
+ - PaUtil_CopyInput will not copy more data than is available in the
+    host buffer(s), so the above steps need to be repeated until the user
+    buffer(s) are full.
+
+ 
+ To copy data to the host output buffer from the user buffers(s) supplied
+ to Pa_WriteStream use the following calling sequence.
+
+ - Repeat the following three steps until all frames from the user buffer(s)
+    have been copied to the host API:
+     -# Call PaUtil_SetOutputFrameCount with the number of frames in the host
+        output buffer.
+     -# Call one of the following functions one or more times to tell the
+        buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
+        PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
+        Which function you call will depend on whether the host buffer(s) are
+        interleaved or not.
+     -# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the
+        array of buffer pointers for a non-interleaved stream) passed to
+        Pa_WriteStream, along with the number of frames in the user buffer(s).
+        Be careful to pass a <i>copy</i> of the user buffer pointers to 
+        PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to
+        the start of the next region to copy.
+ - PaUtil_CopyOutput will not copy more data than fits in the host buffer(s),
+    so the above steps need to be repeated until all user data is copied.
+*/
+
+
+#include "portaudio.h"
+#include "pa_converters.h"
+#include "pa_dither.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type
+ of buffering that the host API uses.
+
+ The mode used depends on whether the host API or the implementation manages
+ the buffers, and how these buffers are used (scatter gather, circular buffer).
+*/
+typedef enum {
+/** The host buffer size is a fixed known size. */
+    paUtilFixedHostBufferSize,
+
+/** The host buffer size may vary, but has a known maximum size. */
+    paUtilBoundedHostBufferSize,
+
+/** Nothing is known about the host buffer size. */
+    paUtilUnknownHostBufferSize,
+
+/** The host buffer size varies, and the client does not require the buffer
+ processor to consume all of the input and fill all of the output buffer. This
+ is useful when the implementation has access to the host API's circular buffer
+ and only needs to consume/fill some of it, not necessarily all of it, with each
+ call to the buffer processor. This is the only mode where
+ PaUtil_EndBufferProcessing() may not consume the whole buffer.
+*/
+    paUtilVariableHostBufferSizePartialUsageAllowed
+}PaUtilHostBufferSizeMode;
+
+
+/** @brief An auxilliary data structure used internally by the buffer processor
+ to represent host input and output buffers. */
+typedef struct PaUtilChannelDescriptor{
+    void *data;
+    unsigned int stride;  /**< stride in samples, not bytes */
+}PaUtilChannelDescriptor;
+
+
+/** @brief The main buffer processor data structure.
+
+ Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor
+ and terminate it with PaUtil_TerminateBufferProcessor.
+*/
+typedef struct {
+    unsigned long framesPerUserBuffer;
+    unsigned long framesPerHostBuffer;
+
+    PaUtilHostBufferSizeMode hostBufferSizeMode;
+    int useNonAdaptingProcess;
+    unsigned long framesPerTempBuffer;
+
+    unsigned int inputChannelCount;
+    unsigned int bytesPerHostInputSample;
+    unsigned int bytesPerUserInputSample;
+    int userInputIsInterleaved;
+    PaUtilConverter *inputConverter;
+    PaUtilZeroer *inputZeroer;
+    
+    unsigned int outputChannelCount;
+    unsigned int bytesPerHostOutputSample;
+    unsigned int bytesPerUserOutputSample;
+    int userOutputIsInterleaved;
+    PaUtilConverter *outputConverter;
+    PaUtilZeroer *outputZeroer;
+
+    unsigned long initialFramesInTempInputBuffer;
+    unsigned long initialFramesInTempOutputBuffer;
+
+    void *tempInputBuffer;          /**< used for slips, block adaption, and conversion. */
+    void **tempInputBufferPtrs;     /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */
+    unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
+
+    void *tempOutputBuffer;         /**< used for slips, block adaption, and conversion. */
+    void **tempOutputBufferPtrs;    /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */
+    unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
+
+    PaStreamCallbackTimeInfo *timeInfo;
+
+    PaStreamCallbackFlags callbackStatusFlags;
+
+    unsigned long hostInputFrameCount[2];
+    PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors.
+                                                        pointers are NULL for half-duplex output processing.
+                                                        hostInputChannels[i].data is NULL when the caller
+                                                        calls PaUtil_SetNoInput()
+                                                        */
+    unsigned long hostOutputFrameCount[2];
+    PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors.
+                                                         pointers are NULL for half-duplex input processing.
+                                                         hostOutputChannels[i].data is NULL when the caller
+                                                         calls PaUtil_SetNoOutput()
+                                                         */
+
+    PaUtilTriangularDitherGenerator ditherGenerator;
+
+    double samplePeriod;
+
+    PaStreamCallback *streamCallback;
+    void *userData;
+} PaUtilBufferProcessor;
+
+
+/** @name Initialization, termination, resetting and info */
+/*@{*/
+
+/** Initialize a buffer processor's representation stored in a
+ PaUtilBufferProcessor structure. Be sure to call
+ PaUtil_TerminateBufferProcessor after finishing with a buffer processor.
+
+ @param bufferProcessor The buffer processor structure to initialize.
+
+ @param inputChannelCount The number of input channels as passed to
+ Pa_OpenStream or 0 for an output-only stream.
+
+ @param userInputSampleFormat Format of user input samples, as passed to
+ Pa_OpenStream. This parameter is ignored for ouput-only streams.
+ 
+ @param hostInputSampleFormat Format of host input samples. This parameter is
+ ignored for output-only streams. See note about host buffer interleave below.
+
+ @param outputChannelCount The number of output channels as passed to
+ Pa_OpenStream or 0 for an input-only stream.
+
+ @param userOutputSampleFormat Format of user output samples, as passed to
+ Pa_OpenStream. This parameter is ignored for input-only streams.
+ 
+ @param hostOutputSampleFormat Format of host output samples. This parameter is
+ ignored for input-only streams. See note about host buffer interleave below.
+
+ @param sampleRate Sample rate of the stream. The more accurate this is the
+ better - it is used for updating time stamps when adapting buffers.
+ 
+ @param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is
+ used for selecting special sample conversion options such as clipping and
+ dithering.
+ 
+ @param framesPerUserBuffer Number of frames per user buffer, as requested
+ by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be
+ zero to indicate that the user will accept any (and varying) buffer sizes.
+
+ @param framesPerHostBuffer Specifies the number of frames per host buffer
+ for the fixed buffer size mode, and the maximum number of frames
+ per host buffer for the bounded host buffer size mode. It is ignored for
+ the other modes.
+
+ @param hostBufferSizeMode A mode flag indicating the size variability of
+ host buffers that will be passed to the buffer processor. See
+ PaUtilHostBufferSizeMode for further details.
+ 
+ @param streamCallback The user stream callback passed to Pa_OpenStream.
+
+ @param userData The user data field passed to Pa_OpenStream.
+    
+ @note The interleave flag is ignored for host buffer formats. Host
+ interleave is determined by the use of different SetInput and SetOutput
+ functions.
+
+ @return An error code indicating whether the initialization was successful.
+ If the error code is not PaNoError, the buffer processor was not initialized
+ and should not be used.
+ 
+ @see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor
+*/
+PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor,
+            int inputChannelCount, PaSampleFormat userInputSampleFormat,
+            PaSampleFormat hostInputSampleFormat,
+            int outputChannelCount, PaSampleFormat userOutputSampleFormat,
+            PaSampleFormat hostOutputSampleFormat,
+            double sampleRate,
+            PaStreamFlags streamFlags,
+            unsigned long framesPerUserBuffer, /* 0 indicates don't care */
+            unsigned long framesPerHostBuffer,
+            PaUtilHostBufferSizeMode hostBufferSizeMode,
+            PaStreamCallback *streamCallback, void *userData );
+
+
+/** Terminate a buffer processor's representation. Deallocates any temporary
+ buffers allocated by PaUtil_InitializeBufferProcessor.
+ 
+ @param bufferProcessor The buffer processor structure to terminate.
+
+ @see PaUtil_InitializeBufferProcessor.
+*/
+void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Clear any internally buffered data. If you call
+ PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you
+ call PaUtil_ResetBufferProcessor in your StartStream call.
+
+ @param bufferProcessor The buffer processor to reset.
+*/
+void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Retrieve the input latency of a buffer processor.
+
+ @param bufferProcessor The buffer processor examine.
+
+ @return The input latency introduced by the buffer processor, in frames.
+
+ @see PaUtil_GetBufferProcessorOutputLatency
+*/
+unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor );
+
+/** Retrieve the output latency of a buffer processor.
+
+ @param bufferProcessor The buffer processor examine.
+
+ @return The output latency introduced by the buffer processor, in frames.
+
+ @see PaUtil_GetBufferProcessorInputLatency
+*/
+unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor );
+
+/*@}*/
+
+
+/** @name Host buffer pointer configuration
+
+ Functions to set host input and output buffers, used by both callback streams
+ and blocking read/write streams.
+*/
+/*@{*/ 
+
+
+/** Set the number of frames in the input host buffer(s) specified by the
+ PaUtil_Set*InputChannel functions.
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The number of host input frames. A 0 frameCount indicates to
+ use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
+
+ @see PaUtil_SetNoInput, PaUtil_SetInputChannel,
+ PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel
+*/
+void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+        
+/** Indicate that no input is avalable. This function should be used when
+ priming the output of a full-duplex stream opened with the
+ paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary
+ to call this or any othe PaUtil_Set*Input* functions for ouput-only streams.
+
+ @param bufferProcessor The buffer processor.
+*/
+void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Provide the buffer processor with a pointer to a host input channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+ @param stride The stride from one sample to the next, in samples. For
+ interleaved host buffers, the stride will usually be the same as the number of
+ channels in the buffer.
+*/
+void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data, unsigned int stride );
+
+
+/** Provide the buffer processor with a pointer to an number of interleaved
+ host input channels.
+
+ @param bufferProcessor The buffer processor.
+ @param firstChannel The first channel number.
+ @param data The buffer.
+ @param channelCount The number of interleaved channels in the buffer. If
+ channelCount is zero, the number of channels specified to
+ PaUtil_InitializeBufferProcessor will be used.
+*/
+void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int firstChannel, void *data, unsigned int channelCount );
+
+
+/** Provide the buffer processor with a pointer to one non-interleaved host
+ output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+*/
+void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data );
+
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInputFrameCount
+*/
+void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInputChannel
+*/
+void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data, unsigned int stride );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInterleavedInputChannels
+*/
+void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int firstChannel, void *data, unsigned int channelCount );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetNonInterleavedInputChannel
+*/
+void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data );
+
+        
+/** Set the number of frames in the output host buffer(s) specified by the
+ PaUtil_Set*OutputChannel functions.
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The number of host output frames. A 0 frameCount indicates to
+ use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
+
+ @see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels,
+ PaUtil_SetNonInterleavedOutputChannel
+*/
+void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+
+/** Indicate that the output will be discarded. This function should be used
+ when implementing the paNeverDropInput mode for full duplex streams.
+
+ @param bufferProcessor The buffer processor.
+*/
+void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Provide the buffer processor with a pointer to a host output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+ @param stride The stride from one sample to the next, in samples. For
+ interleaved host buffers, the stride will usually be the same as the number of
+ channels in the buffer.
+*/
+void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data, unsigned int stride );
+
+
+/** Provide the buffer processor with a pointer to a number of interleaved
+ host output channels.
+
+ @param bufferProcessor The buffer processor.
+ @param firstChannel The first channel number.
+ @param data The buffer.
+ @param channelCount The number of interleaved channels in the buffer. If
+ channelCount is zero, the number of channels specified to
+ PaUtil_InitializeBufferProcessor will be used.
+*/
+void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int firstChannel, void *data, unsigned int channelCount );
+
+        
+/** Provide the buffer processor with a pointer to one non-interleaved host
+ output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+*/
+void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data );
+
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetOutputFrameCount
+*/
+void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetOutputChannel
+*/
+void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data, unsigned int stride );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetInterleavedOutputChannels
+*/
+void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int firstChannel, void *data, unsigned int channelCount );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetNonInterleavedOutputChannel
+*/
+void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data );
+
+/*@}*/
+
+
+/** @name Buffer processing functions for callback streams
+*/
+/*@{*/
+
+/** Commence processing a host buffer (or a pair of host buffers in the
+ full-duplex case) for a callback stream.
+
+ @param bufferProcessor The buffer processor.
+
+ @param timeInfo Timing information for the first sample of the host
+ buffer(s). This information may be adjusted when buffer adaption is being
+ performed.
+
+ @param callbackStatusFlags Flags indicating whether underruns and overruns
+ have occurred since the last time the buffer processor was called.
+*/
+void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
+        PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags );
+
+        
+/** Finish processing a host buffer (or a pair of host buffers in the
+ full-duplex case) for a callback stream.
+
+ @param bufferProcessor The buffer processor.
+ 
+ @param callbackResult On input, indicates a previous callback result, and on
+ exit, the result of the user stream callback, if it is called.
+ On entry callbackResult should contain one of { paContinue, paComplete, or
+ paAbort}. If paComplete is passed, the stream callback will not be called
+ but any audio that was generated by previous stream callbacks will be copied
+ to the output buffer(s). You can check whether the buffer processor's internal
+ buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty.
+
+ If the stream callback is called its result is stored in *callbackResult. If
+ the stream callback returns paComplete or paAbort, all output buffers will be
+ full of valid data - some of which may be zeros to acount for data that
+ wasn't generated by the terminating callback.
+
+ @return The number of frames processed. This usually corresponds to the
+ number of frames specified by the PaUtil_Set*FrameCount functions, exept in
+ the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a
+ smaller value may be returned.
+*/
+unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
+        int *callbackResult );
+
+
+/** Determine whether any callback generated output remains in the bufffer
+ processor's internal buffers. This method may be used to determine when to
+ continue calling PaUtil_EndBufferProcessing() after the callback has returned
+ a callbackResult of paComplete.
+
+ @param bufferProcessor The buffer processor.
+ 
+ @return Returns non-zero when callback generated output remains in the internal
+ buffer and zero (0) when there internal buffer contains no callback generated
+ data.
+*/
+int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor );
+
+/*@}*/
+
+
+/** @name Buffer processing functions for blocking read/write streams
+*/
+/*@{*/
+
+/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels
+ functions to a user supplied buffer. This function is intended for use with
+ blocking read/write streams. Copies the minimum of the number of
+ user frames (specified by the frameCount parameter) and the number of available
+ host frames (specified in a previous call to SetInputFrameCount()).
+
+ @param bufferProcessor The buffer processor.
+
+ @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
+ to an array of user buffer pointers for a non-interleaved stream. It is
+ important that this parameter points to a copy of the user buffer pointers,
+ not to the actual user buffer pointers, because this function updates the
+ pointers before returning.
+
+ @param frameCount The number of frames of data in the buffer(s) pointed to by
+ the buffer parameter.
+
+ @return The number of frames copied. The buffer pointer(s) pointed to by the
+ buffer parameter are advanced to point to the frame(s) following the last one
+ filled.
+*/
+unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor,
+        void **buffer, unsigned long frameCount );
+
+
+/* Copy samples from a user supplied buffer to host output channels set up by
+ the PaUtil_Set*OutputChannels functions. This function is intended for use with
+ blocking read/write streams. Copies the minimum of the number of
+ user frames (specified by the frameCount parameter) and the number of
+ host frames (specified in a previous call to SetOutputFrameCount()).
+
+ @param bufferProcessor The buffer processor.
+
+ @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
+ to an array of user buffer pointers for a non-interleaved stream. It is
+ important that this parameter points to a copy of the user buffer pointers,
+ not to the actual user buffer pointers, because this function updates the
+ pointers before returning.
+
+ @param frameCount The number of frames of data in the buffer(s) pointed to by
+ the buffer parameter.
+
+ @return The number of frames copied. The buffer pointer(s) pointed to by the
+ buffer parameter are advanced to point to the frame(s) following the last one
+ copied.
+*/
+unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor,
+        const void ** buffer, unsigned long frameCount );
+
+
+/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels
+ functions. This function is useful for flushing streams.
+ Zeros the minimum of frameCount and the number of host frames specified in a
+ previous call to SetOutputFrameCount().
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The maximum number of frames to zero.
+ 
+ @return The number of frames zeroed.
+*/
+unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+
+/*@}*/
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_PROCESS_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_skeleton.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_skeleton.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_skeleton.c	(revision 1753)
@@ -0,0 +1,828 @@
+/*
+ * $Id: pa_skeleton.c,v 1.1.2.39 2003/11/26 14:56:09 rossbencina Exp $
+ * Portable Audio I/O Library skeleton implementation
+ * demonstrates how to use the common functions to implement support
+ * for a host API
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+ @brief Skeleton implementation of support for a host API.
+
+ @note This file is provided as a starting point for implementing support for
+ a new host API. IMPLEMENT ME comments are used to indicate functionality
+ which much be customised for each implementation.
+*/
+
+
+#include <string.h> /* strlen() */
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* IMPLEMENT ME: a macro like the following one should be used for reporting
+ host errors */
+#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
+    PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
+
+/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+
+    /* implementation specific data goes here */
+}
+PaSkeletonHostApiRepresentation;  /* IMPLEMENT ME: rename this */
+
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    int i, deviceCount;
+    PaSkeletonHostApiRepresentation *skeletonHostApi;
+    PaDeviceInfo *deviceInfoArray;
+
+    skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) );
+    if( !skeletonHostApi )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    skeletonHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !skeletonHostApi->allocations )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    *hostApi = &skeletonHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paInDevelopment;            /* IMPLEMENT ME: change to correct type id */
+    (*hostApi)->info.name = "skeleton implementation";  /* IMPLEMENT ME: change to correct name */
+
+    (*hostApi)->info.defaultInputDevice = paNoDevice;  /* IMPLEMENT ME */
+    (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
+
+    (*hostApi)->info.deviceCount = 0;  
+
+    deviceCount = 0; /* IMPLEMENT ME */
+    
+    if( deviceCount > 0 )
+    {
+        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+        if( !(*hostApi)->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all device info structs in a contiguous block */
+        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+                skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( i=0; i < deviceCount; ++i )
+        {
+            PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+            deviceInfo->structVersion = 2;
+            deviceInfo->hostApi = hostApiIndex;
+            deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg:
+                deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 );
+                if( !deviceName )
+                {
+                    result = paInsufficientMemory;
+                    goto error;
+                }
+                strcpy( deviceName, srcName );
+                deviceInfo->name = deviceName;
+            */
+
+            deviceInfo->maxInputChannels = 0;  /* IMPLEMENT ME */
+            deviceInfo->maxOutputChannels = 0;  /* IMPLEMENT ME */
+            
+            deviceInfo->defaultLowInputLatency = 0.;  /* IMPLEMENT ME */
+            deviceInfo->defaultLowOutputLatency = 0.;  /* IMPLEMENT ME */
+            deviceInfo->defaultHighInputLatency = 0.;  /* IMPLEMENT ME */
+            deviceInfo->defaultHighOutputLatency = 0.;  /* IMPLEMENT ME */  
+
+            deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */
+            
+            (*hostApi)->deviceInfos[i] = deviceInfo;
+            ++(*hostApi)->info.deviceCount;
+        }
+    }
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    return result;
+
+error:
+    if( skeletonHostApi )
+    {
+        if( skeletonHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+        }
+                
+        PaUtil_FreeMemory( skeletonHostApi );
+    }
+    return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
+
+    /*
+        IMPLEMENT ME:
+            - clean up any resources not handled by the allocation group
+    */
+
+    if( skeletonHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( skeletonHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( inputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+            
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( outputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+            
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support outputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+    
+    /*
+        IMPLEMENT ME:
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported if necessary
+
+            - check that the device supports sampleRate
+
+        Because the buffer adapter handles conversion between all standard
+        sample formats, the following checks are only required if paCustomFormat
+        is implemented, or under some other unusual conditions.
+
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from inputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+    */
+
+
+    /* suppress unused variable warnings */
+    (void) sampleRate;
+
+    return paFormatIsSupported;
+}
+
+/* PaSkeletonStream - a stream data structure specifically for this implementation */
+
+typedef struct PaSkeletonStream
+{ /* IMPLEMENT ME: rename this */
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+    /* IMPLEMENT ME:
+            - implementation specific data goes here
+    */
+    unsigned long framesPerHostCallback; /* just an example */
+}
+PaSkeletonStream;
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
+    PaSkeletonStream *stream = 0;
+    unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+
+
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+        /* IMPLEMENT ME - establish which  host formats are available */
+        hostInputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
+    }
+    else
+    {
+        inputChannelCount = 0;
+        inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+        /* IMPLEMENT ME - establish which  host formats are available */
+        hostOutputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
+    }
+    else
+    {
+        outputChannelCount = 0;
+        outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
+    }
+
+    /*
+        IMPLEMENT ME:
+
+        ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
+
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+
+            - check that the device supports sampleRate
+
+            - alter sampleRate to a close allowable rate if possible / necessary
+
+            - validate suggestedInputLatency and suggestedOutputLatency parameters,
+                use default values where necessary
+    */
+
+
+
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+
+
+    stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) );
+    if( !stream )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    if( streamCallback )
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &skeletonHostApi->callbackStreamInterface, streamCallback, userData );
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &skeletonHostApi->blockingStreamInterface, streamCallback, userData );
+    }
+
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+    /* we assume a fixed host buffer size in this example, but the buffer processor
+        can also support bounded and unknown host buffer sizes by passing 
+        paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
+        paUtilFixedHostBufferSize below. */
+        
+    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+              inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+              outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+              sampleRate, streamFlags, framesPerBuffer,
+              framesPerHostBuffer, paUtilFixedHostBufferSize,
+              streamCallback, userData );
+    if( result != paNoError )
+        goto error;
+
+
+    /*
+        IMPLEMENT ME: initialise the following fields with estimated or actual
+        values.
+    */
+    stream->streamRepresentation.streamInfo.inputLatency =
+            PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor);
+    stream->streamRepresentation.streamInfo.outputLatency =
+            PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+    
+    /*
+        IMPLEMENT ME:
+            - additional stream setup + opening
+    */
+
+    stream->framesPerHostCallback = framesPerHostBuffer;
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( stream )
+        PaUtil_FreeMemory( stream );
+
+    return result;
+}
+
+/*
+    ExampleHostProcessingLoop() illustrates the kind of processing which may
+    occur in a host implementation.
+ 
+*/
+static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)userData;
+    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
+    int callbackResult;
+    unsigned long framesProcessed;
+    
+    PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+    
+    /*
+        IMPLEMENT ME:
+            - generate timing information
+            - handle buffer slips
+    */
+
+    /*
+        If you need to byte swap or shift inputBuffer to convert it into a
+        portaudio format, do it here.
+    */
+
+
+
+    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
+
+    /*
+        depending on whether the host buffers are interleaved, non-interleaved
+        or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
+        PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
+    */
+    
+    PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+    PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
+            0, /* first channel of inputBuffer is channel 0 */
+            inputBuffer,
+            0 ); /* 0 - use inputChannelCount passed to init buffer processor */
+
+    PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
+            0, /* first channel of outputBuffer is channel 0 */
+            outputBuffer,
+            0 ); /* 0 - use outputChannelCount passed to init buffer processor */
+
+    /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
+        in general you would pass paContinue for normal operation, and
+        paComplete to drain the buffer processor's internal output buffer.
+        You can check whether the buffer processor's output buffer is empty
+        using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
+    */
+    callbackResult = paContinue;
+    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+
+    
+    /*
+        If you need to byte swap or shift outputBuffer to convert it to
+        host format, do it here.
+    */
+
+    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+
+    if( callbackResult == paContinue )
+    {
+        /* nothing special to do */
+    }
+    else if( callbackResult == paAbort )
+    {
+        /* IMPLEMENT ME - finish playback immediately  */
+
+        /* once finished, call the finished callback */
+        if( stream->streamRepresentation.streamFinishedCallback != 0 )
+            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    }
+    else
+    {
+        /* User callback has asked us to stop with paComplete or other non-zero value */
+
+        /* IMPLEMENT ME - finish playback once currently queued audio has completed  */
+
+        /* once finished, call the finished callback */
+        if( stream->streamRepresentation.streamFinishedCallback != 0 )
+            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    }
+}
+
+
+/*
+    When CloseStream() is called, the multi-api layer ensures that
+    the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /*
+        IMPLEMENT ME:
+            - additional stream closing + cleanup
+    */
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+    PaUtil_FreeMemory( stream );
+
+    return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    /* suppress unused function warning. the code in ExampleHostProcessingLoop or
+       something similar should be implemented to feed samples to and from the
+       host after StartStream() is called.
+    */
+    (void) ExampleHostProcessingLoop;
+
+    return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    return 0;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    return 0;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_stream.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_stream.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_stream.c	(revision 1753)
@@ -0,0 +1,162 @@
+/*
+ * $Id: pa_stream.c,v 1.1.2.12 2003/09/20 21:31:00 rossbencina Exp $
+ * Portable Audio I/O Library
+ * 
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ streams.
+*/
+
+
+#include "pa_stream.h"
+
+
+void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
+                                       PaError (*Close)( PaStream* ),
+                                       PaError (*Start)( PaStream* ),
+                                       PaError (*Stop)( PaStream* ),
+                                       PaError (*Abort)( PaStream* ),
+                                       PaError (*IsStopped)( PaStream* ),
+                                       PaError (*IsActive)( PaStream* ),
+                                       PaTime (*GetTime)( PaStream* ),
+                                       double (*GetCpuLoad)( PaStream* ),
+                                       PaError (*Read)( PaStream*, void *, unsigned long ),
+                                       PaError (*Write)( PaStream*, const void *, unsigned long ),
+                                       signed long (*GetReadAvailable)( PaStream* ),
+                                       signed long (*GetWriteAvailable)( PaStream* )  )
+{
+    streamInterface->Close = Close;
+    streamInterface->Start = Start;
+    streamInterface->Stop = Stop;
+    streamInterface->Abort = Abort;
+    streamInterface->IsStopped = IsStopped;
+    streamInterface->IsActive = IsActive;
+    streamInterface->GetTime = GetTime;
+    streamInterface->GetCpuLoad = GetCpuLoad;
+    streamInterface->Read = Read;
+    streamInterface->Write = Write;
+    streamInterface->GetReadAvailable = GetReadAvailable;
+    streamInterface->GetWriteAvailable = GetWriteAvailable;
+}
+
+
+void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation,
+        PaUtilStreamInterface *streamInterface,
+        PaStreamCallback *streamCallback,
+        void *userData )
+{
+    streamRepresentation->magic = PA_STREAM_MAGIC;
+    streamRepresentation->nextOpenStream = 0;
+    streamRepresentation->streamInterface = streamInterface;
+    streamRepresentation->streamCallback = streamCallback;
+    streamRepresentation->streamFinishedCallback = 0;
+
+    streamRepresentation->userData = userData;
+
+    streamRepresentation->streamInfo.inputLatency = 0.;
+    streamRepresentation->streamInfo.outputLatency = 0.;
+    streamRepresentation->streamInfo.sampleRate = 0.;
+}
+
+
+void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation )
+{
+    streamRepresentation->magic = 0;
+}
+
+
+PaError PaUtil_DummyRead( PaStream* stream,
+                               void *buffer,
+                               unsigned long frames )
+{
+    (void)stream; /* unused parameter */
+    (void)buffer; /* unused parameter */
+    (void)frames; /* unused parameter */
+
+    return paCanNotReadFromACallbackStream;
+}
+
+
+PaError PaUtil_DummyWrite( PaStream* stream,
+                               const void *buffer,
+                               unsigned long frames )
+{
+    (void)stream; /* unused parameter */
+    (void)buffer; /* unused parameter */
+    (void)frames; /* unused parameter */
+
+    return paCanNotWriteToACallbackStream;
+}
+
+
+signed long PaUtil_DummyGetReadAvailable( PaStream* stream )
+{
+    (void)stream; /* unused parameter */
+
+    return paCanNotReadFromACallbackStream;
+}
+
+
+signed long PaUtil_DummyGetWriteAvailable( PaStream* stream )
+{
+    (void)stream; /* unused parameter */
+
+    return paCanNotWriteToACallbackStream;
+}
+
+
+double PaUtil_DummyGetCpuLoad( PaStream* stream )
+{
+    (void)stream; /* unused parameter */
+
+    return 0.0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_stream.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_stream.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_stream.h	(revision 1753)
@@ -0,0 +1,217 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_STREAM_H
+#define PA_STREAM_H
+/*
+ * $Id: pa_stream.h,v 1.1.2.13 2003/11/01 06:37:28 rossbencina Exp $
+ * Portable Audio I/O Library
+ * stream interface
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ streams.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#define PA_STREAM_MAGIC (0x18273645)
+
+
+/** A structure representing an (abstract) interface to a host API. Contains
+ pointers to functions which implement the interface.
+
+ All PaStreamInterface functions are guaranteed to be called with a non-null,
+ valid stream parameter.
+*/
+typedef struct {
+    PaError (*Close)( PaStream* stream );
+    PaError (*Start)( PaStream *stream );
+    PaError (*Stop)( PaStream *stream );
+    PaError (*Abort)( PaStream *stream );
+    PaError (*IsStopped)( PaStream *stream );
+    PaError (*IsActive)( PaStream *stream );
+    PaTime (*GetTime)( PaStream *stream );
+    double (*GetCpuLoad)( PaStream* stream );
+    PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames );
+    PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames );
+    signed long (*GetReadAvailable)( PaStream* stream );
+    signed long (*GetWriteAvailable)( PaStream* stream );
+} PaUtilStreamInterface;
+
+
+/** Initialize the fields of a PaUtilStreamInterface structure.
+*/
+void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
+    PaError (*Close)( PaStream* ),
+    PaError (*Start)( PaStream* ),
+    PaError (*Stop)( PaStream* ),
+    PaError (*Abort)( PaStream* ),
+    PaError (*IsStopped)( PaStream* ),
+    PaError (*IsActive)( PaStream* ),
+    PaTime (*GetTime)( PaStream* ),
+    double (*GetCpuLoad)( PaStream* ),
+    PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ),
+    PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ),
+    signed long (*GetReadAvailable)( PaStream* stream ),
+    signed long (*GetWriteAvailable)( PaStream* stream ) );
+
+
+/** Dummy Read function for use in interfaces to a callback based streams.
+ Pass to the Read parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+PaError PaUtil_DummyRead( PaStream* stream,
+                       void *buffer,
+                       unsigned long frames );
+
+
+/** Dummy Write function for use in an interfaces to callback based streams.
+ Pass to the Write parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+PaError PaUtil_DummyWrite( PaStream* stream,
+                       const void *buffer,
+                       unsigned long frames );
+
+
+/** Dummy GetReadAvailable function for use in interfaces to callback based
+ streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+signed long PaUtil_DummyGetReadAvailable( PaStream* stream );
+
+
+/** Dummy GetWriteAvailable function for use in interfaces to callback based
+ streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+signed long PaUtil_DummyGetWriteAvailable( PaStream* stream );
+
+
+
+/** Dummy GetCpuLoad function for use in an interface to a read/write stream.
+ Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface.
+ @return Returns 0.
+*/
+double PaUtil_DummyGetCpuLoad( PaStream* stream );
+
+
+/** Non host specific data for a stream. This data is used by pa_front to
+ forward to the appropriate functions in the streamInterface structure.
+*/
+typedef struct PaUtilStreamRepresentation {
+    unsigned long magic;    /**< set to PA_STREAM_MAGIC */
+    struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */
+    PaUtilStreamInterface *streamInterface;
+    PaStreamCallback *streamCallback;
+    PaStreamFinishedCallback *streamFinishedCallback;
+    void *userData;
+    PaStreamInfo streamInfo;
+} PaUtilStreamRepresentation;
+
+
+/** Initialize a PaUtilStreamRepresentation structure.
+
+ @see PaUtil_InitializeStreamRepresentation
+*/
+void PaUtil_InitializeStreamRepresentation(
+        PaUtilStreamRepresentation *streamRepresentation,
+        PaUtilStreamInterface *streamInterface,
+        PaStreamCallback *streamCallback,
+        void *userData );
+        
+
+/** Clean up a PaUtilStreamRepresentation structure previously initialized
+ by a call to PaUtil_InitializeStreamRepresentation.
+
+ @see PaUtil_InitializeStreamRepresentation
+*/
+void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation );
+
+
+/** Check that the stream pointer is valid.
+
+ @return Returns paNoError if the stream pointer appears to be OK, otherwise
+ returns an error indicating the cause of failure.
+*/
+PaError PaUtil_ValidateStreamPointer( PaStream *stream );
+
+
+/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation.
+
+ @see PaUtilStreamRepresentation
+*/
+#define PA_STREAM_REP( stream )\
+    ((PaUtilStreamRepresentation*) (stream) )
+
+
+/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface.
+
+ @see PaUtilStreamRepresentation, PaUtilStreamInterface
+*/
+#define PA_STREAM_INTERFACE( stream )\
+    PA_STREAM_REP( (stream) )->streamInterface
+
+
+    
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_STREAM_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_trace.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_trace.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_trace.c	(revision 1753)
@@ -0,0 +1,109 @@
+/*
+ * $Id: pa_trace.c,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+ @brief Event trace mechanism for debugging.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pa_trace.h"
+
+#if PA_TRACE_REALTIME_EVENTS
+
+static char *traceTextArray[MAX_TRACE_RECORDS];
+static int traceIntArray[MAX_TRACE_RECORDS];
+static int traceIndex = 0;
+static int traceBlock = 0;
+
+/*********************************************************************/
+void PaUtil_ResetTraceMessages()
+{
+    traceIndex = 0;
+}
+
+/*********************************************************************/
+void PaUtil_DumpTraceMessages()
+{
+    int i;
+    int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS;
+
+    printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
+    for( i=0; i<messageCount; i++ )
+    {
+        printf("%3d: %s = 0x%08X\n",
+               i, traceTextArray[i], traceIntArray[i] );
+    }
+    ResetTraceMessages();
+    fflush(stdout);
+}
+
+/*********************************************************************/
+void PaUtil_AddTraceMessage( const char *msg, int data )
+{
+    if( (traceIndex == PA_MAX_TRACE_RECORDS) && (traceBlock == 0) )
+    {
+        traceBlock = 1;
+        /*  PaUtil_DumpTraceMessages(); */
+    }
+    else if( traceIndex < PA_MAX_TRACE_RECORDS )
+    {
+        traceTextArray[traceIndex] = msg;
+        traceIntArray[traceIndex] = data;
+        traceIndex++;
+    }
+}
+
+#endif /* TRACE_REALTIME_EVENTS */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_trace.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_trace.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_trace.h	(revision 1753)
@@ -0,0 +1,91 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_TRACE_H
+#define PA_TRACE_H
+/*
+ * $Id: pa_trace.h,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Event trace mechanism for debugging.
+
+ Allows data to be written to the buffer at interrupt time and dumped later.
+*/
+
+
+#define PA_TRACE_REALTIME_EVENTS     (0)   /* Keep log of various real-time events. */
+#define PA_MAX_TRACE_RECORDS      (2048)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#if PA_TRACE_REALTIME_EVENTS
+
+void PaUtil_ResetTraceMessages();
+void PaUtil_AddTraceMessage( const char *msg, int data );
+void PaUtil_DumpTraceMessages();
+    
+#else
+
+#define PaUtil_ResetTraceMessages() /* noop */
+#define PaUtil_AddTraceMessage(msg,data) /* noop */
+#define PaUtil_DumpTraceMessages() /* noop */
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_TRACE_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_types.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_types.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_types.h	(revision 1753)
@@ -0,0 +1,86 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_TYPES_H
+#define PA_TYPES_H
+
+/*
+    SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script
+    when it is used. Otherwise we default to the common 32 bit values, if your
+    platform doesn't use configure, and doesn't use the default values below
+    you will need to explicitly define these symbols in your make file.
+
+    A PA_VALIDATE_SIZES macro is provided to assert that the values set in this
+    file are correct.
+*/
+
+#ifndef SIZEOF_SHORT
+#define SIZEOF_SHORT 2
+#endif
+
+#ifndef SIZEOF_INT
+#define SIZEOF_INT 4
+#endif
+
+#ifndef SIZEOF_LONG
+#define SIZEOF_LONG 4
+#endif
+
+
+#if SIZEOF_SHORT == 2
+typedef signed short PaInt16;
+typedef unsigned short PaUint16;
+#elif SIZEOF_INT == 2
+typedef signed int PaInt16;
+typedef unsigned int PaUint16;
+#else
+#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform
+#endif
+
+#if SIZEOF_SHORT == 4
+typedef signed short PaInt32;
+typedef unsigned short PaUint32;
+#elif SIZEOF_INT == 4
+typedef signed int PaInt32;
+typedef unsigned int PaUint32;
+#elif SIZEOF_LONG == 4
+typedef signed long PaInt32;
+typedef unsigned long PaUint32;
+#else
+#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform
+#endif
+
+
+/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to
+ ensure that PortAudio was configured correctly, and raises an assertion if
+ they don't match the expected values. <assert.h> must be included in the
+ context in which this macro is used.
+*/
+#define PA_VALIDATE_TYPE_SIZES \
+    { \
+        assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \
+        assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \
+        assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \
+        assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \
+    }
+
+
+#endif /* PA_TYPES_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c	(revision 1753)
@@ -0,0 +1,85 @@
+/*
+ * $Id: pa_unix_hostapis.c,v 1.1.2.5 2003/10/02 12:35:46 pieter Exp $
+ * Portable Audio I/O Library UNIX initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+
+#include "pa_hostapi.h"
+
+PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+/* Added for IRIX, Pieter, oct 2, 2003: */
+PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+    {
+#ifdef PA_USE_OSS
+        PaOSS_Initialize,
+#endif
+
+#ifdef PA_USE_ALSA
+        PaAlsa_Initialize,
+#endif
+
+#ifdef PA_USE_JACK
+        PaJack_Initialize,
+#endif
+                    /* Added for IRIX, Pieter, oct 2, 2003: */
+#ifdef PA_USE_SGI 
+        PaSGI_Initialize,
+#endif
+        0   /* NULL terminated array */
+    };
+
+int paDefaultHostApiIndex = 0;
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c	(revision 1753)
@@ -0,0 +1,1939 @@
+/*
+ * $Id: pa_unix_oss.c,v 1.6.2.22 2005/03/08 21:26:53 aknudsen Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * OSS implementation by:
+ *   Douglas Repetto
+ *   Phil Burk
+ *   Dominic Mazzoni
+ *   Arve Knudsen
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <alloca.h>
+#include <malloc.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <limits.h>
+#include <semaphore.h>
+
+#ifdef __linux__
+# include <linux/soundcard.h>
+# define DEVICE_NAME_BASE            "/dev/dsp"
+#else
+# include <machine/soundcard.h> /* JH20010905 */
+# define DEVICE_NAME_BASE            "/dev/audio"
+#endif
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+#include "pa_unix_util.h"
+
+static int sysErr_;
+static pthread_t mainThread_;
+
+/* Check return value of system call, and map it to PaError */
+#define ENSURE_(expr, code) \
+    do { \
+        if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
+        { \
+            /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+            if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
+            { \
+                PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
+            } \
+            \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = (code); \
+            goto error; \
+        } \
+    } while( 0 );
+
+#ifndef AFMT_S16_NE
+#define AFMT_S16_NE  Get_AFMT_S16_NE()
+/*********************************************************************
+ * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
+ * PowerPC is Big Endian. X86 is Little Endian.
+ */
+static int Get_AFMT_S16_NE( void )
+{
+    long testData = 1; 
+    char *ptr = (char *) &testData;
+    int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
+    return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
+}
+#endif
+
+/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+
+    PaHostApiIndex hostApiIndex;
+}
+PaOSSHostApiRepresentation;
+
+/** Per-direction structure for PaOssStream.
+ *
+ * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
+ * but with different number of channels we will have to adapt between the number of user and host
+ * channels for at least one direction, since the configuration space is the same for both directions
+ * of an OSS device.
+ */
+typedef struct
+{
+    int fd;
+    const char *devName;
+    int userChannelCount, hostChannelCount;
+    int userInterleaved;
+    void *buffer;
+    PaSampleFormat userFormat, hostFormat;
+    double latency;
+    unsigned long hostFrames, numBufs;
+    void **userBuffers; /* For non-interleaved blocking */
+} PaOssStreamComponent;
+
+/** Implementation specific representation of a PaStream.
+ *
+ */
+typedef struct PaOssStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+    PaUtilThreading threading;
+
+    int sharedDevice;
+    unsigned long framesPerHostBuffer;
+    int triggered;  /* Have the devices been triggered yet (first start) */
+
+    int isActive;
+    int isStopped;
+
+    int lastPosPtr;
+    double lastStreamBytes;
+
+    int framesProcessed;
+
+    double sampleRate;
+
+    int callbackMode;
+    int callbackStop, callbackAbort;
+
+    PaOssStreamComponent *capture, *playback;
+    unsigned long pollTimeout;
+    sem_t semaphore;
+}
+PaOssStream;
+
+typedef enum {
+    StreamMode_In,
+    StreamMode_Out
+} StreamMode;
+
+/* prototypes for functions declared in this file */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );
+
+
+/** Initialize the OSS API implementation.
+ *
+ * This function will initialize host API datastructures and query host devices for information.
+ *
+ * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
+ *
+ * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
+ * this happens with the usual "error" label.
+ */
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    PaOSSHostApiRepresentation *ossHostApi = NULL;
+
+    PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
+            paInsufficientMemory );
+    PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+    ossHostApi->hostApiIndex = hostApiIndex;
+
+    /* Initialize host API structure */
+    *hostApi = &ossHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paOSS;
+    (*hostApi)->info.name = "OSS";
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PA_ENSURE( BuildDeviceList( ossHostApi ) );
+
+    PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable,
+                                      PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    mainThread_ = pthread_self();
+
+    return result;
+
+error:
+    if( ossHostApi )
+    {
+        if( ossHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( ossHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+        }
+                
+        PaUtil_FreeMemory( ossHostApi );
+    }
+    return result;
+}
+
+PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
+        int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
+        PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations  )
+{
+    PaError result = paNoError;
+    
+    deviceInfo->structVersion = 2;
+    if( allocations )
+    {
+        size_t len = strlen( name ) + 1;
+        PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
+        strncpy( (char *)deviceInfo->name, name, len );
+    }
+    else
+        deviceInfo->name = name;
+
+    deviceInfo->hostApi = hostApiIndex;
+    deviceInfo->maxInputChannels = maxInputChannels;
+    deviceInfo->maxOutputChannels = maxOutputChannels;
+    deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
+    deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
+    deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
+    deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
+    deviceInfo->defaultSampleRate = defaultSampleRate;
+
+error:
+    return result;
+}
+
+static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
+        double *defaultLowLatency, double *defaultHighLatency )
+{
+    PaError result = paNoError;
+    int numChannels, maxNumChannels;
+    int busy = 0;
+    int devHandle = -1;
+    int sr;
+    *maxChannelCount = 0;  /* Default value in case this fails */
+
+    if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK ))  < 0 )
+    {
+        if( errno == EBUSY || errno == EAGAIN )
+        {
+            PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
+        }
+        else
+        {
+            PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
+        }
+
+        return paDeviceUnavailable;
+    }
+
+    /* Negotiate for the maximum number of channels for this device. PLB20010927
+     * Consider up to 16 as the upper number of channels.
+     * Variable maxNumChannels should contain the actual upper limit after the call.
+     * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
+     */
+    maxNumChannels = 0;
+    for( numChannels = 1; numChannels <= 16; numChannels++ )
+    {
+        int temp = numChannels;
+        if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
+        {
+            busy = EAGAIN == errno || EBUSY == errno;
+            /* ioctl() failed so bail out if we already have stereo */
+            if( maxNumChannels >= 2 )
+                break;
+        }
+        else
+        {
+            /* ioctl() worked but bail out if it does not support numChannels.
+             * We don't want to leave gaps in the numChannels supported.
+             */
+            if( (numChannels > 2) && (temp != numChannels) )
+                break;
+            if( temp > maxNumChannels )
+                maxNumChannels = temp; /* Save maximum. */
+        }
+    }
+    /* A: We're able to open a device for capture if it's busy playing back and vice versa,
+     * but we can't configure anything */
+    if( 0 == maxNumChannels && busy )
+    {
+        result = paDeviceUnavailable;
+        goto error;
+    }
+
+    /* The above negotiation may fail for an old driver so try this older technique. */
+    if( maxNumChannels < 1 )
+    {
+        int stereo = 1;
+        if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
+        {
+            maxNumChannels = 1;
+        }
+        else
+        {
+            maxNumChannels = (stereo) ? 2 : 1;
+        }
+        PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ))
+    }
+
+    /* During channel negotiation, the last ioctl() may have failed. This can
+     * also cause sample rate negotiation to fail. Hence the following, to return
+     * to a supported number of channels. SG20011005 */
+    {
+        /* use most reasonable default value */
+        int temp = PA_MIN( maxNumChannels, 2 );
+        ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError );
+    }
+
+    /* Get supported sample rate closest to 44100 Hz */
+    if( *defaultSampleRate < 0 )
+    {
+        sr = 44100;
+        if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 )
+        {
+            result = paUnanticipatedHostError;
+            goto error;
+        }
+
+        *defaultSampleRate = sr;
+    }
+
+    *maxChannelCount = maxNumChannels;
+    /* TODO */
+    *defaultLowLatency = 512. / *defaultSampleRate;
+    *defaultHighLatency = 2048. / *defaultSampleRate;
+
+error:
+    if( devHandle >= 0 )
+        close( devHandle );
+
+    return result;
+}
+
+/** Query OSS device.
+ *
+ * This is where PaDeviceInfo objects are constructed and filled in with relevant information.
+ *
+ * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed
+ * in place.
+ */
+static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo )
+{
+    PaError result = paNoError;
+    double sampleRate = -1.;
+    int maxInputChannels, maxOutputChannels;
+    PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency;
+    PaError tmpRes = paNoError;
+    int busy = 0;
+    *deviceInfo = NULL;
+
+    /* douglas:
+       we have to do this querying in a slightly different order. apparently
+       some sound cards will give you different info based on their settins. 
+       e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
+       the correct order for OSS is: format, channels, sample rate
+    */
+
+    /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is
+     * opened in, it may have more channels available for capture than playback and vice versa. Therefore
+     * we will open the device in both read- and write-only mode to determine the supported number.
+     */
+    if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency,
+                &defaultHighInputLatency )) != paNoError )
+    {
+        if( tmpRes != paDeviceUnavailable )
+        {
+            PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName ));
+            /* PA_ENSURE( tmpRes ); */
+        }
+        ++busy;
+    }
+    if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency,
+                &defaultHighOutputLatency )) != paNoError )
+    {
+        if( tmpRes != paDeviceUnavailable )
+        {
+            PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName ));
+            /* PA_ENSURE( tmpRes ); */
+        }
+        ++busy;
+    }
+    assert( 0 <= busy && busy <= 2 );
+    if( 2 == busy )     /* Both directions are unavailable to us */
+    {
+        result = paDeviceUnavailable;
+        goto error;
+    }
+
+    PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory );
+    PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels,
+                defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate,
+                ossApi->allocations ) );
+
+error:
+    return result;
+}
+
+/** Query host devices.
+ *
+ * Loop over host devices and query their capabilitiesu
+ *
+ * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object
+ * per device, these are placed in the host api representation's deviceInfos array.
+ */
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
+{
+    PaError result = paNoError;
+    PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
+    int i;
+    int numDevices = 0, maxDeviceInfos = 1;
+    PaDeviceInfo **deviceInfos = NULL;
+
+    /* These two will be set to the first working input and output device, respectively */
+    commonApi->info.defaultInputDevice = paNoDevice;
+    commonApi->info.defaultOutputDevice = paNoDevice;
+
+    /* Find devices by calling QueryDevice on each
+     * potential device names.  When we find a valid one,
+     * add it to a linked list.
+     * A: Can there only be 10 devices? */
+
+    for( i = 0; i < 10; i++ )
+    {
+       char deviceName[32];
+       PaDeviceInfo *deviceInfo;
+       int testResult;
+       struct stat stbuf;
+
+       if( i == 0 )
+          snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
+       else
+          snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
+
+       /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */
+       if( stat( deviceName, &stbuf ) < 0 )
+       {
+           if( ENOENT != errno )
+               PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
+           continue;
+       }
+       if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
+       {
+           if( testResult != paDeviceUnavailable )
+               PA_ENSURE( testResult );
+
+           continue;
+       }
+
+       ++numDevices;
+       if( !deviceInfos || numDevices > maxDeviceInfos )
+       {
+           maxDeviceInfos *= 2;
+           PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ),
+                   paInsufficientMemory );
+       }
+       deviceInfos[numDevices - 1] = deviceInfo;
+
+       if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 )
+           commonApi->info.defaultInputDevice = i;
+       if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 )
+           commonApi->info.defaultOutputDevice = i;
+    }
+
+    /* Make an array of PaDeviceInfo pointers out of the linked list */
+
+    PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices));
+
+    commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+        ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
+    memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) );
+
+    commonApi->info.deviceCount = numDevices;
+
+error:
+    free( deviceInfos );
+
+    return result;
+}
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+
+    if( ossHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( ossHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( ossHostApi );
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    PaError result = paNoError;
+    PaDeviceIndex device;
+    PaDeviceInfo *deviceInfo;
+    char *deviceName;
+    int inputChannelCount, outputChannelCount;
+    int tempDevHandle = -1;
+    int flags;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+
+    if (inputChannelCount == 0 && outputChannelCount == 0)
+        return paInvalidChannelCount;
+
+    /* if full duplex, make sure that they're the same device */
+
+    if (inputChannelCount > 0 && outputChannelCount > 0 &&
+        inputParameters->device != outputParameters->device)
+        return paInvalidDevice;
+
+    /* if full duplex, also make sure that they're the same number of channels */
+
+    if (inputChannelCount > 0 && outputChannelCount > 0 &&
+        inputChannelCount != outputChannelCount)
+       return paInvalidChannelCount;
+
+    /* open the device so we can do more tests */
+    
+    if( inputChannelCount > 0 )
+    {
+        result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
+        if (result != paNoError)
+            return result;
+    }
+    else
+    {
+        result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
+        if (result != paNoError)
+            return result;
+    }
+
+    deviceInfo = hostApi->deviceInfos[device];
+    deviceName = (char *)deviceInfo->name;
+    
+    flags = O_NONBLOCK;
+    if (inputChannelCount > 0 && outputChannelCount > 0)
+       flags |= O_RDWR;
+    else if (inputChannelCount > 0)
+       flags |= O_RDONLY;
+    else
+       flags |= O_WRONLY;
+
+    ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable );
+
+    /* PaOssStream_Configure will do the rest of the checking for us */
+    /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */
+
+    /* everything succeeded! */
+
+ error:
+    if( tempDevHandle >= 0 )
+        close( tempDevHandle );         
+
+    return result;
+}
+
+/** Validate stream parameters.
+ *
+ * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device
+ */
+static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode )
+{
+    int maxChans;
+
+    assert( parameters );
+
+    if( parameters->device == paUseHostApiSpecificDeviceSpecification )
+    {
+        return paInvalidDevice;
+    }
+
+    maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels :
+        deviceInfo->maxOutputChannels);
+    if( parameters->channelCount > maxChans )
+    {
+        return paInvalidChannelCount;
+    }
+
+    return paNoError;
+}
+
+static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters,
+        int callbackMode, int fd, const char *deviceName )
+{
+    PaError result = paNoError;
+    assert( component );
+
+    memset( component, 0, sizeof (PaOssStreamComponent) );
+
+    component->fd = fd;
+    component->devName = deviceName;
+    component->userChannelCount = parameters->channelCount;
+    component->userFormat = parameters->sampleFormat;
+    component->latency = parameters->suggestedLatency;
+    component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved);
+
+    if( !callbackMode && !component->userInterleaved )
+    {
+        /* Pre-allocate non-interleaved user provided buffers */
+        PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ),
+                paInsufficientMemory );
+    }
+
+error:
+    return result;
+}
+
+static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component )
+{
+    assert( component );
+
+    if( component->fd >= 0 )
+        close( component->fd );
+    if( component->buffer )
+        PaUtil_FreeMemory( component->buffer );
+
+    if( component->userBuffers )
+        PaUtil_FreeMemory( component->userBuffers );
+
+    PaUtil_FreeMemory( component );
+}
+
+static PaError ModifyBlocking( int fd, int blocking )
+{
+    PaError result = paNoError;
+    int fflags;
+
+    ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
+
+    if( blocking )
+        fflags &= ~O_NONBLOCK;
+    else
+        fflags |= O_NONBLOCK;
+
+    ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
+
+error:
+    return result;
+}
+
+static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
+{
+    PaError result = paNoError;
+    int flags = O_NONBLOCK, duplex = 0;
+    int enableBits = 0;
+    *idev = *odev = -1;
+
+    if( idevName && odevName )
+    {
+        duplex = 1;
+        flags |= O_RDWR;
+    }
+    else if( idevName )
+        flags |= O_RDONLY;
+    else
+        flags |= O_WRONLY;
+
+    /* open first in nonblocking mode, in case it's busy...
+     * A: then unset the non-blocking attribute */
+    assert( flags & O_NONBLOCK );
+    if( idevName )
+    {
+        ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
+        PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
+
+        /* Initially disable */
+        enableBits = ~PCM_ENABLE_INPUT;
+        ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+    }
+    if( odevName )
+    {
+        if( !idevName )
+        {
+            ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
+            PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
+
+            /* Initially disable */
+            enableBits = ~PCM_ENABLE_OUTPUT;
+            ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+        }
+        else
+        {
+            ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
+        }
+    }
+
+error:
+    return result;
+}
+
+static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
+        PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
+        PaOSSHostApiRepresentation *ossApi )
+{
+    PaError result = paNoError;
+    int idev, odev;
+    PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
+    const char *idevName = NULL, *odevName = NULL;
+
+    assert( stream );
+
+    memset( stream, 0, sizeof (PaOssStream) );
+    stream->isStopped = 1;
+
+    PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
+
+    if( inputParameters && outputParameters )
+    {
+        if( inputParameters->device == outputParameters->device )
+            stream->sharedDevice = 1;
+    }
+
+    if( inputParameters )
+        idevName = hostApi->deviceInfos[inputParameters->device]->name;
+    if( outputParameters )
+        odevName = hostApi->deviceInfos[outputParameters->device]->name;
+    PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
+    if( inputParameters )
+    {
+        PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
+        PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
+    }
+    if( outputParameters )
+    {
+        PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
+        PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
+    }
+
+    if( callback != NULL )
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &ossApi->callbackStreamInterface, callback, userData );
+        stream->callbackMode = 1;
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &ossApi->blockingStreamInterface, callback, userData );
+    }    
+
+    ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
+
+error:
+    return result;
+}
+
+static void PaOssStream_Terminate( PaOssStream *stream )
+{
+    assert( stream );
+
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+    PaUtil_TerminateThreading( &stream->threading );
+
+    if( stream->capture )
+        PaOssStreamComponent_Terminate( stream->capture );
+    if( stream->playback )
+        PaOssStreamComponent_Terminate( stream->playback );
+
+    sem_destroy( &stream->semaphore );
+
+    PaUtil_FreeMemory( stream );
+}
+
+/** Translate from PA format to OSS native.
+ *
+ */
+static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
+{
+    switch( paFormat )
+    {
+        case paUInt8:
+            *ossFormat = AFMT_U8;
+            break;
+        case paInt8:
+            *ossFormat = AFMT_S8;
+            break;
+        case paInt16:
+            *ossFormat = AFMT_S16_NE;
+            break;
+        default:
+            return paInternalError;     /* This shouldn't happen */
+    }
+
+    return paNoError;
+}
+
+/** Return the PA-compatible formats that this device can support.
+ *
+ */
+static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
+{
+    PaError result = paNoError;
+    int mask = 0;
+    PaSampleFormat frmts = 0;
+
+    ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
+    if( mask & AFMT_U8 )
+        frmts |= paUInt8;
+    if( mask & AFMT_S8 )
+        frmts |= paInt8;
+    if( mask & AFMT_S16_NE )
+        frmts |= paInt16;
+    else
+        result = paSampleFormatNotSupported;
+    
+    *availableFormats = frmts;
+
+error:
+    return result;
+}
+
+static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
+{
+    return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
+}
+
+/** Buffer size in bytes.
+ *
+ */
+static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
+{
+    return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
+}
+
+static int CalcHigherLogTwo( int n )
+{
+    int log2 = 0;
+    while( (1<<log2) < n ) log2++;
+    return log2;
+}
+
+static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
+        StreamMode streamMode, PaOssStreamComponent *master )
+{
+    PaError result = paNoError;
+    int temp, nativeFormat;
+    int sr = (int)sampleRate;
+    PaSampleFormat availableFormats, hostFormat;
+    int chans = component->userChannelCount;
+    int frgmt;
+    int numBufs;
+    int bytesPerBuf;
+    double bufSz;
+    unsigned long fragSz;
+    audio_buf_info bufInfo;
+
+    /* We may have a situation where only one component (the master) is configured, if both point to the same device.
+     * In that case, the second component will copy settings from the other */
+    if( !master )
+    {
+        /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
+         * The hardware need not respect the requested fragment size, so we may have to adapt.
+         */
+        if( framesPerBuffer == paFramesPerBufferUnspecified )
+        { 
+            bufSz = component->latency * sampleRate;
+            fragSz = bufSz / 4;
+        }
+        else
+        {
+            fragSz = framesPerBuffer;
+            bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */
+        }
+
+        PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
+        hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
+
+        /* OSS demands at least 2 buffers, and 16 bytes per buffer */
+        numBufs = PA_MAX( bufSz / fragSz, 2 );
+        bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
+
+        /* The fragment parameters are encoded like this:
+         * Most significant byte: number of fragments
+         * Least significant byte: exponent of fragment size (i.e., for 256, 8)
+         */
+        frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
+
+        /* A: according to the OSS programmer's guide parameters should be set in this order:
+         * format, channels, rate */
+
+        /* This format should be deemed good before we get this far */
+        PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
+        nativeFormat = temp;
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
+        PA_UNLESS( temp == nativeFormat, paInternalError );
+
+        /* try to set the number of channels */
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported );   /* XXX: Should be paInvalidChannelCount? */
+        /* It's possible that the minimum number of host channels is greater than what the user requested */
+        PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
+
+        /* try to set the sample rate */
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
+
+        /* reject if there's no sample rate within 1% of the one requested */
+        if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
+        {
+            PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));                 
+            PA_ENSURE( paInvalidSampleRate );
+        }
+
+        ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
+                paUnanticipatedHostError );
+        component->numBufs = bufInfo.fragstotal;
+
+        /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
+
+        component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
+        component->hostChannelCount = chans;
+        component->hostFormat = hostFormat;
+    }
+    else
+    {
+        component->hostFormat = master->hostFormat;
+        component->hostFrames = master->hostFrames;
+        component->hostChannelCount = master->hostChannelCount;
+        component->numBufs = master->numBufs;
+    }
+
+    PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
+            paInsufficientMemory );
+
+error:
+    return result;
+}
+
+static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
+{
+    PaError result = paNoError;
+    size_t len = *frames * PaOssStreamComponent_FrameSize( component );
+    ssize_t bytesRead;
+
+    ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
+    *frames = bytesRead / PaOssStreamComponent_FrameSize( component );
+
+error:
+    return result;
+}
+
+static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
+{
+    PaError result = paNoError;
+    size_t len = *frames * PaOssStreamComponent_FrameSize( component );
+    ssize_t bytesWritten;
+
+    ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
+    *frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
+
+error:
+    return result;
+}
+
+/** Configure the stream according to input/output parameters.
+ *
+ * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
+ * the user, if so we'll record the actual number of host channels and adapt later.
+ */
+static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
+        double *inputLatency, double *outputLatency )
+{
+    PaError result = paNoError;
+    int duplex = stream->capture && stream->playback;
+    unsigned long framesPerHostBuffer = 0;
+
+    /* We should request full duplex first thing after opening the device */
+    if( duplex && stream->sharedDevice )
+        ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
+
+    if( stream->capture )
+    {
+        PaOssStreamComponent *component = stream->capture;
+        PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL );
+
+        assert( component->hostChannelCount > 0 );
+        assert( component->hostFrames > 0 );
+
+        *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
+    }
+    if( stream->playback )
+    {
+        PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL;
+        PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out,
+                    master ) );
+
+        assert( component->hostChannelCount > 0 );
+        assert( component->hostFrames > 0 );
+
+        *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
+    }
+
+    if( duplex )
+        framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames );
+    else if( stream->capture )
+        framesPerHostBuffer = stream->capture->hostFrames;
+    else if( stream->playback )
+        framesPerHostBuffer = stream->playback->hostFrames;
+
+    stream->framesPerHostBuffer = framesPerHostBuffer;
+    stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate );    /* Period in usecs, rounded up */
+
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+error:
+    return result;
+}
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+/** Open a PA OSS stream.
+ *
+ * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the
+ * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both
+ * directions are the same device we will demand the same number of channels. The number of channels can range
+ * from 1 to the maximum supported by the device.
+ *
+ * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback
+ * must reflect this, in addition the host latency per device should approximate the corresponding
+ * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that
+ * both capture and playback can agree on (they can be different devices), the buffer processor can adapt
+ * between host and user buffer size, but the ratio should preferably be integral.
+ */
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+    PaOssStream *stream = NULL;
+    int inputChannelCount = 0, outputChannelCount = 0;
+    PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0;
+    const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
+    int bpInitialized = 0;
+    double inLatency, outLatency;
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+
+    if( inputParameters )
+    {
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+        inputDeviceInfo = hostApi->deviceInfos[inputParameters->device];
+        PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) );
+
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+    }
+    if( outputParameters )
+    {
+        outputDeviceInfo = hostApi->deviceInfos[outputParameters->device];
+        PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) );
+
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+    }
+
+    /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same
+     * device is opened for both directions
+     */
+    if( inputChannelCount > 0 && outputChannelCount > 0 )
+    {
+        if( inputParameters->device == outputParameters->device )
+        {
+            if( inputParameters->channelCount != outputParameters->channelCount )
+                return paInvalidChannelCount;
+        }
+    }
+    
+    /* allocate and do basic initialization of the stream structure */
+    PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
+    PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi );
+
+    PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
+
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+        
+    if( inputParameters )
+    {
+        inputHostFormat = stream->capture->hostFormat;
+        stream->streamRepresentation.streamInfo.inputLatency = inLatency +
+            PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
+    }
+    if( outputParameters )
+    {
+        outputHostFormat = stream->playback->hostFormat;
+        stream->streamRepresentation.streamInfo.outputLatency = outLatency +
+            PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
+    }
+
+    /* Initialize buffer processor with fixed host buffer size.
+     * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will
+     * convert between the two.
+     */
+    PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+              inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat,
+              outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer,
+              paUtilFixedHostBufferSize, streamCallback, userData ) );
+    bpInitialized = 1;
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( bpInitialized )
+        PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    if( stream )
+        PaOssStream_Terminate( stream );
+
+    return result;
+}
+
+/*! Poll on I/O filedescriptors.
+
+  Poll till we've determined there's data for read or write. In the full-duplex case,
+  we don't want to hang around forever waiting for either input or output frames, so
+  whenever we have a timed out filedescriptor we check if we're nearing under/overrun
+  for the other direction (critical limit set at one buffer). If so, we exit the waiting
+  state, and go on with what we got. We align the number of frames on a host buffer
+  boundary because it is possible that the buffer size differs for the two directions and
+  the host buffer size is a compromise between the two.
+  */
+static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames )
+{
+    PaError result = paNoError;
+    int pollPlayback = 0, pollCapture = 0;
+    int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail;
+    audio_buf_info bufInfo;
+    /* int ofs = 0, nfds = stream->nfds; */
+    fd_set readFds, writeFds;
+    int nfds = 0;
+    struct timeval selectTimeval = {0, 0};
+    unsigned long timeout = stream->pollTimeout;    /* In usecs */
+    int captureFd = -1, playbackFd = -1;
+
+    assert( stream );
+    assert( frames );
+
+    if( stream->capture )
+    {
+        pollCapture = 1;
+        captureFd = stream->capture->fd;
+        /* stream->capture->pfd->events = POLLIN; */
+    }
+    if( stream->playback )
+    {
+        pollPlayback = 1;
+        playbackFd = stream->playback->fd;
+        /* stream->playback->pfd->events = POLLOUT; */
+    }
+
+    FD_ZERO( &readFds );
+    FD_ZERO( &writeFds );
+
+    while( pollPlayback || pollCapture )
+    {
+        pthread_testcancel();
+
+        /* select may modify the timeout parameter */
+        selectTimeval.tv_usec = timeout;
+        nfds = 0;
+
+        if( pollCapture )
+        {
+            FD_SET( captureFd, &readFds );
+            nfds = captureFd + 1;
+        }
+        if( pollPlayback )
+        {
+            FD_SET( playbackFd, &writeFds );
+            nfds = PA_MAX( nfds, playbackFd + 1 );
+        }
+        ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError );
+        /*
+        if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 )
+        {
+
+            ENSURE_( -1, paUnanticipatedHostError );
+        }
+        */
+        pthread_testcancel();
+
+        if( pollCapture )
+        {
+            if( FD_ISSET( captureFd, &readFds ) )
+            {
+                FD_CLR( captureFd, &readFds );
+                pollCapture = 0;
+            }
+            /*
+            if( stream->capture->pfd->revents & POLLIN )
+            {
+                --nfds;
+                ++ofs;
+                pollCapture = 0;
+            }
+            */
+            else if( stream->playback ) /* Timed out, go on with playback? */ 
+            {
+                /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
+                            __FUNCTION__, stream->pollTimeout ));*/
+            }
+        }
+        if( pollPlayback )
+        {
+            if( FD_ISSET( playbackFd, &writeFds ) )
+            {
+                FD_CLR( playbackFd, &writeFds );
+                pollPlayback = 0;
+            }
+            /*
+            if( stream->playback->pfd->revents & POLLOUT )
+            {
+                --nfds;
+                pollPlayback = 0;
+            }
+            */
+            else if( stream->capture )  /* Timed out, go on with capture? */
+            {
+                /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n",
+                            __FUNCTION__, stream->pollTimeout ));*/
+            }
+        }
+    }
+
+    if( stream->capture )
+    {
+        ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError );
+        captureAvail = bufInfo.fragments * stream->capture->hostFrames;
+        if( !captureAvail )
+            PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ ));
+
+        captureAvail = captureAvail == 0 ? INT_MAX : captureAvail;      /* Disregard if zero */
+    }
+    if( stream->playback )
+    {
+        ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError );
+        playbackAvail = bufInfo.fragments * stream->playback->hostFrames;
+        if( !playbackAvail )
+        {
+            PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ ));
+        }
+
+        playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail;      /* Disregard if zero */
+    }
+
+    commonAvail = PA_MIN( captureAvail, playbackAvail );
+    if( commonAvail == INT_MAX )
+        commonAvail = 0;
+    commonAvail -= commonAvail % stream->framesPerHostBuffer;
+
+    assert( commonAvail != INT_MAX );
+    assert( commonAvail >= 0 );
+    *frames = commonAvail;
+
+error:
+    return result;
+}
+
+/** Prepare stream for capture/playback.
+ *
+ * In order to synchronize capture and playback properly we use the SETTRIGGER command.
+ */
+static PaError PaOssStream_Prepare( PaOssStream *stream )
+{
+    PaError result = paNoError;
+    int enableBits = 0;
+
+    if( stream->triggered )
+        return result;
+
+    if( stream->playback )
+    {
+        size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
+        memset( stream->playback->buffer, 0, bufSz );
+
+        /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer
+         * OSS will complain. */
+        PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
+        while (1)
+        {
+            if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 )
+                break;
+        }
+        PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) );
+    }
+
+    if( stream->sharedDevice )
+    {
+        enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
+        ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+    }
+    else
+    {
+        if( stream->capture )
+        {
+            enableBits = PCM_ENABLE_INPUT;
+            ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+        }
+        if( stream->playback )
+        {
+            enableBits = PCM_ENABLE_OUTPUT;
+            ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+        }
+    }
+
+    /* Ok, we have triggered the stream */
+    stream->triggered = 1;
+    
+error:
+    return result;
+}
+
+/** Stop audio processing
+ *
+ */
+static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
+     * Also disable capture/playback till the stream is started again */
+    if( stream->capture )
+    {
+        ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+    }
+    if( stream->playback && !stream->sharedDevice )
+    {
+        ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+    }
+
+error:
+    return result;
+}
+
+/** Clean up after thread exit.
+ *
+ * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
+ */
+static void OnExit( void *data )
+{
+    PaOssStream *stream = (PaOssStream *) data;
+    assert( data );
+
+    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+    PaOssStream_Stop( stream, stream->callbackAbort );
+    
+    PA_DEBUG(( "OnExit: Stoppage\n" ));
+
+    /* Eventually notify user all buffers have played */
+    if( stream->streamRepresentation.streamFinishedCallback )
+        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+    stream->callbackAbort = 0;      /* Clear state */
+    stream->isActive = 0;
+}
+
+static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
+{
+    PaError result = paNoError;
+
+    if( stream->capture )
+    {
+        PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
+                stream->capture->hostChannelCount );
+        PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
+    }
+    if( stream->playback )
+    {
+        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
+                stream->playback->hostChannelCount );
+        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
+    }
+
+    return result;
+}
+
+/** Thread procedure for callback processing.
+ *
+ * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
+ * callback should be used for buffer priming. When the stream is cancelled a separate function will
+ * take care of the transition to the Callback Finished state (the stream isn't considered Stopped
+ * before StopStream() or AbortStream() are called).
+ */
+static void *PaOSS_AudioThreadProc( void *userData )
+{
+    PaError result = paNoError;
+    PaOssStream *stream = (PaOssStream*)userData;
+    unsigned long framesAvail, framesProcessed;
+    int callbackResult = paContinue;
+    int triggered = stream->triggered;  /* See if SNDCTL_DSP_TRIGGER has been issued already */
+    int initiateProcessing = triggered;    /* Already triggered? */
+    PaStreamCallbackFlags cbFlags = 0;  /* We might want to keep state across iterations */
+    
+    /*
+#if ( SOUND_VERSION > 0x030904 )
+        audio_errinfo errinfo;
+#endif
+*/
+    
+    assert( stream );
+
+    pthread_cleanup_push( &OnExit, stream );	/* Execute OnExit when exiting */
+
+    /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
+     * playback in sync, when the stream is restarted after being stopped we simply start by reading/
+     * writing.
+     */
+    PA_ENSURE( PaOssStream_Prepare( stream ) );
+
+    /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
+    if( initiateProcessing )
+    {
+        /* Make sure devices are in blocking mode */
+        if( stream->capture )
+            ModifyBlocking( stream->capture->fd, 1 );
+        if( stream->playback )
+            ModifyBlocking( stream->playback->fd, 1 );
+    }
+
+    while( 1 )
+    {
+        PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
+
+        pthread_testcancel();
+
+        if( stream->callbackStop && callbackResult == paContinue )
+        {
+            PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
+            callbackResult = paComplete;
+        }
+
+        /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
+         * the stream has been recently started, we will have to go right ahead and read/write in blocking
+         * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
+         * to non-blocking mode.
+         */
+        if( !initiateProcessing )
+        {
+            PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) );  /* Wait on available frames */
+            assert( framesAvail % stream->framesPerHostBuffer == 0 );
+        }
+        else
+        {
+            framesAvail = stream->framesPerHostBuffer;
+        }
+
+        while( framesAvail > 0 )
+        {
+            unsigned long frames = framesAvail;
+
+            pthread_testcancel();
+
+            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+            /* Read data */
+            if ( stream->capture )
+            {
+                PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
+                assert( frames == framesAvail );
+            }
+
+#if ( SOUND_VERSION >= 0x030904 )
+            /*
+               Check with OSS to see if there have been any under/overruns
+               since last time we checked.
+               */
+            /*
+            if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
+            {
+                if( errinfo.play_underruns )
+                    cbFlags |= paOutputUnderflow ;
+                if( errinfo.record_underruns )
+                    cbFlags |= paInputUnderflow ;
+            }
+            else
+                PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
+                */
+#endif
+
+            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
+                    cbFlags );
+            cbFlags = 0;
+            PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
+
+            framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
+                    &callbackResult );
+            assert( framesProcessed == framesAvail );
+            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+            if ( stream->playback )
+            {
+                frames = framesAvail;
+
+                PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
+                assert( frames == framesAvail );
+
+                /* TODO: handle bytesWritten != bytesRequested (slippage?) */
+            }
+
+            framesAvail -= framesProcessed;
+            stream->framesProcessed += framesProcessed;
+
+            if( callbackResult != paContinue )
+                break;
+        }
+
+        if( initiateProcessing || !triggered )
+        {
+            /* Non-blocking */
+            if( stream->capture )
+                PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
+            if( stream->playback && !stream->sharedDevice )
+                PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
+
+            initiateProcessing = 0;
+            sem_post( &stream->semaphore );
+        }
+
+        if( callbackResult != paContinue )
+        {
+            stream->callbackAbort = callbackResult == paAbort;
+            if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
+                break;
+        }
+    }
+
+    pthread_cleanup_pop( 1 );
+
+error:
+    pthread_exit( NULL );
+}
+
+/** Close the stream.
+ *
+ */
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaOssStream *stream = (PaOssStream*)s;
+
+    assert( stream );
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaOssStream_Terminate( stream );
+
+    return result;
+}
+
+/** Start the stream.
+ *
+ * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
+ * callback will be repeatedly called in a separate thread. If a separate thread is started this function
+ * will block untill it has started processing audio, otherwise audio processing is started directly.
+ */
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaOssStream *stream = (PaOssStream*)s;
+
+    stream->isActive = 1;
+    stream->isStopped = 0;
+    stream->lastPosPtr = 0;
+    stream->lastStreamBytes = 0;
+    stream->framesProcessed = 0;
+
+    /* only use the thread for callback streams */
+    if( stream->bufferProcessor.streamCallback )
+    {
+        PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
+        sem_wait( &stream->semaphore );
+    }
+    else
+        PA_ENSURE( PaOssStream_Prepare( stream ) );
+
+error:
+    return result;
+}
+
+static PaError RealStop( PaOssStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    if( stream->callbackMode )
+    {
+        if( abort )
+            stream->callbackAbort = 1;
+        else
+            stream->callbackStop = 1;
+
+        PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
+
+        stream->callbackStop = stream->callbackAbort = 0;
+    }
+    else
+        PA_ENSURE( PaOssStream_Stop( stream, abort ) );
+
+    stream->isStopped = 1;
+
+error:
+    return result;
+}
+
+/** Stop the stream.
+ *
+ * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
+ * buffers.
+ */
+static PaError StopStream( PaStream *s )
+{
+    return RealStop( (PaOssStream *)s, 0 );
+}
+
+/** Abort the stream.
+ *
+ * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
+ * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
+ * the OSS device.
+ */
+static PaError AbortStream( PaStream *s )
+{
+    return RealStop( (PaOssStream *)s, 1 );
+}
+
+/** Is the stream in the Stopped state.
+ *
+ */
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+
+    return (stream->isStopped);
+}
+
+/** Is the stream in the Active state.
+ *
+ */
+static PaError IsStreamActive( PaStream *s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+
+    return (stream->isActive);
+}
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    count_info info;
+    int delta;
+
+    if( stream->playback ) {
+        if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
+            delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
+            return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
+        }
+    }
+    else {
+        if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
+            delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF;
+            return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
+        }
+    }
+
+    /* the ioctl failed, but we can still give a coarse estimate */
+
+    return stream->framesProcessed / stream->sampleRate;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    int bytesRequested, bytesRead;
+    unsigned long framesRequested;
+    void *userBuffer;
+
+    /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
+     * so we copy the user provided pointers */
+    if( stream->bufferProcessor.userInputIsInterleaved )
+        userBuffer = buffer;
+    else /* Copy channels into local array */
+    {
+        userBuffer = stream->capture->userBuffers;
+        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount );
+    }
+
+    while( frames )
+    {
+        framesRequested = PA_MIN( frames, stream->capture->hostFrames );
+
+	bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
+	bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested );
+	if ( bytesRequested != bytesRead )
+	    return paUnanticipatedHostError;
+
+	PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
+	PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
+        PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
+	frames -= framesRequested;
+    }
+    return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    int bytesRequested, bytesWritten;
+    unsigned long framesConverted;
+    const void *userBuffer;
+
+    /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers,
+     * so we copy the user provided pointers */
+    if( stream->bufferProcessor.userOutputIsInterleaved )
+        userBuffer = buffer;
+    else /* Copy channels into local array */
+    {
+        userBuffer = stream->playback->userBuffers;
+        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount );
+    }
+
+    while( frames )
+    {
+	PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames );
+	PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount );
+
+	framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames );
+	frames -= framesConverted;
+
+	bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
+	bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested );
+
+	if ( bytesRequested != bytesWritten )
+	    return paUnanticipatedHostError;
+    }
+    return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    audio_buf_info info;
+
+    if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 )
+        return paUnanticipatedHostError;
+    return info.fragments * stream->capture->hostFrames;
+}
+
+
+/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    int delay = 0;
+
+    if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
+        return paUnanticipatedHostError;
+    
+    return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_util.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_util.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_util.c	(revision 1753)
@@ -0,0 +1,196 @@
+/*
+ * $Id: pa_unix_util.c,v 1.1.2.7 2005/03/31 15:02:48 aknudsen Exp $
+ * Portable Audio I/O Library
+ * UNIX platform-specific support functions
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+ 
+#include <pthread.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <string.h> /* For memset */
+
+#include "pa_util.h"
+#include "pa_unix_util.h"
+
+/*
+   Track memory allocations to avoid leaks.
+ */
+
+#if PA_TRACK_MEMORY
+static int numAllocations_ = 0;
+#endif
+
+
+void *PaUtil_AllocateMemory( long size )
+{
+    void *result = malloc( size );
+
+#if PA_TRACK_MEMORY
+    if( result != NULL ) numAllocations_ += 1;
+#endif
+    return result;
+}
+
+
+void PaUtil_FreeMemory( void *block )
+{
+    if( block != NULL )
+    {
+        free( block );
+#if PA_TRACK_MEMORY
+        numAllocations_ -= 1;
+#endif
+
+    }
+}
+
+
+int PaUtil_CountCurrentlyAllocatedBlocks( void )
+{
+#if PA_TRACK_MEMORY
+    return numAllocations_;
+#else
+    return 0;
+#endif
+}
+
+
+void Pa_Sleep( long msec )
+{
+    while( msec > 999 )     /* For OpenBSD and IRIX, argument */
+        {                   /* to usleep must be < 1000000.   */
+        usleep( 999000 );
+        msec -= 999;
+        }
+    usleep( msec * 1000 );
+}
+
+/*            *** NOT USED YET: ***
+static int usePerformanceCounter_;
+static double microsecondsPerTick_;
+*/
+
+void PaUtil_InitializeClock( void )
+{
+    /* TODO */
+}
+
+
+PaTime PaUtil_GetTime( void )
+{
+    struct timeval tv;
+    gettimeofday( &tv, NULL );
+    return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec;
+}
+
+PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
+{
+    (void) paUtilErr_;
+    return paNoError;
+}
+
+void PaUtil_TerminateThreading( PaUtilThreading *threading )
+{
+}
+
+PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
+{
+    pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
+    return paNoError;
+}
+
+PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
+{
+    PaError result = paNoError;
+    void *pret;
+
+    if( exitResult )
+        *exitResult = paNoError;
+
+    /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
+    if( !wait )
+        pthread_cancel( threading->callbackThread );   /* XXX: Safe to call this if the thread has exited on its own? */
+    pthread_join( threading->callbackThread, &pret );
+
+#ifdef PTHREAD_CANCELED
+    if( pret && PTHREAD_CANCELED != pret )
+#else
+    /* !wait means the thread may have been canceled */
+    if( pret && wait )
+#endif
+    {
+        if( exitResult )
+            *exitResult = *(PaError *) pret;
+        free( pret );
+    }
+
+    return result;
+}
+
+/*
+static void *CanaryFunc( void *userData )
+{
+    const unsigned intervalMsec = 1000;
+    PaUtilThreading *th = (PaUtilThreading *) userData;
+
+    while( 1 )
+    {
+        th->canaryTime = PaUtil_GetTime();
+
+        pthread_testcancel();
+        Pa_Sleep( intervalMsec );
+    }
+
+    pthread_exit( NULL );
+}
+*/
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_util.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_util.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_unix_util.h	(revision 1753)
@@ -0,0 +1,94 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_UNIX_UTIL_H
+#define PA_UNIX_UTIL_H
+
+#include "pa_cpuload.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) )
+#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) )
+
+/* Utilize GCC branch prediction for error tests */
+#if defined __GNUC__ && __GNUC__ >= 3
+#define UNLIKELY(expr) __builtin_expect( (expr), 0 )
+#else
+#define UNLIKELY(expr) (expr)
+#endif
+
+#define STRINGIZE_HELPER(expr) #expr
+#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
+
+#define PA_UNLESS(expr, code) \
+    do { \
+        if( UNLIKELY( (expr) == 0 ) ) \
+        { \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = (code); \
+            goto error; \
+        } \
+    } while (0);
+
+static PaError paUtilErr_;          /* Used with PA_ENSURE */
+
+/* Check PaError */
+#define PA_ENSURE(expr) \
+    do { \
+        if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \
+        { \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = paUtilErr_; \
+            goto error; \
+        } \
+    } while (0);
+
+typedef struct {
+    pthread_t callbackThread;
+} PaUtilThreading;
+
+PaError PaUtil_InitializeThreading( PaUtilThreading *threading );
+void PaUtil_TerminateThreading( PaUtilThreading *threading );
+PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data );
+PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult );
+
+/* State accessed by utility functions */
+
+/*
+void PaUnix_SetRealtimeScheduling( int rt );
+
+void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm );
+
+PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s );
+
+PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult );
+
+void PaUtil_CallbackUpdate( PaUtilThreading *th );
+*/
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_util.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_util.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_util.h	(revision 1753)
@@ -0,0 +1,188 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_UTIL_H
+#define PA_UTIL_H
+/*
+ * $Id: pa_util.h,v 1.1.2.12 2003/09/20 21:09:55 rossbencina Exp $
+ * Portable Audio I/O Library implementation utilities header
+ * common implementation utilities and interfaces
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+    @brief Prototypes for utility functions used by PortAudio implementations.
+
+    @todo Document and adhere to the alignment guarantees provided by
+    PaUtil_AllocateMemory().
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+struct PaUtilHostApiRepresentation;
+
+
+/** Retrieve a specific host API representation. This function can be used
+ by implementations to retrieve a pointer to their representation in
+ host api specific extension functions which aren't passed a rep pointer
+ by pa_front.c.
+
+ @param hostApi A pointer to a host API represenation pointer. Apon success
+ this will receive the requested representation pointer.
+
+ @param type A valid host API type identifier.
+
+ @returns An error code. If the result is PaNoError then a pointer to the
+ requested host API representation will be stored in *hostApi. If the host API
+ specified by type is not found, this function returns paHostApiNotFound.
+*/
+PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
+        PaHostApiTypeId type );
+
+
+/** Convert a PortAudio device index into a host API specific device index.
+ @param hostApiDevice Pointer to a device index, on success this will recieve the
+ converted device index value.
+ @param device The PortAudio device index to convert.
+ @param hostApi The host api which the index should be converted for.
+
+ @returns On success returns PaNoError and places the converted index in the
+ hostApiDevice parameter.
+*/
+PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
+        PaDeviceIndex *hostApiDevice, PaDeviceIndex device,
+        struct PaUtilHostApiRepresentation *hostApi );
+
+
+/** Set the host error information returned by Pa_GetLastHostErrorInfo. This
+ function and the paUnanticipatedHostError error code should be used as a
+ last resort.  Implementors should use existing PA error codes where possible,
+ or nominate new ones. Note that at it is always better to use
+ PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an
+ ambiguous or inaccurate PaError code.
+
+ @param hostApiType  The host API which encountered the error (ie of the caller)
+
+ @param errorCode The error code returned by the native API function.
+
+ @param errorText A string describing the error. PaUtil_SetLastHostErrorInfo
+ makes a copy of the string, so it is not necessary for the pointer to remain
+ valid after the call to PaUtil_SetLastHostErrorInfo() returns.
+
+*/
+void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
+        const char *errorText );
+
+
+        
+/** PA_DEBUG() provides a simple debug message printing facility. The macro
+ passes it's argument to a printf-like function called PaUtil_DebugPrint()
+ which prints to stderr and always flushes the stream after printing.
+ Because preprocessor macros cannot directly accept variable length argument
+ lists, calls to the macro must include an additional set of parenthesis, eg:
+ PA_DEBUG(("errorno: %d", 1001 ));
+*/
+
+void PaUtil_DebugPrint( const char *format, ... );
+
+#if (0) /* set to 1 to print debug messages */
+#define PA_DEBUG(x) PaUtil_DebugPrint x ;
+#else
+#define PA_DEBUG(x)
+#endif
+
+
+/* the following functions are implemented in a platform platform specific
+ .c file
+*/
+
+/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */
+void *PaUtil_AllocateMemory( long size );
+
+
+/** Realease block if non-NULL. block may be NULL */
+void PaUtil_FreeMemory( void *block );
+
+
+/** Return the number of currently allocated blocks. This function can be
+ used for detecting memory leaks.
+
+ @note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If
+ it isn't, this function will always return 0.
+*/
+int PaUtil_CountCurrentlyAllocatedBlocks( void );
+
+
+/** Initialize the clock used by PaUtil_GetTime(). Call this before calling
+ PaUtil_GetTime.
+
+ @see PaUtil_GetTime
+*/
+void PaUtil_InitializeClock( void );
+
+
+/** Return the system time in seconds. Used to implement CPU load functions
+
+ @see PaUtil_InitializeClock
+*/
+double PaUtil_GetTime( void );
+
+
+/* void Pa_Sleep( long msec );  must also be implemented in per-platform .c file */
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_UTIL_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_ds.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_ds.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_ds.c	(revision 1753)
@@ -0,0 +1,1849 @@
+/*
+ * $Id: pa_win_ds.c,v 1.1.2.49 2004/05/16 04:08:55 rossbencina Exp $
+ * Portable Audio I/O Library DirectSound implementation
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+
+    @todo implement paInputOverflow callback status flag
+    
+    @todo implement paNeverDropInput.
+
+    @todo implement host api specific extension to set i/o buffer sizes in frames
+
+    @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.)
+
+    @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
+
+    @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into
+        a native portaudio error code. Standard DirectSound result codes are documented at msdn.
+
+    @todo implement IsFormatSupported
+
+    @todo check that CoInitialize() CoUninitialize() are always correctly
+        paired, even in error cases.
+
+    @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
+
+    @todo make sure all buffers have been played before stopping the stream
+        when the stream callback returns paComplete
+
+    old TODOs from phil, need to work out if these have been done:
+        O- fix "patest_stop.c"
+*/
+
+#include <stdio.h>
+#include <string.h> /* strlen() */
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "dsound_wrapper.h"
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment( lib, "dsound.lib" )
+#pragma comment( lib, "winmm.lib" )
+#endif
+
+
+#define PRINT(x) /* { printf x; fflush(stdout); } */
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x)  /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+#define PA_USE_HIGH_LATENCY   (0)
+#if PA_USE_HIGH_LATENCY
+#define PA_WIN_9X_LATENCY     (500)
+#define PA_WIN_NT_LATENCY     (600)
+#else
+#define PA_WIN_9X_LATENCY     (140)
+#define PA_WIN_NT_LATENCY     (280)
+#endif
+
+#define PA_WIN_WDM_LATENCY       (120)
+
+#define SECONDS_PER_MSEC      (0.001)
+#define MSEC_PER_SECOND       (1000)
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* FIXME: should convert hr to a string */
+#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
+    PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
+
+/************************************************* DX Prototypes **********/
+static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
+                                     LPCTSTR lpszDesc,
+                                     LPCTSTR lpszDrvName,
+                                     LPVOID lpContext );
+
+/************************************************************************************/
+/********************** Structures **************************************************/
+/************************************************************************************/
+/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct PaWinDsDeviceInfo
+{
+    GUID                             guid;
+    GUID                            *lpGUID;
+    double                           sampleRates[3];
+} PaWinDsDeviceInfo;
+
+typedef struct
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface    callbackStreamInterface;
+    PaUtilStreamInterface    blockingStreamInterface;
+
+    PaUtilAllocationGroup   *allocations;
+
+    /* implementation specific data goes here */
+    PaWinDsDeviceInfo       *winDsDeviceInfos;
+
+} PaWinDsHostApiRepresentation;
+
+/* PaWinDsStream - a stream data structure specifically for this implementation */
+
+typedef struct PaWinDsStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+/* DirectSound specific data. */
+    DSoundWrapper    directSoundWrapper;
+    MMRESULT         timerID;
+    BOOL             ifInsideCallback;  /* Test for reentrancy. */
+    int              framesPerDSBuffer;
+    double           framesWritten;
+    double           secondsPerHostByte; /* Used to optimize latency calculation for outTime */
+
+    PaStreamCallbackFlags callbackFlags;
+    
+/* FIXME - move all below to PaUtilStreamRepresentation */
+    volatile int     isStarted;
+    volatile int     isActive;
+    volatile int     stopProcessing; /* stop thread once existing buffers have been returned */
+    volatile int     abortProcessing; /* stop thread immediately */
+} PaWinDsStream;
+
+
+/************************************************************************************
+** Duplicate the input string using the allocations allocator.
+** A NULL string is converted to a zero length string.
+** If memory cannot be allocated, NULL is returned.
+**/
+static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
+{
+    char *result = 0;
+    
+    if( src != NULL )
+    {
+        size_t len = strlen(src);
+        result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
+        if( result )
+            memcpy( (void *) result, src, len+1 );
+    }
+    else
+    {
+        result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
+        if( result )
+            result[0] = '\0';
+    }
+
+    return result;
+}
+
+/************************************************************************************
+** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
+** information during device enumeration.
+*/
+typedef struct DSDeviceNameAndGUID{
+    char *name; // allocated from parent's allocations, never deleted by this structure
+    GUID guid;
+    LPGUID lpGUID;
+} DSDeviceNameAndGUID;
+
+typedef struct DSDeviceNameAndGUIDVector{
+    PaUtilAllocationGroup *allocations;
+    PaError enumerationError;
+
+    int count;
+    int free;
+    DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
+} DSDeviceNameAndGUIDVector;
+
+static PaError InitializeDSDeviceNameAndGUIDVector(
+        DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
+{
+    PaError result = paNoError;
+
+    guidVector->allocations = allocations;
+    guidVector->enumerationError = paNoError;
+
+    guidVector->count = 0;
+    guidVector->free = 8;
+    guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
+    if( guidVector->items == NULL )
+        result = paInsufficientMemory;
+    
+    return result;
+}
+
+static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
+{
+    PaError result = paNoError;
+    DSDeviceNameAndGUID *newItems;
+    int i;
+    
+    /* double size of vector */
+    int size = guidVector->count + guidVector->free;
+    guidVector->free += size;
+
+    newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
+    if( newItems == NULL )
+    {
+        result = paInsufficientMemory;
+    }
+    else
+    {
+        for( i=0; i < guidVector->count; ++i )
+        {
+            newItems[i].name = guidVector->items[i].name;
+            if( guidVector->items[i].lpGUID == NULL )
+            {
+                newItems[i].lpGUID = NULL;
+            }
+            else
+            {
+                newItems[i].lpGUID = &newItems[i].guid;
+                memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
+            }
+        }
+
+        LocalFree( guidVector->items );
+        guidVector->items = newItems;
+    }                                
+
+    return result;
+}
+
+/*
+    it's safe to call DSDeviceNameAndGUIDVector multiple times
+*/
+static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
+{
+    PaError result = paNoError;
+
+    if( guidVector->items != NULL )
+    {
+        if( LocalFree( guidVector->items ) != NULL )
+            result = paInsufficientMemory;              /** @todo this isn't the correct error to return from a deallocation failure */
+
+        guidVector->items = NULL;
+    }
+
+    return result;
+}
+
+/************************************************************************************
+** Collect preliminary device information during DirectSound enumeration 
+*/
+static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
+                                     LPCTSTR lpszDesc,
+                                     LPCTSTR lpszDrvName,
+                                     LPVOID lpContext )
+{
+    DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
+    PaError error;
+
+    (void) lpszDrvName; /* unused variable */
+
+    if( namesAndGUIDs->free == 0 )
+    {
+        error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
+        if( error != paNoError )
+        {
+            namesAndGUIDs->enumerationError = error;
+            return FALSE;
+        }
+    }
+    
+    /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
+    if( lpGUID == NULL )
+    {
+        namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
+    }
+    else
+    {
+        namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
+                &namesAndGUIDs->items[namesAndGUIDs->count].guid;
+      
+        memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
+    }
+
+    namesAndGUIDs->items[namesAndGUIDs->count].name =
+            DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
+    if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
+    {
+        namesAndGUIDs->enumerationError = paInsufficientMemory;
+        return FALSE;
+    }
+
+    ++namesAndGUIDs->count;
+    --namesAndGUIDs->free;
+    
+    return TRUE;
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_  (13) /* must match array length below */
+static double defaultSampleRateSearchOrder_[] =
+    { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
+        16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
+
+
+/************************************************************************************
+** Extract capabilities from an output device, and add it to the device info list
+** if successful. This function assumes that there is enough room in the
+** device info list to accomodate all entries.
+**
+** The device will not be added to the device list if any errors are encountered.
+*/
+static PaError AddOutputDeviceInfoFromDirectSound(
+        PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
+{
+    PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
+    PaDeviceInfo                 *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
+    PaWinDsDeviceInfo            *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
+    HRESULT                       hr;
+    LPDIRECTSOUND                 lpDirectSound;
+    DSCAPS                        caps;
+    int                           deviceOK = TRUE;
+    PaError                       result = paNoError;
+    int                           i;
+    
+    /* Copy GUID to the device info structure. Set pointer. */
+    if( lpGUID == NULL )
+    {
+        winDsDeviceInfo->lpGUID = NULL;
+    }
+    else
+    {
+        memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+        winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+    }
+
+    
+    /* Create a DirectSound object for the specified GUID
+        Note that using CoCreateInstance doesn't work on windows CE.
+    */
+    hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
+
+    /** try using CoCreateInstance because DirectSoundCreate was hanging under
+        some circumstances - note this was probably related to the
+        #define BOOL short bug which has now been fixed
+        @todo delete this comment and the following code once we've ensured
+        there is no bug.
+    */
+    /*
+    hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
+            &IID_IDirectSound, (void**)&lpDirectSound );
+
+    if( hr == S_OK )
+    {
+        hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
+    }
+    */
+    
+    if( hr != DS_OK )
+    {
+        DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
+        deviceOK = FALSE;
+    }
+    else
+    {
+        /* Query device characteristics. */
+        memset( &caps, 0, sizeof(caps) ); 
+        caps.dwSize = sizeof(caps);
+        hr = IDirectSound_GetCaps( lpDirectSound, &caps );
+        if( hr != DS_OK )
+        {
+            DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
+            deviceOK = FALSE;
+        }
+        else
+        {
+
+#ifndef PA_NO_WMME
+            if( caps.dwFlags & DSCAPS_EMULDRIVER )
+            {
+                /* If WMME supported, then reject Emulated drivers because they are lousy. */
+                deviceOK = FALSE;
+            }
+#endif
+
+            if( deviceOK )
+            {
+                deviceInfo->maxInputChannels = 0;
+                /* Mono or stereo device? */
+                deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
+
+                deviceInfo->defaultLowInputLatency = 0.;    /** @todo IMPLEMENT ME */
+                deviceInfo->defaultLowOutputLatency = 0.;   /** @todo IMPLEMENT ME */
+                deviceInfo->defaultHighInputLatency = 0.;   /** @todo IMPLEMENT ME */
+                deviceInfo->defaultHighOutputLatency = 0.;  /** @todo IMPLEMENT ME */
+                
+                /* initialize defaultSampleRate */
+                
+                if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
+                {
+                    /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
+                    deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+
+                    for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+                    {
+                        if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
+                                && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){
+
+                            deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
+                            break;
+                        }
+                    }
+                }
+                else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
+                {
+                    if( caps.dwMinSecondarySampleRate == 0 )
+                    {
+                        /*
+                        ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
+                        ** But it supports continuous sampling.
+                        ** So fake range of rates, and hope it really supports it.
+                        */
+                        deviceInfo->defaultSampleRate = 44100.0f;
+
+                        DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
+                    }
+                    else
+                    {
+	                    deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+                    }
+                }
+                else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
+                {
+                    /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
+                    ** But we know that they really support a range of rates!
+                    ** So when we see a ridiculous set of rates, assume it is a range.
+                    */
+                  deviceInfo->defaultSampleRate = 44100.0f;
+                  DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
+                }
+                else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+
+
+                //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
+                // dwFlags | DSCAPS_CONTINUOUSRATE 
+            }
+        }
+
+        IDirectSound_Release( lpDirectSound );
+    }
+
+    if( deviceOK )
+    {
+        deviceInfo->name = name;
+
+        if( lpGUID == NULL )
+            hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
+            
+        hostApi->info.deviceCount++;
+    }
+
+    return result;
+}
+
+
+/************************************************************************************
+** Extract capabilities from an input device, and add it to the device info list
+** if successful. This function assumes that there is enough room in the
+** device info list to accomodate all entries.
+**
+** The device will not be added to the device list if any errors are encountered.
+*/
+static PaError AddInputDeviceInfoFromDirectSoundCapture(
+        PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
+{
+    PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
+    PaDeviceInfo                 *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
+    PaWinDsDeviceInfo            *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
+    HRESULT                       hr;
+    LPDIRECTSOUNDCAPTURE          lpDirectSoundCapture;
+    DSCCAPS                       caps;
+    int                           deviceOK = TRUE;
+    PaError                       result = paNoError;
+    
+    /* Copy GUID to the device info structure. Set pointer. */
+    if( lpGUID == NULL )
+    {
+        winDsDeviceInfo->lpGUID = NULL;
+    }
+    else
+    {
+        winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+        memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+    }
+
+
+    hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
+
+    /** try using CoCreateInstance because DirectSoundCreate was hanging under
+        some circumstances - note this was probably related to the
+        #define BOOL short bug which has now been fixed
+        @todo delete this comment and the following code once we've ensured
+        there is no bug.
+    */
+    /*
+    hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
+            &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
+    */
+    if( hr != DS_OK )
+    {
+        DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
+        deviceOK = FALSE;
+    }
+    else
+    {
+        /* Query device characteristics. */
+        memset( &caps, 0, sizeof(caps) );
+        caps.dwSize = sizeof(caps);
+        hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
+        if( hr != DS_OK )
+        {
+            DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
+            deviceOK = FALSE;
+        }
+        else
+        {
+#ifndef PA_NO_WMME
+            if( caps.dwFlags & DSCAPS_EMULDRIVER )
+            {
+                /* If WMME supported, then reject Emulated drivers because they are lousy. */
+                deviceOK = FALSE;
+            }
+#endif
+
+            if( deviceOK )
+            {
+                deviceInfo->maxInputChannels = caps.dwChannels;
+                deviceInfo->maxOutputChannels = 0;
+
+                deviceInfo->defaultLowInputLatency = 0.;    /** @todo IMPLEMENT ME */
+                deviceInfo->defaultLowOutputLatency = 0.;   /** @todo IMPLEMENT ME */
+                deviceInfo->defaultHighInputLatency = 0.;   /** @todo IMPLEMENT ME */
+                deviceInfo->defaultHighOutputLatency = 0.;  /** @todo IMPLEMENT ME */
+
+/*  constants from a WINE patch by Francois Gouget, see:
+    http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+
+    ---
+    Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
+    From: Francois Gouget <fgouget@ ... .fr>
+    To: Ross Bencina <rbencina@ ... .au>
+    Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
+
+    [snip]
+
+    I give you permission to use the patch below under the BSD license.
+    http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+
+    [snip]
+*/
+#ifndef WAVE_FORMAT_48M08
+#define WAVE_FORMAT_48M08      0x00001000    /* 48     kHz, Mono,   8-bit  */
+#define WAVE_FORMAT_48S08      0x00002000    /* 48     kHz, Stereo, 8-bit  */
+#define WAVE_FORMAT_48M16      0x00004000    /* 48     kHz, Mono,   16-bit */
+#define WAVE_FORMAT_48S16      0x00008000    /* 48     kHz, Stereo, 16-bit */
+#define WAVE_FORMAT_96M08      0x00010000    /* 96     kHz, Mono,   8-bit  */
+#define WAVE_FORMAT_96S08      0x00020000    /* 96     kHz, Stereo, 8-bit  */
+#define WAVE_FORMAT_96M16      0x00040000    /* 96     kHz, Mono,   16-bit */
+#define WAVE_FORMAT_96S16      0x00080000    /* 96     kHz, Stereo, 16-bit */
+#endif
+
+                /* defaultSampleRate */
+                if( caps.dwChannels == 2 )
+                {
+                    if( caps.dwFormats & WAVE_FORMAT_4S16 )
+                        deviceInfo->defaultSampleRate = 44100.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_48S16 )
+                        deviceInfo->defaultSampleRate = 48000.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_2S16 )
+                        deviceInfo->defaultSampleRate = 22050.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_1S16 )
+                        deviceInfo->defaultSampleRate = 11025.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_96S16 )
+                        deviceInfo->defaultSampleRate = 96000.0;
+                    else
+                        deviceInfo->defaultSampleRate = 0.;
+                }
+                else if( caps.dwChannels == 1 )
+                {
+                    if( caps.dwFormats & WAVE_FORMAT_4M16 )
+                        deviceInfo->defaultSampleRate = 44100.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_48M16 )
+                        deviceInfo->defaultSampleRate = 48000.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_2M16 )
+                        deviceInfo->defaultSampleRate = 22050.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_1M16 )
+                        deviceInfo->defaultSampleRate = 11025.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_96M16 )
+                        deviceInfo->defaultSampleRate = 96000.0;
+                    else
+                        deviceInfo->defaultSampleRate = 0.;
+                }
+                else deviceInfo->defaultSampleRate = 0.;
+            }
+        }
+        
+        IDirectSoundCapture_Release( lpDirectSoundCapture );
+    }
+
+    if( deviceOK )
+    {
+        deviceInfo->name = name;
+
+        if( lpGUID == NULL )
+            hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
+
+        hostApi->info.deviceCount++;
+    }
+
+    return result;
+}
+
+
+/***********************************************************************************/
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    int i, deviceCount;
+    PaWinDsHostApiRepresentation *winDsHostApi;
+    DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs;
+    PaDeviceInfo *deviceInfoArray;
+
+    HRESULT hr = CoInitialize(NULL);        /** @todo: should uninitialize too */
+    if( FAILED(hr) ){
+        return paUnanticipatedHostError;
+    }            
+
+    /* initialise guid vectors so they can be safely deleted on error */
+    inputNamesAndGUIDs.items = NULL;
+    outputNamesAndGUIDs.items = NULL;
+
+    DSW_InitializeDSoundEntryPoints();
+
+    winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
+    if( !winDsHostApi )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !winDsHostApi->allocations )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    *hostApi = &winDsHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paDirectSound;
+    (*hostApi)->info.name = "Windows DirectSound";
+    
+    (*hostApi)->info.deviceCount = 0;
+    (*hostApi)->info.defaultInputDevice = paNoDevice;
+    (*hostApi)->info.defaultOutputDevice = paNoDevice;
+
+    
+/* DSound - enumerate devices to count them and to gather their GUIDs */
+
+
+    result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations );
+    if( result != paNoError )
+        goto error;
+
+    result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations );
+    if( result != paNoError )
+        goto error;
+
+    dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs );
+
+    dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs );
+
+    if( inputNamesAndGUIDs.enumerationError != paNoError )
+    {
+        result = inputNamesAndGUIDs.enumerationError;
+        goto error;
+    }
+
+    if( outputNamesAndGUIDs.enumerationError != paNoError )
+    {
+        result = outputNamesAndGUIDs.enumerationError;
+        goto error;
+    }
+
+    deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count;
+
+    if( deviceCount > 0 )
+    {
+        /* allocate array for pointers to PaDeviceInfo structs */
+        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+        if( !(*hostApi)->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all PaDeviceInfo structs in a contiguous block */
+        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+                winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all DSound specific info structs in a contiguous block */
+        winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
+                winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
+        if( !winDsHostApi->winDsDeviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( i=0; i < deviceCount; ++i )
+        {
+            PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+            deviceInfo->structVersion = 2;
+            deviceInfo->hostApi = hostApiIndex;
+            deviceInfo->name = 0;
+            (*hostApi)->deviceInfos[i] = deviceInfo;
+        }
+
+        for( i=0; i< inputNamesAndGUIDs.count; ++i )
+        {
+            result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
+                    inputNamesAndGUIDs.items[i].name,
+                    inputNamesAndGUIDs.items[i].lpGUID );
+            if( result != paNoError )
+                goto error;
+        }
+
+        for( i=0; i< outputNamesAndGUIDs.count; ++i )
+        {
+            result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
+                    outputNamesAndGUIDs.items[i].name,
+                    outputNamesAndGUIDs.items[i].lpGUID );
+            if( result != paNoError )
+                goto error;
+        }
+    }    
+
+    result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
+    if( result != paNoError )
+        goto error;
+
+    result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
+    if( result != paNoError )
+        goto error;
+
+    
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    return result;
+
+error:
+    if( winDsHostApi )
+    {
+        if( winDsHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( winDsHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
+        }
+                
+        PaUtil_FreeMemory( winDsHostApi );
+    }
+
+    TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
+    TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
+
+    return result;
+}
+
+
+/***********************************************************************************/
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+
+    /*
+        IMPLEMENT ME:
+            - clean up any resources not handled by the allocation group
+    */
+
+    if( winDsHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( winDsHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( winDsHostApi );
+
+    DSW_TerminateDSoundEntryPoints();
+
+    CoUninitialize();
+}
+
+
+/* Set minimal latency based on whether NT or Win95.
+ * NT has higher latency.
+ */
+static int PaWinDS_GetMinSystemLatency( void )
+{
+    int minLatencyMsec;
+    /* Set minimal latency based on whether NT or other OS.
+     * NT has higher latency.
+     */
+    OSVERSIONINFO osvi;
+	osvi.dwOSVersionInfoSize = sizeof( osvi );
+	GetVersionEx( &osvi );
+    DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
+    DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
+    DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
+    /* Check for NT */
+	if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
+	{
+		minLatencyMsec = PA_WIN_NT_LATENCY;
+	}
+	else if(osvi.dwMajorVersion >= 5)
+	{
+		minLatencyMsec = PA_WIN_WDM_LATENCY;
+	}
+	else
+	{
+		minLatencyMsec = PA_WIN_9X_LATENCY;
+	}
+    return minLatencyMsec;
+}
+
+/***********************************************************************************/
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+    
+    /*
+        IMPLEMENT ME:
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported if necessary
+
+            - check that the device supports sampleRate
+
+        Because the buffer adapter handles conversion between all standard
+        sample formats, the following checks are only required if paCustomFormat
+        is implemented, or under some other unusual conditions.
+
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+    */
+
+    return paFormatIsSupported;
+}
+
+
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+**
+**    set PA_MIN_LATENCY_MSEC=200
+**
+** in the AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be determined
+** based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
+#define PA_ENV_BUF_SIZE  (32)
+
+static int PaWinDs_GetMinLatencyFrames( double sampleRate )
+{
+    char      envbuf[PA_ENV_BUF_SIZE];
+    DWORD     hresult;
+    int       minLatencyMsec = 0;
+
+    /* Let user determine minimal latency by setting environment variable. */
+    hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
+    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+    {
+        minLatencyMsec = atoi( envbuf );
+    }
+    else
+    {
+        minLatencyMsec = PaWinDS_GetMinSystemLatency();
+#if PA_USE_HIGH_LATENCY
+        PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+#endif
+
+    }
+
+    return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
+}
+
+#ifndef NDEBUG
+#define EZ  = 0
+#else
+#define EZ
+#endif
+
+/***********************************************************************************/
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+    PaWinDsStream *stream = 0;
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat EZ, outputSampleFormat EZ;
+    PaSampleFormat hostInputSampleFormat EZ, hostOutputSampleFormat EZ;
+    unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
+
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+        suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
+
+        /* IDEA: the following 3 checks could be performed by default by pa_front
+            unless some flag indicated otherwise */
+            
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+            
+        /* validate hostApiSpecificStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+        suggestedInputLatencyFrames = 0;
+    }
+
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate hostApiSpecificStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */            
+    }
+    else
+    {
+        outputChannelCount = 0;
+        suggestedOutputLatencyFrames = 0;
+    }
+
+
+    /*
+        IMPLEMENT ME:
+
+        ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
+
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+
+            - check that the device supports sampleRate
+
+            - alter sampleRate to a close allowable rate if possible / necessary
+
+            - validate suggestedInputLatency and suggestedOutputLatency parameters,
+                use default values where necessary
+    */
+
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+
+
+    stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
+    if( !stream )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    if( streamCallback )
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &winDsHostApi->callbackStreamInterface, streamCallback, userData );
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &winDsHostApi->blockingStreamInterface, streamCallback, userData );
+    }
+    
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+    if( inputParameters )
+    {
+        /* IMPLEMENT ME - establish which  host formats are available */
+        hostInputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat );
+    }
+
+    if( outputParameters )
+    {
+        /* IMPLEMENT ME - establish which  host formats are available */
+        hostOutputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat );
+    }
+    
+    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+                    inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+                    outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+                    sampleRate, streamFlags, framesPerBuffer,
+                    framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
+                /* This next mode is required because DS can split the host buffer when it wraps around. */
+                    paUtilVariableHostBufferSizePartialUsageAllowed,
+                    streamCallback, userData );
+    if( result != paNoError )
+        goto error;
+
+
+    stream->streamRepresentation.streamInfo.inputLatency =
+            PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor);   /* FIXME: not initialised anywhere else */
+    stream->streamRepresentation.streamInfo.outputLatency =
+            PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);    /* FIXME: not initialised anywhere else */
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+    
+/* DirectSound specific initialization */
+    {
+        HRESULT          hr;
+        int              bytesPerDirectSoundBuffer;
+        DSoundWrapper   *dsw;
+        int              userLatencyFrames;
+        int              minLatencyFrames;
+
+        stream->timerID = 0;
+        dsw = &stream->directSoundWrapper;
+        DSW_Init( dsw );
+
+    /* Get system minimum latency. */
+        minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
+
+    /* Let user override latency by passing latency parameter. */
+        userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames)
+                    ? suggestedInputLatencyFrames
+                    : suggestedOutputLatencyFrames;
+        if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames;
+
+    /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */
+        if( framesPerBuffer == paFramesPerBufferUnspecified )
+        {
+        /* App support variable framesPerBuffer */
+            stream->framesPerDSBuffer = minLatencyFrames;
+
+            stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate;
+        }
+        else
+        {
+        /* Round up to number of buffers needed to guarantee that latency. */
+            int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer;
+            if( numUserBuffers < 1 ) numUserBuffers = 1;
+            numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */
+            stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers;
+
+            stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate;
+        }
+
+        {
+            /** @todo REVIEW: this calculation seems incorrect to me - rossb. */
+            int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate);
+            PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
+        }
+
+
+        /* ------------------ OUTPUT */
+        if( outputParameters )
+        {
+            /*
+            PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
+            DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
+            */
+            
+            bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short);
+            if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
+            {
+                result = paBufferTooSmall;
+                goto error;
+            }
+            else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
+            {
+                result = paBufferTooBig;
+                goto error;
+            }
+
+
+            hr = dswDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID,
+                &dsw->dsw_pDirectSound,   NULL );
+            if( hr != DS_OK )
+            {
+                ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+                goto error;
+            }
+            hr = DSW_InitOutputBuffer( dsw,
+                                       (unsigned long) (sampleRate + 0.5),
+                                       (WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer );
+            DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
+            if( hr != DS_OK )
+            {
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+                goto error;
+            }
+            /* Calculate value used in latency calculation to avoid real-time divides. */
+            stream->secondsPerHostByte = 1.0 /
+                (stream->bufferProcessor.bytesPerHostOutputSample *
+                outputChannelCount * sampleRate);
+        }
+
+        /* ------------------ INPUT */
+        if( inputParameters )
+        {
+            /*
+            PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
+            DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
+            */
+            
+            bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short);
+            if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
+            {
+                result = paBufferTooSmall;
+                goto error;
+            }
+            else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
+            {
+                result = paBufferTooBig;
+                goto error;
+            }
+
+            hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID,
+                &dsw->dsw_pDirectSoundCapture,   NULL );
+            if( hr != DS_OK )
+            {
+                ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+                goto error;
+            }
+            hr = DSW_InitInputBuffer( dsw,
+                                      (unsigned long) (sampleRate + 0.5),
+                                      (WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer );
+            DBUG(("DSW_InitInputBuffer() returns %x\n", hr));
+            if( hr != DS_OK )
+            {
+                ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+                goto error;
+            }
+        }
+
+    }
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( stream )
+        PaUtil_FreeMemory( stream );
+
+    return result;
+}
+
+
+/***********************************************************************************/
+static PaError Pa_TimeSlice( PaWinDsStream *stream )
+{
+    PaError           result = 0;   /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/
+    DSoundWrapper    *dsw;
+    long              numFrames = 0;
+    long              bytesEmpty = 0;
+    long              bytesFilled = 0;
+    long              bytesToXfer = 0;
+    long              framesToXfer = 0;
+    long              numInFramesReady = 0;
+    long              numOutFramesReady = 0;
+    long              bytesProcessed;
+    HRESULT           hresult;
+    double            outputLatency = 0;
+    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
+    
+/* Input */
+    LPBYTE            lpInBuf1 = NULL;
+    LPBYTE            lpInBuf2 = NULL;
+    DWORD             dwInSize1 = 0;
+    DWORD             dwInSize2 = 0;
+/* Output */
+    LPBYTE            lpOutBuf1 = NULL;
+    LPBYTE            lpOutBuf2 = NULL;
+    DWORD             dwOutSize1 = 0;
+    DWORD             dwOutSize2 = 0;
+
+    dsw = &stream->directSoundWrapper;
+
+    /* How much input data is available? */
+    if( stream->bufferProcessor.inputChannelCount > 0 )
+    {
+        DSW_QueryInputFilled( dsw, &bytesFilled );
+        framesToXfer = numInFramesReady = bytesFilled / dsw->dsw_BytesPerInputFrame;
+        outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
+
+        /** @todo Check for overflow */
+    }
+
+    /* How much output room is available? */
+    if( stream->bufferProcessor.outputChannelCount > 0 )
+    {
+        UINT previousUnderflowCount = dsw->dsw_OutputUnderflows;
+        DSW_QueryOutputSpace( dsw, &bytesEmpty );
+        framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame;
+
+        /* Check for underflow */
+        if( dsw->dsw_OutputUnderflows != previousUnderflowCount )
+            stream->callbackFlags |= paOutputUnderflow;
+    }
+
+    if( (numInFramesReady > 0) && (numOutFramesReady > 0) )
+    {
+        framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
+    }
+
+    if( framesToXfer > 0 )
+    {
+
+        PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+    /* The outputBufferDacTime parameter should indicates the time at which
+        the first sample of the output buffer is heard at the DACs. */
+        timeInfo.currentTime = PaUtil_GetTime();
+        timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency;
+
+
+        PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
+        stream->callbackFlags = 0;
+        
+    /* Input */
+        if( stream->bufferProcessor.inputChannelCount > 0 )
+        {
+            bytesToXfer = framesToXfer * dsw->dsw_BytesPerInputFrame;
+            hresult = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer,
+                dsw->dsw_ReadOffset, bytesToXfer,
+                (void **) &lpInBuf1, &dwInSize1,
+                (void **) &lpInBuf2, &dwInSize2, 0);
+            if (hresult != DS_OK)
+            {
+                ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
+                goto error2;
+            }
+
+            numFrames = dwInSize1 / dsw->dsw_BytesPerInputFrame;
+            PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
+            PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
+        /* Is input split into two regions. */
+            if( dwInSize2 > 0 )
+            {
+                numFrames = dwInSize2 / dsw->dsw_BytesPerInputFrame;
+                PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
+                PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
+            }
+        }
+
+    /* Output */
+        if( stream->bufferProcessor.outputChannelCount > 0 )
+        {
+            bytesToXfer = framesToXfer * dsw->dsw_BytesPerOutputFrame;
+            hresult = IDirectSoundBuffer_Lock ( dsw->dsw_OutputBuffer,
+                dsw->dsw_WriteOffset, bytesToXfer,
+                (void **) &lpOutBuf1, &dwOutSize1,
+                (void **) &lpOutBuf2, &dwOutSize2, 0);
+            if (hresult != DS_OK)
+            {
+                ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
+                goto error1;
+            }
+
+            numFrames = dwOutSize1 / dsw->dsw_BytesPerOutputFrame;
+            PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
+            PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
+
+        /* Is output split into two regions. */
+            if( dwOutSize2 > 0 )
+            {
+                numFrames = dwOutSize2 / dsw->dsw_BytesPerOutputFrame;
+                PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
+                PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
+            }
+        }
+
+        result = paContinue;
+        numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result );
+        stream->framesWritten += numFrames;
+        
+        if( stream->bufferProcessor.outputChannelCount > 0 )
+        {
+        /* FIXME: an underflow could happen here */
+
+        /* Update our buffer offset and unlock sound buffer */
+            bytesProcessed = numFrames * dsw->dsw_BytesPerOutputFrame;
+            dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + bytesProcessed) % dsw->dsw_OutputSize;
+            IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
+            dsw->dsw_FramesWritten += numFrames;
+        }
+
+error1:
+        if( stream->bufferProcessor.inputChannelCount > 0 )
+        {
+        /* FIXME: an overflow could happen here */
+
+        /* Update our buffer offset and unlock sound buffer */
+            bytesProcessed = numFrames * dsw->dsw_BytesPerInputFrame;
+            dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + bytesProcessed) % dsw->dsw_InputSize;
+            IDirectSoundCaptureBuffer_Unlock( dsw->dsw_InputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
+        }
+error2:
+
+        PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
+
+    }
+    
+    return result;
+}
+/*******************************************************************/
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
+{
+    PaWinDsStream *stream;
+
+    /* suppress unused variable warnings */
+    (void) uID;
+    (void) uMsg;
+    (void) dw1;
+    (void) dw2;
+    
+    stream = (PaWinDsStream *) dwUser;
+    if( stream == NULL ) return;
+
+    if( stream->isActive )
+    {
+        if( stream->abortProcessing )
+        {
+            stream->isActive = 0;
+        }
+        else if( stream->stopProcessing )
+        {
+            DSoundWrapper   *dsw = &stream->directSoundWrapper;
+            if( stream->bufferProcessor.outputChannelCount > 0 )
+            {
+                DSW_ZeroEmptySpace( dsw );
+                /* clear isActive when all sound played */
+                if( dsw->dsw_FramesPlayed >= stream->framesWritten )
+                {
+                    stream->isActive = 0;
+                }
+            }
+            else
+            {
+                stream->isActive = 0;
+            }
+        }
+        else
+        {
+            if( Pa_TimeSlice( stream ) != 0)  /* Call time slice independant of timing method. */
+            {
+                /* FIXME implement handling of paComplete and paAbort if possible */
+                stream->stopProcessing = 1;
+            }
+        }
+
+        if( !stream->isActive ){
+            if( stream->streamRepresentation.streamFinishedCallback != 0 )
+                stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+        }
+    }
+}
+
+/***********************************************************************************
+    When CloseStream() is called, the multi-api layer ensures that
+    the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    DSW_Term( &stream->directSoundWrapper );
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+    PaUtil_FreeMemory( stream );
+
+    return result;
+}
+
+/***********************************************************************************/
+static PaError StartStream( PaStream *s )
+{
+    PaError          result = paNoError;
+    PaWinDsStream   *stream = (PaWinDsStream*)s;
+    HRESULT          hr;
+
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+    
+    if( stream->bufferProcessor.inputChannelCount > 0 )
+    {
+        hr = DSW_StartInput( &stream->directSoundWrapper );
+        DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
+        if( hr != DS_OK )
+        {
+            result = paUnanticipatedHostError;
+            PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+            goto error;
+        }
+    }
+
+    stream->framesWritten = 0;
+    stream->callbackFlags = 0;
+
+    stream->abortProcessing = 0;
+    stream->stopProcessing = 0;
+    stream->isActive = 1;
+
+    if( stream->bufferProcessor.outputChannelCount > 0 )
+    {
+        /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */
+        result = Pa_TimeSlice( stream );
+        if( result != paNoError ) return result; // FIXME - what if finished?
+
+        hr = DSW_StartOutput( &stream->directSoundWrapper );
+        DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
+        if( hr != DS_OK )
+        {
+            result = paUnanticipatedHostError;
+            PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+            goto error;
+        }
+    }
+
+
+    /* Create timer that will wake us up so we can fill the DSound buffer. */
+    {
+        int resolution;
+        int framesPerWakeup = stream->framesPerDSBuffer / 4;
+        int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
+        if( msecPerWakeup < 10 ) msecPerWakeup = 10;
+        else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
+        resolution = msecPerWakeup/4;
+        stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
+                                             (DWORD) stream, TIME_PERIODIC );
+    }
+    if( stream->timerID == 0 )
+    {
+        stream->isActive = 0;
+        result = paUnanticipatedHostError;
+        PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+        goto error;
+    }
+
+    stream->isStarted = TRUE;
+
+error:
+    return result;
+}
+
+
+/***********************************************************************************/
+static PaError StopStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+    HRESULT          hr;
+    int timeoutMsec;
+
+    stream->stopProcessing = 1;
+    /* Set timeout at 20% beyond maximum time we might wait. */
+    timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate);
+    while( stream->isActive && (timeoutMsec > 0)  )
+    {
+        Sleep(10);
+        timeoutMsec -= 10;
+    }
+    if( stream->timerID != 0 )
+    {
+	timeKillEvent(stream->timerID);  /* Stop callback timer. */
+        stream->timerID = 0;
+    }
+
+
+    if( stream->bufferProcessor.outputChannelCount > 0 )
+    {
+        hr = DSW_StopOutput( &stream->directSoundWrapper );
+    }
+
+    if( stream->bufferProcessor.inputChannelCount > 0 )
+    {
+        hr = DSW_StopInput( &stream->directSoundWrapper );
+    }
+
+    stream->isStarted = FALSE;
+
+    return result;
+}
+
+
+/***********************************************************************************/
+static PaError AbortStream( PaStream *s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    stream->abortProcessing = 1;
+    return StopStream( s );
+}
+
+
+/***********************************************************************************/
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    return !stream->isStarted;
+}
+
+
+/***********************************************************************************/
+static PaError IsStreamActive( PaStream *s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    return stream->isActive;
+}
+
+/***********************************************************************************/
+static PaTime GetStreamTime( PaStream *s )
+{
+    /* suppress unused variable warnings */
+    (void) s;
+
+    
+/*
+    new behavior for GetStreamTime is to return a stream based seconds clock
+    used for the outTime parameter to the callback.
+    FIXME: delete this comment when the other unnecessary related code has
+    been cleaned from this file.
+
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+    DSoundWrapper   *dsw;
+    dsw = &stream->directSoundWrapper;
+    return dsw->dsw_FramesPlayed;
+*/
+    return PaUtil_GetTime();
+}
+
+
+/***********************************************************************************/
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+
+/***********************************************************************************
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return paNoError;
+}
+
+
+/***********************************************************************************/
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return paNoError;
+}
+
+
+/***********************************************************************************/
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+/***********************************************************************************/
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c	(revision 1753)
@@ -0,0 +1,107 @@
+/*
+ * $Id: pa_win_hostapis.c,v 1.1.2.10 2004/09/08 17:31:37 rossbencina Exp $
+ * Portable Audio I/O Library Windows initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+    Win32 host API initialization function table.
+
+    @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what
+    the Unix version does, we should consider being consistent.
+*/
+
+
+#include <pa_hostapi.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+    {
+
+#ifndef PA_NO_WMME
+        PaWinMme_Initialize,
+#endif
+
+#ifndef PA_NO_DS
+        PaWinDs_Initialize,
+#endif
+
+#ifndef PA_NO_ASIO
+        PaAsio_Initialize,
+#endif
+
+/*
+#ifndef PA_NO_WDMKS
+        PaWinWdm_Initialize,
+#endif
+*/
+
+        PaSkeleton_Initialize, /* just for testing */
+
+        0   /* NULL terminated array */
+    };
+
+
+int paDefaultHostApiIndex = 0;
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_util.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_util.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_util.c	(revision 1753)
@@ -0,0 +1,155 @@
+/*
+ * $Id: pa_win_util.c,v 1.1.2.7 2003/09/15 18:30:26 rossbencina Exp $
+ * Portable Audio I/O Library
+ * Win32 platform-specific support functions
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/** @file
+ Win32 platform-specific support functions.
+
+    @todo Implement workaround for QueryPerformanceCounter() skipping forward
+    bug. (see msdn kb Q274323).
+*/
+ 
+#include <windows.h>
+#include <mmsystem.h> /* for timeGetTime() */
+
+#include "pa_util.h"
+
+
+/*
+   Track memory allocations to avoid leaks.
+ */
+
+#if PA_TRACK_MEMORY
+static int numAllocations_ = 0;
+#endif
+
+
+void *PaUtil_AllocateMemory( long size )
+{
+    void *result = GlobalAlloc( GPTR, size );
+
+#if PA_TRACK_MEMORY
+    if( result != NULL ) numAllocations_ += 1;
+#endif
+    return result;
+}
+
+
+void PaUtil_FreeMemory( void *block )
+{
+    if( block != NULL )
+    {
+        GlobalFree( block );
+#if PA_TRACK_MEMORY
+        numAllocations_ -= 1;
+#endif
+
+    }
+}
+
+
+int PaUtil_CountCurrentlyAllocatedBlocks( void )
+{
+#if PA_TRACK_MEMORY
+    return numAllocations_;
+#else
+    return 0;
+#endif
+}
+
+
+void Pa_Sleep( long msec )
+{
+    Sleep( msec );
+}
+
+static int usePerformanceCounter_;
+static double secondsPerTick_;
+
+void PaUtil_InitializeClock( void )
+{
+    LARGE_INTEGER ticksPerSecond;
+
+    if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
+    {
+        usePerformanceCounter_ = 1;
+        secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart;
+    }
+    else
+    {
+        usePerformanceCounter_ = 0;
+    }
+}
+
+
+double PaUtil_GetTime( void )
+{
+    LARGE_INTEGER time;
+
+    if( usePerformanceCounter_ )
+    {
+        /* FIXME:
+            according to this knowledge-base article, QueryPerformanceCounter
+            can skip forward by seconds!
+            http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
+
+            it may be better to use the rtdsc instruction using inline asm,
+            however then a method is needed to calculate a ticks/seconds ratio.
+        */
+        QueryPerformanceCounter( &time );
+        return time.QuadPart * secondsPerTick_;
+    }
+    else
+    {
+        return timeGetTime() * .001;
+    }
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c	(revision 1753)
@@ -0,0 +1,3644 @@
+/*
+ * $Id: pa_win_wmme.c,v 1.6.2.86 2004/02/21 11:38:28 rossbencina Exp $
+ * pa_win_wmme.c
+ * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)       
+ *                                                                                         
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Authors: Ross Bencina and Phil Burk
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+/* Modification History:
+ PLB = Phil Burk
+ JM = Julien Maillard
+ RDB = Ross Bencina
+ PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
+ PLB20010413 - check for excessive numbers of channels
+ PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
+               including conditional inclusion of memory.h,
+               and explicit typecasting on memory allocation
+ PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
+ PLB20010816 - pass process instead of thread to SetPriorityClass()
+ PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
+ JM20020118 - prevent hung thread when buffers underflow.
+ PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount
+ RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init
+ RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices
+               refactoring, renaming and fixed a few edge case bugs
+ RDB20020531 - converted to V19 framework
+ ** NOTE  maintanance history is now stored in CVS **
+*/
+
+/** @file
+	
+	@todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now,
+            needs to be reviewed and tested.)
+
+    @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput.
+
+    @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may
+                be called asynchronously from the callback thread. this is bad.
+
+    @todo implement inputBufferAdcTime in callback thread
+
+    @todo review/fix error recovery and cleanup in marked functions
+
+    @todo implement timeInfo for stream priming
+
+    @todo handle the case where the callback returns paAbort or paComplete during stream priming.
+
+    @todo review input overflow and output underflow handling in ReadStream and WriteStream
+
+Non-critical stuff for the future:
+
+    @todo Investigate supporting host buffer formats > 16 bits
+    
+    @todo define UNICODE and _UNICODE in the project settings and see what breaks
+
+*/
+
+/*
+    How it works:
+
+    For both callback and blocking read/write streams we open the MME devices
+    in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever
+    it has finished with a buffer (either filled it for input, or played it
+    for output). Where necessary we block waiting for Event objects using
+    WaitMultipleObjects().
+
+    When implementing a PA callback stream, we set up a high priority thread
+    which waits on the MME buffer Events and drains/fills the buffers when
+    they are ready.
+
+    When implementing a PA blocking read/write stream, we simply wait on these
+    Events (when necessary) inside the ReadStream() and WriteStream() functions.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <process.h>
+#include <assert.h>
+/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
+#ifndef __MWERKS__
+#include <malloc.h>
+#include <memory.h>
+#endif /* __MWERKS__ */
+
+#include "portaudio.h"
+#include "pa_trace.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "pa_win_wmme.h"
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment(lib, "winmm.lib")
+#endif
+
+/************************************************* Constants ********/
+
+#define PA_MME_USE_HIGH_DEFAULT_LATENCY_    (0)  /* For debugging glitches. */
+
+#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_                     (0.4)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_               (4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_	(4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_	(4)
+ #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_	(16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_				        (0.3)       /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_				        (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+#else
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_                     (0.2)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_               (2)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_	(3)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_	(2)
+ #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_	(16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_				        (0.1)       /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_				        (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+#endif
+
+/* Use higher latency for NT because it is even worse at real-time
+   operation than Win9x.
+*/
+#define PA_MME_WIN_NT_DEFAULT_LATENCY_      (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
+#define PA_MME_WIN_WDM_DEFAULT_LATENCY_     (PA_MME_WIN_9X_DEFAULT_LATENCY_)
+
+
+#define PA_MME_MIN_TIMEOUT_MSEC_        (1000)
+
+static const char constInputMapperSuffix_[] = " - Input";
+static const char constOutputMapperSuffix_[] = " - Output";
+
+/********************************************************************/
+
+typedef struct PaWinMmeStream PaWinMmeStream;     /* forward declaration */
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** stream,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* macros for setting last host error information */
+
+#ifdef UNICODE
+
+#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
+    {                                                                   \
+        wchar_t mmeErrorTextWide[ MAXERRORLENGTH ];                     \
+        char mmeErrorText[ MAXERRORLENGTH ];                            \
+        waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH );   \
+        WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
+            mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL );  \
+        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
+    }
+
+#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
+    {                                                                   \
+        wchar_t mmeErrorTextWide[ MAXERRORLENGTH ];                     \
+        char mmeErrorText[ MAXERRORLENGTH ];                            \
+        waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH );  \
+        WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
+            mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL );  \
+        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
+    }
+    
+#else /* !UNICODE */
+
+#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
+    {                                                                   \
+        char mmeErrorText[ MAXERRORLENGTH ];                            \
+        waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH );   \
+        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
+    }
+
+#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
+    {                                                                   \
+        char mmeErrorText[ MAXERRORLENGTH ];                            \
+        waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH );  \
+        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
+    }
+
+#endif /* UNICODE */
+
+
+static void PaMme_SetLastSystemError( DWORD errorCode )
+{
+    char *lpMsgBuf;
+    FormatMessage(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+        NULL,
+        errorCode,
+        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        (LPTSTR) &lpMsgBuf,
+        0,
+        NULL
+    );
+    PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf );
+    LocalFree( lpMsgBuf );
+}
+
+#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \
+    PaMme_SetLastSystemError( errorCode )
+
+
+/* PaError returning wrappers for some commonly used win32 functions
+    note that we allow passing a null ptr to have no effect.
+*/
+
+static PaError CreateEventWithPaError( HANDLE *handle,
+        LPSECURITY_ATTRIBUTES lpEventAttributes,
+        BOOL bManualReset,
+        BOOL bInitialState,
+        LPCTSTR lpName )
+{
+    PaError result = paNoError;
+
+    *handle = NULL;
+    
+    *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
+    if( *handle == NULL )
+    {
+        result = paUnanticipatedHostError;
+        PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+    }
+
+    return result;
+}
+
+
+static PaError ResetEventWithPaError( HANDLE handle )
+{
+    PaError result = paNoError;
+
+    if( handle )
+    {
+        if( ResetEvent( handle ) == 0 )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+        }
+    }
+
+    return result;
+}
+
+
+static PaError CloseHandleWithPaError( HANDLE handle )
+{
+    PaError result = paNoError;
+    
+    if( handle )
+    {
+        if( CloseHandle( handle ) == 0 )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+        }
+    }
+    
+    return result;
+}
+
+
+/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+    
+    int inputDeviceCount, outputDeviceCount;
+
+    /** winMmeDeviceIds is an array of WinMme device ids.
+        fields in the range [0, inputDeviceCount) are input device ids,
+        and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output
+        device ids.
+     */ 
+    UINT *winMmeDeviceIds;
+}
+PaWinMmeHostApiRepresentation;
+
+
+typedef struct
+{
+    PaDeviceInfo inheritedDeviceInfo;
+    DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
+}
+PaWinMmeDeviceInfo;
+
+
+/*************************************************************************
+ * Returns recommended device ID.
+ * On the PC, the recommended device can be specified by the user by
+ * setting an environment variable. For example, to use device #1.
+ *
+ *    set PA_RECOMMENDED_OUTPUT_DEVICE=1
+ *
+ * The user should first determine the available device ID by using
+ * the supplied application "pa_devs".
+ */
+#define PA_ENV_BUF_SIZE_  (32)
+#define PA_REC_IN_DEV_ENV_NAME_  ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME_  ("PA_RECOMMENDED_OUTPUT_DEVICE")
+static PaDeviceIndex GetEnvDefaultDeviceID( char *envName )
+{
+    PaDeviceIndex recommendedIndex = paNoDevice;
+    DWORD   hresult;
+    char    envbuf[PA_ENV_BUF_SIZE_];
+
+#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */
+
+    /* Let user determine default device by setting environment variable. */
+    hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ );
+    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) )
+    {
+        recommendedIndex = atoi( envbuf );
+    }
+#endif
+
+    return recommendedIndex;
+}
+
+
+static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi )
+{
+    PaDeviceIndex device;
+
+    /* input */
+    device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ );
+    if( device != paNoDevice &&
+            ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
+            hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 )
+    {
+        hostApi->inheritedHostApiRep.info.defaultInputDevice = device;
+    }
+
+    /* output */
+    device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ );
+    if( device != paNoDevice &&
+            ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
+            hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 )
+    {
+        hostApi->inheritedHostApiRep.info.defaultOutputDevice = device;
+    }
+}
+
+
+/** Convert external PA ID to a windows multimedia device ID
+*/
+static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device )
+{
+    assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount );
+
+	return hostApi->winMmeDeviceIds[ device ];
+}
+
+
+static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
+{
+    MMRESULT mmresult;
+    
+    switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
+    {
+        case MMSYSERR_NOERROR:
+            return paNoError;
+        case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
+            return paDeviceUnavailable;
+        case MMSYSERR_NODRIVER:	    /* No device driver is present. */
+            return paDeviceUnavailable;
+        case MMSYSERR_NOMEM:	    /* Unable to allocate or lock memory. */
+            return paInsufficientMemory;
+        case WAVERR_BADFORMAT:      /* Attempted to open with an unsupported waveform-audio format. */
+            return paSampleFormatNotSupported;
+                    
+        case MMSYSERR_BADDEVICEID:	/* Specified device identifier is out of range. */
+            /* falls through */
+        default:
+            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+            return paUnanticipatedHostError;
+    }
+}
+
+
+static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
+{
+    MMRESULT mmresult;
+    
+    switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
+    {
+        case MMSYSERR_NOERROR:
+            return paNoError;
+        case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
+            return paDeviceUnavailable;
+        case MMSYSERR_NODRIVER:	    /* No device driver is present. */
+            return paDeviceUnavailable;
+        case MMSYSERR_NOMEM:	    /* Unable to allocate or lock memory. */
+            return paInsufficientMemory;
+        case WAVERR_BADFORMAT:      /* Attempted to open with an unsupported waveform-audio format. */
+            return paSampleFormatNotSupported;
+                    
+        case MMSYSERR_BADDEVICEID:	/* Specified device identifier is out of range. */
+            /* falls through */
+        default:
+            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+            return paUnanticipatedHostError;
+    }
+}
+
+
+static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
+        PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
+        int winMmeDeviceId, int channels, double sampleRate )
+{
+    PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
+    WAVEFORMATEX waveFormatEx;
+    
+    if( sampleRate == 11025.0
+        && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
+            || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
+
+        return paNoError;
+    }
+
+    if( sampleRate == 22050.0
+        && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
+            || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
+
+        return paNoError;
+    }
+
+    if( sampleRate == 44100.0
+        && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
+            || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
+
+        return paNoError;
+    }
+
+    waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
+    waveFormatEx.nChannels = (WORD)channels;
+    waveFormatEx.nSamplesPerSec = (DWORD)sampleRate;
+    waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short);
+    waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short));
+    waveFormatEx.wBitsPerSample = 16;
+    waveFormatEx.cbSize = 0;
+
+    return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx );
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_  (13) /* must match array length below */
+static double defaultSampleRateSearchOrder_[] =
+    { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
+        16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
+
+static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId,
+        PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels )
+{
+    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+    int i;
+    
+    deviceInfo->defaultSampleRate = 0.;
+
+    for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+    {
+        double sampleRate = defaultSampleRateSearchOrder_[ i ]; 
+        PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate );
+        if( paerror == paNoError )
+        {
+            deviceInfo->defaultSampleRate = sampleRate;
+            break;
+        }
+    }
+}
+
+
+static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
+        PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
+{
+    PaError result = paNoError;
+    char *deviceName; /* non-const ptr */
+    MMRESULT mmresult;
+    WAVEINCAPS wic;
+    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+    
+    *success = 0;
+
+    mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
+    if( mmresult == MMSYSERR_NOMEM )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    else if( mmresult != MMSYSERR_NOERROR )
+    {
+        /* instead of returning paUnanticipatedHostError we return
+            paNoError, but leave success set as 0. This allows
+            Pa_Initialize to just ignore this device, without failing
+            the entire initialisation process.
+        */
+        return paNoError;
+    }           
+
+    if( winMmeInputDeviceId == WAVE_MAPPER )
+    {
+        /* Append I/O suffix to WAVE_MAPPER device. */
+        deviceName = (char *)PaUtil_GroupAllocateMemory(
+                    winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) );
+        if( !deviceName )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        strcpy( deviceName, wic.szPname );
+        strcat( deviceName, constInputMapperSuffix_ );
+    }
+    else
+    {
+        deviceName = (char*)PaUtil_GroupAllocateMemory(
+                    winMmeHostApi->allocations, strlen( wic.szPname ) + 1 );
+        if( !deviceName )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        strcpy( deviceName, wic.szPname  );
+    }
+    deviceInfo->name = deviceName;
+
+    deviceInfo->maxInputChannels = wic.wChannels;
+    /* Sometimes a device can return a rediculously large number of channels.
+     * This happened with an SBLive card on a Windows ME box.
+     * If that happens, then force it to 2 channels.  PLB20010413
+     */
+    if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
+    {
+        PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels ));
+        deviceInfo->maxInputChannels = 2;
+    }
+
+    winMmeDeviceInfo->dwFormats = wic.dwFormats;
+
+    DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
+            QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
+
+    *success = 1;
+    
+error:
+    return result;
+}
+
+
+static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
+        PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
+{
+    PaError result = paNoError;
+    char *deviceName; /* non-const ptr */
+    MMRESULT mmresult;
+    WAVEOUTCAPS woc;
+    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+    
+    *success = 0;
+
+    mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
+    if( mmresult == MMSYSERR_NOMEM )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    else if( mmresult != MMSYSERR_NOERROR )
+    {
+        /* instead of returning paUnanticipatedHostError we return
+            paNoError, but leave success set as 0. This allows
+            Pa_Initialize to just ignore this device, without failing
+            the entire initialisation process.
+        */
+        return paNoError;
+    }
+
+    if( winMmeOutputDeviceId == WAVE_MAPPER )
+    {
+        /* Append I/O suffix to WAVE_MAPPER device. */
+        deviceName = (char *)PaUtil_GroupAllocateMemory(
+                    winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) );
+        if( !deviceName )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        strcpy( deviceName, woc.szPname );
+        strcat( deviceName, constOutputMapperSuffix_ );
+    }
+    else
+    {
+        deviceName = (char*)PaUtil_GroupAllocateMemory(
+                    winMmeHostApi->allocations, strlen( woc.szPname ) + 1 );
+        if( !deviceName )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        strcpy( deviceName, woc.szPname  );
+    }
+    deviceInfo->name = deviceName;
+
+    deviceInfo->maxOutputChannels = woc.wChannels;
+    /* Sometimes a device can return a rediculously large number of channels.
+     * This happened with an SBLive card on a Windows ME box.
+     * It also happens on Win XP!
+     */
+    if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
+    {
+        PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
+        deviceInfo->maxOutputChannels = 2;
+    }
+
+    winMmeDeviceInfo->dwFormats = woc.dwFormats;
+
+    DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
+            QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
+
+    *success = 1;
+    
+error:
+    return result;
+}
+
+
+static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
+{
+    OSVERSIONINFO osvi;
+    osvi.dwOSVersionInfoSize = sizeof( osvi );
+	GetVersionEx( &osvi );
+
+    /* Check for NT */
+    if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
+    {
+        *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
+    }
+    else if(osvi.dwMajorVersion >= 5)
+    {
+        *defaultLowLatency  = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
+    }
+    else
+    {
+        *defaultLowLatency  = PA_MME_WIN_9X_DEFAULT_LATENCY_;
+    }     
+
+    *defaultHighLatency = *defaultLowLatency * 2;
+}
+
+
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    int i;
+    PaWinMmeHostApiRepresentation *winMmeHostApi;
+    int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
+    PaWinMmeDeviceInfo *deviceInfoArray;
+    int deviceInfoInitializationSucceeded;
+    PaTime defaultLowLatency, defaultHighLatency;
+
+    winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
+    if( !winMmeHostApi )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !winMmeHostApi->allocations )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    *hostApi = &winMmeHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paMME;
+    (*hostApi)->info.name = "MME";
+
+    
+    /* initialise device counts and default devices under the assumption that
+        there are no devices. These values are incremented below if and when
+        devices are successfully initialized.
+    */
+    (*hostApi)->info.deviceCount = 0;
+    (*hostApi)->info.defaultInputDevice = paNoDevice;
+    (*hostApi)->info.defaultOutputDevice = paNoDevice;
+    winMmeHostApi->inputDeviceCount = 0;
+    winMmeHostApi->outputDeviceCount = 0;
+
+
+    maximumPossibleDeviceCount = 0;
+
+    inputDeviceCount = waveInGetNumDevs();
+    if( inputDeviceCount > 0 )
+    	maximumPossibleDeviceCount += inputDeviceCount + 1;	/* assume there is a WAVE_MAPPER */
+
+    outputDeviceCount = waveOutGetNumDevs();
+    if( outputDeviceCount > 0 )
+	    maximumPossibleDeviceCount += outputDeviceCount + 1;	/* assume there is a WAVE_MAPPER */
+
+
+    if( maximumPossibleDeviceCount > 0 ){
+
+        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
+        if( !(*hostApi)->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all device info structs in a contiguous block */
+        deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
+                winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
+                winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
+        if( !winMmeHostApi->winMmeDeviceIds )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
+
+        if( inputDeviceCount > 0 ){
+            /* -1 is the WAVE_MAPPER */
+            for( i = -1; i < inputDeviceCount; ++i ){
+                UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
+                PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+                PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
+                deviceInfo->structVersion = 2;
+                deviceInfo->hostApi = hostApiIndex;
+
+                deviceInfo->maxInputChannels = 0;
+                deviceInfo->maxOutputChannels = 0;
+
+                deviceInfo->defaultLowInputLatency = defaultLowLatency;
+                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+                deviceInfo->defaultHighInputLatency = defaultHighLatency;
+                deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+
+                result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
+                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
+                if( result != paNoError )
+                    goto error;
+
+                if( deviceInfoInitializationSucceeded ){
+                    if( (*hostApi)->info.defaultInputDevice == paNoDevice )
+                        (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
+
+                    winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
+                    (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+
+                    winMmeHostApi->inputDeviceCount++;
+                    (*hostApi)->info.deviceCount++;
+                }
+            }
+        }
+
+        if( outputDeviceCount > 0 ){
+            /* -1 is the WAVE_MAPPER */
+            for( i = -1; i < outputDeviceCount; ++i ){
+                UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
+                PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+                PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
+                deviceInfo->structVersion = 2;
+                deviceInfo->hostApi = hostApiIndex;
+
+                deviceInfo->maxInputChannels = 0;
+                deviceInfo->maxOutputChannels = 0;
+
+                deviceInfo->defaultLowInputLatency = defaultLowLatency;
+                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+                deviceInfo->defaultHighInputLatency = defaultHighLatency;
+                deviceInfo->defaultHighOutputLatency = defaultHighLatency; 
+
+                result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
+                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
+                if( result != paNoError )
+                    goto error;
+
+                if( deviceInfoInitializationSucceeded ){
+                    if( (*hostApi)->info.defaultOutputDevice == paNoDevice )
+                        (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
+
+                    winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
+                    (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+
+                    winMmeHostApi->outputDeviceCount++;
+                    (*hostApi)->info.deviceCount++;
+                }
+            }
+        }
+    }
+    
+
+    InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    return result;
+
+error:
+    if( winMmeHostApi )
+    {
+        if( winMmeHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
+        }
+        
+        PaUtil_FreeMemory( winMmeHostApi );
+    }
+
+    return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+
+    if( winMmeHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( winMmeHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+    PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
+    int inputChannelCount, outputChannelCount;
+    int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
+    UINT winMmeInputDeviceId, winMmeOutputDeviceId;
+    unsigned int i;
+    PaError paerror;
+
+    /* The calls to QueryFormatSupported below are intended to detect invalid
+        sample rates. If we assume that the channel count and format are OK,
+        then the only thing that could fail is the sample rate. This isn't
+        strictly true, but I can't think of a better way to test that the
+        sample rate is valid.
+    */  
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+        inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+        
+        /* all standard sample formats are supported by the buffer adapter,
+             this implementation doesn't support any custom sample formats */
+        if( inputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification
+                && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+        {
+            inputMultipleDeviceChannelCount = 0;
+            for( i=0; i< inputStreamInfo->deviceCount; ++i )
+            {
+                inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
+                    
+                inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
+
+                /* check that input device can support inputChannelCount */
+                if( inputStreamInfo->devices[i].channelCount <= 0
+                        || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels )
+                    return paInvalidChannelCount;
+
+                /* test for valid sample rate, see comment above */
+                winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
+                paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate );
+                if( paerror != paNoError )
+                    return paInvalidSampleRate;
+            }
+                
+            if( inputMultipleDeviceChannelCount != inputChannelCount )
+                return paIncompatibleHostApiSpecificStreamInfo;                  
+        }
+        else
+        {
+            if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+                return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */
+
+            inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
+
+            /* check that input device can support inputChannelCount */
+            if( inputChannelCount > inputDeviceInfo->maxInputChannels )
+                return paInvalidChannelCount;
+
+            /* test for valid sample rate, see comment above */
+            winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
+            paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate );
+            if( paerror != paNoError )
+                return paInvalidSampleRate;
+        }
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        outputStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( outputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification
+                && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+        {
+            outputMultipleDeviceChannelCount = 0;
+            for( i=0; i< outputStreamInfo->deviceCount; ++i )
+            {
+                outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
+                    
+                outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
+
+                /* check that output device can support outputChannelCount */
+                if( outputStreamInfo->devices[i].channelCount <= 0
+                        || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels )
+                    return paInvalidChannelCount;
+
+                /* test for valid sample rate, see comment above */
+                winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
+                paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate );
+                if( paerror != paNoError )
+                    return paInvalidSampleRate;
+            }
+                
+            if( outputMultipleDeviceChannelCount != outputChannelCount )
+                return paIncompatibleHostApiSpecificStreamInfo;            
+        }
+        else
+        {
+            if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+                return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */
+
+            outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
+
+            /* check that output device can support outputChannelCount */
+            if( outputChannelCount > outputDeviceInfo->maxOutputChannels )
+                return paInvalidChannelCount;
+
+            /* test for valid sample rate, see comment above */
+            winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
+            paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate );
+            if( paerror != paNoError )
+                return paInvalidSampleRate;
+        }
+    }
+    
+    /*
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+
+            - check that the device supports sampleRate
+
+            for mme all we can do is test that the input and output devices
+            support the requested sample rate and number of channels. we
+            cannot test for full duplex compatibility.
+    */                                             
+
+    return paFormatIsSupported;
+}
+
+
+
+static void SelectBufferSizeAndCount( unsigned long baseBufferSize,
+    unsigned long requestedLatency,
+    unsigned long baseBufferCount, unsigned long minimumBufferCount,
+    unsigned long maximumBufferSize, unsigned long *hostBufferSize,
+    unsigned long *hostBufferCount )
+{
+    unsigned long sizeMultiplier, bufferCount, latency;
+    unsigned long nextLatency, nextBufferSize;
+    int baseBufferSizeIsPowerOfTwo;
+    
+    sizeMultiplier = 1;
+    bufferCount = baseBufferCount;
+
+    /* count-1 below because latency is always determined by one less
+        than the total number of buffers.
+    */
+    latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+
+    if( latency > requestedLatency )
+    {
+
+        /* reduce number of buffers without falling below suggested latency */
+
+        nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
+        while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
+        {
+            --bufferCount;
+            nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
+        }
+
+    }else if( latency < requestedLatency ){
+
+        baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1)));
+        if( baseBufferSizeIsPowerOfTwo ){
+
+            /* double size of buffers without exceeding requestedLatency */
+
+            nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
+            nextLatency = nextBufferSize * (bufferCount-1);
+            while( nextBufferSize <= maximumBufferSize
+                    && nextLatency < requestedLatency )
+            {
+                sizeMultiplier *= 2;
+                nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
+                nextLatency = nextBufferSize * (bufferCount-1);
+            }   
+
+        }else{
+
+            /* increase size of buffers upto first excess of requestedLatency */
+
+            nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
+            nextLatency = nextBufferSize * (bufferCount-1);
+            while( nextBufferSize <= maximumBufferSize
+                    && nextLatency < requestedLatency )
+            {
+                ++sizeMultiplier;
+                nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
+                nextLatency = nextBufferSize * (bufferCount-1);
+            }
+
+            if( nextLatency < requestedLatency )
+                ++sizeMultiplier;            
+        }
+
+        /* increase number of buffers until requestedLatency is reached */
+
+        latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+        while( latency < requestedLatency )
+        {
+            ++bufferCount;
+            latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+        }
+    }
+
+    *hostBufferSize = baseBufferSize * sizeMultiplier;
+    *hostBufferCount = bufferCount;
+}
+
+
+static void ReselectBufferCount( unsigned long bufferSize,
+    unsigned long requestedLatency,
+    unsigned long baseBufferCount, unsigned long minimumBufferCount,
+    unsigned long *hostBufferCount )
+{
+    unsigned long bufferCount, latency;
+    unsigned long nextLatency;
+
+    bufferCount = baseBufferCount;
+
+    /* count-1 below because latency is always determined by one less
+        than the total number of buffers.
+    */
+    latency = bufferSize * (bufferCount-1);
+
+    if( latency > requestedLatency )
+    {
+        /* reduce number of buffers without falling below suggested latency */
+
+        nextLatency = bufferSize * (bufferCount-2);
+        while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
+        {
+            --bufferCount;
+            nextLatency = bufferSize * (bufferCount-2);
+        }
+
+    }else if( latency < requestedLatency ){
+
+        /* increase number of buffers until requestedLatency is reached */
+
+        latency = bufferSize * (bufferCount-1);
+        while( latency < requestedLatency )
+        {
+            ++bufferCount;
+            latency = bufferSize * (bufferCount-1);
+        }                                                         
+    }
+
+    *hostBufferCount = bufferCount;
+}
+
+
+/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount,
+   framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values
+   of the other parameters.
+*/
+
+static PaError CalculateBufferSettings(
+        unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount,
+        unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount,
+        int inputChannelCount, PaSampleFormat hostInputSampleFormat,
+        PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo,
+        int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
+        PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo,
+        double sampleRate, unsigned long framesPerBuffer )
+{
+    PaError result = paNoError;
+    int effectiveInputChannelCount, effectiveOutputChannelCount;
+    int hostInputFrameSize = 0;
+    unsigned int i;
+    
+    if( inputChannelCount > 0 )
+    {
+        int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat );
+        if( hostInputSampleSize < 0 )
+        {
+            result = hostInputSampleSize;
+            goto error;
+        }
+
+        if( inputStreamInfo
+                && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
+        {
+            /* set effectiveInputChannelCount to the largest number of
+                channels on any one device.
+            */
+            effectiveInputChannelCount = 0;
+            for( i=0; i< inputStreamInfo->deviceCount; ++i )
+            {
+                if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount )
+                    effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount;
+            }
+        }
+        else
+        {
+            effectiveInputChannelCount = inputChannelCount;
+        }
+
+        hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount;
+
+        if( inputStreamInfo
+                && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+        {
+            if( inputStreamInfo->bufferCount <= 0
+                    || inputStreamInfo->framesPerBuffer <= 0 )
+            {
+                result = paIncompatibleHostApiSpecificStreamInfo;
+                goto error;
+            }
+
+            *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer;
+            *hostInputBufferCount = inputStreamInfo->bufferCount;
+        }
+        else
+        {
+            unsigned long hostBufferSizeBytes, hostBufferCount;
+            unsigned long minimumBufferCount = (outputChannelCount > 0)
+                    ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
+                    : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
+
+            unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize);
+            if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
+                maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
+
+            /* compute the following in bytes, then convert back to frames */
+
+            SelectBufferSizeAndCount(
+                ((framesPerBuffer == paFramesPerBufferUnspecified)
+                    ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
+                    : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */
+                ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
+                4, /* baseBufferCount */
+                minimumBufferCount, maximumBufferSize,
+                &hostBufferSizeBytes, &hostBufferCount );
+
+            *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
+            *hostInputBufferCount = hostBufferCount;
+        }
+    }
+    else
+    {
+        *framesPerHostInputBuffer = 0;
+        *hostInputBufferCount = 0;
+    }
+
+    if( outputChannelCount > 0 )
+    {
+        if( outputStreamInfo
+                && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+        {
+            if( outputStreamInfo->bufferCount <= 0
+                    || outputStreamInfo->framesPerBuffer <= 0 )
+            {
+                result = paIncompatibleHostApiSpecificStreamInfo;
+                goto error;
+            }
+
+            *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer;
+            *hostOutputBufferCount = outputStreamInfo->bufferCount;
+
+            
+            if( inputChannelCount > 0 ) /* full duplex */
+            {
+                if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer )
+                {
+                    if( inputStreamInfo
+                            && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+                    { 
+                        /* a custom StreamInfo was used for specifying both input
+                            and output buffer sizes, the larger buffer size
+                            must be a multiple of the smaller buffer size */
+
+                        if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer )
+                        {
+                            if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 )
+                            {
+                                result = paIncompatibleHostApiSpecificStreamInfo;
+                                goto error;
+                            }
+                        }
+                        else
+                        {
+                            assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer );
+                            if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 )
+                            {
+                                result = paIncompatibleHostApiSpecificStreamInfo;
+                                goto error;
+                            }
+                        }                        
+                    }
+                    else
+                    {
+                        /* a custom StreamInfo was not used for specifying the input buffer size,
+                            so use the output buffer size, and approximately the same latency. */
+
+                        *framesPerHostInputBuffer = *framesPerHostOutputBuffer;
+                        *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
+
+                        if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ )
+                            *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
+                    }
+                }
+            }
+        }
+        else
+        {
+            unsigned long hostBufferSizeBytes, hostBufferCount;
+            unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
+            unsigned long maximumBufferSize;
+            int hostOutputFrameSize;
+            int hostOutputSampleSize;
+
+            hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat );
+            if( hostOutputSampleSize < 0 )
+            {
+                result = hostOutputSampleSize;
+                goto error;
+            }
+
+            if( outputStreamInfo
+                && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
+            {
+                /* set effectiveOutputChannelCount to the largest number of
+                    channels on any one device.
+                */
+                effectiveOutputChannelCount = 0;
+                for( i=0; i< outputStreamInfo->deviceCount; ++i )
+                {
+                    if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount )
+                        effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount;
+                }
+            }
+            else
+            {
+                effectiveOutputChannelCount = outputChannelCount;
+            }
+
+            hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount;
+            
+            maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize);
+            if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
+                maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
+
+
+            /* compute the following in bytes, then convert back to frames */
+
+            SelectBufferSizeAndCount(
+                ((framesPerBuffer == paFramesPerBufferUnspecified)
+                    ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
+                    : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */
+                ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
+                4, /* baseBufferCount */
+                minimumBufferCount,
+                maximumBufferSize,
+                &hostBufferSizeBytes, &hostBufferCount );
+
+            *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize;
+            *hostOutputBufferCount = hostBufferCount;
+
+
+            if( inputChannelCount > 0 )
+            {
+                /* ensure that both input and output buffer sizes are the same.
+                    if they don't match at this stage, choose the smallest one
+                    and use that for input and output
+                */
+
+                if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer )
+                {
+                    if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
+                    {
+                        unsigned long framesPerHostBuffer = *framesPerHostInputBuffer;
+                        
+                        minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
+                        ReselectBufferCount(
+                            framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */
+                            ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
+                            4, /* baseBufferCount */
+                            minimumBufferCount,
+                            &hostBufferCount );
+
+                        *framesPerHostOutputBuffer = framesPerHostBuffer;
+                        *hostOutputBufferCount = hostBufferCount;
+                    }
+                    else
+                    {
+                        unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer;
+                        
+                        minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
+                        ReselectBufferCount(
+                            framesPerHostBuffer * hostInputFrameSize, /* bufferSize */
+                            ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
+                            4, /* baseBufferCount */
+                            minimumBufferCount,
+                            &hostBufferCount );
+
+                        *framesPerHostInputBuffer = framesPerHostBuffer;
+                        *hostInputBufferCount = hostBufferCount;
+                    }
+                }   
+            }
+        }
+    }
+    else
+    {
+        *framesPerHostOutputBuffer = 0;
+        *hostOutputBufferCount = 0;
+    }
+
+error:
+    return result;
+}
+
+
+typedef struct
+{
+    HANDLE bufferEvent;
+    void *waveHandles;
+    unsigned int deviceCount;
+    /* unsigned int channelCount; */
+    WAVEHDR **waveHeaders;                  /* waveHeaders[device][buffer] */
+    unsigned int bufferCount;
+    unsigned int currentBufferIndex;
+    unsigned int framesPerBuffer;
+    unsigned int framesUsedInCurrentBuffer;
+}PaWinMmeSingleDirectionHandlesAndBuffers;
+
+/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */
+
+static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers );
+static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
+        PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+        unsigned long bytesPerHostSample,
+        double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
+        unsigned int deviceCount, int isInput );
+static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
+static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+        unsigned long hostBufferCount,
+        PaSampleFormat hostSampleFormat,
+        unsigned long framesPerHostBuffer,
+        PaWinMmeDeviceAndChannelCount *devices,
+        int isInput );
+static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput );
+
+
+static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+    handlesAndBuffers->bufferEvent = 0;
+    handlesAndBuffers->waveHandles = 0;
+    handlesAndBuffers->deviceCount = 0;
+    handlesAndBuffers->waveHeaders = 0;
+    handlesAndBuffers->bufferCount = 0;
+}    
+
+static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
+        PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+        unsigned long bytesPerHostSample,
+        double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
+        unsigned int deviceCount, int isInput )
+{
+    PaError result;
+    MMRESULT mmresult;
+    unsigned long bytesPerFrame;
+    WAVEFORMATEX wfx;
+    signed int i;
+
+    /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
+        has already been called to zero some fields */       
+
+    result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
+    if( result != paNoError ) goto error;
+
+    if( isInput )
+        handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount );
+    else
+        handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount );
+    if( !handlesAndBuffers->waveHandles )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    handlesAndBuffers->deviceCount = deviceCount;
+
+    for( i = 0; i < (signed int)deviceCount; ++i )
+    {
+        if( isInput )
+            ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0;
+        else
+            ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
+    }
+
+    wfx.wFormatTag = WAVE_FORMAT_PCM;
+    wfx.nSamplesPerSec = (DWORD) sampleRate;
+    wfx.cbSize = 0;
+    
+    for( i = 0; i < (signed int)deviceCount; ++i )
+    {
+        UINT winMmeDeviceId;
+
+        winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
+        wfx.nChannels = (WORD)devices[i].channelCount;
+
+        bytesPerFrame = wfx.nChannels * bytesPerHostSample;
+
+        wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate);
+        wfx.nBlockAlign = (WORD)bytesPerFrame;
+        wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8);
+
+        /* REVIEW: consider not firing an event for input when a full duplex
+            stream is being used. this would probably depend on the
+            neverDropInput flag. */
+
+        if( isInput )
+            mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+                               (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+        else
+            mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+                                (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+
+        if( mmresult != MMSYSERR_NOERROR )
+        {
+            switch( mmresult )
+            {
+                case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
+                    result = paDeviceUnavailable;
+                    break;
+                case MMSYSERR_NODRIVER:	    /* No device driver is present. */
+                    result = paDeviceUnavailable;
+                    break;
+                case MMSYSERR_NOMEM:	    /* Unable to allocate or lock memory. */
+                    result = paInsufficientMemory;
+                    break;
+
+                case MMSYSERR_BADDEVICEID:	/* Specified device identifier is out of range. */
+                    /* falls through */
+                case WAVERR_BADFORMAT:      /* Attempted to open with an unsupported waveform-audio format. */
+                    /* falls through */
+                default:
+                    result = paUnanticipatedHostError;
+                    if( isInput )
+                    {
+                        PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                    }
+                    else
+                    {
+                        PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                    }
+            }
+            goto error;
+        }
+    }
+
+    return result;
+
+error:
+    TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ );
+
+    return result;
+}
+
+
+static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError )
+{
+    PaError result = paNoError;
+    MMRESULT mmresult;
+    signed int i;
+    
+    if( handlesAndBuffers->waveHandles )
+    {
+        for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
+        {
+            if( isInput )
+            {
+                if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
+                    mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
+            }
+            else
+            {
+                if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
+                    mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
+            }
+
+            if( mmresult != MMSYSERR_NOERROR &&
+                !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */
+            {
+                result = paUnanticipatedHostError;
+                if( isInput )
+                {
+                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                }
+                else
+                {
+                    PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                }
+                /* note that we don't break here, we try to continue closing devices */
+            }
+        }
+
+        PaUtil_FreeMemory( handlesAndBuffers->waveHandles );
+        handlesAndBuffers->waveHandles = 0;
+    }
+
+    if( handlesAndBuffers->bufferEvent )
+    {
+        result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
+        handlesAndBuffers->bufferEvent = 0;
+    }
+    
+    return result;
+}
+
+
+static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+        unsigned long hostBufferCount,
+        PaSampleFormat hostSampleFormat,
+        unsigned long framesPerHostBuffer,
+        PaWinMmeDeviceAndChannelCount *devices,
+        int isInput )
+{
+    PaError result = paNoError;
+    MMRESULT mmresult;
+    WAVEHDR *deviceWaveHeaders;
+    signed int i, j;
+
+    /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
+        has already been called to zero some fields */
+        
+
+    /* allocate an array of pointers to arrays of wave headers, one array of
+        wave headers per device */
+    handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount );
+    if( !handlesAndBuffers->waveHeaders )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    
+    for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
+        handlesAndBuffers->waveHeaders[i] = 0;
+
+    handlesAndBuffers->bufferCount = hostBufferCount;
+
+    for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
+    {
+        int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) *
+                framesPerHostBuffer * devices[i].channelCount;
+        if( bufferBytes < 0 )
+        {
+            result = paInternalError;
+            goto error;
+        }
+
+        /* Allocate an array of wave headers for device i */
+        deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount );
+        if( !deviceWaveHeaders )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( j=0; j < (signed int)hostBufferCount; ++j )
+            deviceWaveHeaders[j].lpData = 0;
+
+        handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders;
+
+        /* Allocate a buffer for each wave header */
+        for( j=0; j < (signed int)hostBufferCount; ++j )
+        {
+            deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes );
+            if( !deviceWaveHeaders[j].lpData )
+            {
+                result = paInsufficientMemory;
+                goto error;
+            }
+            deviceWaveHeaders[j].dwBufferLength = bufferBytes;
+            deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */
+
+            if( isInput )
+            {
+                mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+                if( mmresult != MMSYSERR_NOERROR )
+                {
+                    result = paUnanticipatedHostError;
+                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                    goto error;
+                }
+            }
+            else /* output */
+            {
+                mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+                if( mmresult != MMSYSERR_NOERROR )
+                {
+                    result = paUnanticipatedHostError;
+                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                    goto error;
+                }
+            }
+            deviceWaveHeaders[j].dwUser = devices[i].channelCount;
+        }
+    }
+
+    return result;
+
+error:
+    TerminateWaveHeaders( handlesAndBuffers, isInput );
+    
+    return result;
+}
+
+
+static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput )
+{
+    signed int i, j;
+    WAVEHDR *deviceWaveHeaders;
+    
+    if( handlesAndBuffers->waveHeaders )
+    {
+        for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
+        {
+            deviceWaveHeaders = handlesAndBuffers->waveHeaders[i];  /* wave headers for device i */
+            if( deviceWaveHeaders )
+            {
+                for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j )
+                {
+                    if( deviceWaveHeaders[j].lpData )
+                    {
+                        if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF )
+                        {
+                            if( isInput )
+                                waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+                            else
+                                waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+                        }
+
+                        PaUtil_FreeMemory( deviceWaveHeaders[j].lpData );
+                    }
+                }
+
+                PaUtil_FreeMemory( deviceWaveHeaders );
+            }
+        }
+
+        PaUtil_FreeMemory( handlesAndBuffers->waveHeaders );
+        handlesAndBuffers->waveHeaders = 0;
+    }
+}
+
+
+
+/* PaWinMmeStream - a stream data structure specifically for this implementation */
+/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */
+struct PaWinMmeStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+    int primeStreamUsingCallback;
+
+    PaWinMmeSingleDirectionHandlesAndBuffers input;
+    PaWinMmeSingleDirectionHandlesAndBuffers output;
+
+    /* Processing thread management -------------- */
+    HANDLE abortEvent;
+    HANDLE processingThread;
+    DWORD processingThreadId;
+
+    char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
+    int processingThreadPriority;
+    int highThreadPriority;
+    int throttledThreadPriority;
+    unsigned long throttledSleepMsecs;
+
+    int isStopped;
+    volatile int isActive;
+    volatile int stopProcessing; /* stop thread once existing buffers have been returned */
+    volatile int abortProcessing; /* stop thread immediately */
+
+    DWORD allBuffersDurationMs; /* used to calculate timeouts */
+};
+
+/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
+
+static PaError ValidateWinMmeSpecificStreamInfo(
+        const PaStreamParameters *streamParameters,
+        const PaWinMmeStreamInfo *streamInfo,
+        char *throttleProcessingThreadOnOverload,
+        unsigned long *deviceCount )
+{
+	if( streamInfo )
+	{
+	    if( streamInfo->size != sizeof( PaWinMmeStreamInfo )
+	            || streamInfo->version != 1 )
+	    {
+	        return paIncompatibleHostApiSpecificStreamInfo;
+	    }
+
+	    if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
+	        *throttleProcessingThreadOnOverload = 0;
+            
+	    if( streamInfo->flags & paWinMmeUseMultipleDevices )
+	    {
+	        if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
+	            return paInvalidDevice;
+	
+			*deviceCount = streamInfo->deviceCount;
+		}	
+	}
+
+	return paNoError;
+}
+
+static PaError RetrieveDevicesFromStreamParameters(
+        struct PaUtilHostApiRepresentation *hostApi,
+        const PaStreamParameters *streamParameters,
+        const PaWinMmeStreamInfo *streamInfo,
+        PaWinMmeDeviceAndChannelCount *devices,
+        unsigned long deviceCount )
+{
+    PaError result = paNoError;
+    unsigned int i;
+    int totalChannelCount;
+    PaDeviceIndex hostApiDevice;
+    
+	if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
+	{
+		totalChannelCount = 0;
+	    for( i=0; i < deviceCount; ++i )
+	    {
+	        /* validate that the device number is within range */
+	        result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice,
+	                        streamInfo->devices[i].device, hostApi );
+	        if( result != paNoError )
+	            return result;
+	        
+	        devices[i].device = hostApiDevice;
+	        devices[i].channelCount = streamInfo->devices[i].channelCount;
+	
+	        totalChannelCount += devices[i].channelCount;
+	    }
+	
+	    if( totalChannelCount != streamParameters->channelCount )
+	    {
+	        /* channelCount must match total channels specified by multiple devices */
+	        return paInvalidChannelCount; /* REVIEW use of this error code */
+	    }
+	}	
+	else
+	{		
+	    devices[0].device = streamParameters->device;
+	    devices[0].channelCount = streamParameters->channelCount;
+	}
+
+    return result;
+}
+
+static PaError ValidateInputChannelCounts(
+        struct PaUtilHostApiRepresentation *hostApi,
+        PaWinMmeDeviceAndChannelCount *devices,
+        unsigned long deviceCount )
+{
+    unsigned int i;
+
+	for( i=0; i < deviceCount; ++i )
+	{
+		if( devices[i].channelCount < 1 || devices[i].channelCount
+					> hostApi->deviceInfos[ devices[i].device ]->maxInputChannels )
+        	return paInvalidChannelCount;
+	}
+
+    return paNoError;
+}
+
+static PaError ValidateOutputChannelCounts(
+        struct PaUtilHostApiRepresentation *hostApi,
+        PaWinMmeDeviceAndChannelCount *devices,
+        unsigned long deviceCount )
+{
+    unsigned int i;
+
+	for( i=0; i < deviceCount; ++i )
+	{
+		if( devices[i].channelCount < 1 || devices[i].channelCount
+					> hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels )
+        	return paInvalidChannelCount;
+	}
+
+    return paNoError;
+}
+
+
+/* the following macros are intended to improve the readability of the following code */
+#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles )
+#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles )
+#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles )
+#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) )
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result;
+    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+    PaWinMmeStream *stream = 0;
+    int bufferProcessorIsInitialized = 0;
+    int streamRepresentationIsInitialized = 0;
+    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    double suggestedInputLatency, suggestedOutputLatency;
+    PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
+    unsigned long framesPerHostInputBuffer;
+    unsigned long hostInputBufferCount;
+    unsigned long framesPerHostOutputBuffer;
+    unsigned long hostOutputBufferCount;
+    unsigned long framesPerBufferProcessorCall;
+    PaWinMmeDeviceAndChannelCount *inputDevices = 0;  /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+    unsigned long inputDeviceCount = 0;            
+    PaWinMmeDeviceAndChannelCount *outputDevices = 0;
+    unsigned long outputDeviceCount = 0;                /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+    char throttleProcessingThreadOnOverload = 1;
+
+    
+    if( inputParameters )
+    {
+		inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+        suggestedInputLatency = inputParameters->suggestedLatency;
+
+      	inputDeviceCount = 1;
+
+		/* validate input hostApiSpecificStreamInfo */
+        inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
+		result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo,
+				&throttleProcessingThreadOnOverload,
+				&inputDeviceCount );
+		if( result != paNoError ) return result;
+
+		inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount );
+        if( !inputDevices ) return paInsufficientMemory;
+
+		result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount );
+		if( result != paNoError ) return result;
+
+		result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount );
+		if( result != paNoError ) return result;
+
+        hostInputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
+	}
+    else
+    {
+        inputChannelCount = 0;
+        inputSampleFormat = 0;
+        suggestedInputLatency = 0.;
+        inputStreamInfo = 0;
+        hostInputSampleFormat = 0;
+    }
+
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        suggestedOutputLatency = outputParameters->suggestedLatency;
+
+        outputDeviceCount = 1;
+
+		/* validate output hostApiSpecificStreamInfo */
+        outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
+		result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo,
+				&throttleProcessingThreadOnOverload,
+				&outputDeviceCount );
+		if( result != paNoError ) return result;
+
+		outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount );
+        if( !outputDevices ) return paInsufficientMemory;
+
+		result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount );
+		if( result != paNoError ) return result;
+
+		result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount );
+		if( result != paNoError ) return result;
+
+        hostOutputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
+    }
+    else
+    {
+        outputChannelCount = 0;
+        outputSampleFormat = 0;
+        outputStreamInfo = 0;
+        hostOutputSampleFormat = 0;
+        suggestedOutputLatency = 0.;
+    }
+
+
+    /*
+        IMPLEMENT ME:
+            - alter sampleRate to a close allowable rate if possible / necessary
+    */
+
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+
+
+    result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount,
+                &framesPerHostOutputBuffer, &hostOutputBufferCount,
+                inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo,
+                outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo,
+                sampleRate, framesPerBuffer );
+    if( result != paNoError ) goto error;
+
+
+    stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) );
+    if( !stream )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    InitializeSingleDirectionHandlesAndBuffers( &stream->input );
+    InitializeSingleDirectionHandlesAndBuffers( &stream->output );
+
+    stream->abortEvent = 0;
+    stream->processingThread = 0;
+
+    stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload;
+
+    PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                           ( (streamCallback)
+                                            ? &winMmeHostApi->callbackStreamInterface
+                                            : &winMmeHostApi->blockingStreamInterface ),
+                                           streamCallback, userData );
+    streamRepresentationIsInitialized = 1;
+
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+    if( inputParameters && outputParameters ) /* full duplex */
+    {
+        if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
+        {
+            assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
+
+            framesPerBufferProcessorCall = framesPerHostInputBuffer;
+        }
+        else
+        {
+            assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
+            
+            framesPerBufferProcessorCall = framesPerHostOutputBuffer;
+        }
+    }
+    else if( inputParameters )
+    {
+        framesPerBufferProcessorCall = framesPerHostInputBuffer;
+    }
+    else if( outputParameters )
+    {
+        framesPerBufferProcessorCall = framesPerHostOutputBuffer;
+    }
+
+    stream->input.framesPerBuffer = framesPerHostInputBuffer;
+    stream->output.framesPerBuffer = framesPerHostOutputBuffer;
+
+    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+                    inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+                    outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+                    sampleRate, streamFlags, framesPerBuffer,
+                    framesPerBufferProcessorCall, paUtilFixedHostBufferSize,
+                    streamCallback, userData );
+    if( result != paNoError ) goto error;
+    
+    bufferProcessorIsInitialized = 1;
+
+    stream->streamRepresentation.streamInfo.inputLatency =
+            (double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
+                +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate;
+    stream->streamRepresentation.streamInfo.outputLatency =
+            (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
+                +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+    stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
+
+    /* time to sleep when throttling due to >100% cpu usage.
+        -a quater of a buffer's duration */
+    stream->throttledSleepMsecs =
+            (unsigned long)(stream->bufferProcessor.framesPerHostBuffer *
+             stream->bufferProcessor.samplePeriod * .25 * 1000);
+
+    stream->isStopped = 1;
+    stream->isActive = 0;
+
+
+    /* for maximum compatibility with multi-device multichannel drivers,
+        we first open all devices, then we prepare all buffers, finally
+        we start all devices ( in StartStream() ). teardown in reverse order.
+    */
+
+    if( inputParameters )
+    {
+        result = InitializeWaveHandles( winMmeHostApi, &stream->input,
+                stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
+                inputDevices, inputDeviceCount, 1 /* isInput */ );
+        if( result != paNoError ) goto error;
+    }
+    
+    if( outputParameters )
+    {
+        result = InitializeWaveHandles( winMmeHostApi, &stream->output,
+                stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
+                outputDevices, outputDeviceCount, 0 /* isInput */ );
+        if( result != paNoError ) goto error;
+    }
+
+    if( inputParameters )
+    {
+        result = InitializeWaveHeaders( &stream->input, hostInputBufferCount,
+                hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ );
+        if( result != paNoError ) goto error;
+    }
+
+    if( outputParameters )
+    {
+        result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount,
+                hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ );
+        if( result != paNoError ) goto error;
+
+        stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate);
+    }
+    else
+    {
+        stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
+    }
+
+    
+    if( streamCallback )
+    {
+        /* abort event is only needed for callback streams */
+        result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL );
+        if( result != paNoError ) goto error;
+    }
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+
+    if( stream )
+    {
+        if( stream->abortEvent )
+            CloseHandle( stream->abortEvent );
+            
+        TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
+        TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
+
+        TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ );
+        TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ );
+
+        if( bufferProcessorIsInitialized )
+            PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+
+        if( streamRepresentationIsInitialized )
+            PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+        PaUtil_FreeMemory( stream );
+    }
+
+    return result;
+}
+
+
+/* return non-zero if all current buffers are done */
+static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
+{
+    unsigned int i;
+    
+    for( i=0; i < deviceCount; ++i )
+    {
+        if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
+        {
+            return 0;
+        }         
+    }
+
+    return 1;
+}
+
+static int CurrentInputBuffersAreDone( PaWinMmeStream *stream )
+{
+    return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex );
+}
+
+static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream )
+{
+    return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex );
+}
+
+
+/* return non-zero if any buffers are queued */
+static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+    unsigned int i, j;
+
+    if( handlesAndBuffers->waveHandles )
+    {
+        for( i=0; i < handlesAndBuffers->bufferCount; ++i )
+        {
+            for( j=0; j < handlesAndBuffers->deviceCount; ++j )
+            {
+                if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) )
+                {
+                    return 0;
+                }
+            }
+        }
+    }
+
+    return 1;
+}
+
+
+#define PA_CIRCULAR_INCREMENT_( current, max )\
+    ( (((current) + 1) >= (max)) ? (0) : (current+1) )
+
+#define PA_CIRCULAR_DECREMENT_( current, max )\
+    ( ((current) == 0) ? ((max)-1) : (current-1) )
+    
+
+static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+    signed long result = 0;
+    unsigned int i;
+    
+    if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
+    {
+        /* we could calculate the following in O(1) if we kept track of the
+            last done buffer */
+        result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer;
+
+        i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount );
+        while( i != handlesAndBuffers->currentBufferIndex )
+        {
+            if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) )
+            {
+                result += handlesAndBuffers->framesPerBuffer;
+                i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount );
+            }
+            else
+                break;
+        }
+    }
+
+    return result;
+}
+
+
+static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
+{
+    PaError result = paNoError;
+    MMRESULT mmresult;
+    unsigned int i;
+
+    for( i=0; i < stream->input.deviceCount; ++i )
+    {
+        mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
+                                    &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
+                                    sizeof(WAVEHDR) );
+        if( mmresult != MMSYSERR_NOERROR )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+        }
+    }
+
+    stream->input.currentBufferIndex =
+            PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount );
+
+    stream->input.framesUsedInCurrentBuffer = 0;
+
+    return result;
+}
+
+
+static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream )
+{
+    PaError result = paNoError;
+    MMRESULT mmresult;
+    unsigned int i;
+
+    for( i=0; i < stream->output.deviceCount; ++i )
+    {
+        mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i],
+                                 &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ],
+                                 sizeof(WAVEHDR) );
+        if( mmresult != MMSYSERR_NOERROR )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+        }
+    }
+
+    stream->output.currentBufferIndex =
+            PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
+
+    stream->output.framesUsedInCurrentBuffer = 0;
+    
+    return result;
+}
+
+
+/* requeue all but the most recent input with the driver. Used for catching
+    up after a total input buffer underrun */
+static PaError CatchUpInputBuffers( PaWinMmeStream *stream )
+{
+    PaError result = paNoError;
+    unsigned int i;
+    
+    for( i=0; i < stream->input.bufferCount - 1; ++i )
+    {
+        result = AdvanceToNextInputBuffer( stream );
+        if( result != paNoError )
+            break;
+    }
+
+    return result;
+}
+
+
+/* take the most recent output and duplicate it to all other output buffers
+    and requeue them. Used for catching up after a total output buffer underrun.
+*/
+static PaError CatchUpOutputBuffers( PaWinMmeStream *stream )
+{
+    PaError result = paNoError;
+    unsigned int i, j;
+    unsigned int previousBufferIndex =
+            PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
+
+    for( i=0; i < stream->output.bufferCount - 1; ++i )
+    {
+        for( j=0; j < stream->output.deviceCount; ++j )
+        {
+            if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData
+                    != stream->output.waveHeaders[j][ previousBufferIndex ].lpData )
+            {
+                CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData,
+                            stream->output.waveHeaders[j][ previousBufferIndex ].lpData,
+                            stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength );
+            }
+        }
+
+        result = AdvanceToNextOutputBuffer( stream );
+        if( result != paNoError )
+            break;
+    }
+
+    return result;
+}
+
+
+static DWORD WINAPI ProcessingThreadProc( void *pArg )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream *)pArg;
+    HANDLE events[3];
+    int eventCount = 0;
+    DWORD result = paNoError;
+    DWORD waitResult;
+    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+    int hostBuffersAvailable;
+    signed int hostInputBufferIndex, hostOutputBufferIndex;
+    PaStreamCallbackFlags statusFlags;
+    int callbackResult;
+    int done = 0;
+    unsigned int channel, i;
+    unsigned long framesProcessed;
+    
+    /* prepare event array for call to WaitForMultipleObjects() */
+    if( stream->input.bufferEvent )
+        events[eventCount++] = stream->input.bufferEvent;
+    if( stream->output.bufferEvent )
+        events[eventCount++] = stream->output.bufferEvent;
+    events[eventCount++] = stream->abortEvent;
+
+    statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
+    
+    /* loop until something causes us to stop */
+    do{
+        /* wait for MME to signal that a buffer is available, or for
+            the PA abort event to be signaled.
+
+          When this indicates that one or more buffers are available
+          NoBuffersAreQueued() and Current*BuffersAreDone are used below to
+          poll for additional done buffers. NoBuffersAreQueued() will fail
+          to identify an underrun/overflow if the driver doesn't mark all done
+          buffers prior to signalling the event. Some drivers do this
+          (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a
+          huge problem, it just means that we won't always be able to detect
+          underflow/overflow.
+        */
+        waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout );
+        if( waitResult == WAIT_FAILED )
+        {
+            result = paUnanticipatedHostError;
+            /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */
+            done = 1;
+        }
+        else if( waitResult == WAIT_TIMEOUT )
+        {
+            /* if a timeout is encountered, continue */
+        }
+
+        if( stream->abortProcessing )
+        {
+            /* Pa_AbortStream() has been called, stop processing immediately */
+            done = 1;
+        }
+        else if( stream->stopProcessing )
+        {
+            /* Pa_StopStream() has been called or the user callback returned
+                non-zero, processing will continue until all output buffers
+                are marked as done. The stream will stop immediately if it
+                is input-only.
+            */
+
+            if( PA_IS_OUTPUT_STREAM_(stream) )
+            {
+                if( NoBuffersAreQueued( &stream->output ) )
+                    done = 1; /* Will cause thread to return. */
+            }
+            else
+            {
+                /* input only stream */
+                done = 1; /* Will cause thread to return. */
+            }
+        }
+        else
+        {
+            hostBuffersAvailable = 1;
+
+            /* process all available host buffers */
+            do
+            {
+                hostInputBufferIndex = -1;
+                hostOutputBufferIndex = -1;
+                
+                if( PA_IS_INPUT_STREAM_(stream) )
+                {
+                    if( CurrentInputBuffersAreDone( stream ) )
+                    {
+                        if( NoBuffersAreQueued( &stream->input ) )
+                        {
+                            /** @todo
+                               if all of the other buffers are also ready then
+                               we discard all but the most recent. This is an
+                               input buffer overflow. FIXME: these buffers should
+                               be passed to the callback in a paNeverDropInput
+                               stream.
+
+                               note that it is also possible for an input overflow
+                               to happen while the callback is processing a buffer.
+                               that is handled further down.
+                            */
+                            result = CatchUpInputBuffers( stream );
+                            if( result != paNoError )
+                                done = 1;
+
+                            statusFlags |= paInputOverflow;
+                        }
+
+                        hostInputBufferIndex = stream->input.currentBufferIndex;
+                    }
+                }
+
+                if( PA_IS_OUTPUT_STREAM_(stream) )
+                {
+                    if( CurrentOutputBuffersAreDone( stream ) )
+                    {
+                        /* ok, we have an output buffer */
+                        
+                        if( NoBuffersAreQueued( &stream->output ) )
+                        {
+                            /*
+                            if all of the other buffers are also ready, catch up by copying
+                            the most recently generated buffer into all but one of the output
+                            buffers.
+
+                            note that this catch up code only handles the case where all
+                            buffers have been played out due to this thread not having
+                            woken up at all. a more common case occurs when this thread
+                            is woken up, processes one buffer, but takes too long, and as
+                            a result all the other buffers have become un-queued. that
+                            case is handled further down.
+                            */
+
+                            result = CatchUpOutputBuffers( stream );
+                            if( result != paNoError )
+                                done = 1;
+
+                            statusFlags |= paOutputUnderflow;
+                        }
+
+                        hostOutputBufferIndex = stream->output.currentBufferIndex;
+                    }
+                }
+
+               
+                if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
+                        (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
+                {
+                    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
+
+
+                    if( PA_IS_OUTPUT_STREAM_(stream) )
+                    {
+                        /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime
+                            from the current wave out position */
+                        MMTIME mmtime;
+                        double timeBeforeGetPosition, timeAfterGetPosition;
+                        double time;
+                        long framesInBufferRing; 		
+                        long writePosition;
+                        long playbackPosition;
+                        HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
+                        
+                        mmtime.wType = TIME_SAMPLES;
+                        timeBeforeGetPosition = PaUtil_GetTime();
+                        waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
+                        timeAfterGetPosition = PaUtil_GetTime();
+
+                        timeInfo.currentTime = timeAfterGetPosition;
+
+                        /* approximate time at which wave out position was measured
+                            as half way between timeBeforeGetPosition and timeAfterGetPosition */
+                        time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
+                        
+                        framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
+                        playbackPosition = mmtime.u.sample % framesInBufferRing;
+
+                        writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
+                                + stream->output.framesUsedInCurrentBuffer;
+                       
+                        if( playbackPosition >= writePosition ){
+                            timeInfo.outputBufferDacTime =
+                                    time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
+                        }else{
+                            timeInfo.outputBufferDacTime =
+                                    time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod );
+                        }
+                    }
+
+
+                    PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+                    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags  );
+
+                    /* reset status flags once they have been passed to the buffer processor */
+                    statusFlags = 0;
+
+                    if( PA_IS_INPUT_STREAM_(stream) )
+                    {
+                        PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+                        channel = 0;
+                        for( i=0; i<stream->input.deviceCount; ++i )
+                        {
+                             /* we have stored the number of channels in the buffer in dwUser */
+                            int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+                            
+                            PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
+                                    stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
+                                        stream->input.framesUsedInCurrentBuffer * channelCount *
+                                        stream->bufferProcessor.bytesPerHostInputSample,
+                                    channelCount );
+                                    
+
+                            channel += channelCount;
+                        }
+                    }
+
+                    if( PA_IS_OUTPUT_STREAM_(stream) )
+                    {
+                        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+                        
+                        channel = 0;
+                        for( i=0; i<stream->output.deviceCount; ++i )
+                        {
+                            /* we have stored the number of channels in the buffer in dwUser */
+                            int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+                            PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+                                    stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+                                        stream->output.framesUsedInCurrentBuffer * channelCount *
+                                        stream->bufferProcessor.bytesPerHostOutputSample,
+                                    channelCount );
+
+                            channel += channelCount;
+                        }
+                    }
+
+                    callbackResult = paContinue;
+                    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+
+                    stream->input.framesUsedInCurrentBuffer += framesProcessed;
+                    stream->output.framesUsedInCurrentBuffer += framesProcessed;
+
+                    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+                    if( callbackResult == paContinue )
+                    {
+                        /* nothing special to do */
+                    }
+                    else if( callbackResult == paAbort )
+                    {
+                        stream->abortProcessing = 1;
+                        done = 1;
+                        /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */
+                        result = paNoError;
+                    }
+                    else
+                    {
+                        /* User callback has asked us to stop with paComplete or other non-zero value */
+                        stream->stopProcessing = 1; /* stop once currently queued audio has finished */
+                        result = paNoError;
+                    }
+
+
+                    if( PA_IS_INPUT_STREAM_(stream)
+                            && stream->stopProcessing == 0 && stream->abortProcessing == 0
+                            && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
+                    {
+                        if( NoBuffersAreQueued( &stream->input ) )
+                        {
+                            /** @todo need to handle PaNeverDropInput here where necessary */
+                            result = CatchUpInputBuffers( stream );
+                            if( result != paNoError )
+                                done = 1;
+
+                            statusFlags |= paInputOverflow;
+                        }
+
+                        result = AdvanceToNextInputBuffer( stream );
+                        if( result != paNoError )
+                            done = 1;
+                    }
+
+                    
+                    if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
+                    {
+                        if( stream->stopProcessing &&
+                                stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer )
+                        {
+                            /* zero remaining samples in output output buffer and flush */
+
+                            stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor,
+                                    stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+                            /* we send the entire buffer to the output devices, but we could
+                                just send a partial buffer, rather than zeroing the unused
+                                samples.
+                            */
+                        }
+
+                        if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
+                        {
+                            /* check for underflow before enquing the just-generated buffer,
+                                but recover from underflow after enquing it. This ensures
+                                that the most recent audio segment is repeated */
+                            int outputUnderflow = NoBuffersAreQueued( &stream->output );
+
+                            result = AdvanceToNextOutputBuffer( stream );
+                            if( result != paNoError )
+                                done = 1;
+
+                            if( outputUnderflow && !done && !stream->stopProcessing )
+                            {
+                                /* Recover from underflow in the case where the
+                                    underflow occured while processing the buffer
+                                    we just finished */
+
+                                result = CatchUpOutputBuffers( stream );
+                                if( result != paNoError )
+                                    done = 1;
+
+                                statusFlags |= paOutputUnderflow;
+                            }
+                        }
+                    }
+                    
+                    if( stream->throttleProcessingThreadOnOverload != 0 )
+                    {
+                        if( stream->stopProcessing || stream->abortProcessing )
+                        {
+                            if( stream->processingThreadPriority != stream->highThreadPriority )
+                            {
+                                SetThreadPriority( stream->processingThread, stream->highThreadPriority );
+                                stream->processingThreadPriority = stream->highThreadPriority;
+                            }
+                        }
+                        else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. )
+                        {
+                            if( stream->processingThreadPriority != stream->throttledThreadPriority )
+                            {
+                                SetThreadPriority( stream->processingThread, stream->throttledThreadPriority );
+                                stream->processingThreadPriority = stream->throttledThreadPriority;
+                            }
+
+                            /* sleep to give other processes a go */
+                            Sleep( stream->throttledSleepMsecs );
+                        }
+                        else
+                        {
+                            if( stream->processingThreadPriority != stream->highThreadPriority )
+                            {
+                                SetThreadPriority( stream->processingThread, stream->highThreadPriority );
+                                stream->processingThreadPriority = stream->highThreadPriority;
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    hostBuffersAvailable = 0;
+                }
+            }
+            while( hostBuffersAvailable &&
+                    stream->stopProcessing == 0 &&
+                    stream->abortProcessing == 0 &&
+                    !done );
+        }
+    }
+    while( !done );
+
+    stream->isActive = 0;
+
+    if( stream->streamRepresentation.streamFinishedCallback != 0 )
+        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+    
+    return result;
+}
+
+
+/*
+    When CloseStream() is called, the multi-api layer ensures that
+    the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+    PaError result;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+    result = CloseHandleWithPaError( stream->abortEvent );
+    if( result != paNoError ) goto error;
+    
+    TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
+    TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
+
+    TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
+    TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
+    
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+    PaUtil_FreeMemory( stream );
+
+error:
+    /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */
+    return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+    PaError result;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    MMRESULT mmresult;
+    unsigned int i, j;
+    int callbackResult;
+	unsigned int channel;
+ 	unsigned long framesProcessed;
+	PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
+    
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+    
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        for( i=0; i<stream->input.bufferCount; ++i )
+        {
+            for( j=0; j<stream->input.deviceCount; ++j )
+            {
+                mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
+                if( mmresult != MMSYSERR_NOERROR )
+                {
+                    result = paUnanticipatedHostError;
+                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                    goto error;
+                }
+            }
+        }
+        stream->input.currentBufferIndex = 0;
+        stream->input.framesUsedInCurrentBuffer = 0;
+    }
+
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        for( i=0; i<stream->output.deviceCount; ++i )
+        {
+            if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                goto error;
+            }
+        }
+
+        for( i=0; i<stream->output.bufferCount; ++i )
+        {
+            if( stream->primeStreamUsingCallback )
+            {
+
+                stream->output.framesUsedInCurrentBuffer = 0;
+                do{
+
+                    PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
+                            &timeInfo,
+                            paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0));
+
+                    if( stream->input.bufferCount > 0 )
+                        PaUtil_SetNoInput( &stream->bufferProcessor );
+
+                    PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+                    channel = 0;
+                    for( j=0; j<stream->output.deviceCount; ++j )
+                    {
+                        /* we have stored the number of channels in the buffer in dwUser */
+                        int channelCount = stream->output.waveHeaders[j][i].dwUser;
+
+                        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+                                stream->output.waveHeaders[j][i].lpData +
+                                stream->output.framesUsedInCurrentBuffer * channelCount *
+                                stream->bufferProcessor.bytesPerHostOutputSample,
+                                channelCount );
+
+                        /* we have stored the number of channels in the buffer in dwUser */
+                        channel += channelCount;
+                    }
+
+                    callbackResult = paContinue;
+                    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+                    stream->output.framesUsedInCurrentBuffer += framesProcessed;
+
+                    if( callbackResult != paContinue )
+                    {
+                        /** @todo fix this, what do we do if callback result is non-zero during stream
+                            priming?
+
+                            for complete: play out primed waveHeaders as usual
+                            for abort: clean up immediately.
+                       */
+                    }
+
+                }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer );
+
+            }
+            else
+            {
+                for( j=0; j<stream->output.deviceCount; ++j )
+                {
+                    ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
+                }
+            }   
+
+            /* we queue all channels of a single buffer frame (accross all
+                devices, because some multidevice multichannel drivers work
+                better this way */
+            for( j=0; j<stream->output.deviceCount; ++j )
+            {
+                mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) );
+                if( mmresult != MMSYSERR_NOERROR )
+                {
+                    result = paUnanticipatedHostError;
+                    PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                    goto error;
+                }
+            }
+        }
+        stream->output.currentBufferIndex = 0;
+        stream->output.framesUsedInCurrentBuffer = 0;
+    }
+
+
+    stream->isStopped = 0;
+    stream->isActive = 1;
+    stream->stopProcessing = 0;
+    stream->abortProcessing = 0;
+
+    result = ResetEventWithPaError( stream->input.bufferEvent );
+    if( result != paNoError ) goto error;
+
+    result = ResetEventWithPaError( stream->output.bufferEvent );
+    if( result != paNoError ) goto error;
+    
+    
+    if( stream->streamRepresentation.streamCallback )
+    {
+        /* callback stream */
+
+        result = ResetEventWithPaError( stream->abortEvent );
+        if( result != paNoError ) goto error;
+
+        /* Create thread that waits for audio buffers to be ready for processing. */
+        stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
+        if( !stream->processingThread )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+            goto error;
+        }
+
+        /** @todo could have mme specific stream parameters to allow the user
+            to set the callback thread priorities */
+        stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
+        stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL;
+
+        if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+            goto error;
+        }
+        stream->processingThreadPriority = stream->highThreadPriority;
+    }
+    else
+    {
+        /* blocking read/write stream */
+
+    }
+
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        for( i=0; i < stream->input.deviceCount; ++i )
+        {
+            mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] );
+            PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                goto error;
+            }
+        }
+    }
+
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        for( i=0; i < stream->output.deviceCount; ++i )
+        {
+            if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                goto error;
+            }
+        }
+    }
+
+    return result;
+
+error:
+    /** @todo FIXME: implement recovery as best we can
+    This should involve rolling back to a state as-if this function had never been called
+    */
+    return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    int timeout;
+    DWORD waitResult;
+    MMRESULT mmresult;
+    signed int hostOutputBufferIndex;
+    unsigned int channel, waitCount, i;                  
+    
+    /** @todo
+        REVIEW: the error checking in this function needs review. the basic
+        idea is to return from this function in a known state - for example
+        there is no point avoiding calling waveInReset just because
+        the thread times out.
+    */
+
+    if( stream->processingThread )
+    {
+        /* callback stream */
+
+        /* Tell processing thread to stop generating more data and to let current data play out. */
+        stream->stopProcessing = 1;
+
+        /* Calculate timeOut longer than longest time it could take to return all buffers. */
+        timeout = (int)(stream->allBuffersDurationMs * 1.5);
+        if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+            timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+        PA_DEBUG(("WinMME StopStream: waiting for background thread.\n"));
+
+        waitResult = WaitForSingleObject( stream->processingThread, timeout );
+        if( waitResult == WAIT_TIMEOUT )
+        {
+            /* try to abort */
+            stream->abortProcessing = 1;
+            SetEvent( stream->abortEvent );
+            waitResult = WaitForSingleObject( stream->processingThread, timeout );
+            if( waitResult == WAIT_TIMEOUT )
+            {
+                PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n"));
+                result = paTimedOut;
+            }
+        }
+
+        CloseHandle( stream->processingThread );
+        stream->processingThread = NULL;
+    }
+    else
+    {
+        /* blocking read / write stream */
+
+        if( PA_IS_OUTPUT_STREAM_(stream) )
+        {
+            if( stream->output.framesUsedInCurrentBuffer > 0 )
+            {
+                /* there are still unqueued frames in the current buffer, so flush them */
+
+                hostOutputBufferIndex = stream->output.currentBufferIndex;
+
+                PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
+                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+                
+                channel = 0;
+                for( i=0; i<stream->output.deviceCount; ++i )
+                {
+                    /* we have stored the number of channels in the buffer in dwUser */
+                    int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+                    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+                            stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+                                stream->output.framesUsedInCurrentBuffer * channelCount *
+                                stream->bufferProcessor.bytesPerHostOutputSample,
+                            channelCount );
+
+                    channel += channelCount;
+                }
+
+                PaUtil_ZeroOutput( &stream->bufferProcessor,
+                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+                /* we send the entire buffer to the output devices, but we could
+                    just send a partial buffer, rather than zeroing the unused
+                    samples.
+                */
+                AdvanceToNextOutputBuffer( stream );
+            }
+            
+
+            timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
+            if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+                timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+            waitCount = 0;
+            while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
+            {
+                /* wait for MME to signal that a buffer is available */
+                waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
+                if( waitResult == WAIT_FAILED )
+                {
+                    break;
+                }
+                else if( waitResult == WAIT_TIMEOUT )
+                {
+                    /* keep waiting */
+                }
+
+                ++waitCount;
+            }
+        }
+    }
+
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        for( i =0; i < stream->output.deviceCount; ++i )
+        {
+            mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+            }
+        }
+    }
+
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        for( i=0; i < stream->input.deviceCount; ++i )
+        {
+            mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+            }
+        }
+    }
+
+    stream->isStopped = 1;
+    stream->isActive = 0;
+
+    return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    int timeout;
+    DWORD waitResult;
+    MMRESULT mmresult;
+    unsigned int i;
+    
+    /** @todo
+        REVIEW: the error checking in this function needs review. the basic
+        idea is to return from this function in a known state - for example
+        there is no point avoiding calling waveInReset just because
+        the thread times out.
+    */
+
+    if( stream->processingThread )
+    {
+        /* callback stream */
+        
+        /* Tell processing thread to abort immediately */
+        stream->abortProcessing = 1;
+        SetEvent( stream->abortEvent );
+    }
+
+
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        for( i =0; i < stream->output.deviceCount; ++i )
+        {
+            mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                return paUnanticipatedHostError;
+            }
+        }
+    }
+
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        for( i=0; i < stream->input.deviceCount; ++i )
+        {
+            mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                return paUnanticipatedHostError;
+            }
+        }
+    }
+
+
+    if( stream->processingThread )
+    {
+        /* callback stream */
+        
+        PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
+
+        /* Calculate timeOut longer than longest time it could take to return all buffers. */
+        timeout = (int)(stream->allBuffersDurationMs * 1.5);
+        if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+            timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+            
+        waitResult = WaitForSingleObject( stream->processingThread, timeout );
+        if( waitResult == WAIT_TIMEOUT )
+        {
+            PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n"));
+            return paTimedOut;
+        }
+
+        CloseHandle( stream->processingThread );
+        stream->processingThread = NULL;
+    }
+
+    stream->isStopped = 1;
+    stream->isActive = 0;
+
+    return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+    return stream->isStopped;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+    return stream->isActive;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    (void) s; /* unused parameter */
+    
+    return PaUtil_GetTime();
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaError result = paNoError;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    void *userBuffer;
+    unsigned long framesRead = 0;
+    unsigned long framesProcessed;
+    signed int hostInputBufferIndex;
+    DWORD waitResult;
+    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+    unsigned int channel, i;
+    
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        /* make a local copy of the user buffer pointer(s). this is necessary
+            because PaUtil_CopyInput() advances these pointers every time
+            it is called.
+        */
+        if( stream->bufferProcessor.userInputIsInterleaved )
+        {
+            userBuffer = buffer;
+        }
+        else
+        {
+            userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
+            if( !userBuffer )
+                return paInsufficientMemory;
+            for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
+                ((void**)userBuffer)[i] = ((void**)buffer)[i];
+        }
+        
+        do{
+            if( CurrentInputBuffersAreDone( stream ) )
+            {
+                if( NoBuffersAreQueued( &stream->input ) )
+                {
+                    /** @todo REVIEW: consider what to do if the input overflows.
+                        do we requeue all of the buffers? should we be running
+                        a thread to make sure they are always queued? */
+
+                    result = paInputOverflowed;
+                }
+
+                hostInputBufferIndex = stream->input.currentBufferIndex;
+
+                PaUtil_SetInputFrameCount( &stream->bufferProcessor,
+                        stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
+                
+                channel = 0;
+                for( i=0; i<stream->input.deviceCount; ++i )
+                {
+                    /* we have stored the number of channels in the buffer in dwUser */
+                    int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+
+                    PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
+                            stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
+                                stream->input.framesUsedInCurrentBuffer * channelCount *
+                                stream->bufferProcessor.bytesPerHostInputSample,
+                            channelCount );
+
+                    channel += channelCount;
+                }
+                
+                framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
+
+                stream->input.framesUsedInCurrentBuffer += framesProcessed;
+                if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
+                {
+                    result = AdvanceToNextInputBuffer( stream );
+                    if( result != paNoError )
+                        break;
+                }
+
+                framesRead += framesProcessed;      
+
+            }else{
+                /* wait for MME to signal that a buffer is available */
+                waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout );
+                if( waitResult == WAIT_FAILED )
+                {
+                    result = paUnanticipatedHostError;
+                    break;
+                }
+                else if( waitResult == WAIT_TIMEOUT )
+                {
+                    /* if a timeout is encountered, continue,
+                        perhaps we should give up eventually
+                    */
+                }         
+            }
+        }while( framesRead < frames );
+    }
+    else
+    {
+        result = paCanNotReadFromAnOutputOnlyStream;
+    }
+
+    return result;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaError result = paNoError;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    const void *userBuffer;
+    unsigned long framesWritten = 0;
+    unsigned long framesProcessed;
+    signed int hostOutputBufferIndex;
+    DWORD waitResult;
+    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+    unsigned int channel, i;
+
+        
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        /* make a local copy of the user buffer pointer(s). this is necessary
+            because PaUtil_CopyOutput() advances these pointers every time
+            it is called.
+        */
+        if( stream->bufferProcessor.userOutputIsInterleaved )
+        {
+            userBuffer = buffer;
+        }
+        else
+        {
+            userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
+            if( !userBuffer )
+                return paInsufficientMemory;
+            for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
+                ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
+        }
+
+        do{
+            if( CurrentOutputBuffersAreDone( stream ) )
+            {
+                if( NoBuffersAreQueued( &stream->output ) )
+                {
+                    /** @todo REVIEW: consider what to do if the output
+                    underflows. do we requeue all the existing buffers with
+                    zeros? should we run a separate thread to keep the buffers
+                    enqueued at all times? */
+
+                    result = paOutputUnderflowed;
+                }
+
+                hostOutputBufferIndex = stream->output.currentBufferIndex;
+
+                PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
+                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+                
+                channel = 0;
+                for( i=0; i<stream->output.deviceCount; ++i )
+                {
+                    /* we have stored the number of channels in the buffer in dwUser */
+                    int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+                    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+                            stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+                                stream->output.framesUsedInCurrentBuffer * channelCount *
+                                stream->bufferProcessor.bytesPerHostOutputSample,
+                            channelCount );
+
+                    channel += channelCount;
+                }
+                
+                framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
+
+                stream->output.framesUsedInCurrentBuffer += framesProcessed;
+                if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
+                {
+                    result = AdvanceToNextOutputBuffer( stream );
+                    if( result != paNoError )
+                        break;
+                }
+
+                framesWritten += framesProcessed;
+            }
+            else
+            {
+                /* wait for MME to signal that a buffer is available */
+                waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
+                if( waitResult == WAIT_FAILED )
+                {
+                    result = paUnanticipatedHostError;
+                    break;
+                }
+                else if( waitResult == WAIT_TIMEOUT )
+                {
+                    /* if a timeout is encountered, continue,
+                        perhaps we should give up eventually
+                    */
+                }             
+            }        
+        }while( framesWritten < frames );
+    }
+    else
+    {
+        result = paCanNotWriteToAnInputOnlyStream;
+    }
+    
+    return result;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    
+    if( PA_IS_INPUT_STREAM_(stream) )
+        return GetAvailableFrames( &stream->input );
+    else
+        return paCanNotReadFromAnOutputOnlyStream;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+        return GetAvailableFrames( &stream->output );
+    else
+        return paCanNotWriteToAnInputOnlyStream;
+}
+
+
+/* NOTE: the following functions are MME-stream specific, and are called directly
+    by client code. We need to check for many more error conditions here because
+    we don't have the benefit of pa_front.c's parameter checking.
+*/
+
+static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s )
+{
+    PaError result;
+    PaUtilHostApiRepresentation *hostApi;
+    PaWinMmeHostApiRepresentation *winMmeHostApi;
+    
+    result = PaUtil_ValidateStreamPointer( s );
+    if( result != paNoError )
+        return result;
+
+    result = PaUtil_GetHostApiRepresentation( &hostApi, paMME );
+    if( result != paNoError )
+        return result;
+
+    winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+    
+    /* note, the following would be easier if there was a generic way of testing
+        that a stream belongs to a specific host API */
+    
+    if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
+            || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
+    {
+        /* s is a WinMME stream */
+        *stream = (PaWinMmeStream *)s;
+        return paNoError;
+    }
+    else
+    {
+        return paIncompatibleStreamHostApi;
+    }
+}
+
+
+int PaWinMME_GetStreamInputHandleCount( PaStream* s )
+{
+    PaWinMmeStream *stream;
+    PaError result = GetWinMMEStreamPointer( &stream, s );
+
+    if( result == paNoError )
+        return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0;
+    else
+        return result;
+}
+
+
+HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex )
+{
+    PaWinMmeStream *stream;
+    PaError result = GetWinMMEStreamPointer( &stream, s );
+
+    if( result == paNoError
+            && PA_IS_INPUT_STREAM_(stream)
+            && handleIndex >= 0
+            && (unsigned int)handleIndex < stream->input.deviceCount )
+        return ((HWAVEIN*)stream->input.waveHandles)[handleIndex];
+    else
+        return 0;
+}
+
+
+int PaWinMME_GetStreamOutputHandleCount( PaStream* s)
+{
+    PaWinMmeStream *stream;
+    PaError result = GetWinMMEStreamPointer( &stream, s );
+
+    if( result == paNoError )
+        return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0;
+    else
+        return result;
+}
+
+
+HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex )
+{
+    PaWinMmeStream *stream;
+    PaError result = GetWinMMEStreamPointer( &stream, s );
+
+    if( result == paNoError
+            && PA_IS_OUTPUT_STREAM_(stream)
+            && handleIndex >= 0
+            && (unsigned int)handleIndex < stream->output.deviceCount )
+        return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex];
+    else
+        return 0;
+}
+
+
+
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h	(revision 1753)
@@ -0,0 +1,181 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_WIN_WMME_H
+#define PA_WIN_WMME_H
+/*
+ * $Id: pa_win_wmme.h,v 1.1.2.14 2004/02/20 14:16:53 rossbencina Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * MME specific extensions
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/** @file
+ @brief WMME-specific PortAudio API extension header file.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#define paWinMmeUseLowLevelLatencyParameters            (0x01)
+#define paWinMmeUseMultipleDevices                      (0x02)  /* use mme specific multiple device feature */
+
+
+/* By default, the mme implementation drops the processing thread's priority
+    to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100%
+    This flag disables any priority throttling. The processing thread will always
+    run at THREAD_PRIORITY_TIME_CRITICAL.
+*/
+#define paWinMmeDontThrottleOverloadedProcessingThread  (0x08)
+
+
+typedef struct PaWinMmeDeviceAndChannelCount{
+    PaDeviceIndex device;
+    int channelCount;
+}PaWinMmeDeviceAndChannelCount;
+
+
+typedef struct PaWinMmeStreamInfo{
+    unsigned long size;             /**< sizeof(PaWinMmeStreamInfo) */
+    PaHostApiTypeId hostApiType;    /**< paMME */
+    unsigned long version;          /**< 1 */
+
+    unsigned long flags;
+
+    /* low-level latency setting support
+        These settings control the number and size of host buffers in order
+        to set latency. They will be used instead of the generic parameters
+        to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters
+        flag.
+
+        If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters
+        are supplied for both input and output in a full duplex stream, then the
+        input and output framesPerBuffer must be the same, or the larger of the
+        two must be a multiple of the smaller, otherwise a
+        paIncompatibleHostApiSpecificStreamInfo error will be returned from
+        Pa_OpenStream().
+    */
+    unsigned long framesPerBuffer;
+    unsigned long bufferCount;  /* formerly numBuffers */ 
+
+    /* multiple devices per direction support
+        If flags contains the PaWinMmeUseMultipleDevices flag,
+        this functionality will be used, otherwise the device parameter to
+        Pa_OpenStream() will be used instead.
+        If devices are specified here, the corresponding device parameter
+        to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification,
+        otherwise an paInvalidDevice error will result.
+        The total number of channels accross all specified devices
+        must agree with the corresponding channelCount parameter to
+        Pa_OpenStream() otherwise a paInvalidChannelCount error will result.
+    */
+    PaWinMmeDeviceAndChannelCount *devices;
+    unsigned long deviceCount;
+
+}PaWinMmeStreamInfo;
+
+
+/** Retrieve the number of wave in handles used by a PortAudio WinMME stream.
+ Returns zero if the stream is output only.
+
+ @return A non-negative value indicating the number of wave in handles
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaWinMME_GetStreamInputHandle
+*/
+int PaWinMME_GetStreamInputHandleCount( PaStream* stream );
+
+
+/** Retrieve a wave in handle used by a PortAudio WinMME stream.
+
+ @param stream The stream to query.
+ @param handleIndex The zero based index of the wave in handle to retrieve. This
+    should be in the range [0, PaWinMME_GetStreamInputHandle(stream)-1].
+
+ @return A valid wave in handle, or NULL if an error occurred.
+
+ @see PaWinMME_GetStreamInputHandle
+*/
+HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex );
+
+
+/** Retrieve the number of wave out handles used by a PortAudio WinMME stream.
+ Returns zero if the stream is input only.
+ 
+ @return A non-negative value indicating the number of wave out handles
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaWinMME_GetStreamOutputHandle
+*/
+int PaWinMME_GetStreamOutputHandleCount( PaStream* stream );
+
+
+/** Retrieve a wave out handle used by a PortAudio WinMME stream.
+
+ @param stream The stream to query.
+ @param handleIndex The zero based index of the wave out handle to retrieve.
+    This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1].
+
+ @return A valid wave out handle, or NULL if an error occurred.
+
+ @see PaWinMME_GetStreamOutputHandleCount
+*/
+HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_WIN_WMME_H */                                  
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c	(revision 1753)
@@ -0,0 +1,1188 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include "pa_x86_plain_converters.h"
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+
+/*
+    plain intel assemby versions of standard pa converter functions.
+
+    the main reason these versions are faster than the equivalent C versions
+    is that float -> int casting is expensive in C on x86 because the rounding
+    mode needs to be changed for every cast. these versions only set
+    the rounding mode once outside the loop.
+
+    small additional speed gains are made by the way that clamping is
+    implemented.
+
+TODO:
+    o- inline dither code
+    o- implement Dither only (no-clip) versions
+    o- implement int8 and uint8 versions
+    o- test thouroughly
+
+    o- the packed 24 bit functions could benefit from unrolling and avoiding
+        byte and word sized register access.
+*/
+
+/* -------------------------------------------------------------------------- */
+
+/*
+#define PA_CLIP_( val, min, max )\
+    { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+*/
+
+/*
+    the following notes were used to determine whether a floating point
+    value should be saturated (ie >1 or <-1) by loading it into an integer
+    register. these should be rewritten so that they make sense.
+
+    an ieee floating point value
+
+    1.xxxxxxxxxxxxxxxxxxxx?
+
+
+    is less than  or equal to 1 and greater than or equal to -1 either:
+
+        if the mantissa is 0 and the unbiased exponent is 0
+
+        OR
+
+        if the unbiased exponent < 0
+
+    this translates to:
+
+        if the mantissa is 0 and the biased exponent is 7F
+
+        or
+
+        if the biased exponent is less than 7F
+
+
+    therefore the value is greater than 1 or less than -1 if
+
+        the mantissa is not 0 and the biased exponent is 7F
+
+        or
+
+        if the biased exponent is greater than 7F
+
+
+    in other words, if we mask out the sign bit, the value is
+    greater than 1 or less than -1 if its integer representation is greater than:
+
+    0 01111111 0000 0000 0000 0000 0000 000
+
+    0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000
+*/
+
+/* -------------------------------------------------------------------------- */
+
+static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/
+static const double int32Scaler_ = 0x7FFFFFFF;
+static const double ditheredInt32Scaler_ = 0x7FFFFFFE;
+static const double int24Scaler_ = 0x7FFFFF;
+static const double ditheredInt24Scaler_ = 0x7FFFFE;
+static const double int16Scaler_ = 0x7FFF;
+static const double ditheredInt16Scaler_ = 0x7FFE;
+
+#define PA_DITHER_BITS_   (15)
+/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
+#define PA_FLOAT_DITHER_SCALE_  (1.0 / ((1<<PA_DITHER_BITS_)-1))
+static const float const_float_dither_scale_ = (float) PA_FLOAT_DITHER_SCALE_;
+#define PA_DITHER_SHIFT_  ((32 - PA_DITHER_BITS_) + 1)
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+        // REVIEW
+        double scaled = *src * 0x7FFFFFFF;
+        *dest = (signed long) scaled;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+
+    (void) ditherGenerator; /* unused parameter */
+
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32 and int32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+    
+        mov     edi, destinationBuffer
+        
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int32Scaler_             // stack:  (int)0x7FFFFFFF
+
+    Float32_To_Int32_loop:
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFFFFFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFFFFFF, (int)0x7FFFFFFF
+        /*
+            note: we could store to a temporary qword here which would cause
+            wraparound distortion instead of int indefinite 0x10. that would
+            be more work, and given that not enabling clipping is only advisable
+            when you know that your signal isn't going to clip it isn't worth it.
+        */
+        fistp   dword ptr [edi]         // pop st(0) into dest, stack:  (int)0x7FFFFFFF
+
+        add     edi, ebx                // increment destination ptr
+        //lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int32_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void) ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+        // REVIEW
+        double scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648., 2147483647.  );
+        *dest = (signed long) scaled;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+
+    (void) ditherGenerator; /* unused parameter */
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32 and int32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+    
+        mov     edi, destinationBuffer
+        
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int32Scaler_             // stack:  (int)0x7FFFFFFF
+
+    Float32_To_Int32_Clip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int32_Clip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFFFFFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFFFFFF, (int)0x7FFFFFFF
+        fistp   dword ptr [edi]         // pop st(0) into dest, stack:  (int)0x7FFFFFFF
+        jmp     Float32_To_Int32_Clip_stored
+    
+    Float32_To_Int32_Clip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     edx, 0x7FFFFFFF         // convert to maximum range integers
+        mov     dword ptr [edi], edx
+
+    Float32_To_Int32_Clip_stored:
+
+        //add     edi, ebx                // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int32_Clip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    /*
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+
+    while( count-- )
+    {
+        // REVIEW
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        // use smaller scaler to prevent overflow when we add the dither
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        PA_CLIP_( dithered, -2147483648., 2147483647.  );
+        *dest = (signed long) dithered;
+
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+    */
+
+    short savedFpuControlWord;
+
+    // spill storage:
+    signed long sourceByteStride;
+    signed long highpassedDither;
+
+    // dither state:
+    unsigned long ditherPrevious = ditherGenerator->previous;
+    unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+    unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+                    
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32 and int32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+    
+        mov     edi, destinationBuffer
+        
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     ditheredInt32Scaler_    // stack:  int scaler
+
+    Float32_To_Int32_DitherClip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int32_DitherClip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, int scaler
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*(int scaler), int scaler
+
+        /*
+        // call PaUtil_GenerateFloatTriangularDither with C calling convention
+        mov     sourceByteStride, eax   // save eax
+        mov     sourceEnd, ecx          // save ecx
+        push    ditherGenerator         // pass ditherGenerator parameter on stack
+	    call    PaUtil_GenerateFloatTriangularDither  // stack:  dither, value*(int scaler), int scaler
+	    pop     edx                     // clear parameter off stack
+        mov     ecx, sourceEnd          // restore ecx
+        mov     eax, sourceByteStride   // restore eax
+        */
+
+    // generate dither
+        mov     sourceByteStride, eax   // save eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed1
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     ditherRandSeed1, eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed2
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     edx, ditherRandSeed1
+        shr     edx, PA_DITHER_SHIFT_
+        mov     ditherRandSeed2, eax
+        shr     eax, PA_DITHER_SHIFT_
+        //add     eax, edx                // eax -> current
+        lea     eax, [eax+edx]
+        mov     edx, ditherPrevious
+        neg     edx
+        lea     edx, [eax+edx]          // highpass = current - previous
+        mov     highpassedDither, edx
+        mov     ditherPrevious, eax     // previous = current
+        mov     eax, sourceByteStride   // restore eax
+        fild    highpassedDither
+        fmul    const_float_dither_scale_
+    // end generate dither, dither signal in st(0)
+    
+        faddp   st(1), st(0)            // stack: dither + value*(int scaler), int scaler
+        fistp   dword ptr [edi]         // pop st(0) into dest, stack:  int scaler
+        jmp     Float32_To_Int32_DitherClip_stored
+    
+    Float32_To_Int32_DitherClip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     edx, 0x7FFFFFFF         // convert to maximum range integers
+        mov     dword ptr [edi], edx
+
+    Float32_To_Int32_DitherClip_stored:
+
+        //add     edi, ebx              // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int32_DitherClip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+
+    ditherGenerator->previous = ditherPrevious;
+    ditherGenerator->randSeed1 = ditherRandSeed1;
+    ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; // unused parameter
+    
+    while( count-- )
+    {
+        // convert to 32 bit and drop the low 8 bits
+        double scaled = *src * 0x7FFFFFFF;
+        temp = (signed long) scaled;
+
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+*/
+
+    short savedFpuControlWord;
+    
+    signed long tempInt32;
+
+    (void) ditherGenerator; /* unused parameter */
+                 
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 3                  // sizeof int24
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int24Scaler_             // stack:  (int)0x7FFFFF
+
+    Float32_To_Int24_loop:
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFFFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFFFF, (int)0x7FFFFF
+        fistp   tempInt32               // pop st(0) into tempInt32, stack:  (int)0x7FFFFF
+        mov     edx, tempInt32
+
+        mov     byte ptr [edi], DL
+        shr     edx, 8
+        //mov     byte ptr [edi+1], DL
+        //mov     byte ptr [edi+2], DH
+        mov     word ptr [edi+1], DX
+
+        //add     edi, ebx                // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int24_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; // unused parameter
+    
+    while( count-- )
+    {
+        // convert to 32 bit and drop the low 8 bits
+        double scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648., 2147483647.  );
+        temp = (signed long) scaled;
+
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+*/
+
+    short savedFpuControlWord;
+    
+    signed long tempInt32;
+
+    (void) ditherGenerator; /* unused parameter */
+                 
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 3                  // sizeof int24
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int24Scaler_             // stack:  (int)0x7FFFFF
+
+    Float32_To_Int24_Clip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int24_Clip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFFFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFFFF, (int)0x7FFFFF
+        fistp   tempInt32               // pop st(0) into tempInt32, stack:  (int)0x7FFFFF
+        mov     edx, tempInt32
+        jmp     Float32_To_Int24_Clip_store
+    
+    Float32_To_Int24_Clip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     edx, 0x7FFFFF           // convert to maximum range integers
+
+    Float32_To_Int24_Clip_store:
+
+        mov     byte ptr [edi], DL
+        shr     edx, 8
+        //mov     byte ptr [edi+1], DL
+        //mov     byte ptr [edi+2], DH
+        mov     word ptr [edi+1], DX
+
+        //add     edi, ebx                // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int24_Clip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+    
+    while( count-- )
+    {
+        // convert to 32 bit and drop the low 8 bits
+
+        // FIXME: the dither amplitude here appears to be too small by 8 bits
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        // use smaller scaler to prevent overflow when we add the dither
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        PA_CLIP_( dithered, -2147483648., 2147483647.  );
+        
+        temp = (signed long) dithered;
+
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+*/
+
+    short savedFpuControlWord;
+
+    // spill storage:
+    signed long sourceByteStride;
+    signed long highpassedDither;
+
+    // dither state:
+    unsigned long ditherPrevious = ditherGenerator->previous;
+    unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+    unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+    
+    signed long tempInt32;
+                 
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 3                  // sizeof int24
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     ditheredInt24Scaler_    // stack:  int scaler
+
+    Float32_To_Int24_DitherClip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int24_DitherClip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, int scaler
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*(int scaler), int scaler
+
+    /*
+        // call PaUtil_GenerateFloatTriangularDither with C calling convention
+        mov     sourceByteStride, eax   // save eax
+        mov     sourceEnd, ecx          // save ecx
+        push    ditherGenerator         // pass ditherGenerator parameter on stack
+	    call    PaUtil_GenerateFloatTriangularDither  // stack:  dither, value*(int scaler), int scaler
+	    pop     edx                     // clear parameter off stack
+        mov     ecx, sourceEnd          // restore ecx
+        mov     eax, sourceByteStride   // restore eax
+    */
+    
+    // generate dither
+        mov     sourceByteStride, eax   // save eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed1
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     ditherRandSeed1, eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed2
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     edx, ditherRandSeed1
+        shr     edx, PA_DITHER_SHIFT_
+        mov     ditherRandSeed2, eax
+        shr     eax, PA_DITHER_SHIFT_
+        //add     eax, edx                // eax -> current
+        lea     eax, [eax+edx]
+        mov     edx, ditherPrevious
+        neg     edx
+        lea     edx, [eax+edx]          // highpass = current - previous
+        mov     highpassedDither, edx
+        mov     ditherPrevious, eax     // previous = current
+        mov     eax, sourceByteStride   // restore eax
+        fild    highpassedDither
+        fmul    const_float_dither_scale_
+    // end generate dither, dither signal in st(0)
+
+        faddp   st(1), st(0)            // stack: dither * value*(int scaler), int scaler
+        fistp   tempInt32               // pop st(0) into tempInt32, stack:  int scaler
+        mov     edx, tempInt32
+        jmp     Float32_To_Int24_DitherClip_store
+    
+    Float32_To_Int24_DitherClip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     edx, 0x7FFFFF           // convert to maximum range integers
+
+    Float32_To_Int24_DitherClip_store:
+
+        mov     byte ptr [edi], DL
+        shr     edx, 8
+        //mov     byte ptr [edi+1], DL
+        //mov     byte ptr [edi+2], DH
+        mov     word ptr [edi+1], DX
+
+        //add     edi, ebx                // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int24_DitherClip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+
+    ditherGenerator->previous = ditherPrevious;
+    ditherGenerator->randSeed1 = ditherRandSeed1;
+    ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+
+        short samp = (short) (*src * (32767.0f));
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+   
+    (void) ditherGenerator; /* unused parameter */
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx                // source byte stride
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi                // source end ptr = count * source byte stride + source ptr
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 2                  // sizeof int16
+        mov     ebx, destinationStride
+        imul    ebx, edx                // destination byte stride
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int16Scaler_            // stack:  (int)0x7FFF
+
+    Float32_To_Int16_loop:
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFF, (int)0x7FFF
+        fistp   word ptr [edi]          // store scaled int into dest, stack:  (int)0x7FFF
+
+        add     edi, ebx                // increment destination ptr
+        //lea     edi, [edi+ebx]
+        
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int16_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+        long samp = (signed long) (*src * (32767.0f));
+        PA_CLIP_( samp, -0x8000, 0x7FFF );
+        *dest = (signed short) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+   
+    (void) ditherGenerator; /* unused parameter */
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx                // source byte stride
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi                // source end ptr = count * source byte stride + source ptr
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 2                  // sizeof int16
+        mov     ebx, destinationStride
+        imul    ebx, edx                // destination byte stride
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int16Scaler_            // stack:  (int)0x7FFF
+
+    Float32_To_Int16_Clip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int16_Clip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFF, (int)0x7FFF
+        fistp   word ptr [edi]          // store scaled int into dest, stack:  (int)0x7FFF
+        jmp     Float32_To_Int16_Clip_stored
+    
+    Float32_To_Int16_Clip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     dx, 0x7FFF              // convert to maximum range integers
+        mov     word ptr [edi], dx      // store clamped into into dest
+
+    Float32_To_Int16_Clip_stored:
+
+        add     edi, ebx                // increment destination ptr
+        //lea     edi, [edi+ebx]
+        
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int16_Clip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        // use smaller scaler to prevent overflow when we add the dither 
+        float dithered = (*src * (32766.0f)) + dither;
+        signed long samp = (signed long) dithered;
+        PA_CLIP_( samp, -0x8000, 0x7FFF );
+        *dest = (signed short) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+
+    // spill storage:
+    signed long sourceByteStride;
+    signed long highpassedDither;
+
+    // dither state:
+    unsigned long ditherPrevious = ditherGenerator->previous;
+    unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+    unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx                // source byte stride
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi                // source end ptr = count * source byte stride + source ptr
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 2                  // sizeof int16
+        mov     ebx, destinationStride
+        imul    ebx, edx                // destination byte stride
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     ditheredInt16Scaler_    // stack:  int scaler
+
+    Float32_To_Int16_DitherClip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int16_DitherClip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, int scaler
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*(int scaler), int scaler
+
+        /*
+        // call PaUtil_GenerateFloatTriangularDither with C calling convention
+        mov     sourceByteStride, eax   // save eax
+        mov     sourceEnd, ecx          // save ecx
+        push    ditherGenerator         // pass ditherGenerator parameter on stack
+	    call    PaUtil_GenerateFloatTriangularDither  // stack:  dither, value*(int scaler), int scaler
+	    pop     edx                     // clear parameter off stack
+        mov     ecx, sourceEnd          // restore ecx
+        mov     eax, sourceByteStride   // restore eax
+        */
+
+    // generate dither
+        mov     sourceByteStride, eax   // save eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed1
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     ditherRandSeed1, eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed2
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     edx, ditherRandSeed1
+        shr     edx, PA_DITHER_SHIFT_
+        mov     ditherRandSeed2, eax
+        shr     eax, PA_DITHER_SHIFT_
+        //add     eax, edx                // eax -> current
+        lea     eax, [eax+edx]            // current = randSeed1>>x + randSeed2>>x
+        mov     edx, ditherPrevious
+        neg     edx
+        lea     edx, [eax+edx]          // highpass = current - previous
+        mov     highpassedDither, edx
+        mov     ditherPrevious, eax     // previous = current
+        mov     eax, sourceByteStride   // restore eax
+        fild    highpassedDither
+        fmul    const_float_dither_scale_
+    // end generate dither, dither signal in st(0)
+        
+        faddp   st(1), st(0)            // stack: dither * value*(int scaler), int scaler
+        fistp   word ptr [edi]          // store scaled int into dest, stack:  int scaler
+        jmp     Float32_To_Int16_DitherClip_stored
+    
+    Float32_To_Int16_DitherClip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     dx, 0x7FFF              // convert to maximum range integers
+        mov     word ptr [edi], dx      // store clamped into into dest
+
+    Float32_To_Int16_DitherClip_stored:
+
+        add     edi, ebx                // increment destination ptr
+        //lea     edi, [edi+ebx]
+        
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int16_DitherClip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+
+    ditherGenerator->previous = ditherPrevious;
+    ditherGenerator->randSeed1 = ditherRandSeed1;
+    ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void PaUtil_InitializeX86PlainConverters( void )
+{
+    paConverters.Float32_To_Int32 = Float32_To_Int32;
+    paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip;
+    paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip;
+
+    paConverters.Float32_To_Int24 = Float32_To_Int24;
+    paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip;
+    paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip;
+    
+    paConverters.Float32_To_Int16 = Float32_To_Int16;
+    paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip;
+    paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip;
+}
+
+/* -------------------------------------------------------------------------- */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h	(revision 1753)
@@ -0,0 +1,40 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#ifndef PA_X86_PLAIN_CONVERTERS_H
+#define PA_X86_PLAIN_CONVERTERS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/**
+ @brief Install optimised converter functions suitable for all IA32 processors
+*/
+void PaUtil_InitializeX86PlainConverters( void );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_X86_PLAIN_CONVERTERS_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/portaudio.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/portaudio.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/portaudio/portaudio.h	(revision 1753)
@@ -0,0 +1,1144 @@
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef PORTAUDIO_H
+#define PORTAUDIO_H
+/*
+ * $Id: portaudio.h,v 1.5.2.50 2004/12/13 11:50:40 rossbencina Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.portaudio.com/
+ *
+ * Copyright (c) 1999-2002 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief The PortAudio API.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+ 
+/** Retrieve the release number of the currently running PortAudio build,
+ eg 1900.
+*/
+int Pa_GetVersion( void );
+
+
+/** Retrieve a textual description of the current PortAudio build,
+ eg "PortAudio V19-devel 13 October 2002".
+*/
+const char* Pa_GetVersionText( void );
+
+
+/** Error codes returned by PortAudio functions.
+ Note that with the exception of paNoError, all PaErrorCodes are negative.
+*/
+
+typedef int PaError;
+typedef enum PaErrorCode
+{
+    paNoError = 0,
+
+    paNotInitialized = -10000,
+    paUnanticipatedHostError,
+    paInvalidChannelCount,
+    paInvalidSampleRate,
+    paInvalidDevice,
+    paInvalidFlag,
+    paSampleFormatNotSupported,
+    paBadIODeviceCombination,
+    paInsufficientMemory,
+    paBufferTooBig,
+    paBufferTooSmall,
+    paNullCallback,
+    paBadStreamPtr,
+    paTimedOut,
+    paInternalError,
+    paDeviceUnavailable,
+    paIncompatibleHostApiSpecificStreamInfo,
+    paStreamIsStopped,
+    paStreamIsNotStopped,
+    paInputOverflowed,
+    paOutputUnderflowed,
+    paHostApiNotFound,
+    paInvalidHostApi,
+    paCanNotReadFromACallbackStream,      /**< @todo review error code name */
+    paCanNotWriteToACallbackStream,       /**< @todo review error code name */
+    paCanNotReadFromAnOutputOnlyStream,   /**< @todo review error code name */
+    paCanNotWriteToAnInputOnlyStream,     /**< @todo review error code name */
+    paIncompatibleStreamHostApi
+} PaErrorCode;
+
+
+/** Translate the supplied PortAudio error code into a human readable
+ message.
+*/
+const char *Pa_GetErrorText( PaError errorCode );
+
+
+/** Library initialization function - call this before using PortAudio.
+ This function initialises internal data structures and prepares underlying
+ host APIs for use. This function MUST be called before using any other
+ PortAudio API functions.
+
+ If Pa_Initialize() is called multiple times, each successful 
+ call must be matched with a corresponding call to Pa_Terminate(). 
+ Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not 
+ required to be fully nested.
+
+ Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
+ NOT be called.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Terminate
+*/
+PaError Pa_Initialize( void );
+
+
+/** Library termination function - call this when finished using PortAudio.
+ This function deallocates all resources allocated by PortAudio since it was
+ initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has
+ been called multiple times, each call must be matched with a corresponding call
+ to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
+ close any PortAudio streams that are still open.
+
+ Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
+ Failure to do so may result in serious resource leaks, such as audio devices
+ not being available until the next reboot.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+ 
+ @see Pa_Initialize
+*/
+PaError Pa_Terminate( void );
+
+
+
+/** The type used to refer to audio devices. Values of this type usually
+ range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice
+ and paUseHostApiSpecificDeviceSpecification values.
+
+ @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification
+*/
+typedef int PaDeviceIndex;
+
+
+/** A special PaDeviceIndex value indicating that no device is available,
+ or should be used.
+
+ @see PaDeviceIndex
+*/
+#define paNoDevice ((PaDeviceIndex)-1)
+
+
+/** A special PaDeviceIndex value indicating that the device(s) to be used
+ are specified in the host api specific stream info structure.
+
+ @see PaDeviceIndex
+*/
+#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2)
+
+
+/* Host API enumeration mechanism */
+
+/** The type used to enumerate to host APIs at runtime. Values of this type
+ range from 0 to (Pa_GetHostApiCount()-1).
+
+ @see Pa_GetHostApiCount
+*/
+typedef int PaHostApiIndex;
+
+
+/** Retrieve the number of available host APIs. Even if a host API is
+ available it may have no devices available.
+
+ @return A non-negative value indicating the number of available host APIs
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaHostApiIndex
+*/
+PaHostApiIndex Pa_GetHostApiCount( void );
+
+
+/** Retrieve the index of the default host API. The default host API will be
+ the lowest common denominator host API on the current platform and is
+ unlikely to provide the best performance.
+
+ @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1)
+ indicating the default host API index or, a PaErrorCode (which are always
+ negative) if PortAudio is not initialized or an error is encountered.
+*/
+PaHostApiIndex Pa_GetDefaultHostApi( void );
+
+
+/** Unchanging unique identifiers for each supported host API. This type
+ is used in the PaHostApiInfo structure. The values are guaranteed to be
+ unique and to never change, thus allowing code to be written that
+ conditionally uses host API specific extensions.
+
+ New type ids will be allocated when support for a host API reaches
+ "public alpha" status, prior to that developers should use the
+ paInDevelopment type id.
+
+ @see PaHostApiInfo
+*/
+typedef enum PaHostApiTypeId
+{
+    paInDevelopment=0, /* use while developing support for a new host API */
+    paDirectSound=1,
+    paMME=2,
+    paASIO=3,
+    paSoundManager=4,
+    paCoreAudio=5,
+    paOSS=7,
+    paALSA=8,
+    paAL=9,
+    paBeOS=10,
+    paWDMKS=11,
+    paJACK=12
+} PaHostApiTypeId;
+
+
+/** A structure containing information about a particular host API. */
+
+typedef struct PaHostApiInfo
+{
+    /** this is struct version 1 */
+    int structVersion;
+    /** The well known unique identifier of this host API @see PaHostApiTypeId */
+    PaHostApiTypeId type;
+    /** A textual description of the host API for display on user interfaces. */
+    const char *name;
+
+    /**  The number of devices belonging to this host API. This field may be
+     used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate
+     all devices for this host API.
+     @see Pa_HostApiDeviceIndexToDeviceIndex
+    */
+    int deviceCount;
+
+    /** The the default input device for this host API. The value will be a
+     device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+     if no default input device is available.
+    */
+    PaDeviceIndex defaultInputDevice;
+
+    /** The the default output device for this host API. The value will be a
+     device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+     if no default output device is available.
+    */
+    PaDeviceIndex defaultOutputDevice;
+    
+} PaHostApiInfo;
+
+
+/** Retrieve a pointer to a structure containing information about a specific
+ host Api.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @return A pointer to an immutable PaHostApiInfo structure describing
+ a specific host API. If the hostApi parameter is out of range or an error
+ is encountered, the function returns NULL.
+
+ The returned structure is owned by the PortAudio implementation and must not
+ be manipulated or freed. The pointer is only guaranteed to be valid between
+ calls to Pa_Initialize() and Pa_Terminate().
+*/
+const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi );
+
+
+/** Convert a static host API unique identifier, into a runtime
+ host API index.
+
+ @param type A unique host API identifier belonging to the PaHostApiTypeId
+ enumeration.
+
+ @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+ 
+ The paHostApiNotFound error code indicates that the host API specified by the
+ type parameter is not available.
+
+ @see PaHostApiTypeId
+*/
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type );
+
+
+/** Convert a host-API-specific device index to standard PortAudio device index.
+ This function may be used in conjunction with the deviceCount field of
+ PaHostApiInfo to enumerate all devices for the specified host API.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @param hostApiDeviceIndex A valid per-host device index in the range
+ 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1)
+
+ @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1)
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ A paInvalidHostApi error code indicates that the host API index specified by
+ the hostApi parameter is out of range.
+
+ A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter
+ is out of range.
+ 
+ @see PaHostApiInfo
+*/
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi,
+        int hostApiDeviceIndex );
+
+
+
+/** Structure used to return information about a host error condition.
+*/
+typedef struct PaHostErrorInfo{
+    PaHostApiTypeId hostApiType;    /**< the host API which returned the error code */
+    long errorCode;                 /**< the error code returned */
+    const char *errorText;          /**< a textual description of the error if available, otherwise a zero-length string */
+}PaHostErrorInfo;
+
+
+/** Return information about the last host error encountered. The error
+ information returned by Pa_GetLastHostErrorInfo() will never be modified
+ asyncronously by errors occurring in other PortAudio owned threads
+ (such as the thread that manages the stream callback.)
+
+ This function is provided as a last resort, primarily to enhance debugging
+ by providing clients with access to all available error information.
+
+ @return A pointer to an immutable structure constaining information about
+ the host error. The values in this structure will only be valid if a
+ PortAudio function has previously returned the paUnanticipatedHostError
+ error code.
+*/
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void );
+
+
+
+/* Device enumeration and capabilities */
+
+/** Retrieve the number of available devices. The number of available devices
+ may be zero.
+
+ @return A non-negative value indicating the number of available devices or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+*/
+PaDeviceIndex Pa_GetDeviceCount( void );
+
+
+/** Retrieve the index of the default input device. The result can be
+ used in the inputDevice parameter to Pa_OpenStream().
+
+ @return The default input device index for the default host API, or paNoDevice
+ if no default input device is available or an error was encountered.
+*/
+PaDeviceIndex Pa_GetDefaultInputDevice( void );
+
+
+/** Retrieve the index of the default output device. The result can be
+ used in the outputDevice parameter to Pa_OpenStream().
+
+ @return The default output device index for the defualt host API, or paNoDevice
+ if no default output device is available or an error was encountered.
+
+ @note
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+<pre>
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+</pre>
+ The user should first determine the available device ids by using
+ the supplied application "pa_devs".
+*/
+PaDeviceIndex Pa_GetDefaultOutputDevice( void );
+
+
+/** The type used to represent monotonic time in seconds that can be used
+ for syncronisation. The type is used for the outTime argument to the
+ PaStreamCallback and as the result of Pa_GetStreamTime().
+     
+ @see PaStreamCallback, Pa_GetStreamTime
+*/
+typedef double PaTime;
+
+
+/** A type used to specify one or more sample formats. Each value indicates
+ a possible format for sound data passed to and from the stream callback,
+ Pa_ReadStream and Pa_WriteStream.
+
+ The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
+ and aUInt8 are usually implemented by all implementations.
+
+ The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ maximum and minimum respectively.
+
+ paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+ The paNonInterleaved flag indicates that a multichannel buffer is passed
+ as a set of non-interleaved pointers.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo
+ @see paFloat32, paInt16, paInt32, paInt24, paInt8
+ @see paUInt8, paCustomFormat, paNonInterleaved
+*/
+typedef unsigned long PaSampleFormat;
+
+
+#define paFloat32        ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */
+#define paInt32          ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */
+#define paInt24          ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */
+#define paInt16          ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */
+#define paInt8           ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */
+#define paUInt8          ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */
+#define paCustomFormat   ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */
+
+#define paNonInterleaved ((PaSampleFormat) 0x80000000)
+
+/** A structure providing information and capabilities of PortAudio devices.
+ Devices may support input, output or both input and output.
+*/
+typedef struct PaDeviceInfo
+{
+    int structVersion;  /* this is struct version 2 */
+    const char *name;
+    PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
+    
+    int maxInputChannels;
+    int maxOutputChannels;
+
+    /* Default latency values for interactive performance. */
+    PaTime defaultLowInputLatency;
+    PaTime defaultLowOutputLatency;
+    /* Default latency values for robust non-interactive applications (eg. playing sound files). */
+    PaTime defaultHighInputLatency;
+    PaTime defaultHighOutputLatency;
+
+    double defaultSampleRate;
+} PaDeviceInfo;
+
+
+/** Retrieve a pointer to a PaDeviceInfo structure containing information
+ about the specified device.
+ @return A pointer to an immutable PaDeviceInfo structure. If the device
+ parameter is out of range the function returns NULL.
+
+ @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate().
+
+ @see PaDeviceInfo, PaDeviceIndex
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );
+
+
+/** Parameters for one direction (input or output) of a stream.
+*/
+typedef struct PaStreamParameters
+{
+    /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+     specifying the device to be used or the special constant
+     paUseHostApiSpecificDeviceSpecification which indicates that the actual
+     device(s) to use are specified in hostApiSpecificStreamInfo.
+     This field must not be set to paNoDevice.
+    */
+    PaDeviceIndex device;
+    
+    /** The number of channels of sound to be delivered to the
+     stream callback or accessed by Pa_ReadStream() or Pa_WriteStream().
+     It can range from 1 to the value of maxInputChannels in the
+     PaDeviceInfo record for the device specified by the device parameter.
+    */
+    int channelCount;
+
+    /** The sample format of the buffer provided to the stream callback,
+     a_ReadStream() or Pa_WriteStream(). It may be any of the formats described
+     by the PaSampleFormat enumeration.
+    */
+    PaSampleFormat sampleFormat;
+
+    /** The desired latency in seconds. Where practical, implementations should
+     configure their latency based on these parameters, otherwise they may
+     choose the closest viable latency instead. Unless the suggested latency
+     is greater than the absolute upper limit for the device implementations
+     shouldround the suggestedLatency up to the next practial value - ie to
+     provide an equal or higher latency than suggestedLatency whereever possibe.
+     Actual latency values for an open stream may be retrieved using the
+     inputLatency and outputLatency fields of the PaStreamInfo structure
+     returned by Pa_GetStreamInfo().
+     @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo
+    */
+    PaTime suggestedLatency;
+
+    /** An optional pointer to a host api specific data structure
+     containing additional information for device setup and/or stream processing.
+     hostApiSpecificStreamInfo is never required for correct operation,
+     if not used it should be set to NULL.
+    */
+    void *hostApiSpecificStreamInfo;
+
+} PaStreamParameters;
+
+
+/** Return code for Pa_IsFormatSupported indicating success. */
+#define paFormatIsSupported (0)
+
+/** Determine whether it would be possible to open a stream with the specified
+ parameters.
+
+ @param inputParameters A structure that describes the input parameters used to
+ open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. inputParameters must be NULL for
+ output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used
+ to open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. outputParameters must be NULL for
+ input-only streams.
+
+ @param sampleRate The required sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @return Returns 0 if the format is supported, and an error code indicating why
+ the format is not supported otherwise. The constant paFormatIsSupported is
+ provided to compare with the return value for success.
+
+ @see paFormatIsSupported, PaStreamParameters
+*/
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+                              const PaStreamParameters *outputParameters,
+                              double sampleRate );
+
+
+
+/* Streaming types and functions */
+
+
+/**
+ A single PaStream can provide multiple channels of real-time
+ streaming audio input and output to a client application. A stream
+ provides access to audio hardware represented by one or more
+ PaDevices. Depending on the underlying Host API, it may be possible 
+ to open multiple streams using the same device, however this behavior 
+ is implementation defined. Portable applications should assume that 
+ a PaDevice may be simultaneously used by at most one PaStream.
+
+ Pointers to PaStream objects are passed between PortAudio functions that
+ operate on streams.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream,
+ Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive,
+ Pa_GetStreamTime, Pa_GetStreamCpuLoad
+
+*/
+typedef void PaStream;
+
+
+/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream()
+ or Pa_OpenDefaultStream() to indicate that the stream callback will
+ accept buffers of any size.
+*/
+#define paFramesPerBufferUnspecified  (0)
+
+
+/** Flags used to control the behavior of a stream. They are passed as
+ parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be
+ ORed together.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+ @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput,
+  paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags
+*/
+typedef unsigned long PaStreamFlags;
+
+/** @see PaStreamFlags */
+#define   paNoFlag          ((PaStreamFlags) 0)
+
+/** Disable default clipping of out of range samples.
+ @see PaStreamFlags
+*/
+#define   paClipOff         ((PaStreamFlags) 0x00000001)
+
+/** Disable default dithering.
+ @see PaStreamFlags
+*/
+#define   paDitherOff       ((PaStreamFlags) 0x00000002)
+
+/** Flag requests that where possible a full duplex stream will not discard
+ overflowed input samples without calling the stream callback. This flag is
+ only valid for full duplex callback streams and only when used in combination
+ with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using
+ this flag incorrectly results in a paInvalidFlag error being returned from
+ Pa_OpenStream and Pa_OpenDefaultStream.
+
+ @see PaStreamFlags, paFramesPerBufferUnspecified
+*/
+#define   paNeverDropInput  ((PaStreamFlags) 0x00000004)
+
+/** Call the stream callback to fill initial output buffers, rather than the
+ default behavior of priming the buffers with zeros (silence). This flag has
+ no effect for input-only and blocking read/write streams.
+ 
+ @see PaStreamFlags
+*/
+#define   paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008)
+
+/** A mask specifying the platform specific bits.
+ @see PaStreamFlags
+*/
+#define   paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000)
+
+/**
+ Timing information for the buffers passed to the stream callback.
+*/
+typedef struct PaStreamCallbackTimeInfo{
+    PaTime inputBufferAdcTime;
+    PaTime currentTime;
+    PaTime outputBufferDacTime;
+} PaStreamCallbackTimeInfo;
+
+
+/**
+ Flag bit constants for the statusFlags to PaStreamCallback.
+
+ @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow,
+ paPrimingOutput
+*/
+typedef unsigned long PaStreamCallbackFlags;
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that
+ input data is all silence (zeros) because no real data is available. In a
+ stream opened without paFramesPerBufferUnspecified, it indicates that one or
+ more zero samples have been inserted into the input buffer to compensate
+ for an input underflow.
+ @see PaStreamCallbackFlags
+*/
+#define paInputUnderflow   ((PaStreamCallbackFlags) 0x00000001)
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that data
+ prior to the first sample of the input buffer was discarded due to an
+ overflow, possibly because the stream callback is using too much CPU time.
+ Otherwise indicates that data prior to one or more samples in the
+ input buffer was discarded.
+ @see PaStreamCallbackFlags
+*/
+#define paInputOverflow    ((PaStreamCallbackFlags) 0x00000002)
+
+/** Indicates that output data (or a gap) was inserted, possibly because the
+ stream callback is using too much CPU time.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputUnderflow  ((PaStreamCallbackFlags) 0x00000004)
+
+/** Indicates that output data will be discarded because no room is available.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputOverflow   ((PaStreamCallbackFlags) 0x00000008)
+
+/** Some of all of the output data will be used to prime the stream, input
+ data may be zero.
+ @see PaStreamCallbackFlags
+*/
+#define paPrimingOutput    ((PaStreamCallbackFlags) 0x00000010)
+
+/**
+ Allowable return values for the PaStreamCallback.
+ @see PaStreamCallback
+*/
+typedef enum PaStreamCallbackResult
+{
+    paContinue=0,
+    paComplete=1,
+    paAbort=2
+} PaStreamCallbackResult;
+
+
+/**
+ Functions of type PaStreamCallback are implemented by PortAudio clients.
+ They consume, process or generate audio in response to requests from an
+ active PortAudio stream.
+     
+ @param input and @param output are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream().
+     
+ @param frameCount The number of sample frames to be processed by
+ the stream callback.
+
+ @param timeInfo The time in seconds when the first sample of the input
+ buffer was received at the audio input, the time in seconds when the first
+ sample of the output buffer will begin being played at the audio output, and
+ the time in seconds when the stream callback was called.
+ See also Pa_GetStreamTime()
+
+ @param statusFlags Flags indicating whether input and/or output buffers
+ have been inserted or will be dropped to overcome underflow or overflow
+ conditions.
+
+ @param userData The value of a user supplied pointer passed to
+ Pa_OpenStream() intended for storing synthesis data etc.
+
+ @return
+ The stream callback should return one of the values in the
+ PaStreamCallbackResult enumeration. To ensure that the callback continues
+ to be called, it should return paContinue (0). Either paComplete or paAbort
+ can be returned to finish stream processing, after either of these values is
+ returned the callback will not be called again. If paAbort is returned the
+ stream will finish as soon as possible. If paComplete is returned, the stream
+ will continue until all buffers generated by the callback have been played.
+ This may be useful in applications such as soundfile players where a specific
+ duration of output is required. However, it is not necessary to utilise this
+ mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also
+ be used to stop the stream. The callback must always fill the entire output
+ buffer irrespective of its return value.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+
+ @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
+ PortAudio API functions from within the stream callback.
+*/
+typedef int PaStreamCallback(
+    const void *input, void *output,
+    unsigned long frameCount,
+    const PaStreamCallbackTimeInfo* timeInfo,
+    PaStreamCallbackFlags statusFlags,
+    void *userData );
+
+
+/** Opens a stream for either input, output or both.
+     
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+     
+ @param inputParameters A structure that describes the input parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ inputParameters must be NULL for output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ outputParameters must be NULL for input-only streams.
+ 
+ @param sampleRate The desired sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+     
+ @param framesPerBuffer The number of frames passed to the stream callback
+ function, or the preferred block granularity for a blocking read/write stream.
+ The special value paFramesPerBufferUnspecified (0) may be used to request that
+ the stream callback will recieve an optimal (and possibly varying) number of
+ frames based on host requirements and the requested latency settings.
+ Note: With some host APIs, the use of non-zero framesPerBuffer for a callback
+ stream may introduce an additional layer of buffering which could introduce
+ additional latency. PortAudio guarantees that the additional latency
+ will be kept to the theoretical minimum however, it is strongly recommended
+ that a non-zero framesPerBuffer value only be used when your algorithm
+ requires a fixed number of frames per stream callback.
+ 
+ @param streamFlags Flags which modify the behaviour of the streaming process.
+ This parameter may contain a combination of flags ORed together. Some flags may
+ only be relevant to certain buffer formats.
+     
+ @param streamCallback A pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers. If this parameter is NULL
+ the stream will be opened in 'blocking read/write' mode. In blocking mode,
+ the client can receive sample data using Pa_ReadStream and write sample data
+ using Pa_WriteStream, the number of samples that may be read or written
+ without blocking is returned by Pa_GetStreamReadAvailable and
+ Pa_GetStreamWriteAvailable respectively.
+
+ @param userData A client supplied pointer which is passed to the stream callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers. This parameter is ignored if streamCallback
+ is NULL.
+     
+ @return
+ Upon success Pa_OpenStream() returns paNoError and places a pointer to a
+ valid PaStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails, a non-zero error code is returned (see
+ PaError for possible error codes) and the value of stream is invalid.
+
+ @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream,
+ Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable
+*/
+PaError Pa_OpenStream( PaStream** stream,
+                       const PaStreamParameters *inputParameters,
+                       const PaStreamParameters *outputParameters,
+                       double sampleRate,
+                       unsigned long framesPerBuffer,
+                       PaStreamFlags streamFlags,
+                       PaStreamCallback *streamCallback,
+                       void *userData );
+
+
+/** A simplified version of Pa_OpenStream() that opens the default input
+ and/or output devices.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+ 
+ @param numInputChannels  The number of channels of sound that will be supplied
+ to the stream callback or returned by Pa_ReadStream. It can range from 1 to
+ the value of maxInputChannels in the PaDeviceInfo record for the default input
+ device. If 0 the stream is opened as an output-only stream.
+
+ @param numOutputChannels The number of channels of sound to be delivered to the
+ stream callback or passed to Pa_WriteStream. It can range from 1 to the value
+ of maxOutputChannels in the PaDeviceInfo record for the default output dvice.
+ If 0 the stream is opened as an output-only stream.
+
+ @param sampleFormat The sample format of both the input and output buffers
+ provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream.
+ sampleFormat may be any of the formats described by the PaSampleFormat
+ enumeration.
+ 
+ @param sampleRate Same as Pa_OpenStream parameter of the same name.
+ @param framesPerBuffer Same as Pa_OpenStream parameter of the same name.
+ @param streamCallback Same as Pa_OpenStream parameter of the same name.
+ @param userData Same as Pa_OpenStream parameter of the same name.
+
+ @return As for Pa_OpenStream
+
+ @see Pa_OpenStream, PaStreamCallback
+*/
+PaError Pa_OpenDefaultStream( PaStream** stream,
+                              int numInputChannels,
+                              int numOutputChannels,
+                              PaSampleFormat sampleFormat,
+                              double sampleRate,
+                              unsigned long framesPerBuffer,
+                              PaStreamCallback *streamCallback,
+                              void *userData );
+
+
+/** Closes an audio stream. If the audio stream is active it
+ discards any pending buffers as if Pa_AbortStream() had been called.
+*/
+PaError Pa_CloseStream( PaStream *stream );
+
+
+/** Functions of type PaStreamFinishedCallback are implemented by PortAudio 
+ clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback
+ function. Once registered they are called when the stream becomes inactive
+ (ie once a call to Pa_StopStream() will not block).
+ A stream will become inactive after the stream callback returns non-zero,
+ or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio
+ output, if the stream callback returns paComplete, or Pa_StopStream is called,
+ the stream finished callback will not be called until all generated sample data
+ has been played.
+ 
+ @param userData The userData parameter supplied to Pa_OpenStream()
+
+ @see Pa_SetStreamFinishedCallback
+*/
+typedef void PaStreamFinishedCallback( void *userData );
+
+
+/** Register a stream finished callback function which will be called when the 
+ stream becomes inactive. See the description of PaStreamFinishedCallback for 
+ further details about when the callback will be called.
+
+ @param stream a pointer to a PaStream that is in the stopped state - if the
+ stream is not stopped, the stream's finished callback will remain unchanged 
+ and an error code will be returned.
+
+ @param streamFinishedCallback a pointer to a function with the same signature
+ as PaStreamFinishedCallback, that will be called when the stream becomes
+ inactive. Passing NULL for this parameter will un-register a previously
+ registered stream finished callback function.
+
+ @return on success returns paNoError, otherwise an error code indicating the cause
+ of the error.
+
+ @see PaStreamFinishedCallback
+*/
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ); 
+
+
+/** Commences audio processing.
+*/
+PaError Pa_StartStream( PaStream *stream );
+
+
+/** Terminates audio processing. It waits until all pending
+ audio buffers have been played before it returns.
+*/
+PaError Pa_StopStream( PaStream *stream );
+
+
+/** Terminates audio processing immediately without waiting for pending
+ buffers to complete.
+*/
+PaError Pa_AbortStream( PaStream *stream );
+
+
+/** Determine whether the stream is stopped.
+ A stream is considered to be stopped prior to a successful call to
+ Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream.
+ If a stream callback returns a value other than paContinue the stream is NOT
+ considered to be stopped.
+
+ @return Returns one (1) when the stream is stopped, zero (0) when
+ the stream is running or, a PaErrorCode (which are always negative) if
+ PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive
+*/
+PaError Pa_IsStreamStopped( PaStream *stream );
+
+
+/** Determine whether the stream is active.
+ A stream is active after a successful call to Pa_StartStream(), until it
+ becomes inactive either as a result of a call to Pa_StopStream() or
+ Pa_AbortStream(), or as a result of a return value other than paContinue from
+ the stream callback. In the latter case, the stream is considered inactive
+ after the last buffer has finished playing.
+
+ @return Returns one (1) when the stream is active (ie playing or recording
+ audio), zero (0) when not playing or, a PaErrorCode (which are always negative)
+ if PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped
+*/
+PaError Pa_IsStreamActive( PaStream *stream );
+
+
+
+/** A structure containing unchanging information about an open stream.
+ @see Pa_GetStreamInfo
+*/
+
+typedef struct PaStreamInfo
+{
+    /** this is struct version 1 */
+    int structVersion;
+
+    /** The input latency of the stream in seconds. This value provides the most
+     accurate estimate of input latency available to the implementation. It may
+     differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+     The value of this field will be zero (0.) for output-only streams.
+     @see PaTime
+    */
+    PaTime inputLatency;
+
+    /** The output latency of the stream in seconds. This value provides the most
+     accurate estimate of output latency available to the implementation. It may
+     differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+     The value of this field will be zero (0.) for input-only streams.
+     @see PaTime
+    */
+    PaTime outputLatency;
+
+    /** The sample rate of the stream in Hertz (samples per second). In cases
+     where the hardware sample rate is inaccurate and PortAudio is aware of it,
+     the value of this field may be different from the sampleRate parameter
+     passed to Pa_OpenStream(). If information about the actual hardware sample
+     rate is not available, this field will have the same value as the sampleRate
+     parameter passed to Pa_OpenStream().
+    */
+    double sampleRate;
+    
+} PaStreamInfo;
+
+
+/** Retrieve a pointer to a PaStreamInfo structure containing information
+ about the specified stream.
+ @return A pointer to an immutable PaStreamInfo structure. If the stream
+ parameter invalid, or an error is encountered, the function returns NULL.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid until the specified stream is closed.
+
+ @see PaStreamInfo
+*/
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream );
+
+
+/** Determine the current time for the stream according to the same clock used
+ to generate buffer timestamps. This time may be used for syncronising other
+ events to the audio stream, for example synchronizing audio to MIDI.
+                                        
+ @return The stream's current time in seconds, or 0 if an error occurred.
+
+ @see PaTime, PaStreamCallback
+*/
+PaTime Pa_GetStreamTime( PaStream *stream );
+
+
+/** Retrieve CPU usage information for the specified stream.
+ The "CPU Load" is a fraction of total CPU time consumed by a callback stream's
+ audio processing routines including, but not limited to the client supplied
+ stream callback. This function does not work with blocking read/write streams.
+
+ This function may be called from the stream callback function or the
+ application.
+     
+ @return
+ A floating point value, typically between 0.0 and 1.0, where 1.0 indicates
+ that the stream callback is consuming the maximum number of CPU cycles possible
+ to maintain real-time operation. A value of 0.5 would imply that PortAudio and
+ the stream callback was consuming roughly 50% of the available CPU time. The
+ return value may exceed 1.0. A value of 0.0 will always be returned for a
+ blocking read/write stream, or if an error occurrs.
+*/
+double Pa_GetStreamCpuLoad( PaStream* stream );
+
+
+/** Read samples from an input stream. The function doesn't return until
+ the entire buffer has been filled - this may involve waiting for the operating
+ system to supply the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+ 
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the inputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ inputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be read into buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or PaInputOverflowed if input
+ data was discarded by PortAudio after the previous call and before this call.
+*/
+PaError Pa_ReadStream( PaStream* stream,
+                       void *buffer,
+                       unsigned long frames );
+
+
+/** Write samples to an output stream. This function doesn't return until the
+ entire buffer has been consumed - this may involve waiting for the operating
+ system to consume the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the outputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ outputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be written from buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or paOutputUnderflowed if
+ additional output data was inserted after the previous call and before this
+ call.
+*/
+PaError Pa_WriteStream( PaStream* stream,
+                        const void *buffer,
+                        unsigned long frames );
+
+
+/** Retrieve the number of frames that can be read from the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be read from the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamReadAvailable( PaStream* stream );
+
+
+/** Retrieve the number of frames that can be written to the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be written to the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamWriteAvailable( PaStream* stream );
+
+
+/* Miscellaneous utilities */
+
+
+/** Retrieve the size of a given sample format in bytes.
+
+ @return The size in bytes of a single sample in the specified format,
+ or paSampleFormatNotSupported if the format is not supported.
+*/
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+
+/** Put the caller to sleep for at least 'msec' milliseconds. This function is
+ provided only as a convenience for authors of portable code (such as the tests
+ and examples in the PortAudio distribution.)
+
+ The function may sleep longer than requested so don't rely on this for accurate
+ musical timing.
+*/
+void Pa_Sleep( long msec );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORTAUDIO_H */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtcp.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtcp.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtcp.c	(revision 1753)
@@ -0,0 +1,221 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/rtcp.c 4     8/24/05 10:30a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#include <pjmedia/rtcp.h>
+#include <pj/os.h>	/* pj_gettimeofday */
+#include <pj/sock.h>	/* pj_htonx, pj_ntohx */
+#include <string.h>	/* memset */
+
+#define RTCP_SR   200
+#define RTCP_RR   201
+
+
+
+/*
+ * Get NTP time.
+ */
+static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp)
+{
+    pj_time_val tv;
+
+    pj_gettimeofday(&tv);
+    
+    ntp->hi = tv.sec;
+    tv.msec = tv.msec % 1000;
+    ntp->lo = tv.msec * 0xFFFF / 1000;
+    ntp->lo <<= 16;
+}
+
+
+PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc)
+{
+    pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+    
+    memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt));
+    
+    /* Init time */
+    s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0;
+    s->rtcp_lsr_time = 0;
+    
+    /* Init common RTCP header */
+    rtcp_pkt->common.version = 2;
+    rtcp_pkt->common.count = 1;
+    rtcp_pkt->common.pt = RTCP_SR;
+    rtcp_pkt->common.length = pj_htons(12);
+    
+    /* Init SR */
+    rtcp_pkt->sr.ssrc = pj_htonl(ssrc);
+    
+    /* RR will be initialized on receipt of the first RTP packet. */
+}
+
+PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session)
+{
+    /* Nothing to do. */
+    PJ_UNUSED_ARG(session)
+}
+
+static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t  seq)
+{
+    s->received = 0;
+    s->expected_prior = 0;
+    s->received_prior = 0;
+    s->transit = 0;
+    s->jitter = 0;
+
+    pj_rtp_seq_restart(&s->seq_ctrl, seq);
+}
+
+PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts)
+{   
+    pj_uint32_t arrival;
+    pj_int32_t transit;
+    unsigned long timer_tick;
+    pj_time_val tv;
+    int status;
+
+    /* Update sequence numbers (received, lost, etc). */
+    status = pj_rtp_seq_update(&s->seq_ctrl, seq);
+    if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
+	rtcp_init_seq(s, seq);
+	status = 0;
+    }
+    
+    if (status != 0)
+	return;
+
+    ++s->received;
+
+    pj_gettimeofday(&tv);
+    timer_tick = tv.sec * 1000 + tv.msec;
+    
+    /*
+     * Calculate jitter (s->jitter is in timer tick unit)
+     */
+    PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
+
+    arrival = timer_tick << 3;	// 8 samples per ms.
+    transit = arrival - rtp_ts;
+    
+    if (s->transit == 0) {
+	s->transit = transit;
+    } else {
+	pj_int32_t d, jitter = s->jitter;
+	
+	d = transit - s->transit;
+	s->transit = transit;
+	if (d < 0) 
+	    d = -d;
+	
+	jitter += d - ((jitter + 8) >> 4);
+	s->jitter = jitter;
+    }
+}
+
+PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t  bytes_payload_size)
+{
+    pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+    rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1);
+    rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size );
+}
+
+static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc)
+{   
+    pj_uint32_t expected;
+    pj_uint32_t u32;
+    pj_uint32_t expected_interval, received_interval, lost_interval;
+    pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+    
+    /* SSRC and last_seq */
+    rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc);
+    rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L);
+    rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq;
+    rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq);
+
+    /* Jitter */
+    rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4);
+    
+    /* Total lost. */
+    expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1;
+    u32 = expected - s->received;
+    rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF;
+    rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF;
+    rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF;
+
+    /* Fraction lost calculation */
+    expected_interval = expected - s->expected_prior;
+    s->expected_prior = expected;
+    
+    received_interval = s->received - s->received_prior;
+    s->received_prior = s->received;
+    
+    lost_interval = expected_interval - received_interval;
+    
+    if (expected_interval==0 || lost_interval == 0) {
+	rtcp_pkt->rr.fract_lost = 0;
+    } else {
+	rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval;
+    }
+}
+
+PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len)
+{
+    pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt;
+    pj_rtcp_ntp_rec ntp;
+    pj_time_val now;
+    
+    rtcp_build_rtcp(session, session->peer_ssrc);
+    
+    /* Get current NTP time. */
+    rtcp_get_ntp_time(&ntp);
+    
+    /* Fill in NTP timestamp in SR. */
+    rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi);
+    rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo);
+    
+    if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) {
+	rtcp_pkt->rr.lsr = 0;
+	rtcp_pkt->rr.dlsr = 0;
+    } else {
+	unsigned msec_elapsed;
+	
+	/* Fill in LSR.
+	   LSR is the middle 32bit of the last SR NTP time received.
+	 */
+	rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) | 
+			   ((session->rtcp_lsr.lo >> 16) & 0xFFFF);
+	rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr);
+	
+	/* Fill in DLSR.
+	   DLSR is Delay since Last SR, in 1/65536 seconds.
+	 */
+	pj_gettimeofday(&now);
+	msec_elapsed = (now.msec - session->rtcp_lsr_time);
+	rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000);
+    }
+    
+    /* Return pointer. */
+    *ret_p_pkt = rtcp_pkt;
+    *len = sizeof(pj_rtcp_pkt);
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtcp.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtcp.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtcp.h	(revision 1753)
@@ -0,0 +1,197 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/rtcp.h 5     8/24/05 10:30a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJMEDIA_RTCP_H__
+#define __PJMEDIA_RTCP_H__
+
+/**
+ * @file rtcp.h
+ * @brief RTCP implementation.
+ */
+
+#include <pj/types.h>
+#include <pjmedia/rtp.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_RTCP RTCP
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/**
+ * RTCP sender report.
+ */
+struct pj_rtcp_sr
+{
+    pj_uint32_t	    ssrc;
+    pj_uint32_t	    ntp_sec;
+    pj_uint32_t	    ntp_frac;
+    pj_uint32_t	    rtp_ts;
+    pj_uint32_t	    sender_pcount;
+    pj_uint32_t	    sender_bcount;
+};
+
+typedef struct pj_rtcp_sr pj_rtcp_sr;
+
+/**
+ * RTCP receiver report.
+ */
+struct pj_rtcp_rr
+{
+    pj_uint32_t	    ssrc;
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+    pj_uint32_t	    fract_lost:8;
+    pj_uint32_t	    total_lost_2:8;
+    pj_uint32_t	    total_lost_1:8;
+    pj_uint32_t	    total_lost_0:8;
+#else
+    pj_uint32_t	    fract_lost:8;
+    pj_uint32_t	    total_lost_0:8;
+    pj_uint32_t	    total_lost_1:8;
+    pj_uint32_t	    total_lost_2:8;
+#endif	
+    pj_uint32_t	    last_seq;
+    pj_uint32_t	    jitter;
+    pj_uint32_t	    lsr;
+    pj_uint32_t	    dlsr;
+};
+
+typedef struct pj_rtcp_rr pj_rtcp_rr;
+
+/**
+ * RTCP common header.
+ */
+struct pj_rtcp_common
+{
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+    unsigned	    version:2;	/* packet type            */
+    unsigned	    p:1;	/* padding flag           */
+    unsigned	    count:5;	/* varies by payload type */
+    unsigned	    pt:8;	/* payload type           */
+#else
+    unsigned	    count:5;	/* varies by payload type */
+    unsigned	    p:1;	/* padding flag           */
+    unsigned	    version:2;	/* packet type            */
+    unsigned	    pt:8;	/* payload type           */
+#endif
+    pj_uint16_t	    length;	/* packet length          */
+};
+
+typedef struct pj_rtcp_common pj_rtcp_common;
+
+/**
+ * RTCP packet.
+ */
+struct pj_rtcp_pkt
+{
+    pj_rtcp_common  common;	
+    pj_rtcp_sr	    sr;
+    pj_rtcp_rr	    rr;		/* variable-length list */
+};
+
+typedef struct pj_rtcp_pkt pj_rtcp_pkt;
+
+/**
+ * NTP time representation.
+ */
+struct pj_rtcp_ntp_rec
+{
+    pj_uint32_t	    hi;
+    pj_uint32_t	    lo;
+};
+
+typedef struct pj_rtcp_ntp_rec pj_rtcp_ntp_rec;
+
+/**
+ * RTCP session.
+ */
+struct pj_rtcp_session
+{
+    pj_rtcp_pkt		rtcp_pkt;
+    
+    pj_rtp_seq_session	seq_ctrl;
+
+    pj_uint32_t		received;       /* packets received */
+    pj_uint32_t		expected_prior; /* packet expected at last interval */
+    pj_uint32_t		received_prior; /* packet received at last interval */
+    pj_int32_t		transit;        /* relative trans time for prev pkt */
+    pj_uint32_t		jitter;		/* estimated jitter */
+    
+    pj_rtcp_ntp_rec	rtcp_lsr;	/* NTP timestamp in last sender report received */
+    unsigned 		rtcp_lsr_time;  /* Time when last RTCP SR is received. */
+    unsigned 		peer_ssrc;	/* Peer SSRC */
+    
+};
+
+typedef struct pj_rtcp_session pj_rtcp_session;
+
+/**
+ * Init RTCP session.
+ * @param session The session
+ * @param ssrc The SSRC used in to identify the session.
+ */
+PJ_DECL(void) pj_rtcp_init( pj_rtcp_session *session, pj_uint32_t ssrc );
+
+/**
+ * Deinit RTCP session.
+ * @param session The session.
+ */
+PJ_DECL(void) pj_rtcp_fini( pj_rtcp_session *session);
+
+/**
+ * Call this function everytime an RTP packet is received to let the RTCP
+ * session do its internal calculations.
+ * @param session The session.
+ * @param seq The RTP packet sequence number, in host byte order.
+ * @param ts The RTP packet timestamp, in host byte order.
+ */
+PJ_DECL(void) pj_rtcp_rx_rtp( pj_rtcp_session *session, pj_uint16_t seq, pj_uint32_t ts );
+
+/**
+ * Call this function everytime an RTP packet is sent to let the RTCP session
+ * do its internal calculations.
+ * @param session The session.
+ * @param bytes_payload_size The payload size of the RTP packet (ie packet minus
+ *             RTP header).
+ */
+PJ_DECL(void) pj_rtcp_tx_rtp( pj_rtcp_session *session, pj_uint16_t bytes_payload_size );
+
+/**
+ * Build a RTCP SR/RR packet to be transmitted to remote RTP peer.
+ * @param session The session.
+ * @param rtcp_pkt [output] Upon return, it will contain pointer to the RTCP packet.
+ * @param len [output] Upon return, it will indicate the size of the RTCP packet.
+ */
+PJ_DECL(void) pj_rtcp_build_rtcp( pj_rtcp_session *session, pj_rtcp_pkt **rtcp_pkt, int *len );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_RTCP_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtp.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtp.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtp.c	(revision 1753)
@@ -0,0 +1,266 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/rtp.c 3     4/17/05 11:59a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#include <pjmedia/rtp.h>
+#include <pj/log.h>
+#include <pj/os.h>	/* pj_gettimeofday() */
+#include <pj/sock.h>	/* pj_htonx, pj_htonx */
+#include <string.h>	/* memset() */
+
+#define THIS_FILE   "rtp.c"
+
+#define RTP_VERSION	2
+
+#define RTP_SEQ_MOD	(1 << 16)
+#define MAX_DROPOUT 	((pj_int16_t)3000)
+#define MAX_MISORDER 	((pj_int16_t)100)
+#define MIN_SEQUENTIAL  ((pj_int16_t)2)
+
+
+PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
+					 int default_pt, pj_uint32_t sender_ssrc )
+{
+    PJ_LOG(4, (THIS_FILE, "pj_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
+	       ses, default_pt, sender_ssrc));
+
+    /* Check RTP header packing. */
+    if (sizeof(struct pj_rtp_hdr) != 12) {
+	pj_assert(!"Wrong RTP header packing!");
+	return PJ_RTP_ERR_RTP_PACKING;
+    }
+
+    /* If sender_ssrc is not specified, create from time value. */
+    if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) {
+	pj_time_val tv;
+
+	pj_gettimeofday(&tv);
+	sender_ssrc  = (pj_uint32_t) pj_htonl(tv.sec);
+    } else {
+	sender_ssrc = pj_htonl(sender_ssrc);
+    }
+
+    /* Initialize session. */
+    ses->out_extseq = 0;
+    ses->peer_ssrc = 0;
+    
+    /* Sequence number will be initialized when the first RTP packet is receieved. */
+
+    /* Build default header for outgoing RTP packet. */
+    memset(ses, 0, sizeof(*ses));
+    ses->out_hdr.v = RTP_VERSION;
+    ses->out_hdr.p = 0;
+    ses->out_hdr.x = 0;
+    ses->out_hdr.cc = 0;
+    ses->out_hdr.m = 0;
+    ses->out_hdr.pt = (pj_uint8_t) default_pt;
+    ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq );
+    ses->out_hdr.ts = 0;
+    ses->out_hdr.ssrc = sender_ssrc;
+
+    /* Keep some arguments as session defaults. */
+    ses->out_pt = (pj_uint16_t) default_pt;
+
+    return PJ_OK;
+}
+
+
+PJ_DEF(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
+				       int payload_len, int ts_len,
+				       const void **rtphdr, int *hdrlen )
+{
+    PJ_UNUSED_ARG(payload_len)
+
+    PJ_LOG(6, (THIS_FILE, 
+	      "pj_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d",
+	      ses, pt, m, payload_len, ts_len));
+
+    /* Update session. */
+    ses->out_extseq++;
+    ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
+
+    /* Create outgoing header. */
+    ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
+    ses->out_hdr.m = (pj_uint16_t) m;
+    ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);
+
+    /* Return values */
+    *rtphdr = &ses->out_hdr;
+    *hdrlen = sizeof(pj_rtp_hdr);
+
+    return PJ_OK;
+}
+
+
+PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, 
+				       const void *pkt, int pkt_len,
+				       const pj_rtp_hdr **hdr,
+				       const void **payload,
+				       unsigned *payloadlen)
+{
+    int offset;
+
+    PJ_UNUSED_ARG(ses)
+
+    PJ_LOG(6, (THIS_FILE, 
+	      "pj_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d",
+	      ses, pkt, pkt_len));
+
+    /* Assume RTP header at the start of packet. We'll verify this later. */
+    *hdr = (pj_rtp_hdr*)pkt;
+
+    /* Check RTP header sanity. */
+    if ((*hdr)->v != RTP_VERSION) {
+	PJ_LOG(4, (THIS_FILE, "  invalid RTP version!"));
+	return PJ_RTP_ERR_INVALID_VERSION;
+    }
+
+    /* Payload is located right after header plus CSRC */
+    offset = sizeof(pj_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
+
+    /* Adjust offset if RTP extension is used. */
+    if ((*hdr)->x) {
+	pj_rtp_ext_hdr *ext = (pj_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset);
+	offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t));
+    }
+
+    /* Check that offset is less than packet size */
+    if (offset >= pkt_len)
+	return PJ_RTP_ERR_INVALID_PACKET;
+
+    /* Find and set payload. */
+    *payload = ((pj_uint8_t*)pkt) + offset;
+    *payloadlen = pkt_len - offset;
+
+    return PJ_OK;
+}
+
+
+PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr *hdr)
+{
+    int status;
+
+    /* Check SSRC. */
+    if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
+    /*
+    if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) {
+	PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)",
+		   ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc));
+	return PJ_RTP_ERR_INVALID_SSRC;
+    }
+    */
+
+    /* Check payload type. */
+    if (hdr->pt != ses->out_pt) {
+	PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)",
+		   ses, hdr->pt, ses->out_pt));
+	return PJ_RTP_ERR_INVALID_PT;
+    }
+
+    /* Initialize sequence number on first packet received. */
+    if (ses->received == 0)
+	pj_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
+
+    /* Check sequence number to see if remote session has been restarted. */
+    status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq));
+    if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
+	pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq));
+	++ses->received;
+    } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) {
+	++ses->received;
+    }
+
+
+    return status;
+}
+
+
+void pj_rtp_seq_restart(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
+{
+    sctrl->base_seq = seq;
+    sctrl->max_seq = seq;
+    sctrl->bad_seq = RTP_SEQ_MOD + 1;
+    sctrl->cycles = 0;
+}
+
+
+void pj_rtp_seq_init(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
+{
+    pj_rtp_seq_restart(sctrl, seq);
+
+    sctrl->max_seq = (pj_uint16_t) (seq - 1);
+    sctrl->probation = MIN_SEQUENTIAL;
+}
+
+
+int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
+{
+    pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq);
+    
+    /*
+     * Source is not valid until MIN_SEQUENTIAL packets with
+     * sequential sequence numbers have been received.
+     */
+    if (sctrl->probation) {
+	/* packet is in sequence */
+        if (seq == sctrl->max_seq+ 1) {
+	    sctrl->probation--;
+            sctrl->max_seq = seq;
+            if (sctrl->probation == 0) {
+                return PJ_RTP_ERR_SESSION_RESTARTED;
+            }
+	} else {
+	    sctrl->probation = MIN_SEQUENTIAL - 1;
+	    sctrl->max_seq = seq;
+        }
+        return PJ_RTP_ERR_SESSION_PROBATION;
+
+    } else if (udelta < MAX_DROPOUT) {
+	/* in order, with permissible gap */
+	if (seq < sctrl->max_seq) {
+	    /* Sequence number wrapped - count another 64K cycle. */
+	    sctrl->cycles += RTP_SEQ_MOD;
+        }
+        sctrl->max_seq = seq;
+
+    } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
+	/* the sequence number made a very large jump */
+        if (seq == sctrl->bad_seq) {
+	    /*
+	     * Two sequential packets -- assume that the other side
+	     * restarted without telling us so just re-sync
+	     * (i.e., pretend this was the first packet).
+	     */
+	    return PJ_RTP_ERR_SESSION_RESTARTED;
+	}
+        else {
+	    sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
+            return PJ_RTP_ERR_BAD_SEQUENCE;
+        }
+    } else {
+	/* duplicate or reordered packet */
+    }
+    
+    return 0;
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtp.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtp.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/rtp.h	(revision 1753)
@@ -0,0 +1,261 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/rtp.h 6     8/24/05 10:30a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJMEDIA_RTP_H__
+#define __PJMEDIA_RTP_H__
+
+#include <pj/types.h>
+
+/**
+ * @file rtp.h
+ * @brief RTP implementation.
+ */
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_RTP RTP
+ * @ingroup PJMEDIA
+ * @{
+ *
+ * The RTP module is designed to be dependent only to PJLIB, it does not depend
+ * on any other parts of PJMEDIA library. The RTP module does not even depend
+ * on any transports (sockets), to promote even more use.
+ *
+ * An RTCP implementation is also separated from this module.
+ *
+ * The functions that are provided by this module:
+ *  - creating RTP header for each outgoing packet.
+ *  - decoding RTP packet into RTP header and payload.
+ *  - provide simple RTP session management (sequence number, etc.)
+ *
+ * The RTP module does not use any dynamic memory at all.
+ *
+ * \section P1 How to Use the RTP Module
+ * 
+ * First application must call #pj_rtp_session_init to initialize the RTP 
+ * session.
+ *
+ * When application wants to send RTP packet, it needs to call 
+ * #pj_rtp_encode_rtp to build the RTP header. Note that this WILL NOT build
+ * the complete RTP packet, but instead only the header. Application can
+ * then either concatenate the header with the payload, or send the two
+ * fragments (the header and the payload) using scatter-gather transport API
+ * (e.g. \a sendv()).
+ *
+ * When application receives an RTP packet, first it should call
+ * #pj_rtp_decode_rtp to decode RTP header and payload, then it should call
+ * #pj_rtp_session_update to check whether we can process the RTP payload,
+ * and to let the RTP session updates its internal status. The decode function
+ * is guaranteed to point the payload to the correct position regardless of
+ * any options present in the RTP packet.
+ *
+ */
+
+
+#ifdef _MSC_VER
+#  pragma warning ( disable : 4214 )
+#endif
+
+
+/**
+ * Error codes.
+ */
+enum pj_rtp_error_t
+{
+    PJ_RTP_ERR_RTP_PACKING,	    /**< Invalid RTP packet. */
+    PJ_RTP_ERR_INVALID_VERSION,	    /**< Invalid RTP version. */
+    PJ_RTP_ERR_INVALID_SSRC,	    /**< Invalid SSRC. */
+    PJ_RTP_ERR_INVALID_PT,	    /**< Invalid payload type. */
+    PJ_RTP_ERR_INVALID_PACKET,	    /**< Invalid packet. */
+    PJ_RTP_ERR_SESSION_RESTARTED,   /**< Session has just been restarted. */
+    PJ_RTP_ERR_SESSION_PROBATION,   /**< Session in probation. */
+    PJ_RTP_ERR_BAD_SEQUENCE,	    /**< Bad RTP sequence number. */
+};
+
+#pragma pack(1)
+/**
+ * RTP packet header.
+ */
+struct pj_rtp_hdr
+{
+#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0)
+    pj_uint16_t v:2;	/**< packet type/version	    */
+    pj_uint16_t p:1;	/**< padding flag		    */
+    pj_uint16_t x:1;	/**< extension flag	    */
+    pj_uint16_t cc:4;	/**< CSRC count		    */
+    pj_uint16_t m:1;	/**< marker bit		    */
+    pj_uint16_t pt:7;	/**< payload type		    */
+#else
+    pj_uint16_t cc:4;	/**< CSRC count		    */
+    pj_uint16_t x:1;	/**< header extension flag    */ 
+    pj_uint16_t p:1;	/**< padding flag		    */
+    pj_uint16_t v:2;	/**< packet type/version	    */
+    pj_uint16_t pt:7;	/**< payload type		    */
+    pj_uint16_t m:1;	/**< marker bit		    */
+#endif
+    pj_uint16_t seq;	/**< sequence number	    */
+    pj_uint32_t ts;	/**< timestamp		    */
+    pj_uint32_t ssrc;	/**< synchronization source   */
+};
+#pragma pack()
+
+typedef struct pj_rtp_hdr pj_rtp_hdr;
+
+/**
+ * RTP extendsion header.
+ */
+struct pj_rtp_ext_hdr
+{
+    pj_uint16_t	profile_data;
+    pj_uint16_t	length;
+};
+
+typedef struct pj_rtp_ext_hdr pj_rtp_ext_hdr;
+
+/**
+ * A generic sequence number management, used by both RTP and RTCP.
+ */
+struct pj_rtp_seq_session
+{
+    pj_uint16_t	    max_seq;	    /**< highest sequence number heard */
+    pj_uint32_t	    cycles;	    /**< shifted count of seq. number cycles */
+    pj_uint32_t	    base_seq;	    /**< base seq number */
+    pj_uint32_t	    bad_seq;        /**< last 'bad' seq number + 1 */
+    pj_uint32_t	    probation;      /**< sequ. packets till source is valid */
+};
+
+typedef struct pj_rtp_seq_session pj_rtp_seq_session;
+
+/**
+ * RTP session descriptor.
+ */
+struct pj_rtp_session
+{
+    pj_rtp_hdr		out_hdr;    /**< Saved header for outgoing packets. */
+    pj_rtp_seq_session	seq_ctrl;   /**< Sequence number management. */
+    pj_uint16_t	        out_pt;	    /**< Default outgoing payload type. */
+    pj_uint32_t	        out_extseq; /**< Outgoing extended sequence number. */
+    pj_uint32_t	        peer_ssrc;  /**< Peer SSRC. */
+    pj_uint32_t	        received;   /**< Number of received packets. */
+};
+
+typedef struct pj_rtp_session pj_rtp_session;
+
+/**
+ * \brief Initialize RTP session.
+ * This function will initialize the RTP session according to given parameters.
+ *
+ * @param ses		The session.
+ * @param default_pt	Default payload type.
+ * @param sender_ssrc	SSRC used for outgoing packets.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
+					  int default_pt, pj_uint32_t sender_ssrc );
+
+/**
+ * \brief Encode outgoing RTP packet header.
+ * Create the RTP header based on arguments and current state of the RTP
+ * session.
+ *
+ * @param ses		The session.
+ * @param pt		Payload type.
+ * @param m		Marker flag.
+ * @param payload_len	Payload length in bytes.
+ * @param ts_len	Timestamp length.
+ * @param rtphdr	Upon return will point to RTP packet header.
+ * @param hdrlen	Upon return will indicate the size of RTP packet header
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
+				        int payload_len, int ts_len,
+					const void **rtphdr, int *hdrlen );
+
+/**
+ * \brief Decode an incoming RTP packet.
+ * This function will decode incoming packet into RTP header and payload.
+ * The decode function is guaranteed to point the payload to the correct 
+ * position regardless of any options present in the RTP packet.
+ *
+ * @param ses		The session.
+ * @param pkt		The received RTP packet.
+ * @param pkt_len	The length of the packet.
+ * @param hdr		Upon return will point to the location of the RTP header
+ *			inside the packet.
+ * @param payload	Upon return will point to the location of the
+ *			payload inside the packet.
+ * @param payloadlen	Upon return will indicate the size of the payload.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, 
+				        const void *pkt, int pkt_len,
+					const pj_rtp_hdr **hdr,
+					const void **payload,
+					unsigned *payloadlen);
+
+/**
+ * \brief Update RTP session with an incoming RTP packet. 
+ * Call this function everytime
+ * an RTP packet is received to check whether the packet can be received and to
+ * let the RTP session performs its internal calculations.
+ *
+ * @param ses	    The session.
+ * @param hdr	    The RTP header of the incoming packet.
+ *
+ * @return zero if the packet is valid and can be processed, otherwise will
+ *         return one of the error in #pj_rtp_error_t.
+ */
+PJ_DECL(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, 
+					    const pj_rtp_hdr *hdr);
+
+/** 
+* \brief Internal.
+ * Internal function for sequence control, shared by RTCP implementation. 
+ */
+void pj_rtp_seq_init(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
+
+/** 
+* \brief Internal.
+ * Internal function for sequence control, shared by RTCP implementation. 
+ */
+void pj_rtp_seq_restart(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
+
+/** 
+* \brief Internal.
+ * Internal function for sequence control, shared by RTCP implementation. 
+ */
+int  pj_rtp_seq_update(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_RTP_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/sdp.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/sdp.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/sdp.c	(revision 1753)
@@ -0,0 +1,955 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/sdp.c 9     6/12/05 11:36a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+
+#include <pjmedia/sdp.h>
+#include <pj/scanner.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+enum {
+    SKIP_WS = 0,
+    SYNTAX_ERROR = 1,
+};
+#define TOKEN		"-.!%*_=`'~"
+#define NTP_OFFSET	((pj_uint32_t)2208988800)
+#define LOG_THIS	"sdp"
+
+/*
+ * Prototypes for line parser.
+ */
+static void parse_version(pj_scanner *scanner);
+static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses);
+static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses);
+static void parse_generic_line(pj_scanner *scanner, pj_str_t *str);
+static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn);
+static pjsdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner);
+static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med);
+
+/*
+ * Prototypes for attribute parsers.
+ */
+static pjsdp_rtpmap_attr *  parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_attr_string *  parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_attr_num *	    parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_attr *	    parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_fmtp_attr *    parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner );
+
+
+/* 
+ * Prototypes for functions to print attribute.
+ * All of them returns integer for the length printed, or -1 on error.
+ */
+static int print_rtpmap_attr(const pjsdp_rtpmap_attr *attr, 
+			     char *buf, int length);
+static int print_generic_string_attr(const pjsdp_attr_string *attr, 
+				     char *buf, int length);
+static int print_generic_num_attr(const pjsdp_attr_num *attr, 
+				  char *buf, int length);
+static int print_name_only_attr(const pjsdp_attr *attr, 
+				char *buf, int length);
+static int print_fmtp_attr(const pjsdp_fmtp_attr *attr, 
+			   char *buf, int length);
+
+/*
+ * Prototypes for cloning attributes.
+ */
+static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+
+
+/*
+ * Prototypes
+ */
+static void init_sdp_parser(void);
+
+
+typedef void *  (*FPARSE)(pj_pool_t *pool, pj_scanner *scanner);
+typedef int (*FPRINT)(const void *attr, char *buf, int length);
+typedef pjsdp_attr*  (*FCLONE)(pj_pool_t *pool, const pjsdp_attr *rhs);
+
+/*
+ * Array of functions to print attribute.
+ */
+static struct attr_map_rec
+{
+    pj_str_t name;
+    FPARSE   parse_attr;
+    FPRINT   print_attr;
+    FCLONE   clone;
+} attr_map[] = 
+{
+    {{"rtpmap", 6},    (FPARSE)&parse_rtpmap_attr,	   (FPRINT)&print_rtpmap_attr,		(FCLONE)&clone_rtpmap_attr},
+    {{"cat", 3},       (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr},
+    {{"keywds", 6},    (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr},
+    {{"tool", 4},      (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr},
+    {{"ptime", 5},     (FPARSE)&parse_generic_num_attr,    (FPRINT)&print_generic_num_attr,	(FCLONE)&clone_generic_num_attr},
+    {{"recvonly", 8},  (FPARSE)&parse_name_only_attr,	   (FPRINT)&print_name_only_attr,	(FCLONE)&clone_name_only_attr},
+    {{"sendonly", 8},  (FPARSE)&parse_name_only_attr,	   (FPRINT)&print_name_only_attr,	(FCLONE)&clone_name_only_attr},
+    {{"sendrecv", 8},  (FPARSE)&parse_name_only_attr,	   (FPRINT)&print_name_only_attr,	(FCLONE)&clone_name_only_attr},
+    {{"orient", 6},    (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr},
+    {{"type", 4},      (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr},
+    {{"charset", 7},   (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr},
+    {{"sdplang", 7},   (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr},
+    {{"lang", 4},      (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr},
+    {{"framerate", 9}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr},
+    {{"quality", 7},   (FPARSE)&parse_generic_num_attr,    (FPRINT)&print_generic_num_attr,	(FCLONE)&clone_generic_num_attr},
+    {{"fmtp", 4},      (FPARSE)&parse_fmtp_attr,	   (FPRINT)&print_fmtp_attr,		(FCLONE)&clone_fmtp_attr},
+    {{"inactive", 8},  (FPARSE)&parse_name_only_attr,	   (FPRINT)&print_name_only_attr,	(FCLONE)&clone_name_only_attr},
+    {{"", 0},	       NULL, (FPRINT)&print_generic_string_attr,	(FCLONE)&clone_generic_string_attr}
+};
+
+/*
+ * Scanner character specification.
+ */
+static int is_initialized;
+static pj_char_spec cs_token;
+
+static void init_sdp_parser(void)
+{
+    if (is_initialized == 0) {
+	is_initialized = 1;
+	if (is_initialized != 1) {
+	    return;
+	}
+    }
+    pj_cs_add_alpha(cs_token);
+    pj_cs_add_num(cs_token);
+    pj_cs_add_str( cs_token, TOKEN);
+}
+
+static int print_rtpmap_attr(const pjsdp_rtpmap_attr *rtpmap, 
+			     char *buf, int len)
+{
+    char *p = buf;
+
+    if (len < 16+rtpmap->encoding_name.slen+rtpmap->parameter.slen) {
+	return -1;
+    }
+    
+    /* colon and payload type. */
+    *p++ = ':';
+    len = pj_utoa(rtpmap->payload_type, p);
+    p += len;
+
+    /* space, encoding name */
+    *p++ = ' ';
+    pj_memcpy(p, rtpmap->encoding_name.ptr, rtpmap->encoding_name.slen);
+    p += rtpmap->encoding_name.slen;
+
+    /* slash, clock-rate. */
+    *p++ = '/';
+    len = pj_utoa(rtpmap->clock_rate, p);
+    p += len;
+
+    /* optionally add encoding parameter. */
+    if (rtpmap->parameter.slen) {
+	*p++ = '/';
+	pj_memcpy(p, rtpmap->parameter.ptr, rtpmap->parameter.slen);
+	p += rtpmap->parameter.slen;
+    }
+
+    return p-buf;
+}
+
+static int print_generic_string_attr(const pjsdp_attr_string *attr, 
+				     char *buf, int len)
+{
+    char *p = buf;
+
+    if (len < attr->value.slen + 4) {
+	return -1;
+    }
+
+    /* colon and attribute value. */
+    *p++ = ':';
+    pj_memcpy(p, attr->value.ptr, attr->value.slen);
+    p += attr->value.slen;
+
+    return p-buf;
+}
+
+static int print_generic_num_attr(const pjsdp_attr_num *attr, char *buf, int len)
+{
+    char *p = buf;
+
+    if (len < 10) {
+	return -1;
+    }
+    *p++ = ':';
+    return pj_utoa(attr->value, p);
+}
+
+static int print_name_only_attr(const pjsdp_attr *attr, char *buf, int len)
+{
+    PJ_UNUSED_ARG(attr)
+    PJ_UNUSED_ARG(buf)
+    PJ_UNUSED_ARG(len)
+    return 0;
+}
+
+static int print_fmtp_attr(const pjsdp_fmtp_attr *fmtp, char *buf, int len)
+{
+    char *p = buf;
+
+    if (len < 4+fmtp->format.slen+fmtp->param.slen) {
+	return -1;
+    }
+
+    /* colon and format. */
+    *p++ = ':';
+    pj_memcpy(p, fmtp->format.ptr, fmtp->format.slen);
+    p += fmtp->format.slen;
+
+    /* space and parameter. */
+    *p++ = ' ';
+    pj_memcpy(p, fmtp->param.ptr, fmtp->param.slen);
+    p += fmtp->param.slen;
+
+    return p-buf;
+}
+
+
+static int print_attr(const pjsdp_attr *attr, char *buf, int len)
+{
+    char *p = buf;
+    struct attr_map_rec *desc = &attr_map[attr->type];
+
+    if (len < 16) {
+	return -1;
+    }
+
+    *p++ = 'a';
+    *p++ = '=';
+    pj_memcpy(p, desc->name.ptr, desc->name.slen);
+    p += desc->name.slen;
+    
+    len = (*desc->print_attr)(attr, p, (buf+len)-p);
+    if (len < 0) {
+	return -1;
+    }
+    p += len;
+    *p++ = '\r';
+    *p++ = '\n';
+    return p-buf;
+}
+
+static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+    const pjsdp_rtpmap_attr *rhs = (const pjsdp_rtpmap_attr*)p;
+    pjsdp_rtpmap_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_rtpmap_attr));
+    if (!attr)
+	return NULL;
+
+    attr->type = rhs->type;
+    attr->payload_type = rhs->payload_type;
+    if (!pj_strdup (pool, &attr->encoding_name, &rhs->encoding_name)) return NULL;
+    attr->clock_rate = rhs->clock_rate;
+    if (!pj_strdup (pool, &attr->parameter, &rhs->parameter)) return NULL;
+
+    return (pjsdp_attr*)attr;
+}
+
+static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+    const pjsdp_attr_string* rhs = (const pjsdp_attr_string*) p;
+    pjsdp_attr_string *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_string));
+    if (!attr)
+	return NULL;
+
+    attr->type = rhs->type;
+    if (!pj_strdup (pool, &attr->value, &rhs->value)) return NULL;
+
+    return (pjsdp_attr*)attr;
+}
+
+static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+    const pjsdp_attr_num* rhs = (const pjsdp_attr_num*) p;
+    pjsdp_attr_num *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_num));
+    if (!attr)
+	return NULL;
+
+    attr->type = rhs->type;
+    attr->value = rhs->value;
+
+    return (pjsdp_attr*)attr;
+}
+
+static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs)
+{
+    pjsdp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr));
+    if (!attr)
+	return NULL;
+
+    attr->type = rhs->type;
+    return attr;
+}
+
+static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+    const pjsdp_fmtp_attr* rhs = (const pjsdp_fmtp_attr*) p;
+    pjsdp_fmtp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_fmtp_attr));
+    if (!attr)
+	return NULL;
+
+    attr->type = rhs->type;
+    if (!pj_strdup (pool, &attr->format, &rhs->format)) return NULL;
+    if (!pj_strdup (pool, &attr->param, &rhs->param)) return NULL;
+
+    return (pjsdp_attr*)attr;
+}
+
+PJ_DEF(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs)
+{
+    struct attr_map_rec *desc;
+
+    if (rhs->type >= PJSDP_END_OF_ATTR) {
+	pj_assert(0);
+	return NULL;
+    }
+
+    desc = &attr_map[rhs->type];
+    return (*desc->clone) (pool, rhs);
+}
+
+PJ_DEF(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type)
+{
+    int i;
+
+    for (i=0; i<count; ++i) {
+	if (attr_array[i]->type == type)
+	    return attr_array[i];
+    }
+    return NULL;
+}
+
+static int print_connection_info( pjsdp_conn_info *c, char *buf, int len)
+{
+    char *p = buf;
+
+    if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) {
+	return -1;
+    }
+    *p++ = 'c';
+    *p++ = '=';
+    pj_memcpy(p, c->net_type.ptr, c->net_type.slen);
+    p += c->net_type.slen;
+    *p++ = ' ';
+    pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen);
+    p += c->addr_type.slen;
+    *p++ = ' ';
+    pj_memcpy(p, c->addr.ptr, c->addr.slen);
+    p += c->addr.slen;
+    *p++ = '\r';
+    *p++ = '\n';
+
+    return p-buf;
+}
+
+PJ_DEF(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, const pjsdp_conn_info *rhs)
+{
+    pjsdp_conn_info *c = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
+    if (!c) return NULL;
+
+    if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL;
+    if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL;
+    if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL;
+
+    return c;
+}
+
+static int print_media_desc( pjsdp_media_desc *m, char *buf, int len)
+{
+    char *p = buf;
+    char *end = buf+len;
+    unsigned i;
+    int printed;
+
+    /* check length for the "m=" line. */
+    if (len < m->desc.media.slen+m->desc.transport.slen+12+24) {
+	return -1;
+    }
+    *p++ = 'm';	    /* m= */
+    *p++ = '=';
+    pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
+    p += m->desc.media.slen;
+    *p++ = ' ';
+    printed = pj_utoa(m->desc.port, p);
+    p += printed;
+    if (m->desc.port_count > 1) {
+	*p++ = '/';
+	printed = pj_utoa(m->desc.port_count, p);
+	p += printed;
+    }
+    *p++ = ' ';
+    pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
+    p += m->desc.transport.slen;
+    for (i=0; i<m->desc.fmt_count; ++i) {
+	*p++ = ' ';
+	pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
+	p += m->desc.fmt[i].slen;
+    }
+    *p++ = '\r';
+    *p++ = '\n';
+
+    /* print connection info, if present. */
+    if (m->conn) {
+	printed = print_connection_info(m->conn, p, end-p);
+	if (printed < 0) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+    /* print attributes. */
+    for (i=0; i<m->attr_count; ++i) {
+	printed = print_attr(m->attr[i], p, end-p);
+	if (printed < 0) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+    return p-buf;
+}
+
+PJ_DEF(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool, 
+						  const pjsdp_media_desc *rhs)
+{
+    unsigned int i;
+    pjsdp_media_desc *m = pj_pool_alloc (pool, sizeof(pjsdp_media_desc));
+    if (!m)
+	return NULL;
+
+    pj_strdup (pool, &m->desc.media, &rhs->desc.media);
+    m->desc.port = rhs->desc.port;
+    m->desc.port_count = rhs->desc.port_count;
+    pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
+    m->desc.fmt_count = rhs->desc.fmt_count;
+    for (i=0; i<rhs->desc.fmt_count; ++i)
+	m->desc.fmt[i] = rhs->desc.fmt[i];
+
+    if (rhs->conn) {
+	m->conn = pjsdp_conn_info_clone (pool, rhs->conn);
+	if (!m->conn)
+	    return NULL;
+    } else {
+	m->conn = NULL;
+    }
+
+    m->attr_count = rhs->attr_count;
+    for (i=0; i < rhs->attr_count; ++i) {
+	m->attr[i] = pjsdp_attr_clone (pool, rhs->attr[i]);
+	if (!m->attr[i])
+	    return NULL;
+    }
+
+    return m;
+}
+
+/** Check if the media description has the specified attribute. */
+PJ_DEF(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m, 
+					     pjsdp_attr_type_e attr_type)
+{
+    unsigned i;
+    for (i=0; i<m->attr_count; ++i) {
+	pjsdp_attr *attr = m->attr[i];
+	if (attr->type == attr_type)
+	    return 1;
+    }
+    return 0;
+}
+
+/** Find rtpmap attribute for the specified payload type. */
+PJ_DEF(const pjsdp_rtpmap_attr*) 
+pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt)
+{
+    unsigned i;
+    for (i=0; i<m->attr_count; ++i) {
+	pjsdp_attr *attr = m->attr[i];
+	if (attr->type == PJSDP_ATTR_RTPMAP) {
+	    const pjsdp_rtpmap_attr* rtpmap = (const pjsdp_rtpmap_attr*)attr;
+	    if (rtpmap->payload_type == pt)
+		return rtpmap;
+	}
+    }
+    return NULL;
+}
+
+
+static int print_session(const pjsdp_session_desc *ses, char *buf, pj_ssize_t len)
+{
+    char *p = buf;
+    char *end = buf+len;
+    unsigned i;
+    int printed;
+
+    /* Check length for v= and o= lines. */
+    if (len < 5+ 
+	      2+ses->origin.user.slen+18+
+	      ses->origin.net_type.slen+ses->origin.addr.slen + 2)
+    {
+	return -1;
+    }
+
+    /* SDP version (v= line) */
+    pj_memcpy(p, "v=0\r\n", 5);
+    p += 5;
+
+    /* Owner (o=) line. */
+    *p++ = 'o';
+    *p++ = '=';
+    pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
+    p += ses->origin.user.slen;
+    *p++ = ' ';
+    printed = pj_utoa(ses->origin.id, p);
+    p += printed;
+    *p++ = ' ';
+    printed = pj_utoa(ses->origin.version, p);
+    p += printed;
+    *p++ = ' ';
+    pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
+    p += ses->origin.net_type.slen;
+    *p++ = ' ';
+    pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
+    p += ses->origin.addr_type.slen;
+    *p++ = ' ';
+    pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
+    p += ses->origin.addr.slen;
+    *p++ = '\r';
+    *p++ = '\n';
+
+    /* Session name (s=) line. */
+    if ((end-p)  < 8+ses->name.slen) {
+	return -1;
+    }
+    *p++ = 's';
+    *p++ = '=';
+    pj_memcpy(p, ses->name.ptr, ses->name.slen);
+    p += ses->name.slen;
+    *p++ = '\r';
+    *p++ = '\n';
+
+    /* Time */
+    if ((end-p) < 24) {
+	return -1;
+    }
+    *p++ = 't';
+    *p++ = '=';
+    printed = pj_utoa(ses->time.start, p);
+    p += printed;
+    *p++ = ' ';
+    printed = pj_utoa(ses->time.stop, p);
+    p += printed;
+    *p++ = '\r';
+    *p++ = '\n';
+
+    /* Connection line (c=) if exist. */
+    if (ses->conn) {
+	printed = print_connection_info(ses->conn, p, end-p);
+	if (printed < 1) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+    /* Print all attribute (a=) lines. */
+    for (i=0; i<ses->attr_count; ++i) {
+	printed = print_attr(ses->attr[i], p, end-p);
+	if (printed < 0) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+    /* Print media (m=) lines. */
+    for (i=0; i<ses->media_count; ++i) {
+	printed = print_media_desc(ses->media[i], p, end-p);
+	if (printed < 0) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+    return p-buf;
+}
+
+/******************************************************************************
+ * PARSERS
+ */
+
+static void parse_version(pj_scanner *scanner)
+{
+    pj_scan_advance_n(scanner, 3, SKIP_WS);
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses)
+{
+    pj_str_t str;
+
+    /* o= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* username. */
+    pj_scan_get_until_ch(scanner, ' ', &ses->origin.user);
+    pj_scan_get_char(scanner);
+
+    /* id */
+    pj_scan_get_until_ch(scanner, ' ', &str);
+    ses->origin.id = pj_strtoul(&str);
+    pj_scan_get_char(scanner);
+
+    /* version */
+    pj_scan_get_until_ch(scanner, ' ', &str);
+    ses->origin.version = pj_strtoul(&str);
+    pj_scan_get_char(scanner);
+
+    /* network-type */
+    pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type);
+    pj_scan_get_char(scanner);
+
+    /* addr-type */
+    pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type);
+    pj_scan_get_char(scanner);
+
+    /* address */
+    pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr);
+
+    /* newline */
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses)
+{
+    pj_str_t str;
+
+    /* t= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* start time */
+    pj_scan_get_until_ch(scanner, ' ', &str);
+    ses->time.start = pj_strtoul(&str);
+
+    pj_scan_get_char(scanner);
+
+    /* stop time */
+    pj_scan_get_until_ch(scanner, '\r', &str);
+    ses->time.stop = pj_strtoul(&str);
+
+    /* newline */
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_generic_line(pj_scanner *scanner, pj_str_t *str)
+{
+    /* x= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* get anything until newline. */
+    pj_scan_get_until_ch(scanner, '\r', str);
+
+    /* newline. */
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn)
+{
+    /* c= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* network-type */
+    pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
+    pj_scan_get_char(scanner);
+
+    /* addr-type */
+    pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
+    pj_scan_get_char(scanner);
+
+    /* address. */
+    pj_scan_get_until_ch(scanner, '\r', &conn->addr);
+
+    /* newline */
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med)
+{
+    pj_str_t str;
+
+    /* m= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* type */
+    pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
+    pj_scan_get_char(scanner);
+
+    /* port */
+    pj_scan_get(scanner, cs_token, &str);
+    med->desc.port = (unsigned short)pj_strtoul(&str);
+    if (*scanner->current == '/') {
+	/* port count */
+	pj_scan_get_char(scanner);
+	pj_scan_get(scanner, cs_token, &str);
+	med->desc.port_count = pj_strtoul(&str);
+
+    } else {
+	med->desc.port_count = 0;
+    }
+
+    if (pj_scan_get_char(scanner) != ' ') {
+	PJ_THROW(SYNTAX_ERROR);
+    }
+
+    /* transport */
+    pj_scan_get_until_ch(scanner, ' ', &med->desc.transport);
+
+    /* format list */
+    med->desc.fmt_count = 0;
+    while (*scanner->current == ' ') {
+	pj_scan_get_char(scanner);
+	pj_scan_get(scanner, cs_token, &med->desc.fmt[med->desc.fmt_count++]);
+    }
+
+    /* newline */
+    pj_scan_get_newline(scanner);
+}
+
+static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+    pjsdp_rtpmap_attr *rtpmap;
+    pj_str_t str;
+
+    rtpmap = pj_pool_calloc(pool, 1, sizeof(*rtpmap));
+    if (pj_scan_get_char(scanner) != ':') {
+	PJ_THROW(SYNTAX_ERROR);
+    }
+    pj_scan_get_until_ch(scanner, ' ', &str);
+    rtpmap->payload_type = pj_strtoul(&str);
+    pj_scan_get_char(scanner);
+
+    pj_scan_get_until_ch(scanner, '/', &rtpmap->encoding_name);
+    pj_scan_get_char(scanner);
+    pj_scan_get(scanner, cs_token, &str);
+    rtpmap->clock_rate = pj_strtoul(&str);
+
+    if (*scanner->current == '/') {
+	pj_scan_get_char(scanner);
+	pj_scan_get_until_ch(scanner, '\r', &rtpmap->parameter);
+    }
+
+    return rtpmap;
+}
+
+static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+    pjsdp_attr_string *attr;
+    attr = pj_pool_calloc(pool, 1, sizeof(*attr));
+
+    if (pj_scan_get_char(scanner) != ':') {
+	PJ_THROW(SYNTAX_ERROR);
+    }
+    pj_scan_get_until_ch(scanner, '\r', &attr->value);
+    return attr;
+}
+
+static pjsdp_attr_num *	parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+    pjsdp_attr_num *attr;
+    pj_str_t str;
+
+    attr = pj_pool_calloc(pool, 1, sizeof(*attr));
+
+    if (pj_scan_get_char(scanner) != ':') {
+	PJ_THROW(SYNTAX_ERROR);
+    }
+    pj_scan_get_until_ch(scanner, '\r', &str);
+    attr->value = pj_strtoul(&str);
+    return attr;
+}
+
+static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+    pjsdp_attr *attr;
+
+    PJ_UNUSED_ARG(scanner)
+    attr = pj_pool_calloc(pool, 1, sizeof(*attr));
+    return attr;
+}
+
+static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+    pjsdp_fmtp_attr *fmtp;
+
+    fmtp = pj_pool_calloc(pool, 1, sizeof(*fmtp));
+
+    if (pj_scan_get_char(scanner) != ':') {
+	PJ_THROW(SYNTAX_ERROR);
+    }
+    pj_scan_get_until_ch(scanner, ' ', &fmtp->format);
+    pj_scan_get_char(scanner);
+    pj_scan_get_until_ch(scanner, '\r', &fmtp->param);
+    return fmtp;
+}
+
+static pjsdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner)
+{
+    void * (*parse_func)(pj_pool_t *pool, pj_scanner *scanner) = NULL;
+    pj_str_t attrname;
+    unsigned i;
+    pjsdp_attr *attr;
+
+    /* skip a= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+    
+    /* get attr name. */
+    pj_scan_get(scanner, cs_token, &attrname);
+
+    /* find entry to handle attrname */
+    for (i=0; i<PJ_ARRAY_SIZE(attr_map); ++i) {
+	struct attr_map_rec *p = &attr_map[i];
+	if (pj_strcmp(&attrname, &p->name) == 0) {
+	    parse_func = p->parse_attr;
+	    break;
+	}
+    }
+
+    /* fallback to generic string parser. */
+    if (parse_func == NULL) {
+	parse_func = &parse_generic_string_attr;
+    }
+
+    attr = (*parse_func)(pool, scanner);
+    attr->type = i;
+	
+    /* newline */
+    pj_scan_get_newline(scanner);
+
+    return attr;
+}
+
+static void on_scanner_error(pj_scanner *scanner)
+{
+    PJ_UNUSED_ARG(scanner)
+
+    PJ_THROW(SYNTAX_ERROR);
+}
+
+/*
+ * Parse SDP message.
+ */
+PJ_DEF(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len, 
+					 pj_pool_t *pool)
+{
+    pj_scanner scanner;
+    pjsdp_session_desc *session;
+    pjsdp_media_desc *media = NULL;
+    void *attr;
+    pjsdp_conn_info *conn;
+    pj_str_t dummy;
+    int cur_name = 254;
+    PJ_USE_EXCEPTION;
+
+    init_sdp_parser();
+
+    pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
+    session = pj_pool_calloc(pool, 1, sizeof(*session));
+
+    PJ_TRY {
+	while (!pj_scan_is_eof(&scanner)) {
+		cur_name = *scanner.current;
+		switch (cur_name) {
+		case 'a':
+		    attr = parse_attr(pool, &scanner);
+		    if (attr) {
+			if (media) {
+			    media->attr[media->attr_count++] = attr;
+			} else {
+			    session->attr[session->attr_count++] = attr;
+			}
+		    }
+		    break;
+		case 'o':
+		    parse_origin(&scanner, session);
+		    break;
+		case 's':
+		    parse_generic_line(&scanner, &session->name);
+		    break;
+		case 'c':
+		    conn = pj_pool_calloc(pool, 1, sizeof(*conn));
+		    parse_connection_info(&scanner, conn);
+		    if (media) {
+			media->conn = conn;
+		    } else {
+			session->conn = conn;
+		    }
+		    break;
+		case 't':
+		    parse_time(&scanner, session);
+		    break;
+		case 'm':
+		    media = pj_pool_calloc(pool, 1, sizeof(*media));
+		    parse_media(&scanner, media);
+		    session->media[ session->media_count++ ] = media;
+		    break;
+		case 'v':
+		    parse_version(&scanner);
+		    break;
+		default:
+		    parse_generic_line(&scanner, &dummy);
+		    break;
+		}
+	}
+    }
+    PJ_CATCH(SYNTAX_ERROR) {
+	PJ_LOG(2, (LOG_THIS, "Syntax error in SDP parser '%c' line %d col %d",
+		cur_name, scanner.line, scanner.col));
+	if (!pj_scan_is_eof(&scanner)) {
+	    if (*scanner.current != '\r') {
+		pj_scan_get_until_ch(&scanner, '\r', &dummy);
+	    }
+	    pj_scan_get_newline(&scanner);
+	}
+    }
+    PJ_END;
+
+    pj_scan_fini(&scanner);
+    return session;
+}
+
+/*
+ * Print SDP description.
+ */
+PJ_DEF(int) pjsdp_print( const pjsdp_session_desc *desc, char *buf, pj_size_t size)
+{
+    return print_session(desc, buf, size);
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/sdp.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/sdp.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/sdp.h	(revision 1753)
@@ -0,0 +1,337 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/sdp.h 9     6/17/05 11:16p Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJSDP_SDP_H__
+#define __PJSDP_SDP_H__
+
+/**
+ * @defgroup PJSDP SDP Library
+ */
+/**
+ * @file sdp.h
+ * @brief SDP header file.
+ */
+/**
+ * @defgroup PJ_SDP_H SDP stack.
+ * @ingroup PJSDP
+ * @{
+ *
+ * This SDP module consists of SDP parser, SDP structure, and function to
+ * print back the structure as SDP message.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+#define PJSDP_MAX_FMT	32
+#define PJSDP_MAX_ATTR	32
+#define PJSDP_MAX_MEDIA	16
+
+/**
+ * This enumeration describes the attribute type.
+ */
+typedef enum pjsdp_attr_type_e
+{
+    PJSDP_ATTR_RTPMAP,
+    PJSDP_ATTR_CAT,
+    PJSDP_ATTR_KEYWORDS,
+    PJSDP_ATTR_TOOL,
+    PJSDP_ATTR_PTIME,
+    PJSDP_ATTR_RECV_ONLY,
+    PJSDP_ATTR_SEND_ONLY,
+    PJSDP_ATTR_SEND_RECV,
+    PJSDP_ATTR_ORIENT,
+    PJSDP_ATTR_TYPE,
+    PJSDP_ATTR_CHARSET,
+    PJSDP_ATTR_SDP_LANG,
+    PJSDP_ATTR_LANG,
+    PJSDP_ATTR_FRAME_RATE,
+    PJSDP_ATTR_QUALITY,
+    PJSDP_ATTR_FMTP,
+    PJSDP_ATTR_INACTIVE,
+    PJSDP_ATTR_GENERIC,
+    PJSDP_END_OF_ATTR,
+} pjsdp_attr_type_e;
+
+
+/**
+ * This structure keeps the common attributes that all 'descendants' 
+ * will have.
+ */
+typedef struct pjsdp_attr
+{
+    pjsdp_attr_type_e	type;	/**< Attribute type. */
+} pjsdp_attr;
+
+
+/**
+ * This is the structure to represent generic attribute which has a 
+ * string value.
+ */
+typedef struct pjsdp_attr_string
+{
+    pjsdp_attr_type_e	type;
+    pj_str_t		value;
+} pjsdp_attr_string;
+
+
+/**
+ * This is the structure to represent generic SDP attribute which has
+ * a numeric value.
+ */
+typedef struct pjsdp_attr_num
+{
+    pjsdp_attr_type_e	type;
+    pj_uint32_t		value;
+} pjsdp_attr_num;
+
+
+/**
+ * SDP \a rtpmap attribute.
+ */
+typedef struct pjsdp_rtpmap_attr
+{
+    pjsdp_attr_type_e	type;
+    unsigned		payload_type;
+    pj_str_t		encoding_name;
+    unsigned		clock_rate;
+    pj_str_t		parameter;
+} pjsdp_rtpmap_attr;
+
+
+/**
+ * SDP \a fmtp attribute.
+ */
+typedef struct pjsdp_fmtp_attr
+{
+    pjsdp_attr_type_e	type;
+    pj_str_t		format;
+    pj_str_t		param;
+} pjsdp_fmtp_attr;
+
+
+/** 
+ * SDP generic attribute.
+ */
+typedef struct pjsdp_generic_attr
+{
+    pjsdp_attr_type_e	type;
+    pj_str_t		name;
+    pj_str_t		value;
+} pjsdp_generic_attr;
+
+
+/** SDP \a cat attribute. */
+typedef struct pjsdp_attr_string pjsdp_cat_attr;
+
+/** SDP \a keywds attribute. */
+typedef struct pjsdp_attr_string pjsdp_keywds_attr;
+
+/** SDP \a tool attribute. */
+typedef struct pjsdp_attr_string pjsdp_tool_attr;
+
+/** SDP \a ptime attribute. */
+typedef struct pjsdp_attr_num pjsdp_ptime_attr;
+
+/** SDP \a recvonly attribute. */
+typedef struct pjsdp_attr pjsdp_recv_only_attr;
+
+/** SDP \a sendonly attribute. */
+typedef struct pjsdp_attr pjsdp_send_only_attr;
+
+/** SDP \a sendrecv attribute. */
+typedef struct pjsdp_attr pjsdp_send_recv_attr;
+
+/** SDP \a orient attribute. */
+typedef struct pjsdp_attr_string pjsdp_orient_attr;
+
+/** SDP \a type attribute. */
+typedef struct pjsdp_attr_string pjsdp_type_attr;
+
+/** SDP \a charset attribute. */
+typedef struct pjsdp_attr_string pjsdp_charset_attr;
+
+/** SDP \a sdplang attribute. */
+typedef struct pjsdp_attr_string pjsdp_sdp_lang_attr;
+
+/** SDP \a lang attribute. */
+typedef struct pjsdp_attr_string pjsdp_lang_attr;
+
+/** SDP \a framerate attribute. */
+typedef struct pjsdp_attr_string pjsdp_frame_rate_attr;
+
+/** SDP \a quality attribute. */
+typedef struct pjsdp_attr_num pjsdp_quality_attr;
+
+/** SDP \a inactive attribute. */
+typedef struct pjsdp_attr pjsdp_inactive_attr;
+
+/** Clone attribute */
+PJ_DECL(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs);
+
+/** Find attribute */
+PJ_DECL(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type);
+
+/**
+ * SDP connection info.
+ */
+typedef struct pjsdp_conn_info
+{
+    pj_str_t	net_type;
+    pj_str_t	addr_type;
+    pj_str_t	addr;
+} pjsdp_conn_info;
+
+/** 
+ *Clone connection info. 
+ * 
+ * @param pool	    Pool to allocate memory for the new connection info.
+ * @param rhs	    The connection into to clone.
+ *
+ * @return the new connection info.
+ */
+PJ_DECL(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, 
+						 const pjsdp_conn_info *rhs);
+
+/**
+ * SDP media description.
+ */
+typedef struct pjsdp_media_desc
+{
+    struct
+    {
+	pj_str_t    media;
+	pj_uint16_t port;
+	unsigned    port_count;
+	pj_str_t    transport;
+	unsigned    fmt_count;
+	pj_str_t    fmt[PJSDP_MAX_FMT];
+    } desc;
+
+    pjsdp_conn_info *conn;
+    unsigned	    attr_count;
+    pjsdp_attr	   *attr[PJSDP_MAX_ATTR];
+
+} pjsdp_media_desc;
+
+/** 
+ * Clone SDP media description. 
+ *
+ * @param pool	    Pool to allocate memory for the new media description.
+ * @param rhs	    The media descriptin to clone.
+ *
+ * @return a new media description.
+ */
+PJ_DECL(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool, 
+						   const pjsdp_media_desc *rhs);
+
+/** 
+ * Check if the media description has the specified attribute.
+ *
+ * @param m		The SDP media description.
+ * @param attr_type	The attribute type.
+ *
+ * @return nonzero if true.
+ */
+PJ_DECL(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m, 
+					      pjsdp_attr_type_e attr_type);
+
+/** 
+ * Find rtpmap attribute for the specified payload type. 
+ *
+ * @param m	    The SDP media description.
+ * @param pt	    RTP payload type.
+ *
+ * @return the SDP rtpmap attribute for the payload type, or NULL if not found.
+ */
+PJ_DECL(const pjsdp_rtpmap_attr*) 
+pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt);
+
+
+/**
+ * This structure describes SDP session description.
+ */
+typedef struct pjsdp_session_desc
+{
+    struct
+    {
+	pj_str_t    user;
+	pj_uint32_t id;
+	pj_uint32_t version;
+	pj_str_t    net_type;
+	pj_str_t    addr_type;
+	pj_str_t    addr;
+    } origin;
+
+    pj_str_t	     name;
+    pjsdp_conn_info *conn;
+    
+    struct
+    {
+	pj_uint32_t start;
+	pj_uint32_t stop;
+    } time;
+
+    unsigned	    attr_count;
+    pjsdp_attr	   *attr[PJSDP_MAX_ATTR];
+
+    unsigned	      media_count;
+    pjsdp_media_desc *media[PJSDP_MAX_MEDIA];
+
+} pjsdp_session_desc;
+
+
+/**
+ * Parse SDP message.
+ *
+ * @param buf	    The message buffer.
+ * @param len	    The length of the message.
+ * @param pool	    The pool to allocate SDP session description.
+ *
+ * @return SDP session description.
+ */
+PJ_DECL(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len, 
+					  pj_pool_t *pool);
+
+/**
+ * Print SDP description to a buffer.
+ *
+ * @param buf	    The buffer.
+ * @param size	    The buffer length.
+ * @param desc	    The SDP session description.
+ *
+ * @return the length printed, or -1.
+ */
+PJ_DECL(int) pjsdp_print( const pjsdp_session_desc *desc, 
+			  char *buf, pj_size_t size);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSDP_SDP_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/session.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/session.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/session.c	(revision 1753)
@@ -0,0 +1,837 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/session.c 10    6/14/05 12:54a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjmedia/session.h>
+#include <pj/log.h>
+#include <pj/os.h> 
+#include <pj/pool.h>
+#include <pj/string.h>
+
+typedef struct pj_media_stream_desc
+{
+    pj_media_stream_info    info;
+    pj_media_stream_t	   *enc_stream, *dec_stream;
+} pj_media_stream_desc;
+
+struct pj_media_session_t
+{
+    pj_pool_t		   *pool;
+    pj_med_mgr_t	   *mediamgr;
+    unsigned		    stream_cnt;
+    pj_media_stream_desc   *stream_desc[PJSDP_MAX_MEDIA];
+};
+
+#define THIS_FILE		"session.c"
+
+#define PJ_MEDIA_SESSION_SIZE	(48*1024)
+#define PJ_MEDIA_SESSION_INC	1024
+
+static const pj_str_t ID_AUDIO = { "audio", 5};
+static const pj_str_t ID_VIDEO = { "video", 5};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+
+static void session_init (pj_media_session_t *ses)
+{
+    pj_memset (ses, 0, sizeof(pj_media_session_t));
+}
+
+
+/**
+ * Create new session offering.
+ */
+PJ_DEF(pj_media_session_t*) 
+pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info)
+{
+    pj_pool_factory *pf;
+    pj_pool_t *pool;
+    pj_media_session_t *session;
+    pj_media_stream_desc *sd;
+    unsigned i, codec_cnt;
+    pj_codec_mgr *cm;
+    const pj_codec_id *codecs[PJSDP_MAX_FMT];
+
+    pf = pj_med_mgr_get_pool_factory(mgr);
+
+    pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
+    if (!pool)
+	return NULL;
+
+    session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
+    if (!session)
+	return NULL;
+
+    session_init (session);
+
+    session->pool = pool;
+    session->mediamgr = mgr;
+
+    /* Create first stream  */
+    sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
+    if (!sd)
+	return NULL;
+
+    sd->info.type = ID_AUDIO;
+    sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
+    sd->info.transport = ID_RTP_AVP;
+    pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
+
+    /* Enum audio codecs. */
+    sd->info.fmt_cnt = 0;
+    cm = pj_med_mgr_get_codec_mgr (mgr);
+    codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs);
+    if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
+    for (i=0; i<codec_cnt; ++i) {
+	if (codecs[i]->type != PJ_MEDIA_TYPE_AUDIO)
+	    continue;
+
+	sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt;
+	sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate;
+	pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name);
+	++sd->info.fmt_cnt;
+    }
+
+    session->stream_desc[session->stream_cnt++] = sd;
+
+    return session;
+}
+
+static int sdp_check (const pjsdp_session_desc *sdp)
+{
+    int has_conn = 0;
+    unsigned i;
+
+    if (sdp->conn)
+	has_conn = 1;
+
+    if (sdp->media_count == 0) {
+	PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition"));
+	return -1;
+    }
+
+    for (i=0; i<sdp->media_count; ++i) {
+	pjsdp_media_desc *m = sdp->media[i];
+
+	if (!m) {
+	    pj_assert(0);
+	    return -1;
+	}
+
+	if (m->desc.fmt_count == 0) {
+	    PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream"));
+	    return -1;
+	}
+
+	if (!has_conn && m->conn == NULL) {
+	    PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media"));
+	    return -1;
+	}
+    }
+
+    return 0;
+}
+
+/*
+ * Create local stream definition that matches SDP received from peer.
+ */
+static pj_media_stream_desc* 
+create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn,
+			const pjsdp_media_desc *m, const pj_media_sock_info *sock_info)
+{
+    pj_media_stream_desc *sd;
+
+    sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
+    if (!sd) {
+	PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor"));
+	return NULL;
+    }
+
+    if (pj_stricmp(&conn->net_type, &ID_IN)==0 && 
+	pj_stricmp(&conn->addr_type, &ID_IP4)==0 &&
+	pj_stricmp(&m->desc.media, &ID_AUDIO)==0 &&
+	pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0) 
+    {
+	/*
+	 * Got audio stream.
+	 */
+	unsigned i, codec_cnt;
+	pj_codec_mgr *cm;
+	const pj_codec_id *codecs[PJSDP_MAX_FMT];
+
+	sd->info.type = ID_AUDIO;
+	sd->info.transport = ID_RTP_AVP;
+	pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
+	sd->info.rem_port = m->desc.port;
+	pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
+
+	/* Enum audio codecs. */
+	sd->info.fmt_cnt = 0;
+	cm = pj_med_mgr_get_codec_mgr (mgr);
+	codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs);
+	if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
+
+	/* Find just one codec which we can support. */
+	for (i=0; i<m->desc.fmt_count && sd->info.fmt_cnt == 0; ++i) {
+	    unsigned j, fmt_i;
+
+	    /* For static payload, just match payload type. */
+	    /* Else match clock rate and encoding name. */
+	    fmt_i = pj_strtoul(&m->desc.fmt[i]);
+	    if (fmt_i < PJ_RTP_PT_DYNAMIC) {
+		for (j=0; j<codec_cnt; ++j) {
+		    if (codecs[j]->pt == fmt_i) {
+			sd->info.fmt_cnt = 1;
+			sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
+			sd->info.fmt[0].pt = codecs[j]->pt;
+			sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
+			pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name);
+			break;
+		    }
+		}
+	    } else {
+
+		/* Find the rtpmap for the payload type. */
+		const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i);
+
+		/* Don't accept the media if no rtpmap for dynamic PT. */
+		if (rtpmap == NULL) {
+		    PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i]));
+		    continue;
+		}
+
+		/* Check whether we can take this codec. */
+		for (j=0; j<codec_cnt; ++j) {
+		    if (rtpmap->clock_rate == codecs[j]->sample_rate && 
+			pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0)
+		    {
+			sd->info.fmt_cnt = 1;
+			sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
+			sd->info.fmt[0].pt = codecs[j]->pt;
+			sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
+			sd->info.fmt[0].encoding_name = codecs[j]->encoding_name;
+			break;
+		    }
+		}
+	    }
+	}
+
+	/* Match codec and direction. */
+	if (sd->info.fmt_cnt == 0 || m->desc.port == 0 || 
+	    pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) 
+	{
+	    sd->info.dir = PJ_MEDIA_DIR_NONE;
+	}
+	else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
+	    sd->info.dir = PJ_MEDIA_DIR_ENCODING;
+	}
+	else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
+	    sd->info.dir = PJ_MEDIA_DIR_DECODING;
+	}
+	else {
+	    sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
+	}
+
+    } else {
+	/* Unsupported media stream. */
+	unsigned fmt_num;
+	const pjsdp_rtpmap_attr *rtpmap = NULL;
+
+	pj_strdup(pool, &sd->info.type, &m->desc.media);
+	pj_strdup(pool, &sd->info.transport, &m->desc.transport);
+	pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info));
+	pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
+	sd->info.rem_port = m->desc.port;
+
+	/* Just put one format and rtpmap, so that we don't have to make 
+	 * special exception when we convert this stream to SDP.
+	 */
+
+	/* Find the rtpmap for the payload type. */
+	fmt_num = pj_strtoul(&m->desc.fmt[0]);
+	rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num);
+
+	sd->info.fmt_cnt = 1;
+	if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) {
+	    sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO;
+	    sd->info.fmt[0].pt = fmt_num;
+	    if (rtpmap) {
+		pj_strdup (pool, &sd->info.fmt[0].encoding_name, 
+			   &rtpmap->encoding_name);
+		sd->info.fmt[0].sample_rate = rtpmap->clock_rate;
+	    }
+	} else {
+	    sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN;
+	    pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]);
+	}
+	
+	sd->info.dir = PJ_MEDIA_DIR_NONE;
+    }
+
+    return sd;
+}
+
+/**
+ * Create new session based on peer's offering.
+ */
+PJ_DEF(pj_media_session_t*) 
+pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
+				  const pj_media_sock_info *sock_info)
+{
+    pj_pool_factory *pf;
+    pj_pool_t *pool;
+    pj_media_session_t *session;
+    unsigned i;
+
+    if (sdp_check(sdp) != 0)
+	return NULL;
+
+    pf = pj_med_mgr_get_pool_factory(mgr);
+    pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
+    if (!pool)
+	return NULL;
+
+    session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
+    if (!session) {
+	PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
+	pj_pool_release (pool);
+	return NULL;
+    }
+
+    session_init (session);
+
+    session->pool = pool;
+    session->mediamgr = mgr;
+
+    /* Enumerate each media stream and create our peer. */
+    for (i=0; i<sdp->media_count; ++i) {
+	const pjsdp_conn_info *conn;
+	const pjsdp_media_desc *m;
+	pj_media_stream_desc *sd;
+
+	m = sdp->media[i];
+	conn = m->conn ? m->conn : sdp->conn;
+
+	/*
+	 * Bug:
+	 *  the sock_info below is used by more than one 'm' lines
+	 */
+	PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES)
+
+	sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info);
+	pj_assert (sd);
+
+	session->stream_desc[session->stream_cnt++] = sd;
+    }
+
+    return session;
+}
+
+/**
+ * Duplicate session. The new session is inactive.
+ */
+PJ_DEF(pj_media_session_t*)
+pj_media_session_clone (const pj_media_session_t *rhs)
+{
+    pj_pool_factory *pf;
+    pj_pool_t *pool;
+    pj_media_session_t *session;
+    unsigned i;
+
+    pf = pj_med_mgr_get_pool_factory(rhs->mediamgr);
+    pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
+    if (!pool) {
+	return NULL;
+    }
+
+    session = pj_pool_alloc (pool, sizeof(*session));
+    if (!session) {
+	PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
+	pj_pool_release (pool);
+	return NULL;
+    }
+
+    session->pool = pool;
+    session->mediamgr = rhs->mediamgr;
+    session->stream_cnt = rhs->stream_cnt;
+
+    for (i=0; i<rhs->stream_cnt; ++i) {
+	pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc));
+	const pj_media_stream_desc *sd2 = rhs->stream_desc[i];
+
+	if (!sd1) {
+	    PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor"));
+	    pj_pool_release (pool);
+	    return NULL;
+	}
+
+	session->stream_desc[i] = sd1;
+	sd1->enc_stream = sd1->dec_stream = NULL;
+	pj_strdup (pool, &sd1->info.type, &sd2->info.type);
+	sd1->info.dir = sd2->info.dir;
+	pj_strdup (pool, &sd1->info.transport, &sd2->info.transport);
+	pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info));
+	pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr);
+	sd1->info.rem_port = sd2->info.rem_port;
+	sd1->info.fmt_cnt = sd2->info.fmt_cnt;
+	pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt));
+    }
+
+    return session;
+}
+
+/**
+ * Create SDP description from the session.
+ */
+PJ_DEF(pjsdp_session_desc*)
+pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool,
+			     pj_bool_t only_first_fmt)
+{
+    pjsdp_session_desc *sdp;
+    pj_time_val tv;
+    unsigned i;
+    pj_media_sock_info *c_addr = NULL;
+
+    if (session->stream_cnt == 0) {
+	pj_assert(0);
+	return NULL;
+    }
+
+    sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc));
+    if (!sdp) {
+	PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor"));
+	return NULL;
+    }
+
+    pj_gettimeofday(&tv);
+
+    sdp->origin.user = pj_str("-");
+    sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
+    sdp->origin.net_type = ID_IN;
+    sdp->origin.addr_type = ID_IP4;
+    sdp->origin.addr = *pj_gethostname();
+
+    sdp->name = ID_SDP_NAME;
+
+    /* If all media addresses are the same, then put the connection
+     * info in the session level, otherwise put it in media stream
+     * level.
+     */
+    for (i=0; i<session->stream_cnt; ++i) {
+	if (c_addr == NULL) {
+	    c_addr = &session->stream_desc[i]->info.sock_info;
+	} else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr)
+	{
+	    c_addr = NULL;
+	    break;
+	}
+    }
+
+    if (c_addr) {
+	/* All addresses are the same, put connection info in session level. */
+	sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
+	if (!sdp->conn) {
+	    PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info"));
+	    return NULL;
+	}
+
+	sdp->conn->net_type = ID_IN;
+	sdp->conn->addr_type = ID_IP4;
+	pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr));
+    }
+
+    sdp->time.start = sdp->time.stop = 0;
+    sdp->attr_count = 0;
+
+    /* Create each media. */
+    sdp->media_count = 0;
+    for (i=0; i<session->stream_cnt; ++i) {
+	const pj_media_stream_desc *sd = session->stream_desc[i];
+	pjsdp_media_desc *m;
+	unsigned j;
+	unsigned fmt_cnt;
+	pjsdp_attr *attr;
+
+	m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc));
+	if (!m) {
+	    PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor"));
+	    return NULL;
+	}
+
+	sdp->media[sdp->media_count++] = m;
+
+	pj_strdup (pool, &m->desc.media, &sd->info.type);
+	m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port);
+	m->desc.port_count = 1;
+	pj_strdup (pool, &m->desc.transport, &sd->info.transport);
+
+	/* Add format and rtpmap for each codec. */
+	m->desc.fmt_count = 0;
+	m->attr_count = 0;
+	fmt_cnt = sd->info.fmt_cnt;
+	if (fmt_cnt > 0 && only_first_fmt)
+	    fmt_cnt = 1;
+	for (j=0; j<fmt_cnt; ++j) {
+	    pjsdp_rtpmap_attr *rtpmap;
+	    pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++];
+
+	    if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) {
+		pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name);
+	    } else {
+		fmt->ptr = pj_pool_alloc(pool, 8);
+		fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr);
+
+		rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr));
+		if (rtpmap) {
+		    m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap;
+		    rtpmap->type = PJSDP_ATTR_RTPMAP;
+		    rtpmap->payload_type = sd->info.fmt[j].pt;
+		    rtpmap->clock_rate = sd->info.fmt[j].sample_rate;
+		    pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name);
+		} else {
+		    PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor"));
+		}
+	    }
+	}
+
+	/* If we don't have connection info in session level, create one. */
+	if (sdp->conn == NULL) {
+	    m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
+	    if (m->conn) {
+		m->conn->net_type = ID_IN;
+		m->conn->addr_type = ID_IP4;
+		pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr));
+	    } else {
+		PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info"));
+		return NULL;
+	    }
+	}
+
+	/* Add additional attribute to the media stream. */
+	attr = pj_pool_alloc(pool, sizeof(pjsdp_attr));
+	if (!attr) {
+	    PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute"));
+	    return NULL;
+	}
+	m->attr[m->attr_count++] = attr;
+
+	switch (sd->info.dir) {
+	case PJ_MEDIA_DIR_NONE:
+	    attr->type = PJSDP_ATTR_INACTIVE;
+	    break;
+	case PJ_MEDIA_DIR_ENCODING:
+	    attr->type = PJSDP_ATTR_SEND_ONLY;
+	    break;
+	case PJ_MEDIA_DIR_DECODING:
+	    attr->type = PJSDP_ATTR_RECV_ONLY;
+	    break;
+	case PJ_MEDIA_DIR_ENCODING_DECODING:
+	    attr->type = PJSDP_ATTR_SEND_RECV;
+	    break;
+	}
+    }
+
+    return sdp;
+}
+
+/**
+ * Update session with SDP answer from peer.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_update (pj_media_session_t *session, 
+			 const pjsdp_session_desc *sdp)
+{
+    unsigned i;
+    unsigned count;
+
+    /* Check SDP */
+    if (sdp_check (sdp) != 0) {
+	return -1;
+    }
+
+    /* If the media stream count doesn't match, only update one. */
+    if (session->stream_cnt != sdp->media_count) {
+	PJ_LOG(3,(THIS_FILE, "pj_media_session_update : "
+		 "SDP media count mismatch! (rmt=%d, lcl=%d)",
+		 sdp->media_count, session->stream_cnt));
+	count = (session->stream_cnt < sdp->media_count) ?
+	    session->stream_cnt : sdp->media_count;
+    } else {
+	count = session->stream_cnt;
+    }
+
+    for (i=0; i<count; ++i) {
+	pj_media_stream_desc *sd = session->stream_desc[i];
+	const pjsdp_media_desc *m = sdp->media[i];
+	const pjsdp_conn_info *conn;
+	unsigned j;
+
+	/* Check that the session is not active. */
+	pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL);
+
+	conn = m->conn ? m->conn : sdp->conn;
+	pj_assert(conn);
+
+	/* Update remote address. */
+	sd->info.rem_port = m->desc.port;
+	pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr);
+
+	/* Select one active codec according to what peer wants. */
+	for (j=0; j<sd->info.fmt_cnt; ++j) {
+	    unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]);
+	    if (sd->info.fmt[j].pt == fmt_0) {
+		pj_codec_id temp;
+
+		/* Put active format to the front. */
+		if (j == 0)
+		    break;
+
+		pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp));
+		pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp));
+		pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp));
+		break;
+	    }
+	}
+
+	if (j == sd->info.fmt_cnt) {
+	    /* Peer has answered SDP with new codec, which doesn't exist
+	     * in the offer!
+	     * Mute this media.
+	     */
+	    PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!"));
+	    sd->info.dir = PJ_MEDIA_DIR_NONE;
+	    continue;
+	}
+
+	/* Check direction. */
+	if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) {
+	    sd->info.dir = PJ_MEDIA_DIR_NONE;
+	}
+	else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
+	    sd->info.dir = PJ_MEDIA_DIR_ENCODING;
+	}
+	else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
+	    sd->info.dir = PJ_MEDIA_DIR_DECODING;
+	}
+	else {
+	    sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
+	}
+    }
+
+    return 0;
+}
+
+/**
+ * Enumerate media streams in the session.
+ */
+PJ_DEF(unsigned)
+pj_media_session_enum_streams (const pj_media_session_t *session, 
+			       unsigned count, const pj_media_stream_info *info[])
+{
+    unsigned i;
+
+    if (count > session->stream_cnt)
+	count = session->stream_cnt;
+
+    for (i=0; i<count; ++i) {
+	info[i] = &session->stream_desc[i]->info;
+    }
+
+    return session->stream_cnt;
+}
+
+/**
+ * Get statistics
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
+			   pj_media_stream_stat *tx_stat,
+			   pj_media_stream_stat *rx_stat)
+{
+    pj_media_stream_desc *sd;
+    int stat_cnt = 0;
+
+    if (index >= session->stream_cnt) {
+	pj_assert(0);
+	return -1;
+    }
+    
+    sd = session->stream_desc[index];
+
+    if (sd->enc_stream && tx_stat) {
+	pj_media_stream_get_stat (sd->enc_stream, tx_stat);
+	++stat_cnt;
+    } else if (tx_stat) {
+	pj_memset (tx_stat, 0, sizeof(*tx_stat));
+    }
+
+    if (sd->dec_stream && rx_stat) {
+	pj_media_stream_get_stat (sd->dec_stream, rx_stat);
+	++stat_cnt;
+    } else if (rx_stat) {
+	pj_memset (rx_stat, 0, sizeof(*rx_stat));
+    }
+
+    return stat_cnt ? 0 : -1;
+}
+
+/**
+ * Modify stream, only when stream is inactive.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
+				unsigned modify_flag, const pj_media_stream_info *info)
+{
+    pj_media_stream_desc *sd;
+
+    if (index >= session->stream_cnt) {
+	pj_assert(0);
+	return -1;
+    }
+    
+    sd = session->stream_desc[index];
+
+    if (sd->enc_stream || sd->dec_stream) {
+	pj_assert(0);
+	return -1;
+    }
+
+    if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) {
+	sd->info.dir = info->dir;
+    }
+
+    return 0;
+}
+
+/**
+ * Activate media session.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_activate (pj_media_session_t *session)
+{
+    unsigned i;
+    pj_status_t status = 0;
+
+    for (i=0; i<session->stream_cnt; ++i) {
+	pj_status_t rc;
+	rc = pj_media_session_activate_stream (session, i);
+	if (status == 0)
+	    status = rc;
+    }
+    return status;
+}
+
+/**
+ * Activate individual stream in media session.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_activate_stream (pj_media_session_t *session, unsigned index)
+{
+    pj_media_stream_desc *sd;
+    pj_media_stream_create_param scp;
+    pj_status_t status;
+    pj_time_val tv;
+
+    if (index < 0 || index >= session->stream_cnt) {
+	pj_assert(0);
+	return -1;
+    }
+
+    sd = session->stream_desc[index];
+
+    if (sd->enc_stream || sd->dec_stream) {
+	/* Stream already active. */
+	pj_assert(0);
+	return 0;
+    }
+
+    pj_gettimeofday(&tv);
+
+    /* Initialize parameter to create stream. */
+    pj_memset (&scp, 0, sizeof(scp));
+    scp.codec_id = &sd->info.fmt[0];
+    scp.mediamgr = session->mediamgr;
+    scp.dir = sd->info.dir;
+    scp.rtp_sock = sd->info.sock_info.rtp_sock;
+    scp.rtcp_sock = sd->info.sock_info.rtcp_sock;
+    scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in));
+    pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port);
+    scp.ssrc = tv.sec;
+    scp.jb_min = 1;
+    scp.jb_max = 15;
+    scp.jb_maxcnt = 16;
+
+    /* Build the stream! */
+    status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp);
+
+    if (status==0 && sd->enc_stream) {
+	status = pj_media_stream_start (sd->enc_stream);
+	if (status != 0)
+	    goto on_error;
+    }
+    if (status==0 && sd->dec_stream) {
+	status = pj_media_stream_start (sd->dec_stream);
+	if (status != 0)
+	    goto on_error;
+    }
+    return status;
+
+on_error:
+    if (sd->enc_stream) {
+	pj_media_stream_destroy (sd->enc_stream);
+	sd->enc_stream = NULL;
+    }
+    if (sd->dec_stream) {
+	pj_media_stream_destroy (sd->dec_stream);
+	sd->dec_stream = NULL;
+    }
+    return status;
+}
+
+/**
+ * Destroy media session.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_destroy (pj_media_session_t *session)
+{
+    unsigned i;
+
+    if (!session)
+	return -1;
+
+    for (i=0; i<session->stream_cnt; ++i) {
+	pj_media_stream_desc *sd = session->stream_desc[i];
+
+	if (sd->enc_stream) {
+	    pj_media_stream_destroy (sd->enc_stream);
+	    sd->enc_stream = NULL;
+	}
+	if (sd->dec_stream) {
+	    pj_media_stream_destroy (sd->dec_stream);
+	    sd->dec_stream = NULL;
+	}
+    }
+    pj_pool_release (session->pool);
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/session.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/session.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/session.h	(revision 1753)
@@ -0,0 +1,157 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/session.h 8     8/24/05 10:30a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJMEDIA_SESSION_H__
+#define __PJMEDIA_SESSION_H__
+
+
+/**
+ * @file session.h
+ * @brief Media Session.
+ */
+
+#include <pj/types.h>
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/stream.h>
+#include <pjmedia/sdp.h>
+
+PJ_BEGIN_DECL 
+
+/**
+ * @defgroup PJMED_SES Media session
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/** Opaque declaration of media session. */
+typedef struct pj_media_session_t pj_media_session_t;
+
+/** Media socket info. */
+typedef struct pj_media_sock_info
+{
+    pj_sock_t	    rtp_sock, rtcp_sock;
+    pj_sockaddr_in  rtp_addr_name;
+} pj_media_sock_info;
+
+/** Stream info. */
+typedef struct pj_media_stream_info
+{
+    pj_str_t		type;
+    pj_media_dir_t	dir;
+    pj_str_t		transport;
+    pj_media_sock_info	sock_info;
+    pj_str_t		rem_addr;
+    unsigned short	rem_port;
+    unsigned		fmt_cnt;
+    pj_codec_id		fmt[PJSDP_MAX_FMT];
+
+} pj_media_stream_info;
+
+/** Flag for modifying stream. */
+enum
+{
+    PJ_MEDIA_STREAM_MODIFY_DIR = 1,
+};
+
+/**
+ * Create new session offering.
+ */
+PJ_DECL(pj_media_session_t*) 
+pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo );
+
+/**
+ * Create new session based on peer's offering.
+ */
+PJ_DECL(pj_media_session_t*) 
+pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
+				   const pj_media_sock_info *skinfo);
+
+/**
+ * Duplicate session. The new session is inactive.
+ */
+PJ_DECL(pj_media_session_t*)
+pj_media_session_clone (const pj_media_session_t *session);
+
+/**
+ * Create SDP description from the session.
+ */
+PJ_DECL(pjsdp_session_desc*)
+pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool,
+			      pj_bool_t only_first_fmt);
+
+/**
+ * Update session with SDP answer from peer. The session must NOT active.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_update ( pj_media_session_t *session, 
+			  const pjsdp_session_desc *sdp);
+
+/**
+ * Enumerate media streams in the session.
+ * @return the actual number of streams.
+ */
+PJ_DECL(unsigned)
+pj_media_session_enum_streams (const pj_media_session_t *session, 
+			       unsigned count, const pj_media_stream_info *info[]);
+
+/**
+ * Get stream statistics.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
+			   pj_media_stream_stat *tx_stat,
+			   pj_media_stream_stat *rx_stat);
+
+/**
+ * Modify stream, only when stream is inactive.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
+				unsigned modify_flag, const pj_media_stream_info *info);
+
+/**
+ * Activate all streams in media session.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_activate (pj_media_session_t *session);
+
+/**
+ * Activate individual stream in media session.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_activate_stream (pj_media_session_t *session, unsigned index);
+
+/**
+ * Destroy media session.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_destroy (pj_media_session_t *session);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJMEDIA_SESSION_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/sound.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/sound.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/sound.h	(revision 1753)
@@ -0,0 +1,206 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/sound.h 8     8/24/05 10:30a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJMEDIA_SOUND_H__
+#define __PJMEDIA_SOUND_H__
+
+
+/**
+ * @file sound.h
+ * @brief Sound player and recorder device framework.
+ */
+
+#include <pj/pool.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMED_SND Sound device abstraction.
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/** Opaque data type for audio stream. */
+typedef struct pj_snd_stream pj_snd_stream;
+
+/**
+ * Device information structure returned by #pj_snd_get_dev_info.
+ */
+typedef struct pj_snd_dev_info
+{
+    char	name[64];	        /**< Device name.		    */
+    unsigned	input_count;	        /**< Max number of input channels.  */
+    unsigned	output_count;	        /**< Max number of output channels. */
+    unsigned	default_samples_per_sec;/**< Default sampling rate.	    */
+} pj_snd_dev_info;
+
+/**
+ * Sound device parameter, to be specified when calling #pj_snd_open_recorder
+ * or #pj_snd_open_player.
+ */
+typedef struct pj_snd_stream_info
+{
+    unsigned samples_per_sec;		/* Sampling rate.	    */
+    unsigned bits_per_sample;		/* No of bits per sample.   */
+    unsigned samples_per_frame;		/* No of samples per frame. */
+    unsigned bytes_per_frame;		/* No of bytes per frame.   */
+    unsigned frames_per_packet;		/* No of frames per packet. */
+} pj_snd_stream_info;
+
+/** 
+ * This callback is called by player stream when it needs additional data
+ * to be played by the device. Application must fill in the whole of output 
+ * buffer with sound samples.
+ *
+ * @param user_data	User data associated with the stream.
+ * @param timestamp	Timestamp, in samples.
+ * @param output	Buffer to be filled out by application.
+ * @param size		The size requested in bytes, which will be equal to
+ *			the size of one whole packet.
+ *
+ * @return		Non-zero to stop the stream.
+ */
+typedef pj_status_t (*pj_snd_play_cb)(/* in */   void *user_data,
+				      /* in */   pj_uint32_t timestamp,
+				      /* out */  void *output,
+				      /* out */  unsigned size);
+
+/**
+ * This callback is called by recorder stream when it has captured the whole
+ * packet worth of audio samples.
+ *
+ * @param user_data	User data associated with the stream.
+ * @param timestamp	Timestamp, in samples.
+ * @param output	Buffer containing the captured audio samples.
+ * @param size		The size of the data in the buffer, in bytes.
+ *
+ * @return		Non-zero to stop the stream.
+ */
+typedef pj_status_t (*pj_snd_rec_cb)(/* in */   void *user_data,
+				     /* in */   pj_uint32_t timestamp,
+				     /* in */   const void *input,
+				     /* in*/    unsigned size);
+
+/**
+ * Init the sound library.
+ *
+ * @param factory	The sound factory.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_init(pj_pool_factory *factory);
+
+
+/**
+ * Get the number of devices detected by the library.
+ *
+ * @return		Number of devices.
+ */
+PJ_DECL(int) pj_snd_get_dev_count(void);
+
+
+/**
+ * Get device info.
+ *
+ * @param index		The index of the device, which should be in the range
+ *			from zero to #pj_snd_get_dev_count - 1.
+ */
+PJ_DECL(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index);
+
+
+/**
+ * Create a new audio stream for audio capture purpose.
+ *
+ * @param index		Device index, or -1 to let the library choose the first
+ *			available device, or -2 to use NULL device.
+ * @param param		Stream parameters.
+ * @param rec_cb	Callback to handle captured audio samples.
+ * @param user_data	User data to be associated with the stream.
+ *
+ * @return		Audio stream, or NULL if failed.
+ */
+PJ_DECL(pj_snd_stream*) pj_snd_open_recorder(int index,
+					     const pj_snd_stream_info *param,
+					     pj_snd_rec_cb rec_cb,
+					     void *user_data);
+
+/**
+ * Create a new audio stream for playing audio samples.
+ *
+ * @param index		Device index, or -1 to let the library choose the first
+ *			available device, or -2 to use NULL device.
+ * @param param		Stream parameters.
+ * @param play_cb	Callback to supply audio samples.
+ * @param user_data	User data to be associated with the stream.
+ *
+ * @return		Audio stream, or NULL if failed.
+ */
+PJ_DECL(pj_snd_stream*) pj_snd_open_player(int index,
+					   const pj_snd_stream_info *param,
+					   pj_snd_play_cb play_cb,
+					   void *user_data);
+
+/**
+ * Start the stream.
+ *
+ * @param stream	The recorder or player stream.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream);
+
+/**
+ * Stop the stream.
+ *
+ * @param stream	The recorder or player stream.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream);
+
+/**
+ * Destroy the stream.
+ *
+ * @param stream	The recorder of player stream.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream);
+
+/**
+ * Deinitialize sound library.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_deinit(void);
+
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_SOUND_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/stream.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/stream.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/stream.c	(revision 1753)
@@ -0,0 +1,649 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/stream.c 14    6/24/05 11:14p Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#include <pjmedia/stream.h>
+#include <pjmedia/rtp.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/jbuf.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/string.h>	    /* memcpy() */
+#include <pj/pool.h>
+#include <stdlib.h>
+
+#define THISFILE    "stream.c"
+#define ERRLEVEL    1
+
+#define PJ_MAX_FRAME_DURATION_MS    200
+#define PJ_MAX_BUFFER_SIZE_MS	    2000
+#define PJ_MAX_MTU		    1500
+
+struct jb_frame
+{
+    unsigned size;
+    void    *buf;
+};
+
+#define pj_fifobuf_alloc(fifo,size)	malloc(size)
+#define pj_fifobuf_unalloc(fifo,buf)	free(buf)
+#define pj_fifobuf_free(fifo, buf)	free(buf)
+
+enum stream_state
+{
+    STREAM_STOPPED,
+    STREAM_STARTED,
+};
+
+struct pj_media_stream_t
+{
+    pj_media_dir_t	    dir;
+    int			    pt;
+    int			    state;
+    pj_media_stream_stat    stat;
+    pj_media_stream_t	   *peer;
+    pj_snd_stream_info	    snd_info;
+    pj_snd_stream	   *snd_stream;
+    pj_mutex_t		   *mutex;
+    unsigned		    in_pkt_size;
+    void		   *in_pkt;
+    unsigned		    out_pkt_size;
+    void		   *out_pkt;
+    unsigned		    pcm_buf_size;
+    void		   *pcm_buf;
+    //pj_fifobuf_t	    fifobuf;
+    pj_codec_mgr	   *codec_mgr;
+    pj_codec		   *codec;
+    pj_rtp_session	    rtp;
+    pj_rtcp_session	   *rtcp;
+    pj_jitter_buffer	   *jb;
+    pj_sock_t		    rtp_sock;
+    pj_sock_t		    rtcp_sock;
+    pj_sockaddr_in	    dst_addr;
+    pj_thread_t		   *transport_thread;
+    int			    thread_quit_flag;
+};
+
+
+static pj_status_t play_callback(/* in */   void *user_data,
+				 /* in */   pj_uint32_t timestamp,
+				 /* out */  void *frame,
+				 /*inout*/  unsigned size)
+{
+    pj_media_stream_t *channel = user_data;
+    struct jb_frame *jb_frame;
+    void *p;
+    pj_uint32_t extseq;
+    pj_status_t status;
+    struct pj_audio_frame frame_in, frame_out;
+
+    PJ_UNUSED_ARG(timestamp)
+
+    /* Lock mutex */
+    pj_mutex_lock (channel->mutex);
+
+    if (!channel->codec) {
+	pj_mutex_unlock (channel->mutex);
+	return -1;
+    }
+
+    /* Get frame from jitter buffer. */
+    status = pj_jb_get (channel->jb, &extseq, &p);
+    jb_frame = p;
+    if (status != 0 || jb_frame == NULL) {
+	pj_memset(frame, 0, size);
+	pj_mutex_unlock(channel->mutex);
+	return 0;
+    }
+
+    /* Decode */
+    frame_in.buf = jb_frame->buf;
+    frame_in.size = jb_frame->size;
+    frame_in.type = PJ_AUDIO_FRAME_AUDIO;  /* ignored */
+    frame_out.buf = channel->pcm_buf;
+    status = channel->codec->op->decode (channel->codec, &frame_in,
+					 channel->pcm_buf_size, &frame_out);
+    if (status != 0) {
+	PJ_LOG(3, (THISFILE, "decode() has return error status %d", 
+			  status));
+
+	pj_memset(frame, 0, size);
+	pj_fifobuf_free (&channel->fifobuf, jb_frame);
+	pj_mutex_unlock(channel->mutex);
+	return 0;
+    }
+
+    /* Put in sound buffer. */
+    if (frame_out.size > size) {
+	PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes", 
+			  frame_out.size - size));
+	frame_out.size = size;
+    }
+
+    pj_memcpy(frame, frame_out.buf, size);
+
+    pj_fifobuf_free (&channel->fifobuf, jb_frame);
+    pj_mutex_unlock(channel->mutex);
+    return 0;
+}
+
+static pj_status_t rec_callback( /* in */ void *user_data,
+			         /* in */ pj_uint32_t timestamp,
+			         /* in */ const void *frame,
+			         /* in */ unsigned size)
+{
+    pj_media_stream_t *channel = user_data;
+    pj_status_t status = 0;
+    struct pj_audio_frame frame_in, frame_out;
+    int ts_len;
+    void *rtphdr;
+    int rtphdrlen;
+    int sent;
+#if 0
+    static FILE *fhnd = NULL;
+#endif
+
+    PJ_UNUSED_ARG(timestamp)
+
+    /* Start locking channel mutex */
+    pj_mutex_lock (channel->mutex);
+
+    if (!channel->codec) {
+	status = -1;
+	goto on_return;
+    }
+
+    /* Encode. */
+    frame_in.type = PJ_MEDIA_TYPE_AUDIO;
+    frame_in.buf = (void*)frame;
+    frame_in.size = size;
+    frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr);
+    status = channel->codec->op->encode (channel->codec, &frame_in, 
+					 channel->out_pkt_size - sizeof(pj_rtp_hdr), 
+					 &frame_out);
+    if (status != 0) {
+	PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d", 
+			     status));
+	goto on_return;
+    }
+
+    /* Encapsulate. */
+    ts_len = size / (channel->snd_info.bits_per_sample / 8);
+    status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0, 
+				frame_out.size, ts_len, 
+				(const void**)&rtphdr, &rtphdrlen);
+    if (status != 0) {
+	PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d", 
+			    status));
+	goto on_return;
+    }
+
+    if (rtphdrlen != sizeof(pj_rtp_hdr)) {
+	/* We don't support RTP with extended header yet. */
+	PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER);
+	PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission"));
+	goto on_return;
+    }
+
+    pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr));
+
+    /* Send. */
+    sent = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 0, 
+			   &channel->dst_addr, sizeof(channel->dst_addr));
+    if (sent != (int)frame_out.size + (int)sizeof(pj_rtp_hdr))  {
+	pj_perror(THISFILE, "Error sending RTP packet to %s:%d", 
+		  pj_sockaddr_get_str_addr(&channel->dst_addr),
+		  pj_sockaddr_get_port(&channel->dst_addr));
+	goto on_return;
+    }
+
+    /* Update stat */
+    channel->stat.pkt_tx++;
+    channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr);
+
+#if 0
+    if (fhnd == NULL) {
+	fhnd = fopen("RTP.DAT", "wb");
+	if (fhnd) {
+	    fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd);
+	    fclose(fhnd);
+	}
+    }
+#endif
+
+on_return:
+    pj_mutex_unlock (channel->mutex);
+    return status;
+}
+
+
+static void* PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg)
+{
+    pj_media_stream_t *channel = arg;
+
+    while (!channel->thread_quit_flag) {
+	int len, size;
+	const pj_rtp_hdr *hdr;
+	const void *payload;
+	unsigned payloadlen;
+	int status;
+	struct jb_frame *jb_frame;
+
+	/* Wait for packet. */
+	fd_set fds;
+	pj_time_val timeout;
+
+	PJ_FD_ZERO (&fds);
+	PJ_FD_SET (channel->rtp_sock, &fds);
+	timeout.sec = 0;
+	timeout.msec = 100;
+
+	/* Wait with timeout. */
+	status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout);
+	if (status != 1)
+	    continue;
+
+	/* Get packet from socket. */
+	len = pj_sock_recv (channel->rtp_sock, channel->in_pkt, channel->in_pkt_size, 0);
+	if (len < 1) {
+	    if (pj_getlasterror() == PJ_ECONNRESET) {
+		/* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET
+		   when the sending side receives ICMP port unreachable.
+		 */
+		continue;
+	    }
+	    pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len);
+	    pj_thread_sleep(1);
+	    continue;
+	}
+
+	if (channel->state != STREAM_STARTED)
+	    continue;
+
+	if (channel->thread_quit_flag)
+	    break;
+
+	/* Start locking the channel. */
+	pj_mutex_lock (channel->mutex);
+
+	/* Update RTP and RTCP session. */
+	status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen);
+	if (status != 0) {
+	    pj_mutex_unlock (channel->mutex);
+	    PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status));
+	    continue;
+	}
+	status = pj_rtp_session_update (&channel->rtp, hdr);
+	if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) {
+	    pj_mutex_unlock (channel->mutex);
+	    PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status));
+	    continue;
+	}
+	pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts));
+
+	/* Update stat */
+	channel->stat.pkt_rx++;
+	channel->stat.oct_rx += len;
+
+	/* Copy to FIFO buffer. */
+	size = payloadlen+sizeof(struct jb_frame);
+	jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size);
+	if (jb_frame == NULL) {
+	    pj_mutex_unlock (channel->mutex);
+	    PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size));
+	    continue;
+	}
+
+	/* Copy the payload */
+	jb_frame->size = payloadlen;
+	jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame);
+	pj_memcpy (jb_frame->buf, payload, payloadlen);
+
+	/* Put to jitter buffer. */
+	status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame);
+	if (status != 0) {
+	    pj_fifobuf_unalloc (&channel->fifobuf, jb_frame);
+	    pj_mutex_unlock (channel->mutex);
+	    PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status));
+	    continue;
+	}
+
+	pj_mutex_unlock (channel->mutex);
+    }
+
+    return NULL;
+}
+
+static void init_snd_param_from_codec_attr (pj_snd_stream_info *param,
+					    const pj_codec_attr *attr)
+{
+    param->bits_per_sample = attr->pcm_bits_per_sample;
+    param->bytes_per_frame = 2;
+    param->frames_per_packet = attr->sample_rate * attr->ptime / 1000;
+    param->samples_per_frame = 1;
+    param->samples_per_sec = attr->sample_rate;
+}
+
+static pj_media_stream_t *create_channel ( pj_pool_t *pool,
+					   pj_media_dir_t dir,
+					   pj_media_stream_t *peer,
+					   pj_codec_id *codec_id,
+					   pj_media_stream_create_param *param)
+{
+    pj_media_stream_t *channel;
+    pj_codec_attr codec_attr;
+    void *ptr;
+    unsigned size;
+    int status;
+    
+    /* Allocate memory for channel descriptor */
+    size = sizeof(pj_media_stream_t);
+    channel = pj_pool_calloc(pool, 1, size);
+    if (!channel) {
+	PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor", 
+			 size));
+	return NULL;
+    }
+
+    channel->dir = dir;
+    channel->pt = codec_id->pt;
+    channel->peer = peer;
+    channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr);
+    channel->rtp_sock = param->rtp_sock;
+    channel->rtcp_sock = param->rtcp_sock;
+    channel->dst_addr = *param->remote_addr;
+    channel->state = STREAM_STOPPED;
+
+    /* Create mutex for the channel. */
+    channel->mutex = pj_mutex_create(pool, NULL, PJ_MUTEX_SIMPLE);
+    if (channel->mutex == NULL)
+	goto err_cleanup;
+
+    /* Create and initialize codec, only if peer is not present.
+       We only use one codec instance for both encoder and decoder.
+     */
+    if (peer && peer->codec) {
+	channel->codec = peer->codec;
+	status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, 
+							   &codec_attr);
+	if (status != 0) {
+	    goto err_cleanup;
+	}
+
+    } else {
+	channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id);
+	if (channel->codec == NULL) {
+	    goto err_cleanup;
+	}
+
+	status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, 
+							   &codec_attr);
+	if (status != 0) {
+	    goto err_cleanup;
+	}
+
+	codec_attr.pt = codec_id->pt;
+	status = channel->codec->op->open(channel->codec, &codec_attr);
+	if (status != 0) {
+	    goto err_cleanup;
+	}
+    }
+
+    /* Allocate buffer for incoming packet. */
+    channel->in_pkt_size = PJ_MAX_MTU;
+    channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size);
+    if (!channel->in_pkt) {
+	PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer", 
+			  channel->in_pkt_size));
+	goto err_cleanup;
+    }
+
+    /* Allocate buffer for outgoing packet. */
+    channel->out_pkt_size = sizeof(pj_rtp_hdr) + 
+			    codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000;
+    if (channel->out_pkt_size > PJ_MAX_MTU)
+	channel->out_pkt_size = PJ_MAX_MTU;
+    channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size);
+    if (!channel->out_pkt) {
+	PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer", 
+			  channel->out_pkt_size));
+	goto err_cleanup;
+    }
+
+    /* Allocate buffer for decoding to PCM */
+    channel->pcm_buf_size = codec_attr.sample_rate * 
+			    codec_attr.pcm_bits_per_sample / 8 *
+			    PJ_MAX_FRAME_DURATION_MS / 1000;
+    channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size);
+    if (!channel->pcm_buf) {
+	PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer", 
+			  channel->pcm_buf_size));
+	goto err_cleanup;
+    }
+
+    /* Allocate buffer for frames put in jitter buffer. */
+    size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000;
+    ptr = pj_pool_alloc(pool, size);
+    if (!ptr) {
+	PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer", 
+			  channel->pcm_buf_size));
+	goto err_cleanup;
+    }
+    //pj_fifobuf_init (&channel->fifobuf, ptr, size);
+
+    /* Create and initialize sound device */
+    init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr);
+
+    if (dir == PJ_MEDIA_DIR_ENCODING)
+	channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info, 
+						   &rec_callback, channel);
+    else
+	channel->snd_stream = pj_snd_open_player(-1, &channel->snd_info, 
+						 &play_callback, channel);
+
+    if (!channel->snd_stream)
+	goto err_cleanup;
+
+    /* Create RTP and RTCP sessions. */
+    if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) {
+	PJ_LOG(1, (THISFILE, "RTP session initialization error"));
+	goto err_cleanup;
+    }
+
+    /* For decoder, create RTCP session, jitter buffer, and transport thread. */
+    if (dir == PJ_MEDIA_DIR_DECODING) {
+	channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session));
+	if (!channel->rtcp) {
+	    PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session"));
+	    goto err_cleanup;
+	}
+
+	pj_rtcp_init(channel->rtcp, param->ssrc);
+
+	channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer));
+	if (!channel->jb) {
+	    PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor"));
+	    goto err_cleanup;
+	}
+	if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) {
+	    PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer"));
+	    goto err_cleanup;
+	}
+
+	channel->transport_thread = pj_thread_create(pool, "decode", 
+						     &stream_decoder_transport_thread, channel,
+						     0, NULL, 0);
+	if (!channel->transport_thread) {
+	    pj_perror(THISFILE, "Unable to create transport thread");
+	    goto err_cleanup;
+	}
+    }
+
+    /* Done. */
+    return channel;
+
+err_cleanup:
+    pj_media_stream_destroy(channel);
+    return NULL;
+}
+
+
+PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
+					    pj_media_stream_t **enc_stream,
+					    pj_media_stream_t **dec_stream,
+					    pj_media_stream_create_param *param)
+{
+    *dec_stream = *enc_stream = NULL;
+
+    if (param->dir & PJ_MEDIA_DIR_DECODING) {
+	*dec_stream = 
+	    create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param);
+	if (!*dec_stream)
+	    return -1;
+    }
+
+    if (param->dir & PJ_MEDIA_DIR_ENCODING) {
+	*enc_stream = 
+	    create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param);
+	if (!*enc_stream) {
+	    if (*dec_stream) {
+		pj_media_stream_destroy(*dec_stream);
+		*dec_stream = NULL;
+	    }
+	    return -1;
+	}
+
+	if (*dec_stream) {
+	    (*dec_stream)->peer = *enc_stream;
+	}
+    }
+
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel)
+{
+    pj_status_t status;
+
+    status = pj_snd_stream_start(channel->snd_stream);
+
+    if (status == 0)
+	channel->state = STREAM_STARTED;
+    return status;
+}
+
+PJ_DEF(pj_status_t)  pj_media_stream_get_stat (const pj_media_stream_t *stream,
+					       pj_media_stream_stat *stat)
+{
+    if (stream->dir == PJ_MEDIA_DIR_ENCODING) {
+	pj_memcpy (stat, &stream->stat, sizeof(*stat));
+    } else {
+	pj_rtcp_pkt *rtcp_pkt;
+	int len;
+
+	pj_memset (stat, 0, sizeof(*stat));
+	pj_assert (stream->rtcp != 0);
+	pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len);
+
+	stat->pkt_rx = stream->stat.pkt_rx;
+	stat->oct_rx = stream->stat.oct_rx;
+
+	PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
+	stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8;
+	stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) +
+			 (rtcp_pkt->rr.total_lost_1 << 8) +
+			 rtcp_pkt->rr.total_lost_0;
+    }
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel)
+{
+    PJ_UNUSED_ARG(channel)
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel)
+{
+    PJ_UNUSED_ARG(channel)
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel)
+{
+    channel->thread_quit_flag = 1;
+
+    pj_mutex_lock (channel->mutex);
+    if (channel->peer)
+	pj_mutex_lock (channel->peer->mutex);
+
+    if (channel->jb) {
+	/* No need to deinitialize jitter buffer. */
+    }
+    if (channel->transport_thread) {
+	pj_thread_join(channel->transport_thread);
+	pj_thread_destroy(channel->transport_thread);
+	channel->transport_thread = NULL;
+    }
+    if (channel->snd_stream != NULL) {
+	pj_mutex_unlock (channel->mutex);
+	pj_snd_stream_stop(channel->snd_stream);
+	pj_mutex_lock (channel->mutex);
+	pj_snd_stream_close(channel->snd_stream);
+	channel->snd_stream = NULL;
+    }
+    if (channel->codec) {
+	channel->codec->op->close(channel->codec);
+	pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec);
+	channel->codec = NULL;
+    }
+    if (channel->peer) {
+	pj_media_stream_t *peer = channel->peer;
+	peer->peer = NULL;
+	peer->codec = NULL;
+	peer->thread_quit_flag = 1;
+	if (peer->transport_thread) {
+	    pj_mutex_unlock (peer->mutex);
+	    pj_thread_join(peer->transport_thread);
+	    pj_mutex_lock (peer->mutex);
+	    pj_thread_destroy(peer->transport_thread);
+	    peer->transport_thread = NULL;
+	}
+	if (peer->snd_stream) {
+	    pj_mutex_unlock (peer->mutex);
+	    pj_snd_stream_stop(peer->snd_stream);
+	    pj_mutex_lock (peer->mutex);
+	    pj_snd_stream_close(peer->snd_stream);
+	    peer->snd_stream = NULL;
+	}
+    }
+
+    channel->state = STREAM_STOPPED;
+
+    if (channel->peer)
+	pj_mutex_unlock (channel->peer->mutex);
+    pj_mutex_unlock(channel->mutex);
+    pj_mutex_destroy(channel->mutex);
+
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/stream.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/stream.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/pjmedia/stream.h	(revision 1753)
@@ -0,0 +1,104 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/stream.h 6     8/24/05 10:30a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+
+#ifndef __PJMEDIA_STREAM_H__
+#define __PJMEDIA_STREAM_H__
+
+
+/**
+ * @file stream.h
+ * @brief Stream of media.
+ */
+
+#include <pjmedia/sound.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/mediamgr.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_SES Media session
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+typedef struct pj_media_stream_t pj_media_stream_t;
+
+/** Parameter for creating channel. */
+typedef struct pj_media_stream_create_param
+{
+    /** Codec ID, must NOT be NULL. */
+    pj_codec_id		 *codec_id;
+
+    /** Media manager, must NOT be NULL. */
+    pj_med_mgr_t	 *mediamgr;
+
+    /** Direction: IN_OUT, or IN only, or OUT only. */
+    pj_media_dir_t	  dir;
+
+    /** RTP socket. */
+    pj_sock_t		 rtp_sock;
+
+    /** RTCP socket. */
+    pj_sock_t		 rtcp_sock;
+
+    /** Address of remote */
+    pj_sockaddr_in	 *remote_addr;
+
+    /** RTP SSRC */
+    pj_uint32_t		  ssrc;
+
+    /** Jitter buffer parameters. */
+    int			  jb_min, jb_max, jb_maxcnt;
+
+} pj_media_stream_create_param;
+
+typedef struct pj_media_stream_stat
+{
+    pj_uint32_t pkt_tx, pkt_rx;	/* packets transmitted/received */
+    pj_uint32_t oct_tx, oct_rx;	/* octets transmitted/received */
+    pj_uint32_t jitter;		/* receive jitter in ms */
+    pj_uint32_t pkt_lost;	/* total packet lost count */
+} pj_media_stream_stat;
+
+PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
+					     pj_media_stream_t **enc_stream,
+					     pj_media_stream_t **dec_stream,
+					     pj_media_stream_create_param *param);
+PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream);
+PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
+					       pj_media_stream_stat *stat);
+PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream);
+PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream);
+PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_STREAM_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/audio_tool.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/audio_tool.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/audio_tool.c	(revision 1753)
@@ -0,0 +1,413 @@
+/* $Header: /pjproject/pjmedia/src/test/audio_tool.c 11    6/24/05 11:17p Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjmedia.h>
+#include <pjlib.h>
+#include <stdio.h>
+
+#define THIS_FILE	"audio_tool.c"
+
+static pj_caching_pool caching_pool;
+static pj_pool_factory *pf;
+static FILE *fhnd;
+static pj_med_mgr_t *mm;
+static pj_codec *codec;
+static pj_codec_attr cattr;
+
+
+#define WRITE_ORIGINAL_PCM 0
+#if WRITE_ORIGINAL_PCM
+static FILE *fhnd_pcm;
+#endif
+
+static char talker_sdp[] = 
+    "v=0\r\n"
+    "o=- 0 0 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "c=IN IP4 127.0.0.1\r\n"
+    "t=0 0\r\n"
+    "m=audio 4002 RTP/AVP 0\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=sendonly\r\n";
+static char listener_sdp[] = 
+    "v=0\r\n"
+    "o=- 0 0 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "c=IN IP4 127.0.0.1\r\n"
+    "t=0 0\r\n"
+    "m=audio 4000 RTP/AVP 0\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=recvonly\r\n";
+
+static pj_status_t play_callback(/* in */   void *user_data,
+				 /* in */   pj_uint32_t timestamp,
+				 /* out */  void *frame,
+				 /* out */  unsigned size)
+{
+    char pkt[160];
+    struct pj_audio_frame in, out;
+    int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
+
+    if (fread(pkt, frmsz, 1, fhnd) != 1) {
+	puts("EOF");
+	return -1;
+    } else {
+	in.type = PJ_AUDIO_FRAME_AUDIO;
+	in.buf = pkt;
+	in.size = frmsz;
+	out.buf = frame;
+	if (codec->op->decode (codec, &in, size, &out) != 0)
+	    return -1;
+
+	size = out.size;
+	return 0;
+    }
+}
+
+static pj_status_t rec_callback( /* in */   void *user_data,
+			         /* in */   pj_uint32_t timestamp,
+			         /* in */   const void *frame,
+			         /* in*/    unsigned size)
+{
+    char pkt[160];
+    struct pj_audio_frame in, out;
+    //int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
+
+#if WRITE_ORIGINAL_PCM
+    fwrite(frame, size, 1, fhnd_pcm);
+#endif
+
+    in.type = PJ_AUDIO_FRAME_AUDIO;
+    in.buf = (void*)frame;
+    in.size = size;
+    out.buf = pkt;
+
+    if (codec->op->encode(codec, &in, sizeof(pkt), &out) != 0)
+	return -1;
+
+    if (fwrite(pkt, out.size, 1, fhnd) != 1)
+	return -1;
+    return 0;
+}
+
+static pj_status_t init()
+{
+    pj_codec_mgr *cm;
+    pj_codec_id id;
+    int i;
+
+    pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
+    pf = &caching_pool.factory;
+
+    if (pj_snd_init(&caching_pool.factory))
+	return -1;
+
+    PJ_LOG(3,(THIS_FILE, "Dumping audio devices:"));
+    for (i=0; i<pj_snd_get_dev_count(); ++i) {
+	const pj_snd_dev_info *info;
+	info = pj_snd_get_dev_info(i);
+	PJ_LOG(3,(THIS_FILE, "  %d: %s\t(%d in, %d out", 
+			     i, info->name, 
+			     info->input_count, info->output_count));
+    }
+
+    mm = pj_med_mgr_create (&caching_pool.factory);
+    cm = pj_med_mgr_get_codec_mgr (mm);
+
+    id.type = PJ_MEDIA_TYPE_AUDIO;
+    id.pt = 0;
+    id.encoding_name = pj_str("PCMU");
+    id.sample_rate = 8000;
+
+    codec = pj_codec_mgr_alloc_codec (cm, &id);
+    codec->op->default_attr(codec, &cattr);
+    codec->op->open(codec, &cattr);
+    return 0;
+}
+
+static pj_status_t deinit()
+{
+    pj_codec_mgr *cm;
+    cm = pj_med_mgr_get_codec_mgr (mm);
+    codec->op->close(codec);
+    pj_codec_mgr_dealloc_codec (cm, codec);
+    pj_med_mgr_destroy (mm);
+    pj_caching_pool_destroy(&caching_pool);
+    return 0;
+}
+
+static pj_status_t record_file (const char *filename)
+{
+    pj_snd_stream *stream;
+    pj_snd_stream_info info;
+    int status;
+    char s[10];
+
+    printf("Recording to file %s...\n", filename);
+
+    fhnd = fopen(filename, "wb");
+    if (!fhnd)
+	return -1;
+
+#if WRITE_ORIGINAL_PCM
+    fhnd_pcm = fopen("ORIGINAL.PCM", "wb");
+    if (!fhnd_pcm)
+	return -1;
+#endif
+
+    pj_memset(&info, 0, sizeof(info));
+    info.bits_per_sample = 16;
+    info.bytes_per_frame = 2;
+    info.frames_per_packet = 160;
+    info.samples_per_frame = 1;
+    info.samples_per_sec = 8000;
+
+    stream = pj_snd_open_recorder(-1, &info, &rec_callback, NULL);
+    if (!stream)
+	return -1;
+
+    status = pj_snd_stream_start(stream);
+    if (status != 0)
+	goto on_error;
+
+    puts("Press <ENTER> to exit recording");
+    fgets(s, sizeof(s), stdin);
+
+    pj_snd_stream_stop(stream);
+    pj_snd_stream_close(stream);
+
+#if WRITE_ORIGINAL_PCM
+    fclose(fhnd_pcm);
+#endif
+    fclose(fhnd);
+    return 0;
+
+on_error:
+    pj_snd_stream_stop(stream);
+    pj_snd_stream_close(stream);
+    return -1;
+}
+
+
+static pj_status_t play_file (const char *filename)
+{
+    pj_snd_stream *stream;
+    pj_snd_stream_info info;
+    int status;
+    char s[10];
+
+    printf("Playing file %s...\n", filename);
+
+    fhnd = fopen(filename, "rb");
+    if (!fhnd)
+	return -1;
+
+    pj_memset(&info, 0, sizeof(info));
+    info.bits_per_sample = 16;
+    info.bytes_per_frame = 2;
+    info.frames_per_packet = 160;
+    info.samples_per_frame = 1;
+    info.samples_per_sec = 8000;
+
+    stream = pj_snd_open_player(-1, &info, &play_callback, NULL);
+    if (!stream)
+	return -1;
+
+    status = pj_snd_stream_start(stream);
+    if (status != 0)
+	goto on_error;
+
+    puts("Press <ENTER> to exit playing");
+    fgets(s, sizeof(s), stdin);
+
+    pj_snd_stream_stop(stream);
+    pj_snd_stream_close(stream);
+
+    fclose(fhnd);
+    return 0;
+
+on_error:
+    pj_snd_stream_stop(stream);
+    pj_snd_stream_close(stream);
+    return -1;
+}
+
+static int create_ses_by_remote_sdp(int local_port, char *sdp)
+{
+    pj_media_session_t *ses = NULL;
+    pjsdp_session_desc *sdp_ses;
+    pj_media_sock_info skinfo;
+    pj_pool_t *pool;
+    char s[4];
+    const pj_media_stream_info *info[2];
+    int i, count;
+
+    pool = pj_pool_create(pf, "sdp", 1024, 0, NULL);
+    if (!pool) {
+	PJ_LOG(1,(THIS_FILE, "Unable to create pool"));
+	return -1;
+    }
+
+    pj_memset(&skinfo, 0, sizeof(skinfo));
+    skinfo.rtp_sock = skinfo.rtcp_sock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+    if (skinfo.rtp_sock == PJ_INVALID_SOCKET) {
+	PJ_LOG(1,(THIS_FILE, "Unable to create socket"));
+	goto on_error;
+    }
+
+    pj_sockaddr_init2(&skinfo.rtp_addr_name, "0.0.0.0", local_port);
+    if (pj_sock_bind(skinfo.rtp_sock, (struct pj_sockaddr*)&skinfo.rtp_addr_name, sizeof(pj_sockaddr_in)) != 0) {
+	PJ_LOG(1,(THIS_FILE, "Unable to bind socket"));
+	goto on_error;
+    }
+
+    sdp_ses = pjsdp_parse(sdp, strlen(sdp), pool);
+    if (!sdp_ses) {
+	PJ_LOG(1,(THIS_FILE, "Error parsing SDP"));
+	goto on_error;
+    }
+
+    ses = pj_media_session_create_from_sdp(mm, sdp_ses, &skinfo);
+    if (!ses) {
+	PJ_LOG(1,(THIS_FILE, "Unable to create session from SDP"));
+	goto on_error;
+    }
+
+    if (pj_media_session_activate(ses) != 0) {
+	PJ_LOG(1,(THIS_FILE, "Error activating session"));
+	goto on_error;
+    }
+
+    count = pj_media_session_enum_streams(ses, 2, info);
+    printf("\nDumping streams: \n");
+    for (i=0; i<count; ++i) {
+	const char *dir;
+	char *local_ip;
+
+	switch (info[i]->dir) {
+	case PJ_MEDIA_DIR_NONE:
+	    dir = "- NONE -"; break;
+	case PJ_MEDIA_DIR_ENCODING:
+	    dir = "SENDONLY"; break;
+	case PJ_MEDIA_DIR_DECODING:
+	    dir = "RECVONLY"; break;
+	case PJ_MEDIA_DIR_ENCODING_DECODING:
+	    dir = "SENDRECV"; break;
+	default:
+	    dir = "?UNKNOWN"; break;
+	}
+
+	local_ip = pj_sockaddr_get_str_addr(&info[i]->sock_info.rtp_addr_name);
+
+	printf("  Stream %d: %.*s %s local=%s:%d remote=%.*s:%d\n",
+	       i, info[i]->type.slen, info[i]->type.ptr,
+	       dir, 
+	       local_ip, pj_sockaddr_get_port(&info[i]->sock_info.rtp_addr_name),
+	       info[i]->rem_addr.slen, info[i]->rem_addr.ptr, info[i]->rem_port);
+    }
+
+    puts("Press <ENTER> to quit");
+    fgets(s, sizeof(s), stdin);
+
+    pj_media_session_destroy(ses);
+    pj_sock_close(skinfo.rtp_sock);
+    pj_pool_release(pool);
+
+    return 0;
+
+on_error:
+    if (ses)
+	pj_media_session_destroy(ses);
+    if (skinfo.rtp_sock != PJ_INVALID_SOCKET)
+	pj_sock_close(skinfo.rtp_sock);
+    if (pool)
+	pj_pool_release(pool);
+    return -1;
+}
+
+#if WRITE_ORIGINAL_PCM
+static pj_status_t convert(const char *src, const char *dst)
+{
+    char pcm[320];
+    char frame[160];
+    struct pj_audio_frame in, out;
+
+    fhnd_pcm = fopen(src, "rb");
+    if (!fhnd_pcm)
+	return -1;
+    fhnd = fopen(dst, "wb");
+    if (!fhnd)
+	return -1;
+
+    while (fread(pcm, 320, 1, fhnd_pcm) == 1) {
+
+	in.type = PJ_AUDIO_FRAME_AUDIO;
+	in.buf = pcm;
+	in.size = 320;
+	out.buf = frame;
+
+	if (codec->op->encode(codec, &in, 160, &out) != 0)
+	    break;
+
+	if (fwrite(frame, out.size, 1, fhnd) != 1)
+	    break;
+
+    }
+
+    fclose(fhnd);
+    fclose(fhnd_pcm);
+    return 0;
+}
+#endif
+
+static void usage(const char *exe)
+{
+    printf("Usage: %s <command> <file>\n", exe);
+    puts("where:");
+    puts("  <command>     play|record|send|recv");
+}
+
+int main(int argc, char *argv[])
+{
+    if (argc < 2) {
+	usage(argv[0]);
+	return 1;
+    }
+
+    pj_init();
+
+    init();
+
+    if (stricmp(argv[1], "record")==0) {
+	record_file("FILE.PCM");
+    } else if (stricmp(argv[1], "play")==0) {
+	play_file("FILE.PCM");
+    } else if (stricmp(argv[1], "send")==0) {
+	create_ses_by_remote_sdp(4002, listener_sdp);
+    } else if (stricmp(argv[1], "recv")==0) {
+	create_ses_by_remote_sdp(4000, talker_sdp);
+    } else {
+	usage(argv[0]);
+    }
+    deinit();
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/jbuf_test.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/jbuf_test.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/jbuf_test.c	(revision 1753)
@@ -0,0 +1,158 @@
+/* $Header: /pjproject/pjmedia/src/test/jbuf_test.c 9     6/24/05 11:18p Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <pjmedia/jbuf.h>
+#include <pj/pool.h>
+
+#define JB_MIN	    1
+#define JB_MAX	    8
+#define JB_BUF_SIZE 10
+
+#define REPORT
+//#define PRINT_COMMENT
+
+int jbuf_main(pj_pool_factory *pf)
+{
+    pj_jitter_buffer jb;
+    FILE *input = fopen("JBTEST.DAT", "rt");
+    unsigned lastseq;
+    void *data = "Hello world";
+    char line[1024], *p;
+    int lastget = 0, lastput = 0;
+    pj_pool_t *pool;
+
+    pj_init();
+    pool = pj_pool_create(pf, "JBPOOL", 256*16, 256*16, NULL);
+
+    pj_jb_init(&jb, pool, JB_MIN, JB_MAX, JB_BUF_SIZE);
+
+    lastseq = 1;
+
+    while ((p=fgets(line, sizeof(line), input)) != NULL) {
+
+	while (*p && isspace(*p))
+	    ++p;
+
+	if (!*p)
+	    continue;
+
+	if (*p == '#') {
+#ifdef PRINT_COMMENT
+	    printf("\n%s", p);
+#endif
+	    continue;
+	}
+
+	pj_jb_reset(&jb);
+#ifdef REPORT
+	printf( "Initial\t%c   size=%d  prefetch=%d level=%d\n", 
+	        ' ', jb.lst.count, jb.prefetch, jb.level);
+#endif
+
+	while (*p) {
+	    int c;
+	    unsigned seq = 0;
+	    void *thedata;
+	    int status = 1234;
+	    
+	    c = *p++;
+	    if (isspace(c))
+		continue;
+	    
+	    if (c == '/') {
+		char *end;
+
+		printf("/*");
+
+		do {
+		    putchar(*++p);
+		} while (*p != '/');
+
+		putchar('\n');
+
+		c = *++p;
+		end = p;
+
+	    }
+
+	    if (isspace(c))
+		continue;
+
+	    if (isdigit(c)) {
+		seq = c - '0';
+		while (*p) {
+		    c = *p++;
+		    
+		    if (isspace(c))
+			continue;
+		    
+		    if (!isdigit(c))
+			break;
+		    
+		    seq = seq * 10 + c - '0';
+		}
+	    }
+
+	    if (!*p)
+		break;
+
+	    switch (toupper(c)) {
+	    case 'G':
+		seq = -1;
+		status = pj_jb_get(&jb, &seq, &thedata);
+		lastget = seq;
+		break;
+	    case 'P':
+		if (seq == 0) 
+		    seq = lastseq++;
+		else
+		    lastseq = seq;
+		status = pj_jb_put(&jb, seq, data);
+		if (status == 0)
+		    lastput = seq;
+		break;
+	    default:
+		printf("Unknown character '%c'\n", c);
+		break;
+	    }
+
+#ifdef REPORT
+	    printf("seq=%d\t%c rc=%d\tsize=%d\tpfch=%d\tlvl=%d\tmxl=%d\tdelay=%d\n", 
+		    seq, toupper(c), status, jb.lst.count, jb.prefetch, jb.level, jb.max_level,
+		    (lastget>0 && lastput>0) ? lastput-lastget : -1);
+#endif
+	}
+    }
+
+#ifdef REPORT
+    printf("0\t%c   size=%d  prefetch=%d level=%d\n", 
+	    ' ', jb.lst.count, jb.prefetch, jb.level);
+#endif
+
+    if (input != stdin)
+	fclose(input);
+
+    pj_pool_release(pool);
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/main.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/main.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/main.c	(revision 1753)
@@ -0,0 +1,46 @@
+/* $Header: /pjproject/pjmedia/src/test/main.c 9     6/24/05 11:18p Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pjmedia/sound.h>
+
+pj_status_t session_test (pj_pool_factory *pf);
+pj_status_t rtp_test (pj_pool_factory *pf);
+pj_status_t sdp_test(pj_pool_factory *pf);
+int jbuf_main(pj_pool_factory *pf);
+
+int main()
+{
+    pj_caching_pool caching_pool;
+
+    pj_init();
+    pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
+
+    sdp_test (&caching_pool.factory);
+    rtp_test(&caching_pool.factory);
+    session_test (&caching_pool.factory);
+    //jbuf_main(&caching_pool.factory);
+
+    pj_caching_pool_destroy(&caching_pool);
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/rtp_test.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/rtp_test.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/rtp_test.c	(revision 1753)
@@ -0,0 +1,41 @@
+/* $Header: /pjproject/pjmedia/src/test/rtp_test.c 4     3/12/05 4:21p Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjmedia/rtp.h>
+#include <stdio.h>
+
+int rtp_test()
+{
+    pj_rtp_session rtp;
+    FILE *fhnd = fopen("RTP.DAT", "wb");
+    const void *rtphdr;
+    int hdrlen;
+
+    if (!fhnd)
+	return -1;
+
+    pj_rtp_session_init (&rtp, 4, 0x12345678);
+    pj_rtp_encode_rtp (&rtp, 4, 0, 0, 160, &rtphdr, &hdrlen);
+    fwrite (rtphdr, hdrlen, 1, fhnd);
+    fclose(fhnd);
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/sdptest.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/sdptest.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/sdptest.c	(revision 1753)
@@ -0,0 +1,125 @@
+/* $Header: /pjproject/pjmedia/src/test/sdptest.c 8     6/12/05 11:36a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjmedia/sdp.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <stdio.h>
+#include <string.h>
+
+static char *sdp[] = {
+    /*
+    "v=0\r\n"
+    "o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4\r\n"
+    "s=SDP Seminar\r\n"
+    "i=A Seminar on the session description protocol\r\n"
+    "u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps\r\n"
+    "e=mjh@isi.edu (Mark Handley)\r\n"
+    "c=IN IP4 224.2.17.12/127\r\n"
+    "t=2873397496 2873404696\r\n"
+    "a=recvonly\r\n"
+    "m=audio 49170 RTP/AVP 0\r\n"
+    "m=video 51372 RTP/AVP 31\r\n"
+    "m=application 32416 udp wb\r\n"
+    "a=orient:portrait\r\n"
+    "m=audio 49230 RTP/AVP 96 97 98\r\n"
+    "a=rtpmap:96 L8/8000\r\n"
+    "a=rtpmap:97 L16/8000\r\n"
+    "a=rtpmap:98 L16/11025/2\r\n",
+    */
+    "v=0\r\n"
+    "o=usera 2890844526 2890844527 IN IP4 alice.example.com\r\n"
+    "s=\r\n"
+    "c=IN IP4 alice.example.com\r\n"
+    "t=0 0\r\n"
+    "m=message 7394 msrp/tcp *\r\n"
+    "a=accept-types: message/cpim text/plain text/html\r\n"
+    "a=path:msrp://alice.example.com:7394/2s93i9;tcp\r\n"
+};
+
+static int sdp_perform_test(pj_pool_factory *pf)
+{
+    pj_pool_t *pool;
+    int inputlen, len;
+    pjsdp_session_desc *ses;
+    char buf[1500];
+    enum { LOOP=1000000 };
+    int i;
+    pj_time_val start, end;
+
+    printf("Parsing and printing %d SDP messages..\n", LOOP);
+
+    pool = pj_pool_create(pf, "", 4096, 0, NULL);
+    inputlen = strlen(sdp[0]);
+    pj_gettimeofday(&start);
+    for (i=0; i<LOOP; ++i) {
+	ses = pjsdp_parse(sdp[0], inputlen, pool);
+	len = pjsdp_print(ses, buf, sizeof(buf));
+	buf[len] = '\0';
+	pj_pool_reset(pool);
+    }
+    pj_gettimeofday(&end);
+
+    printf("Original:\n%s\n", sdp[0]);
+    printf("Parsed:\n%s\n", buf);
+
+    PJ_TIME_VAL_SUB(end, start);
+    printf("Time: %ld:%03lds\n", end.sec, end.msec);
+
+    if (end.msec==0 && end.sec==0) end.msec=1;
+    printf("Performance: %ld msg/sec\n", LOOP*1000/PJ_TIME_VAL_MSEC(end));
+    puts("");
+
+    pj_pool_release(pool);
+    return 0;
+}
+
+static int sdp_conform_test(pj_pool_factory *pf)
+{
+    pj_pool_t *pool;
+    pjsdp_session_desc *ses;
+    int i, len;
+    char buf[1500];
+
+    for (i=0; i<sizeof(sdp)/sizeof(sdp[0]); ++i) {
+	pool = pj_pool_create(pf, "sdp", 4096, 0, NULL);
+	ses = pjsdp_parse(sdp[i], strlen(sdp[i]), pool);
+	len = pjsdp_print(ses, buf, sizeof(buf)); 
+	buf[len] = '\0';
+	printf("%s\n", buf);
+	pj_pool_release(pool);
+    }
+
+    return 0;
+}
+
+pj_status_t sdp_test(pj_pool_factory *pf)
+{
+    if (sdp_conform_test(pf) != 0)
+	return -2;
+
+    if (sdp_perform_test(pf) != 0)
+	return -3;
+
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/session_test.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/session_test.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjmedia/src/test/session_test.c	(revision 1753)
@@ -0,0 +1,134 @@
+/* $Header: /pjproject/pjmedia/src/test/session_test.c 8     6/12/05 11:36a Bennylp $ */
+/* 
+ * PJMEDIA - Multimedia over IP Stack 
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ */
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/session.h>
+#include <pj/sock.h>
+#include <pj/pool.h>
+#include <stdio.h>
+#include <pj/string.h>
+
+pj_status_t session_test (pj_pool_factory *pf)
+{
+    pj_med_mgr_t *mm;
+    pj_media_session_t *s1, *s2;
+    pj_pool_t *pool;
+    pjsdp_session_desc *sdp;
+    pj_media_stream_info sd_info;
+    char buf[1024];
+    int len;
+    pj_media_stream_stat tx_stat, rx_stat;
+
+    pool = pj_pool_create(pf, "test", 4096, 1024, NULL);
+
+    // Init media manager.
+    mm = pj_med_mgr_create ( pf );
+
+    // Create caller session.
+    // THIS WILL DEFINITELY CRASH (NULL as argument)!
+    s1 = pj_media_session_create (mm, NULL);
+
+    // Set caller's media to send-only.
+    sd_info.dir = PJ_MEDIA_DIR_ENCODING;
+    pj_media_session_modify_stream (s1, 0, PJ_MEDIA_STREAM_MODIFY_DIR, &sd_info);
+
+    // Create caller SDP.
+    sdp = pj_media_session_create_sdp (s1, pool, 0);
+    len = pjsdp_print (sdp, buf, sizeof(buf));
+    buf[len] = '\0';
+    printf("Caller's initial SDP:\n<BEGIN>\n%s\n<END>\n", buf);
+
+    // Parse SDP from caller.
+    sdp = pjsdp_parse (buf, len, pool);
+
+    // Create callee session based on caller's SDP.
+    // THIS WILL DEFINITELY CRASH (NULL as argument)!
+    s2 = pj_media_session_create_from_sdp (mm, sdp, NULL);
+    
+    // Create callee SDP
+    sdp = pj_media_session_create_sdp (s2, pool, 0);
+    len = pjsdp_print (sdp, buf, sizeof(buf));
+    buf[len] = '\0';
+    printf("Callee's SDP:\n<BEGIN>\n%s\n<END>\n", buf);
+
+    // Parse SDP from callee.
+    sdp = pjsdp_parse (buf, len, pool);
+
+    // Update caller
+    pj_media_session_update (s1, sdp);
+    sdp = pj_media_session_create_sdp (s1, pool, 0);
+    pjsdp_print (sdp, buf, sizeof(buf));
+    printf("Caller's SDP after update:\n<BEGIN>\n%s\n<END>\n", buf);
+
+    // Now start media.
+    pj_media_session_activate (s2);
+    pj_media_session_activate (s1);
+
+    // Wait
+    for (;;) {
+	int has_stat;
+
+	printf("Enter q to exit, 1 or 2 to print statistics.\n");
+	fgets (buf, 10, stdin);
+	has_stat = 0;
+
+	switch (buf[0]) {
+	case 'q':
+	case 'Q':
+	    goto done;
+	    break;
+	case '1':
+	    pj_media_session_get_stat (s1, 0, &tx_stat, &rx_stat);
+	    has_stat = 1;
+	    break;
+	case '2':
+	    pj_media_session_get_stat (s2, 0, &tx_stat, &rx_stat);
+	    has_stat = 1;
+	    break;
+	}
+
+	if (has_stat) {
+	    pj_media_stream_stat *stat[2] = { &tx_stat, &rx_stat };
+	    const char *statname[2] = { "TX", "RX" };
+	    int i;
+
+	    for (i=0; i<2; ++i) {
+		printf("%s statistics:\n", statname[i]);
+		printf(" Pkt      TX=%d RX=%d\n", stat[i]->pkt_tx, stat[i]->pkt_rx);
+		printf(" Octets   TX=%d RX=%d\n", stat[i]->oct_tx, stat[i]->oct_rx);
+		printf(" Jitter   %d ms\n", stat[i]->jitter);
+		printf(" Pkt lost %d\n", stat[i]->pkt_lost);
+	    }
+	    printf("\n");
+	}
+    }
+
+done:
+
+    // Done.
+    pj_pool_release (pool);
+    pj_media_session_destroy (s2);
+    pj_media_session_destroy (s1);
+    pj_med_mgr_destroy (mm);
+
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/LGPL.TXT
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/LGPL.TXT	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/LGPL.TXT	(revision 1753)
@@ -0,0 +1,513 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/Makefile
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/Makefile	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/Makefile	(revision 1753)
@@ -0,0 +1,73 @@
+include make-$(TARGET).inc
+
+export PJSIP_SRCDIR = ../src/pjsip
+export PJSIP_SRCEXT = .c
+export PJSIP_SRCS = $(PJSIP_SOURCES) sip_auth.c sip_auth_msg.c sip_auth_parser.c \
+		    sip_endpoint.c sip_misc.c sip_msg.c sip_parser.c \
+		    sip_resolve.c sip_transaction.c sip_transport.c sip_uri.c
+
+export PJSIP_UA_SRCDIR = ../src/pjsip_mod_ua
+export PJSIP_UA_SRCEXT = .c
+export PJSIP_UA_SRCS = $(PJSIP_UA_SOURCES) sip_dialog.c sip_reg.c sip_ua.c 
+
+export PJSIP_SIMPLE_SRCDIR = ../src/pjsip_simple
+export PJSIP_SIMPLE_SRCEXT = .c
+export PJSIP_SIMPLE_SRCS = $(PJSIP_SIMPLE_SOURCES) event_notify.c event_notify_msg.c \
+		    messaging.c pidf.c presence.c xpidf.c
+
+export PJSUA_SRCDIR = ../src/pjsua
+export PJSUA_SRCEXT = .c
+export PJSUA_SRCS = $(PJSUA_SOURCES) main.c getopt.c
+
+export TARGET CCOUT CC AR RANLIB MV RM RMDIR MKDIR OBJEXT LD LDOUT 
+
+all: pjsip pjsip_ua pjsip_simple pjsua 
+
+doc:
+	cd .. && doxygen docs/doxygen.cfg
+
+print:
+	$(MAKE) -f make-rules APP=PJSIP app=pjsip print_lib
+	$(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua print_lib
+	$(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple print_lib
+	$(MAKE) -f make-rules APP=PJSUA app=pjsua print_bin
+	
+depend:
+	$(MAKE) -f make-rules APP=PJSIP app=pjsip depend
+	$(MAKE) -f make-rules APP=PJSUA app=pjsua depend
+	$(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua depend
+	$(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple depend
+	echo '$(PJSUA_EXE): $(PJSIP_LIB) $(PJSIP_UA_LIB)' >> .pjsua.depend
+
+dep: depend
+
+pjsip:
+	$(MAKE) -f make-rules APP=PJSIP app=pjsip $(PJSIP_LIB)
+
+pjsua: 
+	$(MAKE) -f make-rules APP=PJSUA app=pjsua $(PJSUA_EXE)
+
+pjsip_ua:
+	$(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua $(PJSIP_UA_LIB)
+
+pjsip_simple:
+	$(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple $(PJSIP_SIMPLE_LIB)
+
+samples:
+	gcc $(_CFLAGS) -o ../bin/simpleua ../src/samples/simpleua.c $(_LDFLAGS) 
+
+clean:
+	$(MAKE) -f make-rules APP=PJSIP app=pjsip clean
+	$(MAKE) -f make-rules APP=PJSUA app=pjsua clean
+	$(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua clean
+	$(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple clean
+
+realclean:
+	$(MAKE) -f make-rules APP=PJSIP app=pjsip realclean
+	$(MAKE) -f make-rules APP=PJSUA app=pjsua realclean
+	$(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua realclean
+	$(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple realclean
+
+distclean: realclean
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/TODO.txt
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/TODO.txt	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/TODO.txt	(revision 1753)
@@ -0,0 +1,21 @@
+- regc refresh automatically
+- if dialog CANCEL return 481, disconnect the dialog
+- presence implement callback from event_sub
+- presence supports multiple tuples!
+- implement event headers!
+
+Prio Task
+ 10  General authentication framework in pjsua.
+ 10  Start on SUBSCRIBE/NOTIFY framework.
+ 10  Refactor pjsip_event
+---
+ 10  Concurrency in pool factory because endpt pool is shared by app.
+     Choices:
+	- another pool factory (thread safe) for app ==> waste memory.
+	- endpt pool is thread safe ==> slow
+ 10  Sound in Linux
+ 10  Support TCP
+ 10  Per instance configuration:
+		- max number of tsxs
+		- max number of dialogs
+		- socket buffer size
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-linux-i386.inc
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-linux-i386.inc	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-linux-i386.inc	(revision 1753)
@@ -0,0 +1,58 @@
+#
+# make-linux-i386.inc: Platform specific rules for Linux i386 compile.
+#
+
+#
+# Include PJLIB settings.
+#
+include ../../pjlib/build/make-$(TARGET).inc
+
+#
+# make-optimize.inc declares PJSIP_OPTIMIZE variable
+#
+include make-optimize.inc
+
+
+
+_CFLAGS := $(_CFLAGS) -I../../pjlib/src -I../../pjmedia/src \
+          -I../src $(PJSIP_OPTIMIZE)
+_LDFLAGS := $(_LDFLAGS) -L../lib -L../../pjlib/lib  \
+		      -L../../pjmedia/lib -lpjsip_core -lpjsip_ua -lpjsip_simple \
+		      -lpjmedia -lpj -lpthread
+
+#
+# libpjsip_core.a
+#
+export PJSIP_SOURCES = 
+export PJSIP_CFLAGS = $(_CFLAGS) 
+export PJSIP_LIB = ../lib/libpjsip_core.a
+export PJSIP_EXTRA_DEP := 
+
+#
+# libpjsip_ua.a
+#
+export PJSIP_UA_SOURCES = 
+export PJSIP_UA_CFLAGS = $(_CFLAGS) 
+export PJSIP_UA_LIB = ../lib/libpjsip_ua.a
+export PJSIP_UA_EXTRA_DEP :=
+
+#
+# libpjsip_simple.a
+#
+export PJSIP_SIMPLE_SOURCES :=
+export PJSIP_SIMPLE_CFLAGS := $(_CFLAGS)
+export PJSIP_SIMPLE_LIB := ../lib/libpjsip_simple.a
+export PJSIP_SIMPLE_EXTRA_LIB :=
+
+#
+# pjsua.exe
+#
+export PJSUA_SOURCES = 
+export PJSUA_CFLAGS = $(_CFLAGS)
+export PJSUA_LDFLAGS =  $(_LDFLAGS)
+export PJSUA_EXE = ../bin/pjsua
+export PJSUA_EXTRA_DEP := ../lib/libpjsip_core.a ../lib/libpjsip_ua.a \
+			  ../lib/libpjsip_simple.a ../../pjlib/lib/libpj.a \
+			  ../../pjmedia/lib/libpjmedia.a
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-mingw.inc
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-mingw.inc	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-mingw.inc	(revision 1753)
@@ -0,0 +1,56 @@
+#
+# Platform specific flags
+#
+
+#
+# Include PJLIB settings.
+#
+include ../../pjlib/build/make-$(TARGET).inc
+
+
+#
+# make-optimize.inc declares PJSIP_OPTIMIZE variable
+#
+include make-optimize.inc
+
+
+_CFLAGS := $(_CFLAGS) -I../../pjlib/src -I../../pjmedia/src \
+	  $(PJSIP_OPTIMIZE)
+_LDFLAGS := -L../../pjlib/lib -L../../pjmedia/lib \
+	   -lpjsip_ua -lpjsip_simple -lpjsip_core -lpjmedia $(_LDFLAGS)
+
+#
+# libpjsip_core.a
+#
+export PJSIP_SOURCES := 
+export PJSIP_CFLAGS := $(_CFLAGS)
+export PJSIP_LIB := ../lib/libpjsip_core.a
+export PJSIP_EXTRA_DEP := 
+
+#
+# libpjsip_ua.a
+#
+export PJSIP_UA_SOURCES := 
+export PJSIP_UA_CFLAGS := $(_CFLAGS)
+export PJSIP_UA_LIB := ../lib/libpjsip_ua.a
+export PJSIP_UA_EXTRA_DEP :=
+
+#
+# libpjsip_simple.a
+#
+export PJSIP_SIMPLE_SOURCES :=
+export PJSIP_SIMPLE_CFLAGS := $(_CFLAGS)
+export PJSIP_SIMPLE_LIB := ../lib/libpjsip_simple.a
+export PJSIP_SIMPLE_EXTRA_LIB :=
+
+
+#
+# pjsua.exe
+#
+export PJSUA_SOURCES := 
+export PJSUA_CFLAGS := $(_CFLAGS)
+export PJSUA_LDFLAGS = $(_LDFLAGS)
+export PJSUA_EXE = ../bin/pjsua_mingw.exe
+export PJSUA_EXTRA_DEP := ../lib/libpjsip_core.a ../lib/libpjsip_ua.a \
+			  ../lib/libpjsip_simple.a ../../pjlib/lib/libpj.a \
+			  ../../pjmedia/lib/libpjmedia.a
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-optimize.inc
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-optimize.inc	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-optimize.inc	(revision 1753)
@@ -0,0 +1,10 @@
+
+#
+# If MINSIZE is defined, optimize for code size.
+#
+ifdef MINSIZE
+PJSIP_OPTIMIZE := $(PJSIP_OPTIMIZE) -DPJSIP_HAS_DUMP=0
+else
+PJSIP_OPTIMIZE := $(PJSIP_OPTIMIZE)
+endif
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-rules
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-rules	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/make-rules	(revision 1753)
@@ -0,0 +1,119 @@
+LIBDIR = ../lib
+BINDIR = ../bin
+
+#
+# The full path of output lib file (e.g. ../lib/libapp.a).
+#
+LIB = $($(APP)_LIB)
+
+#
+# The full path of output executable file (e.g. ../bin/app.exe).
+#
+EXE = $($(APP)_EXE)
+
+#
+# Source directory
+#
+SRCDIR = $($(APP)_SRCDIR)
+
+#
+# SRCEXT is .c
+# SRCS is file.c
+# FULL_SRCS is ../src/app/file.c
+#
+SRCEXT = $($(APP)_SRCEXT)
+SRCS = $($(APP)_SRCS)
+FULL_SRCS = $(foreach file, $(SRCS), $(SRCDIR)/$(file))
+
+
+#
+# Output directory for object files (i.e. output/target)
+#
+OBJDIR = ./output/$(app)-$(TARGET)
+
+#
+# OBJS1 is ./output/target/file.c
+# OBJS is ./output/target/file.o
+#
+OBJS1 = $(foreach file, $(SRCS), $(OBJDIR)/$(file))
+OBJS = $(OBJS1:%$(SRCEXT)=%$(OBJEXT))
+OBJDIRS := $(sort $(foreach file, $(SRCS), $(dir $(OBJDIR)/$(file))))
+
+
+#
+# When generating dependency (gcc -MM), ideally we use only either
+# CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work.
+#
+DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS)
+
+print_common:
+	@echo "###"
+	@echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):"
+	@echo "###"
+	@echo APP=$(APP)
+	@echo SRCEXT=$(SRCEXT)
+	@echo OBJDIR=$(OBJDIR)
+	@echo OBJS=$(OBJS)
+	@echo SRCDIR=$(SRCDIR)
+	@echo FULL_SRCS=$(FULL_SRCS)
+	@echo $(APP)_CFLAGS=$($(APP)_CFLAGS)
+	@echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS)
+	@echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS)
+	@echo DEPFLAGS=$(DEPFLAGS)
+
+print_bin: print_common
+	@echo EXE=$(EXE)
+	@echo BINDIR=$(BINDIR)
+
+print_lib: print_common
+	@echo LIB=$(LIB)
+	@echo LIBDIR=$(LIBDIR)
+
+$(LIB): $(LIBDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP)
+	$(AR) $(LIB) $(OBJS)
+	$(RANLIB) $(LIB)
+
+$(EXE): $(BINDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP)
+	$(LD) $(LDOUT) $(EXE) $(OBJS) $($(APP)_LDFLAGS)
+
+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c
+	$(CC) $($(APP)_CFLAGS) $< $(CCOUT) $@
+
+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp
+	$(CC) $($(APP)_CXXFLAGS) $< $(CCOUT) $@
+
+#$(OBJDIR):
+#	$(MKDIR) $(OBJDIR)
+$(OBJDIRS):
+	$(MKDIR) $@
+
+$(LIBDIR):
+	$(MKDIR) $(LIBDIR)
+
+$(BINDIR):
+	$(MKDIR) $(BINDIR)
+
+clean:
+	$(RM) -r $(OBJDIR)/*
+	$(RMDIR) $(OBJDIR)
+
+realclean: clean
+	$(RM) $(LIB) $(EXE)
+	$(RM) .$(app).depend
+
+depend:
+	$(RM) .$(app).depend
+	for F in $(FULL_SRCS); do \
+	   echo -n $(OBJDIR)/ >> .$(app).depend; \
+	   if gcc -MM $(DEPFLAGS) $$F >> .$(app).depend; then \
+		true; \
+	   else \
+		echo 'err:' >> .$(app).depend; \
+		exit 1; \
+	   fi; \
+	done
+
+dep: depend
+
+-include .$(app).depend
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip.dsw
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip.dsw	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip.dsw	(revision 1753)
@@ -0,0 +1,147 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "pjlib"="..\..\pjlib\build\pjlib.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", RIAAAAAA
+    ..\..\..\..\project\pjproject\pjsip\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjmedia"="..\..\pjmedia\build\pjmedia.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjmedia/build", MJCAAAAA
+    ..\..\pjmedia\build
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjsip_core"=".\pjsip_core.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", LJCAAAAA
+    .
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjsip_core_test"=".\pjsip_core_test.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjsip_simple"=".\pjsip_simple.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", RIAAAAAA
+    .
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjsip_ua"=".\pjsip_ua.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", LJCAAAAA
+    .
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pjsua"=".\pjsua.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", LJCAAAAA
+    .
+    end source code control
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjsip_core
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjsip_ua
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjmedia
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjsdp
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjsip_simple
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+    begin source code control
+    "$/pjproject/pjsip/build", LJCAAAAA
+    .
+    end source code control
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip.sln
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip.sln	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip.sln	(revision 1753)
@@ -0,0 +1,117 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_core_lib", "pjsip_core.vcproj", "{6A2C9762-EB5C-44B3-BC41-471DA2D0234A}"
+	ProjectSection(ProjectDependencies) = postProject
+		{A0EC3D31-5FC2-45A5-85FB-76376FBBBD78} = {A0EC3D31-5FC2-45A5-85FB-76376FBBBD78}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_ua_lib", "pjsip_ua.vcproj", "{B05FA649-6AD8-42E3-986D-C6E95E206B76}"
+	ProjectSection(ProjectDependencies) = postProject
+		{6A2C9762-EB5C-44B3-BC41-471DA2D0234A} = {6A2C9762-EB5C-44B3-BC41-471DA2D0234A}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib", "..\..\pjlib\build\pjlib.vcproj", "{A0EC3D31-5FC2-45A5-85FB-76376FBBBD78}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia_lib", "..\..\pjmedia\build\pjmedia.vcproj", "{99D06BCE-43AE-40B6-AD4F-14261AB7C5D9}"
+	ProjectSection(ProjectDependencies) = postProject
+		{A0EC3D31-5FC2-45A5-85FB-76376FBBBD78} = {A0EC3D31-5FC2-45A5-85FB-76376FBBBD78}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsua", "pjsua.vcproj", "{DBEC405B-6E6E-419E-BE29-FF6AFC8FEC70}"
+	ProjectSection(ProjectDependencies) = postProject
+		{A0EC3D31-5FC2-45A5-85FB-76376FBBBD78} = {A0EC3D31-5FC2-45A5-85FB-76376FBBBD78}
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE} = {B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}
+		{B05FA649-6AD8-42E3-986D-C6E95E206B76} = {B05FA649-6AD8-42E3-986D-C6E95E206B76}
+		{6A2C9762-EB5C-44B3-BC41-471DA2D0234A} = {6A2C9762-EB5C-44B3-BC41-471DA2D0234A}
+		{99D06BCE-43AE-40B6-AD4F-14261AB7C5D9} = {99D06BCE-43AE-40B6-AD4F-14261AB7C5D9}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_core_test", "pjsip_core_test.vcproj", "{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}"
+	ProjectSection(ProjectDependencies) = postProject
+		{A0EC3D31-5FC2-45A5-85FB-76376FBBBD78} = {A0EC3D31-5FC2-45A5-85FB-76376FBBBD78}
+		{6A2C9762-EB5C-44B3-BC41-471DA2D0234A} = {6A2C9762-EB5C-44B3-BC41-471DA2D0234A}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_simple_lib", "pjsip_simple.vcproj", "{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SourceCodeControl) = preSolution
+		SccNumberOfProjects = 8
+		SccProjectName0 = \u0022$/pjproject\u0022,\u0020PIAAAAAA
+		SccLocalPath0 = ..\\..
+		SccProvider0 = MSSCCI:Microsoft\u0020Visual\u0020SourceSafe
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection0 = pjsip\\build\\
+		SolutionUniqueID = {084A6F63-C2AA-4AF1-B551-6F4550ACB44F}
+		SccProjectUniqueName1 = ..\\..\\pjlib\\build\\pjlib.vcproj
+		SccLocalPath1 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection1 = pjlib\\build\\
+		SccProjectUniqueName2 = ..\\..\\pjmedia\\build\\pjmedia.vcproj
+		SccLocalPath2 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection2 = pjmedia\\build\\
+		SccProjectUniqueName3 = pjsip_core.vcproj
+		SccLocalPath3 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection3 = pjsip\\build\\
+		SccProjectUniqueName4 = pjsip_ua.vcproj
+		SccLocalPath4 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection4 = pjsip\\build\\
+		SccProjectUniqueName5 = pjsua.vcproj
+		SccLocalPath5 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection5 = pjsip\\build\\
+		SccProjectUniqueName6 = pjsip_core_test.vcproj
+		SccLocalPath6 = ..\\..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection6 = pjsip\\build\\
+		SccProjectUniqueName7 = pjsip_simple.vcproj
+		SccProjectName7 = \u0022$/pjproject/pjsip\u0022,\u0020QIAAAAAA
+		SccLocalPath7 = ..
+		CanCheckoutShared = false
+		SccProjectFilePathRelativizedFromConnection7 = build\\
+	EndGlobalSection
+	GlobalSection(SolutionConfiguration) = preSolution
+		Debug = Debug
+		Release = Release
+	EndGlobalSection
+	GlobalSection(ProjectConfiguration) = postSolution
+		{6A2C9762-EB5C-44B3-BC41-471DA2D0234A}.Debug.ActiveCfg = Debug|Win32
+		{6A2C9762-EB5C-44B3-BC41-471DA2D0234A}.Debug.Build.0 = Debug|Win32
+		{6A2C9762-EB5C-44B3-BC41-471DA2D0234A}.Release.ActiveCfg = Release|Win32
+		{6A2C9762-EB5C-44B3-BC41-471DA2D0234A}.Release.Build.0 = Release|Win32
+		{B05FA649-6AD8-42E3-986D-C6E95E206B76}.Debug.ActiveCfg = Debug|Win32
+		{B05FA649-6AD8-42E3-986D-C6E95E206B76}.Debug.Build.0 = Debug|Win32
+		{B05FA649-6AD8-42E3-986D-C6E95E206B76}.Release.ActiveCfg = Release|Win32
+		{B05FA649-6AD8-42E3-986D-C6E95E206B76}.Release.Build.0 = Release|Win32
+		{A0EC3D31-5FC2-45A5-85FB-76376FBBBD78}.Debug.ActiveCfg = Debug|Win32
+		{A0EC3D31-5FC2-45A5-85FB-76376FBBBD78}.Debug.Build.0 = Debug|Win32
+		{A0EC3D31-5FC2-45A5-85FB-76376FBBBD78}.Release.ActiveCfg = Release|Win32
+		{A0EC3D31-5FC2-45A5-85FB-76376FBBBD78}.Release.Build.0 = Release|Win32
+		{99D06BCE-43AE-40B6-AD4F-14261AB7C5D9}.Debug.ActiveCfg = Debug|Win32
+		{99D06BCE-43AE-40B6-AD4F-14261AB7C5D9}.Debug.Build.0 = Debug|Win32
+		{99D06BCE-43AE-40B6-AD4F-14261AB7C5D9}.Release.ActiveCfg = Release|Win32
+		{99D06BCE-43AE-40B6-AD4F-14261AB7C5D9}.Release.Build.0 = Release|Win32
+		{DBEC405B-6E6E-419E-BE29-FF6AFC8FEC70}.Debug.ActiveCfg = Debug|Win32
+		{DBEC405B-6E6E-419E-BE29-FF6AFC8FEC70}.Debug.Build.0 = Debug|Win32
+		{DBEC405B-6E6E-419E-BE29-FF6AFC8FEC70}.Release.ActiveCfg = Release|Win32
+		{DBEC405B-6E6E-419E-BE29-FF6AFC8FEC70}.Release.Build.0 = Release|Win32
+		{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}.Debug.ActiveCfg = Debug|Win32
+		{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}.Debug.Build.0 = Debug|Win32
+		{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}.Release.ActiveCfg = Release|Win32
+		{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}.Release.Build.0 = Release|Win32
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}.Debug.ActiveCfg = Debug|Win32
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}.Debug.Build.0 = Debug|Win32
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}.Release.ActiveCfg = Release|Win32
+		{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}.Release.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+	EndGlobalSection
+	GlobalSection(ExtensibilityAddIns) = postSolution
+	EndGlobalSection
+EndGlobal
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_auth.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_auth.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_auth.vcproj	(revision 1753)
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjsip_auth_lib"
+	ProjectGUID="{FC63AE7E-423F-464D-B84E-B20B38046B19}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjsip_auth_vc7_Release"
+			IntermediateDirectory=".\output\pjsip_auth_vc7_Release"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjsip_auth_vc7_Release/pjsip_auth.pch"
+				AssemblerListingLocation=".\output\pjsip_auth_vc7_Release/"
+				ObjectFile=".\output\pjsip_auth_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjsip_auth_vc7_Release/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="3"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsip_auth_vc7s.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjsip_auth_vc7_Debug"
+			IntermediateDirectory=".\output\pjsip_auth_vc7_Debug"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile=".\output\pjsip_auth_vc7_Debug/pjsip_auth.pch"
+				AssemblerListingLocation=".\output\pjsip_auth_vc7_Debug/"
+				ObjectFile=".\output\pjsip_auth_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjsip_auth_vc7_Debug/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsip_auth_vc7sd.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\pjsip_auth\sip_auth.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_auth\sip_auth_msg.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_auth\sip_auth_parser.c">
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\pjsip_auth.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_auth\sip_auth.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_auth\sip_auth_msg.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_auth\sip_auth_parser.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Inline Files"
+			Filter="">
+		</Filter>
+		<Filter
+			Name="Private Files"
+			Filter="">
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_auth_lib.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_auth_lib.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_auth_lib.dsp	(revision 1753)
@@ -0,0 +1,122 @@
+# Microsoft Developer Studio Project File - Name="pjsip_auth_lib" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pjsip_auth_lib - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_auth_lib.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_auth_lib.mak" CFG="pjsip_auth_lib - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjsip_auth_lib - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pjsip_auth_lib - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjsip/build", RIAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjsip_auth_lib - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "./output/pjsip_auth_vc6_Release"
+# PROP BASE Intermediate_Dir "./output/pjsip_auth_vc6_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "./output/pjsip_auth_vc6_Release"
+# PROP Intermediate_Dir "./output/pjsip_auth_vc6_Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /GX /O2 /I "../src" /I "../../pjsip/src" /I "../../pjlib/src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjsip_auth_vc6s.lib"
+
+!ELSEIF  "$(CFG)" == "pjsip_auth_lib - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "./output/pjsip_auth_vc6_Debug"
+# PROP BASE Intermediate_Dir "./output/pjsip_auth_vc6_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "./output/pjsip_auth_vc6_Debug"
+# PROP Intermediate_Dir "./output/pjsip_auth_vc6_Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../src" /I "../../pjsip/src" /I "../../pjlib/src" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjsip_auth_vc6sd.lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjsip_auth_lib - Win32 Release"
+# Name "pjsip_auth_lib - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\pjsip_auth\sip_auth.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_auth\sip_auth_msg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_auth\sip_auth_parser.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\pjsip_auth.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_auth\sip_auth.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_auth\sip_auth_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_auth\sip_auth_parser.h
+# End Source File
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_callgen.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_callgen.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_callgen.vcproj	(revision 1753)
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjsip_callgen"
+	ProjectGUID="{DCBEF2A3-D444-46FC-82E7-D939113EECA7}"
+	SccProjectName="SAK"
+	SccAuxPath="SAK"
+	SccLocalPath="SAK"
+	SccProvider="SAK"
+	Keyword="Win32Proj">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="./output/pjsip_callgen_vc7_Debug"
+			IntermediateDirectory="./output/pjsip_callgen_vc7_Debug"
+			ConfigurationType="1"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="TRUE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="5"
+				UsePrecompiledHeader="3"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="TRUE"
+				DebugInformationFormat="4"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				OutputFile="../bin/pjcallgen_vc7d.exe"
+				LinkIncremental="2"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile="$(OutDir)/pjcallgen.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="./output/pjsip_callgen_vc7_Release"
+			IntermediateDirectory="./output/pjsip_callgen_vc7_Release"
+			ConfigurationType="1"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="4"
+				UsePrecompiledHeader="3"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="TRUE"
+				DebugInformationFormat="3"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				OutputFile="../bin/pjcallgen_vc7.exe"
+				LinkIncremental="1"
+				GenerateDebugInformation="TRUE"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core.dsp	(revision 1753)
@@ -0,0 +1,226 @@
+# Microsoft Developer Studio Project File - Name="pjsip_core" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pjsip_core - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_core.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_core.mak" CFG="pjsip_core - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjsip_core - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pjsip_core - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjsip/build", RIAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjsip_core - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\output\pjsip_core_vc6_Release"
+# PROP Intermediate_Dir ".\output\pjsip_core_vc6_Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /Zi /O2 /I "../src" /I "../../pjlib/src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjsip_core_vc6s.lib"
+
+!ELSEIF  "$(CFG)" == "pjsip_core - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\pjsip_core_vc6_Debug"
+# PROP Intermediate_Dir ".\output\pjsip_core_vc6_Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../src" /I "../../pjlib/src" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjsip_core_vc6sd.lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjsip_core - Win32 Release"
+# Name "pjsip_core - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_auth.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_auth_msg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_auth_parser.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_endpoint.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_misc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_msg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_parser.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_resolve.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_transaction.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_transport.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_uri.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\pjsip_core.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\print.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_auth.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_auth_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_auth_parser.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_endpoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_event.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_misc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_module.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_parser.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_private.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_resolve.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_transaction.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_transport.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_uri.h
+# End Source File
+# End Group
+# Begin Group "Inline Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_msg_i.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\INSTALL.txt
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\RELNOTES.txt
+# End Source File
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core.vcproj	(revision 1753)
@@ -0,0 +1,388 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjsip_core_lib"
+	ProjectGUID="{58A72B82-7369-4B89-B511-7191A6B0D8C3}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjsip_core_vc7_Release"
+			IntermediateDirectory=".\output\pjsip_core_vc7_Release"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjsip_core_vc7_Release/pjsip_core.pch"
+				AssemblerListingLocation=".\output\pjsip_core_vc7_Release/"
+				ObjectFile=".\output\pjsip_core_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjsip_core_vc7_Release/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="3"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsip_core_vc7s.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjsip_core_vc7_Debug"
+			IntermediateDirectory=".\output\pjsip_core_vc7_Debug"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile=".\output\pjsip_core_vc7_Debug/pjsip_core.pch"
+				AssemblerListingLocation=".\output\pjsip_core_vc7_Debug/"
+				ObjectFile=".\output\pjsip_core_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjsip_core_vc7_Debug/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsip_core_vc7sd.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\pjsip\sip_auth.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_auth_msg.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_auth_parser.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_endpoint.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_misc.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_msg.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_parser.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_resolve.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_transaction.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_transport.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_uri.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\pjsip_core.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\print.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_auth.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_auth_msg.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_auth_parser.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_config.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_endpoint.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_event.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_misc.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_module.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_msg.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_parser.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_private.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_resolve.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_transaction.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_transport.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_types.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip\sip_uri.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Inline Files"
+			Filter="">
+			<File
+				RelativePath="..\src\pjsip\sip_msg_i.h">
+			</File>
+		</Filter>
+		<File
+			RelativePath="..\..\RELNOTES.txt">
+		</File>
+		<File
+			RelativePath=".\TODO.txt">
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core_test.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core_test.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core_test.dsp	(revision 1753)
@@ -0,0 +1,116 @@
+# Microsoft Developer Studio Project File - Name="pjsip_core_test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=pjsip_core_test - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_core_test.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_core_test.mak" CFG="pjsip_core_test - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjsip_core_test - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "pjsip_core_test - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjsip/build", RIAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjsip_core_test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\output\pjsip_core_test_vc6_Release"
+# PROP Intermediate_Dir ".\output\pjsip_core_test_vc6_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../src" /I "../../pjlib/src" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib kernel32.lib netapi32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /profile /map /debug /machine:I386 /out:"..\bin\pjsip_core_test_vc6.exe"
+
+!ELSEIF  "$(CFG)" == "pjsip_core_test - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\pjsip_core_test_vc6_Debug"
+# PROP Intermediate_Dir ".\output\pjsip_core_test_vc6_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../src" /I "../../pjlib/src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 wsock32.lib kernel32.lib netapi32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"..\bin\pjsip_core_test_vc6d.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjsip_core_test - Win32 Release"
+# Name "pjsip_core_test - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\tests\pjsip_core\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\tests\pjsip_core\test_msg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\tests\pjsip_core\test_uri.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\tests\pjsip_core\test.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core_test.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core_test.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_core_test.vcproj	(revision 1753)
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjsip_core_test"
+	ProjectGUID="{782BF0C1-FE0E-48A9-B875-31E7BF9F4B5B}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjsip_core_test_vc7_Release"
+			IntermediateDirectory=".\output\pjsip_core_test_vc7_Release"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjsip_core_test_vc7_Release/pjsip_core_test.pch"
+				AssemblerListingLocation=".\output\pjsip_core_test_vc7_Release/"
+				ObjectFile=".\output\pjsip_core_test_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjsip_core_test_vc7_Release/"
+				BrowseInformation="1"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="3"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib"
+				OutputFile="..\bin\pjsip_core_test_vc7.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="TRUE"
+				GenerateDebugInformation="TRUE"
+				GenerateMapFile="TRUE"
+				MapFileName=".\output\pjsip_core_test_vc7_Release/pjsip_core_test.map"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\output\pjsip_core_test_vc7_Release/pjsip_core_test.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjsip_core_test_vc7_Debug"
+			IntermediateDirectory=".\output\pjsip_core_test_vc7_Debug"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile=".\output\pjsip_core_test_vc7_Debug/pjsip_core_test.pch"
+				AssemblerListingLocation=".\output\pjsip_core_test_vc7_Debug/"
+				ObjectFile=".\output\pjsip_core_test_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjsip_core_test_vc7_Debug/"
+				BrowseInformation="1"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="netapi32.lib wsock32.lib odbc32.lib odbccp32.lib"
+				OutputFile="..\bin\pjsip_core_test_vc7d.exe"
+				LinkIncremental="0"
+				SuppressStartupBanner="TRUE"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile=".\output\pjsip_core_test_vc7_Debug/pjsip_core_test.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\output\pjsip_core_test_vc7_Debug/pjsip_core_test.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\tests\pjsip_core\main.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\tests\pjsip_core\test_msg.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\tests\pjsip_core\test_uri.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\tests\pjsip_core\test.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_simple.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_simple.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_simple.dsp	(revision 1753)
@@ -0,0 +1,144 @@
+# Microsoft Developer Studio Project File - Name="pjsip_simple" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pjsip_simple - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_simple.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_simple.mak" CFG="pjsip_simple - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjsip_simple - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pjsip_simple - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjsip/build", RIAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjsip_simple - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "./output/pjsip_simple_Win32_Release"
+# PROP BASE Intermediate_Dir "./output/pjsip_simple_Win32_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "./output/pjsip_simple_Win32_Release"
+# PROP Intermediate_Dir "./output/pjsip_simple_Win32_Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../src" /I "../../pjlib/src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjsip_simple_vc6.lib"
+
+!ELSEIF  "$(CFG)" == "pjsip_simple - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "./output/pjsip_simple_Win32_Debug"
+# PROP BASE Intermediate_Dir "./output/pjsip_simple_Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "./output/pjsip_simple_Win32_Debug"
+# PROP Intermediate_Dir "./output/pjsip_simple_Win32_Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../src" /I "../../pjlib/src" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjsip_simple_vc6d.lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjsip_simple - Win32 Release"
+# Name "pjsip_simple - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\event_notify.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\event_notify_msg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\messaging.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\pidf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\presence.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\xpidf.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\event_notify.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\event_notify_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\messaging.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\pidf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\presence.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_simple\xpidf.h
+# End Source File
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_simple.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_simple.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_simple.vcproj	(revision 1753)
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjsip_simple_lib"
+	ProjectGUID="{B5C20C39-AF03-405D-BF59-B4C2E8D68BDE}"
+	SccProjectName="&quot;$/pjproject/pjsip&quot;, QIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath=".."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\./output/pjsip_simple_Win32_Debug"
+			IntermediateDirectory=".\./output/pjsip_simple_Win32_Debug"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\./output/pjsip_simple_Win32_Debug/pjsip_simple.pch"
+				AssemblerListingLocation=".\./output/pjsip_simple_Win32_Debug/"
+				ObjectFile=".\./output/pjsip_simple_Win32_Debug/"
+				ProgramDataBaseFileName=".\./output/pjsip_simple_Win32_Debug/"
+				BrowseInformation="1"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="../lib/pjsip_simple_vc6d.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\./output/pjsip_simple_Win32_Release"
+			IntermediateDirectory=".\./output/pjsip_simple_Win32_Release"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\./output/pjsip_simple_Win32_Release/pjsip_simple.pch"
+				AssemblerListingLocation=".\./output/pjsip_simple_Win32_Release/"
+				ObjectFile=".\./output/pjsip_simple_Win32_Release/"
+				ProgramDataBaseFileName=".\./output/pjsip_simple_Win32_Release/"
+				WarningLevel="3"
+				SuppressStartupBanner="TRUE"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="../lib/pjsip_simple_vc6.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\pjsip_simple\event_notify.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\event_notify_msg.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\messaging.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\pidf.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\presence.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\xpidf.c">
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\pjsip_simple\event_notify.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\event_notify_msg.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\messaging.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\pidf.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\presence.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_simple\xpidf.h">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_ua.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_ua.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_ua.dsp	(revision 1753)
@@ -0,0 +1,126 @@
+# Microsoft Developer Studio Project File - Name="pjsip_ua" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pjsip_ua - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_ua.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsip_ua.mak" CFG="pjsip_ua - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjsip_ua - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pjsip_ua - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjsip/build", RIAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjsip_ua - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\output\pjsip_ua_vc6_Release"
+# PROP BASE Intermediate_Dir ".\output\pjsip_ua_vc6_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\output\pjsip_ua_vc6_Release"
+# PROP Intermediate_Dir ".\output\pjsip_ua_vc6_Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /Zi /O2 /I "../src" /I "../../pjlib/src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjsip_ua_vc6s.lib"
+
+!ELSEIF  "$(CFG)" == "pjsip_ua - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\output\pjsip_ua_vc6_Debug"
+# PROP BASE Intermediate_Dir ".\output\pjsip_ua_vc6_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\pjsip_ua_vc6_Debug"
+# PROP Intermediate_Dir ".\output\pjsip_ua_vc6_Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../src" /I "../../pjlib/src" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"../lib/pjsip_ua_vc6sd.lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjsip_ua - Win32 Release"
+# Name "pjsip_ua - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\pjsip_mod_ua\sip_dialog.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_mod_ua\sip_reg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_mod_ua\sip_ua.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\pjsip_ua.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_mod_ua\sip_dialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_mod_ua\sip_reg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_mod_ua\sip_ua.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip_mod_ua\sip_ua_private.h
+# End Source File
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_ua.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_ua.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsip_ua.vcproj	(revision 1753)
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjsip_ua_lib"
+	ProjectGUID="{DEE358A5-ADD3-4403-AD82-4967E63F17D1}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\output\pjsip_ua_vc7_Debug"
+			IntermediateDirectory=".\output\pjsip_ua_vc7_Debug"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile=".\output\pjsip_ua_vc7_Debug/pjsip_ua.pch"
+				AssemblerListingLocation=".\output\pjsip_ua_vc7_Debug/"
+				ObjectFile=".\output\pjsip_ua_vc7_Debug/"
+				ProgramDataBaseFileName=".\output\pjsip_ua_vc7_Debug/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsip_ua_vc7sd.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\output\pjsip_ua_vc7_Release"
+			IntermediateDirectory=".\output\pjsip_ua_vc7_Release"
+			ConfigurationType="4"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../src,../../pjlib/src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\output\pjsip_ua_vc7_Release/pjsip_ua.pch"
+				AssemblerListingLocation=".\output\pjsip_ua_vc7_Release/"
+				ObjectFile=".\output\pjsip_ua_vc7_Release/"
+				ProgramDataBaseFileName=".\output\pjsip_ua_vc7_Release/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="3"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsip_ua_vc7s.lib"
+				SuppressStartupBanner="TRUE"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\pjsip_mod_ua\sip_dialog.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsip_mod_ua\sip_reg.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_mod_ua\sip_ua.c">
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\pjsip_ua.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_mod_ua\sip_dialog.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_mod_ua\sip_reg.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_mod_ua\sip_ua.h">
+			</File>
+			<File
+				RelativePath="..\src\pjsip_mod_ua\sip_ua_private.h">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsua.dsp
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsua.dsp	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsua.dsp	(revision 1753)
@@ -0,0 +1,118 @@
+# Microsoft Developer Studio Project File - Name="pjsua" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=pjsua - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsua.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjsua.mak" CFG="pjsua - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjsua - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "pjsua - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/pjproject/pjsip/build", RIAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjsua - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\output\pjsua_vc6_Release"
+# PROP BASE Intermediate_Dir ".\output\pjsua_vc6_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\output\pjsua_vc6_Release"
+# PROP Intermediate_Dir ".\output\pjsua_vc6_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../src" /I "../../pjlib/src" /I "../../pjmedia/src" /I "../../pjsdp/src" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /map /machine:I386 /out:"../bin/pjsua_vc6.exe" /fixed:no
+# SUBTRACT LINK32 /pdb:none /debug
+
+!ELSEIF  "$(CFG)" == "pjsua - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\output\pjsua_vc6_Debug"
+# PROP BASE Intermediate_Dir ".\output\pjsua_vc6_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\output\pjsua_vc6_Debug"
+# PROP Intermediate_Dir ".\output\pjsua_vc6_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../src" /I "../../pjlib/src" /I "../../pjmedia/src" /I "../../pjsdp/src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjsua_vc6d.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjsua - Win32 Release"
+# Name "pjsua - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\pjsua\getopt.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\misc.c
+# PROP Exclude_From_Build 1
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\pjsua\getopt.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsua.vcproj
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsua.vcproj	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/pjsua.vcproj	(revision 1753)
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="pjsua"
+	ProjectGUID="{B8500B7B-C6A8-46B9-84E6-90E200C1B35A}"
+	SccProjectName="&quot;$/pjproject&quot;, PIAAAAAA"
+	SccAuxPath=""
+	SccLocalPath="..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\.\output\pjsua_vc7_Release"
+			IntermediateDirectory=".\.\output\pjsua_vc7_Release"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="../src,../../pjlib/src,../../pjmedia/src,../../pjsdp/src"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				StringPooling="TRUE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile=".\.\output\pjsua_vc7_Release/pjsua.pch"
+				AssemblerListingLocation=".\.\output\pjsua_vc7_Release/"
+				ObjectFile=".\.\output\pjsua_vc7_Release/"
+				ProgramDataBaseFileName=".\.\output\pjsua_vc7_Release/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="3"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalOptions="/FIXED:NO"
+				AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib"
+				OutputFile="..\bin\pjsua_vc7.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="TRUE"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile=".\.\output\pjsua_vc7_Release/pjsua.pdb"
+				GenerateMapFile="TRUE"
+				MapFileName=".\.\output\pjsua_vc7_Release/pjsua.map"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\.\output\pjsua_vc7_Release/pjsua.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\.\output\pjsua_vc7_Debug"
+			IntermediateDirectory=".\.\output\pjsua_vc7_Debug"
+			ConfigurationType="1"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="FALSE"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../src,../../pjlib/src,../../pjmedia/src,../../pjsdp/src"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile=".\.\output\pjsua_vc7_Debug/pjsua.pch"
+				AssemblerListingLocation=".\.\output\pjsua_vc7_Debug/"
+				ObjectFile=".\.\output\pjsua_vc7_Debug/"
+				ProgramDataBaseFileName=".\.\output\pjsua_vc7_Debug/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				SuppressStartupBanner="TRUE"
+				DebugInformationFormat="4"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib"
+				OutputFile="..\bin\pjsua_vc7d.exe"
+				LinkIncremental="0"
+				SuppressStartupBanner="TRUE"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile=".\.\output\pjsua_vc7_Debug/pjsua.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\.\output\pjsua_vc7_Debug/pjsua.tlb"
+				HeaderFileName=""/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+			<File
+				RelativePath="..\src\pjsua\getopt.c">
+			</File>
+			<File
+				RelativePath="..\src\pjsua\main.c">
+				<FileConfiguration
+					Name="Release|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="2"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BrowseInformation="1"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32">
+					<Tool
+						Name="VCCLCompilerTool"
+						Optimization="0"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+						BasicRuntimeChecks="3"
+						BrowseInformation="1"/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjsua\misc.c">
+				<FileConfiguration
+					Name="Release|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					ExcludedFromBuild="TRUE">
+					<Tool
+						Name="VCCLCompilerTool"/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl">
+			<File
+				RelativePath="..\src\pjsua\getopt.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/build/tounix
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/build/tounix	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/build/tounix	(revision 1753)
@@ -0,0 +1,5 @@
+#!/bin/sh
+name=`basename $1`
+cp $1 /tmp
+cat /tmp/$name | tr -d '\r' > $1
+rm -f /tmp/$name
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/docs/doxygen.cfg
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/docs/doxygen.cfg	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/docs/doxygen.cfg	(revision 1753)
@@ -0,0 +1,1046 @@
+# Doxyfile 1.3-rc3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           =  PJSIP
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = docs
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, 
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en 
+# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, 
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these class will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = NO
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. It is allowed to use relative paths in the argument list.
+
+#STRIP_FROM_PATH        = "/cygdrive/e/project/bulukucing.org/pjsip/src"
+STRIP_FROM_PATH        = "/c/project/bulukucing.org/pjsip/src"
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower case letters. If set to YES upper case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments  will behave just like the Qt-style comments (thus requiring an 
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consist of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  =  src/pjsip src/pjsip_mod_ua src/pjsip_simple src
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS          = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = *_i.h
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
+INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output dir.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non empty doxygen will try to run 
+# the html help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimised for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assigments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_XML           = NO
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed.
+
+
+PREDEFINED             = PJ_DECL(x)=x PJ_DEF(x)=x PJ_IDECL(x)=x \
+
+			 PJ_IDEF(x)=x PJ_INLINE(x)=x
+
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermedate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
+
+# The CGI_NAME tag should be the name of the CGI script that 
+# starts the search engine (doxysearch) with the correct parameters. 
+# A script with this name will be generated by doxygen.
+
+CGI_NAME               = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the 
+# cgi binaries are located. See the documentation of your http daemon for 
+# details.
+
+CGI_URL                = 
+
+# The DOC_URL tag should be the absolute URL to the directory where the 
+# documentation is located. If left blank the absolute path to the 
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL                = 
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the 
+# documentation is located. If left blank the directory on the local machine 
+# will be used.
+
+DOC_ABSPATH            = 
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary 
+# is installed.
+
+BIN_ABSPATH            = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to 
+# documentation generated for other projects. This allows doxysearch to search 
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS          = 
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/print.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/print.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/print.h	(revision 1753)
@@ -0,0 +1,120 @@
+/* $Header: /pjproject/pjsip/src/pjsip/print.h 9     6/22/05 12:27a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_PRINT_H__
+#define __PJSIP_PRINT_H__
+
+#define copy_advance_check(buf,str)   \
+	do { \
+	    if ((str).slen+10 >= (endbuf-buf)) return -1;	\
+	    pj_memcpy(buf, (str).ptr, (str).slen); \
+	    buf += (str).slen; \
+	} while (0)
+
+/*
+static char *imp_copy_advance_pair(char *buf, char *endbuf, const char *str1, int len1, const pj_str_t *str2)
+{
+    if (str2->slen) {
+	int printed = len1+str2->slen;
+	if (printed+10 >= (endbuf-buf)) return NULL;
+	pj_memcpy(buf,str1,len1);
+	pj_memcpy(buf+len1, str2->ptr, str2->slen);
+	return buf + printed;
+    } else
+	return buf;
+}
+*/
+
+#define copy_advance_pair_check(buf,str1,len1,str2)   \
+	do { \
+	    if (str2.slen) { \
+		printed = len1+str2.slen; \
+		if (printed+10 >= (endbuf-buf)) return -1;	\
+		pj_memcpy(buf,str1,len1); \
+		pj_memcpy(buf+len1, str2.ptr, str2.slen); \
+		buf += printed; \
+	    } \
+	} while (0)
+/*
+#define copy_advance_pair(buf,str1,len1,str2)   \
+	do { \
+	    buf = imp_copy_advance_pair(buf, endbuf, str1, len1, &str2); \
+	    if (buf == NULL) return -1; \
+	} while (0)
+*/
+
+#define copy_advance_pair_quote_check(buf,str1,len1,str2,quotebegin,quoteend) \
+	do { \
+	    if (str2.slen) { \
+		printed = len1+str2.slen+2; \
+		if (printed+10 >= (endbuf-buf)) return -1;	\
+		pj_memcpy(buf,str1,len1); \
+		*(buf+len1)=quotebegin; \
+		pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \
+		*(buf+printed-1) = quoteend; \
+		buf += printed; \
+	    } \
+	} while (0)
+
+#define copy_advance_no_check(buf,str)   \
+	pj_memcpy(buf, (str).ptr, (str).slen); \
+	buf += (str).slen;
+
+#define copy_advance_pair_no_check(buf,str1,len1,str2)   \
+	if (str2.slen) { \
+	    pj_memcpy(buf,str1,len1); \
+	    pj_memcpy(buf+len1, str2.ptr, str2.slen); \
+	    buf += len1+str2.slen; \
+	}
+
+#define copy_advance 		copy_advance_check
+#define copy_advance_pair 	copy_advance_pair_check
+#define copy_advance_pair_quote	copy_advance_pair_quote_check
+
+#define copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend) \
+	do {	\
+	    if (str2.slen && *str2.ptr!=quotebegin) \
+		copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend); \
+	    else \
+		copy_advance_pair(buf,str1,len1,str2); \
+	} while (0)
+
+/*
+ * Internal type declarations.
+ */
+typedef void* (*pjsip_hdr_clone_fptr)(pj_pool_t *, const void*);
+typedef int   (*pjsip_hdr_print_fptr)(void *hdr, char *buf, pj_size_t len);
+
+extern const pj_str_t pjsip_hdr_names[];
+
+PJ_INLINE(void) init_hdr(void *hptr, pjsip_hdr_e htype, void *vptr)
+{
+    pjsip_hdr *hdr = hptr;
+    hdr->type = htype;
+    hdr->name = hdr->sname = pjsip_hdr_names[htype];
+    hdr->vptr = vptr;
+    pj_list_init(hdr);
+}
+
+#endif	/* __PJSIP_PRINT_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth.c	(revision 1753)
@@ -0,0 +1,809 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth.c 14    12/02/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_auth.h>
+#include <pjsip/sip_auth_parser.h>	/* just to get pjsip_DIGEST_STR */
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pj/md5.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/guid.h>
+
+/* Length of digest string. */
+#define MD5STRLEN 32
+
+/* Maximum stack size we use for storing username+realm+password etc. */
+#define MAX_TEMP  128
+
+/* A macro just to get rid of type mismatch between char and unsigned char */
+#define MD5_APPEND(pms,buf,len)	md5_append(pms, (const unsigned char*)buf, len)
+
+/* Logging. */
+#define THIS_FILE   "sip_auth.c"
+#if 0
+#  define AUTH_TRACE_(expr)  PJ_LOG(3, expr)
+#else
+#  define AUTH_TRACE_(expr)
+#endif
+
+static const char hex[] = "0123456789abcdef";
+
+/* Transform digest to string.
+ * output must be at least MD5STRLEN+1 bytes.
+ *
+ * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED!
+ */
+static void digest2str(const unsigned char digest[], char *output)
+{
+    char *p = output;
+    int i;
+
+    for (i = 0; i<16; ++i) {
+	int val = digest[i];
+	*p++ = hex[val >> 4];
+	*p++ = hex[val & 0x0F];
+    }
+}
+
+/*
+ * Create response digest based on the parameters and store the
+ * digest ASCII in 'result'. 
+ */
+static void create_digest( pj_str_t *result,
+			   const pj_str_t *nonce,
+			   const pj_str_t *nc,
+			   const pj_str_t *cnonce,
+			   const pj_str_t *qop,
+			   const pj_str_t *uri,
+			   const pjsip_cred_info *cred_info,
+			   const pj_str_t *method)
+{
+    char ha1[MD5STRLEN];
+    char ha2[MD5STRLEN];
+    unsigned char digest[16];
+    md5_state_t pms;
+
+    pj_assert(result->slen >= MD5STRLEN);
+
+    AUTH_TRACE_((THIS_FILE, "Begin creating digest"));
+
+    if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) {
+	/*** 
+	 *** ha1 = MD5(username ":" realm ":" password) 
+	 ***/
+	md5_init(&pms);
+	MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen);
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen);
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen);
+	md5_finish(&pms, digest);
+
+	digest2str(digest, ha1);
+
+    } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {
+	pj_assert(cred_info->data.slen == 32);
+	pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen );
+    }
+
+    AUTH_TRACE_((THIS_FILE, "  ha1=%.32s", ha1));
+
+    /***
+     *** ha2 = MD5(method ":" req_uri) 
+     ***/
+    md5_init(&pms);
+    MD5_APPEND( &pms, method->ptr, method->slen);
+    MD5_APPEND( &pms, ":", 1);
+    MD5_APPEND( &pms, uri->ptr, uri->slen);
+    md5_finish(&pms, digest);
+    digest2str(digest, ha2);
+
+    AUTH_TRACE_((THIS_FILE, "  ha2=%.32s", ha2));
+
+    /***
+     *** When qop is not used:
+     ***    response = MD5(ha1 ":" nonce ":" ha2) 
+     ***
+     *** When qop=auth is used:
+     ***    response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
+     ***/
+    md5_init(&pms);
+    MD5_APPEND( &pms, ha1, MD5STRLEN);
+    MD5_APPEND( &pms, ":", 1);
+    MD5_APPEND( &pms, nonce->ptr, nonce->slen);
+    if (qop && qop->slen != 0) {
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, nc->ptr, nc->slen);
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, cnonce->ptr, cnonce->slen);
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, qop->ptr, qop->slen);
+    }
+    MD5_APPEND( &pms, ":", 1);
+    MD5_APPEND( &pms, ha2, MD5STRLEN);
+
+    /* This is the final response digest. */
+    md5_finish(&pms, digest);
+    
+    /* Convert digest to string and store in chal->response. */
+    result->slen = MD5STRLEN;
+    digest2str(digest, result->ptr);
+
+    AUTH_TRACE_((THIS_FILE, "  digest=%.32s", result->ptr));
+    AUTH_TRACE_((THIS_FILE, "Digest created"));
+}
+
+/*
+ * Finds out if qop offer contains "auth" token.
+ */
+static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer)
+{
+    pj_str_t qop;
+    char *p;
+
+    pj_strdup_with_null( pool, &qop, qop_offer);
+    p = qop.ptr;
+    while (*p) {
+	*p = (char)tolower(*p);
+	++p;
+    }
+
+    p = qop.ptr;
+    while (*p) {
+	if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') {
+	    int e = *(p+4);
+	    if (e=='"' || e==',' || e==0)
+		return PJ_TRUE;
+	    else
+		p += 4;
+	} else {
+	    ++p;
+	}
+    }
+
+    return PJ_FALSE;
+}
+
+/*
+ * Generate response digest. 
+ * Most of the parameters to generate the digest (i.e. username, realm, uri,
+ * and nonce) are expected to be in the credential. Additional parameters (i.e.
+ * password and method param) should be supplied in the argument.
+ *
+ * The resulting digest will be stored in cred->response.
+ * The pool is used to allocate 32 bytes to store the digest in cred->response.
+ */
+static pj_status_t respond_digest( pj_pool_t *pool,
+				   pjsip_digest_credential *cred,
+				   const pjsip_digest_challenge *chal,
+				   const pj_str_t *uri,
+				   const pjsip_cred_info *cred_info,
+				   const pj_str_t *cnonce,
+				   pj_uint32_t nc,
+				   const pj_str_t *method)
+{
+    /* Check algorithm is supported. We only support MD5. */
+    if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR))
+    {
+	PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"",
+		  chal->algorithm.slen, chal->algorithm.ptr));
+	return -1;
+    }
+
+    /* Build digest credential from arguments. */
+    pj_strdup(pool, &cred->username, &cred_info->username);
+    pj_strdup(pool, &cred->realm, &chal->realm);
+    pj_strdup(pool, &cred->nonce, &chal->nonce);
+    pj_strdup(pool, &cred->uri, uri);
+    cred->algorithm = pjsip_MD5_STR;
+    pj_strdup(pool, &cred->opaque, &chal->opaque);
+    
+    /* Allocate memory. */
+    cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN);
+    cred->response.slen = MD5STRLEN;
+
+    if (chal->qop.slen == 0) {
+	/* Server doesn't require quality of protection. */
+
+	/* Convert digest to string and store in chal->response. */
+	create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL,
+		       uri, cred_info, method);
+
+    } else if (has_auth_qop(pool, &chal->qop)) {
+	/* Server requires quality of protection. 
+	 * We respond with selecting "qop=auth" protection.
+	 */
+	cred->qop = pjsip_AUTH_STR;
+	cred->nc.ptr = pj_pool_alloc(pool, 16);
+	sprintf(cred->nc.ptr, "%06u", nc);
+
+	if (cnonce && cnonce->slen) {
+	    pj_strdup(pool, &cred->cnonce, cnonce);
+	} else {
+	    pj_str_t dummy_cnonce = { "b39971", 6};
+	    pj_strdup(pool, &cred->cnonce, &dummy_cnonce);
+	}
+
+	create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce, 
+		       &pjsip_AUTH_STR, uri, cred_info, method );
+
+    } else {
+	/* Server requires quality protection that we don't support. */
+	PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s", 
+		  chal->qop.slen, chal->qop.ptr));
+	return -1;
+    }
+
+    return 0;
+}
+
+#if PJSIP_AUTH_QOP_SUPPORT
+/*
+ * Update authentication session with a challenge.
+ */
+static void update_digest_session( pj_pool_t *ses_pool, 
+				   pjsip_auth_session *auth_sess,
+				   const pjsip_www_authenticate_hdr *hdr )
+{
+    if (hdr->challenge.digest.qop.slen == 0)
+	return;
+
+    /* Initialize cnonce and qop if not present. */
+    if (auth_sess->cnonce.slen == 0) {
+	/* Save the whole challenge */
+	auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr);
+
+	/* Create cnonce */
+	pj_create_unique_string( ses_pool, &auth_sess->cnonce );
+
+	/* Initialize nonce-count */
+	auth_sess->nc = 1;
+
+	/* Save realm. */
+	pj_assert(auth_sess->realm.slen != 0);
+	if (auth_sess->realm.slen == 0) {
+	    pj_strdup(ses_pool, &auth_sess->realm, 
+		      &hdr->challenge.digest.realm);
+	}
+
+    } else {
+	/* Update last_nonce and nonce-count */
+	if (!pj_strcmp(&hdr->challenge.digest.nonce, 
+		       &auth_sess->last_chal->challenge.digest.nonce)) 
+	{
+	    /* Same nonce, increment nonce-count */
+	    ++auth_sess->nc;
+	} else {
+	    /* Server gives new nonce. */
+	    pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce,
+		      &hdr->challenge.digest.nonce);
+	    /* Has the opaque changed? */
+	    if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque,
+			  &hdr->challenge.digest.opaque)) 
+	    {
+		pj_strdup(ses_pool, 
+			  &auth_sess->last_chal->challenge.digest.opaque,
+			  &hdr->challenge.digest.opaque);
+	    }
+	    auth_sess->nc = 1;
+	}
+    }
+}
+#endif	/* PJSIP_AUTH_QOP_SUPPORT */
+
+
+/* Find authentication session in the list. */
+static pjsip_auth_session *find_session( pjsip_auth_session *sess_list,
+					 const pj_str_t *realm )
+{
+    pjsip_auth_session *sess = sess_list->next;
+    while (sess != sess_list) {
+	if (pj_stricmp(&sess->realm, realm) == 0)
+	    return sess;
+	sess = sess->next;
+    }
+
+    return NULL;
+}
+
+/* 
+ * Create Authorization/Proxy-Authorization response header based on the challege
+ * in WWW-Authenticate/Proxy-Authenticate header.
+ */
+PJ_DEF(pjsip_authorization_hdr*)
+pjsip_auth_respond( pj_pool_t *req_pool,
+		    const pjsip_www_authenticate_hdr *hdr,
+		    const pjsip_uri *uri,
+		    const pjsip_cred_info *cred_info,
+		    const pjsip_method *method,
+		    pj_pool_t *sess_pool,
+		    pjsip_auth_session *auth_sess)
+{
+    pjsip_authorization_hdr *auth;
+    char tmp[PJSIP_MAX_URL_SIZE];
+    pj_str_t uri_str;
+    pj_pool_t *pool;
+
+    pj_assert(hdr != NULL);
+    pj_assert(uri != NULL);
+    pj_assert(cred_info != NULL);
+    pj_assert(method != NULL);
+
+    /* Print URL in the original request. */
+    uri_str.ptr = tmp;
+    uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp));
+    if (uri_str.slen < 1) {
+	pj_assert(!"URL is too long!");
+	PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!"));
+	return NULL;
+    }
+
+#   if (PJSIP_AUTH_HEADER_CACHING)
+    {
+	pool = sess_pool;
+	PJ_UNUSED_ARG(req_pool);
+    }
+#   else
+    {
+	pool = req_pool;
+	PJ_UNUSED_ARG(sess_pool);
+    }
+#   endif
+
+    if (hdr->type == PJSIP_H_WWW_AUTHENTICATE)
+	auth = pjsip_authorization_hdr_create(pool);
+    else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE)
+	auth = pjsip_proxy_authorization_hdr_create(pool);
+    else {
+	pj_assert(0);
+	return NULL;
+    }
+
+    /* Only support digest scheme at the moment. */
+    if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+	pj_status_t rc;
+	pj_str_t *cnonce = NULL;
+	pj_uint32_t nc = 1;
+
+	/* Update the session (nonce-count etc) if required. */
+#	if PJSIP_AUTH_QOP_SUPPORT
+	{
+	    if (auth_sess) {
+		update_digest_session( sess_pool, auth_sess, hdr );
+
+		cnonce = &auth_sess->cnonce;
+		nc = auth_sess->nc;
+	    }
+	}
+#	endif	/* PJSIP_AUTH_QOP_SUPPORT */
+
+	auth->scheme = pjsip_DIGEST_STR;
+	rc = respond_digest( pool, &auth->credential.digest,
+			     &hdr->challenge.digest, &uri_str, cred_info,
+			     cnonce, nc, &method->name);
+	if (rc != 0)
+	    return NULL;
+
+	/* Set qop type in auth session the first time only. */
+	if (hdr->challenge.digest.qop.slen != 0 && auth_sess) {
+	    if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+		pj_str_t *qop_val = &auth->credential.digest.qop;
+		if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) {
+		    auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH;
+		} else {
+		    auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN;
+		}
+	    }
+	}
+    } else {
+	auth = NULL;
+    }
+
+    /* Keep the new authorization header in the cache, only
+     * if no qop is not present.
+     */
+#   if PJSIP_AUTH_HEADER_CACHING
+    {
+	if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+	    pjsip_cached_auth_hdr *cached_hdr;
+
+	    /* Delete old header with the same method. */
+	    cached_hdr = auth_sess->cached_hdr.next;
+	    while (cached_hdr != &auth_sess->cached_hdr) {
+		if (pjsip_method_cmp(method, &cached_hdr->method)==0)
+		    break;
+		cached_hdr = cached_hdr->next;
+	    }
+
+	    /* Save the header to the list. */
+	    if (cached_hdr != &auth_sess->cached_hdr) {
+		cached_hdr->hdr = auth;
+	    } else {
+		cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr));
+		pjsip_method_copy( pool, &cached_hdr->method, method);
+		cached_hdr->hdr = auth;
+		pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr );
+	    }
+	}
+    }
+#   endif
+
+    return auth;
+
+}
+
+/* Verify incoming Authorization/Proxy-Authorization header against existing
+ * credentials. Will return TRUE if the authorization request matches any of
+ * the credential.
+ */
+PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr,
+				    const pj_str_t *method,
+				    const pjsip_cred_info *cred_info )
+{
+    if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
+	char digest_buf[MD5STRLEN];
+	pj_str_t digest;
+	const pjsip_digest_credential *dig = &hdr->credential.digest;
+
+	/* Check that username match. */
+	if (pj_strcmp(&dig->username, &cred_info->username) != 0)
+	    return PJ_FALSE;
+
+	/* Check that realm match. */
+	if (pj_strcmp(&dig->realm, &cred_info->realm) != 0)
+	    return PJ_FALSE;
+
+	/* Prepare for our digest calculation. */
+	digest.ptr = digest_buf;
+	digest.slen = MD5STRLEN;
+
+	/* Create digest for comparison. */
+	create_digest(  &digest, 
+			&hdr->credential.digest.nonce,
+			&hdr->credential.digest.nc, 
+			&hdr->credential.digest.cnonce,
+			&hdr->credential.digest.qop,
+			&hdr->credential.digest.uri,
+			cred_info, 
+			method );
+
+	return pj_stricmp(&digest, &hdr->credential.digest.response) == 0;
+
+    } else {
+	pj_assert(0);
+	return PJ_FALSE;
+    }
+}
+
+/* Find credential to use for the specified realm and scheme. */
+PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
+						     const pjsip_cred_info cred[],
+						     const pj_str_t *realm,
+						     const pj_str_t *scheme)
+{
+    unsigned i;
+    PJ_UNUSED_ARG(scheme)
+    for (i=0; i<count; ++i) {
+	if (pj_stricmp(&cred[i].realm, realm) == 0)
+	    return &cred[i];
+    }
+    return NULL;
+}
+
+#if PJSIP_AUTH_AUTO_SEND_NEXT
+static void new_auth_for_req( pjsip_tx_data *tdata,
+			      pj_pool_t *sess_pool,
+			      pjsip_auth_session *sess,
+			      int cred_count,
+			      const pjsip_cred_info cred_info[])
+{
+    const pjsip_cred_info *cred;
+    pjsip_authorization_hdr *hauth;
+
+    if (sess->last_chal == NULL)
+	return;
+
+    cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm,
+				 &sess->last_chal->scheme );
+    if (!cred)
+	return;
+
+    
+    hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
+				tdata->msg->line.req.uri,
+				cred, &tdata->msg->line.req.method,
+				sess_pool, sess);
+    if (hauth) {
+	pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth);
+    }
+}
+#endif
+
+/* 
+ * Initialize new request message with authorization headers.
+ * This function will put Authorization/Proxy-Authorization headers to the
+ * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
+ * and the session has previously sent Authorization/Proxy-Authorization header
+ * with the same method, then the same Authorization/Proxy-Authorization header
+ * will be resent from the cache only if qop is not present. If the stack is 
+ * configured to automatically generate next Authorization/Proxy-Authorization
+ * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
+ * Authorization headers are calculated and generated when they are not present
+ * in the case or if authorization session has qop.
+ *
+ * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
+ * are not set, this function will do nothing. The stack then will only send
+ * Authorization/Proxy-Authorization to respond 401/407 response.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
+					 pjsip_tx_data *tdata,
+					 pjsip_auth_session *sess_list,
+					 int cred_count, 
+					 const pjsip_cred_info cred_info[])
+{
+    pjsip_auth_session *sess;
+    pjsip_method *method = &tdata->msg->line.req.method;
+
+    pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
+
+    if (!sess_list)
+	return 0;
+
+    sess = sess_list->next;
+    while (sess != sess_list) {
+	if (sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+#	    if (PJSIP_AUTH_HEADER_CACHING)
+	    {
+		pjsip_cached_auth_hdr *entry = sess->cached_hdr.next;
+		while (entry != &sess->cached_hdr) {
+		    if (pjsip_method_cmp(&entry->method, method)==0) {
+			pjsip_authorization_hdr *hauth;
+			hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr);
+			pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+		    } else {
+#			if (PJSIP_AUTH_AUTO_SEND_NEXT)
+			{
+			    new_auth_for_req( tdata, sess_pool, sess, 
+					      cred_count, cred_info);
+			}
+#			else
+			{
+			    PJ_UNUSED_ARG(sess_pool);
+			    PJ_UNUSED_ARG(cred_count);
+			    PJ_UNUSED_ARG(cred_info);
+			}
+#			endif	/* PJSIP_AUTH_AUTO_SEND_NEXT */
+		    }
+		    entry = entry->next;
+		}
+	    }
+#	    elif (PJSIP_AUTH_AUTO_SEND_NEXT)
+	    {
+		new_auth_for_req( tdata, sess_pool, sess, 
+				  cred_count, cred_info);
+	    }
+#	    else
+	    {
+		PJ_UNUSED_ARG(sess_pool);
+		PJ_UNUSED_ARG(cred_count);
+		PJ_UNUSED_ARG(cred_info);
+	    }
+#	    endif   /* PJSIP_AUTH_HEADER_CACHING */
+
+	} 
+#	if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
+	else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) {
+	    /* For qop="auth", we have to re-create the authorization header. 
+	     */
+	    const pjsip_cred_info *cred;
+	    pjsip_authorization_hdr *hauth;
+
+	    cred = pjsip_auth_find_cred( cred_count, cred_info, 
+					 &sess->realm, 
+					 &sess->last_chal->scheme);
+	    if (!cred) {
+		sess = sess->next;
+		continue;
+	    }
+
+	    hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, 
+					tdata->msg->line.req.uri, 
+					cred,
+					&tdata->msg->line.req.method,
+					sess_pool, sess );
+	    if (hauth) {
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+	    }
+	}
+#	endif	/* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */
+
+	sess = sess->next;
+    }
+    return 0;
+}
+
+/* Process authorization challenge */
+static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
+					      const pjsip_www_authenticate_hdr *hchal,
+					      const pjsip_uri *uri,
+					      pjsip_tx_data *tdata,
+					      int cred_count,
+					      const pjsip_cred_info cred_info[],
+					      pj_pool_t *ses_pool,
+					      pjsip_auth_session *auth_sess)
+{
+    const pjsip_cred_info *cred;
+    pjsip_authorization_hdr *sent_auth = NULL, *hauth;
+    pjsip_hdr *hdr;
+
+    /* See if we have sent authorization header for this realm */
+    hdr = tdata->msg->hdr.next;
+    while (hdr != &tdata->msg->hdr) {
+	if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE &&
+	     hdr->type == PJSIP_H_AUTHORIZATION) ||
+	    (hchal->type == PJSIP_H_PROXY_AUTHENTICATE &&
+	     hdr->type == PJSIP_H_PROXY_AUTHORIZATION))
+	{
+	    sent_auth = (pjsip_authorization_hdr*) hdr;
+	    if (pj_stricmp(&hchal->challenge.common.realm, 
+			   &sent_auth->credential.common.realm )==0)
+	    {
+		break;
+	    }
+	}
+	hdr = hdr->next;
+    }
+
+    /* If we have sent, see if server rejected because of stale nonce or
+     * other causes.
+     */
+    if (hdr != &tdata->msg->hdr) {
+	if (hchal->challenge.digest.stale == 0) {
+	    /* Our credential is rejected. No point in trying to re-supply
+	     * the same credential.
+	     */
+	    PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s",
+		       sent_auth->credential.digest.username.slen,
+		       sent_auth->credential.digest.username.ptr,
+		       sent_auth->credential.digest.realm.slen,
+		       sent_auth->credential.digest.realm.ptr));
+	    return NULL;
+	}
+
+	/* Otherwise remove old, stale authorization header from the mesasge.
+	 * We will supply a new one.
+	 */
+	pj_list_erase(sent_auth);
+    }
+
+    /* Find credential to be used for the challenge. */
+    cred = pjsip_auth_find_cred( cred_count, cred_info, 
+				 &hchal->challenge.common.realm, &hchal->scheme);
+    if (!cred) {
+	const pj_str_t *realm = &hchal->challenge.common.realm;
+	PJ_LOG(4,(THIS_FILE, 
+		  "Unable to set auth for %s: can not find credential for %.*s/%.*s",
+		  tdata->obj_name, 
+		  realm->slen, realm->ptr,
+		  hchal->scheme.slen, hchal->scheme.ptr));
+	return NULL;
+    }
+
+    /* Respond to authorization challenge. */
+    hauth = pjsip_auth_respond( req_pool, hchal, uri, cred, 
+				&tdata->msg->line.req.method, 
+				ses_pool, auth_sess);
+    return hauth;
+}
+
+
+/* Reinitialize outgoing request after 401/407 response is received.
+ * The purpose of this function is:
+ *  - to add a Authorization/Proxy-Authorization header.
+ *  - to put the newly created Authorization/Proxy-Authorization header
+ *    in cached_list.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt, 
+					      pj_pool_t *ses_pool, 
+					      pjsip_auth_session *sess_list,
+					      int cred_count, 
+					      const pjsip_cred_info cred_info[],
+					      pjsip_tx_data *tdata, 
+					      const pjsip_rx_data *rdata)
+{
+    const pjsip_hdr *hdr;
+    pjsip_via_hdr *via;
+
+    PJ_UNUSED_ARG(endpt)
+
+    pj_assert(rdata->msg->type == PJSIP_RESPONSE_MSG);
+    pj_assert(rdata->msg->line.status.code == 401 ||
+	      rdata->msg->line.status.code == 407 );
+
+    /*
+     * Respond to each authentication challenge.
+     */
+    hdr = rdata->msg->hdr.next;
+    while (hdr != &rdata->msg->hdr) {
+	pjsip_auth_session *sess;
+	const pjsip_www_authenticate_hdr *hchal;
+	pjsip_authorization_hdr *hauth;
+
+	/* Find WWW-Authenticate or Proxy-Authenticate header. */
+	while (hdr->type != PJSIP_H_WWW_AUTHENTICATE &&
+	       hdr->type != PJSIP_H_PROXY_AUTHENTICATE &&
+	       hdr != &rdata->msg->hdr)
+	{
+	    hdr = hdr->next;
+	}
+	if (hdr == &rdata->msg->hdr)
+	    break;
+
+	hchal = (const pjsip_www_authenticate_hdr*) hdr;
+
+	/* Find authentication session for this realm, create a new one
+	 * if not present.
+	 */
+	sess = find_session(sess_list, &hchal->challenge.common.realm );
+	if (!sess) {
+	    sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess));
+	    pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm);
+	    sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE);
+#	    if (PJSIP_AUTH_HEADER_CACHING)
+	    {
+		pj_list_init(&sess->cached_hdr);
+	    }
+#	    endif
+	    pj_list_insert_before( sess_list, sess );
+	}
+
+	/* Create authorization header for this challenge, and update
+	 * authorization session.
+	 */
+	hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, 
+			      tdata, cred_count, cred_info, ses_pool, sess );
+	if (!hauth)
+	    return NULL;
+
+	/* Add to the message. */
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+
+	/* Process next header. */
+	hdr = hdr->next;
+    }
+
+
+    /* Remove branch param in Via header. */
+    via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+    if (via)
+	via->branch_param.slen = 0;
+
+    /* Increment reference counter. */
+    pjsip_tx_data_add_ref(tdata);
+
+    /* Done. */
+    return tdata;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth.h	(revision 1753)
@@ -0,0 +1,252 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth.h 11    8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_H__
+#define __PJSIP_AUTH_SIP_AUTH_H__
+
+/**
+ * @file pjsip_auth.h
+ * @brief SIP Authorization Module.
+ */
+
+#include <pjsip/sip_config.h>
+#include <pjsip/sip_auth_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_AUTH_API Authorization API's
+ * @ingroup PJSIP_AUTH
+ * @{
+ */
+
+ /** Type of data in the credential information. */
+typedef enum pjsip_cred_data_type
+{
+    PJSIP_CRED_DATA_PLAIN_PASSWD,   /**< Plain text password.	*/
+    PJSIP_CRED_DATA_DIGEST,	    /**< Hashed digest.		*/
+} pjsip_cred_data_type;
+
+/** Authentication's quality of protection (qop) type. */
+typedef enum pjsip_auth_qop_type
+{
+    PJSIP_AUTH_QOP_NONE,	    /**< No quality of protection. */
+    PJSIP_AUTH_QOP_AUTH,	    /**< Authentication. */
+    PJSIP_AUTH_QOP_AUTH_INT,	    /**< Authentication with integrity protection. */
+    PJSIP_AUTH_QOP_UNKNOWN,	    /**< Unknown protection. */
+} pjsip_auth_qop_type;
+
+
+/** 
+ * This structure describes credential information. 
+ * A credential information is a static, persistent information that identifies
+ * username and password required to authorize to a specific realm.
+ */
+struct pjsip_cred_info
+{
+    pj_str_t    realm;		/**< Realm.	    */
+    pj_str_t	scheme;		/**< Scheme.	    */
+    pj_str_t	username;	/**< User name.	    */
+    int		data_type;	/**< Type of data.  */
+    pj_str_t	data;		/**< The data, which can be a plaintext 
+				     password or a hashed digest. */
+};
+
+/**
+ * This structure describes cached value of previously sent Authorization
+ * or Proxy-Authorization header. The authentication framework keeps a list
+ * of this structure and will resend the same header to the same server
+ * as long as the method, uri, and nonce stays the same.
+ */
+typedef struct pjsip_cached_auth_hdr
+{
+    PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr)
+
+    pjsip_method	     method;
+    pjsip_authorization_hdr *hdr;
+
+} pjsip_cached_auth_hdr;
+
+
+/**
+ * This structure describes authentication information for the specified
+ * realm. Each instance of this structure describes authentication "session"
+ * between this endpoint and remote server. This "session" information is
+ * usefull to keep information that persists for more than one challenge,
+ * such as nonce-count and cnonce value.
+ *
+ * Other than that, this structure also keeps the last authorization headers
+ * that have been sent in the cache list.
+ */
+typedef struct pjsip_auth_session
+{
+    PJ_DECL_LIST_MEMBER(struct pjsip_auth_session)
+
+    pj_str_t			 realm;
+    pj_bool_t			 is_proxy;
+    pjsip_auth_qop_type		 qop_value;
+#if PJSIP_AUTH_QOP_SUPPORT
+    pj_uint32_t			 nc;
+    pj_str_t			 cnonce;
+#endif
+#if PJSIP_AUTH_AUTO_SEND_NEXT
+    pjsip_www_authenticate_hdr	*last_chal;
+#endif
+#if PJSIP_AUTH_HEADER_CACHING
+    pjsip_cached_auth_hdr	 cached_hdr;
+#endif
+
+} pjsip_auth_session;
+
+
+/**
+ * Create authorization header for the specified credential.
+ * Application calls this function to create Authorization or Proxy-Authorization
+ * header after receiving WWW-Authenticate or Proxy-Authenticate challenge
+ * (normally in 401/407 response).
+ * If authorization session argument is specified, this function will update
+ * the session with the updated information if required (e.g. to update
+ * nonce-count when qop is "auth" or "auth-int"). This function will also
+ * save the authorization header in the session's cached header list.
+ *
+ * @param req_pool	Pool to allocate new header for the request.
+ * @param hdr		The WWW-Authenticate or Proxy-Authenticate found in 
+ *			the response.
+ * @param uri		The URI for which authorization is targeted to.
+ * @param cred_info	The credential to be used for authentication.
+ * @param method	The method.
+ * @param sess_pool	Session pool to update session or to allocate message
+ *			in the cache. May be NULL if auth_sess is NULL.
+ * @param auth_sess	If not NULL, this specifies the specific authentication
+ *			session to be used or updated.
+ *
+ * @return		The Authorization header, which can be typecasted to 
+ *			Proxy-Authorization.
+ */
+PJ_DECL(pjsip_authorization_hdr*) pjsip_auth_respond( 
+					 pj_pool_t *req_pool,
+					 const pjsip_www_authenticate_hdr *hdr,
+					 const pjsip_uri *uri,
+					 const pjsip_cred_info *cred_info,
+					 const pjsip_method *method,
+					 pj_pool_t *sess_pool,
+					 pjsip_auth_session *auth_sess);
+
+/**
+ * Verify digest in the authorization request.
+ *
+ * @param hdr		The incoming Authorization/Proxy-Authorization header.
+ * @param method	The method.
+ * @param password	The plaintext password to verify.
+ *
+ * @return		Non-zero if authorization succeed.
+ */
+PJ_DECL(pj_bool_t) pjsip_auth_verify(	const pjsip_authorization_hdr *hdr,
+					const pj_str_t *method,
+					const pjsip_cred_info *cred_info );
+
+
+/**
+ * This function can be used to find credential information which matches
+ * the specified realm.
+ *
+ * @param count		Number of credentials in the parameter.
+ * @param cred		The array of credentials.
+ * @param realm		Realm to search.
+ * @param scheme	Authentication scheme.
+ *
+ * @return		The credential which matches the specified realm.
+ */
+PJ_DECL(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
+						      const pjsip_cred_info cred[],
+						      const pj_str_t *realm,
+						      const pj_str_t *scheme );
+
+
+/**
+ * Initialize new request message with authorization headers.
+ * This function will put Authorization/Proxy-Authorization headers to the
+ * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
+ * and the session has previously sent Authorization/Proxy-Authorization header
+ * with the same method, then the same Authorization/Proxy-Authorization header
+ * will be resent from the cache only if qop is not present. If the stack is 
+ * configured to automatically generate next Authorization/Proxy-Authorization
+ * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
+ * Authorization headers are calculated and generated when they are not present
+ * in the case or if authorization session has qop.
+ *
+ * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
+ * are not set, this function will do nothing. The stack then will only send
+ * Authorization/Proxy-Authorization to respond 401/407 response.
+ *
+ * @param sess_pool	Session level pool, where memory will be allocated from
+ *			for data that persists across requests (e.g. caching).
+ * @param tdata		The request message to be initialized.
+ * @param sess_list	List of authorization sessions that have been recorded.
+ * @param cred_count	Number of credentials.
+ * @param cred_info	Array of credentials.
+ *
+ * @return		Zero if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
+					  pjsip_tx_data *tdata,
+					  pjsip_auth_session *sess_list,
+					  int cred_count, 
+					  const pjsip_cred_info cred_info[]);
+
+/**
+ * Call this function when a transaction failed with 401 or 407 response.
+ * This function will reinitialize the original request message with the
+ * authentication challenge found in the response message, and add the
+ * new authorization header in the authorization cache.
+ *
+ * Note that upon return the reference counter of the transmit data
+ * will be incremented.
+ *
+ * @param endpt		Endpoint.
+ * @param pool		The pool to allocate memory for new cred_info.
+ * @param cached_list	Cached authorization headers.
+ * @param cred_count	Number of credentials.
+ * @param cred_info	Array of credentials to use.
+ * @param tdata		The original request message, which normally can be
+ *			retrieved from tsx->last_tx.
+ * @param rdata		The response message containing 401/407 status.
+ *
+ * @return		New transmit data buffer, or NULL if the dialog
+ *			can not respond to the authorization challenge.
+ */
+PJ_DECL(pjsip_tx_data*) 
+pjsip_auth_reinit_req( pjsip_endpoint *endpt,
+		       pj_pool_t *ses_pool,
+		       pjsip_auth_session *sess_list,
+		       int cred_count, const pjsip_cred_info cred_info[],
+		       pjsip_tx_data *tdata, const pjsip_rx_data *rdata);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_AUTH_SIP_AUTH_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_msg.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_msg.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_msg.c	(revision 1753)
@@ -0,0 +1,314 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_msg.c 9     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_auth_parser.h>
+#include <pj/pool.h>
+#include <pj/list.h>
+#include <pj/string.h>
+#include <pjsip/print.h>
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Authorization and Proxy-Authorization header.
+ */
+static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
+							       const pjsip_authorization_hdr *hdr);
+static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
+								       const pjsip_authorization_hdr *hdr);
+static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
+					  char *buf, pj_size_t size);
+
+static pjsip_hdr_vptr authorization_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print,
+};
+
+
+PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool)
+{
+    pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr);
+    return hdr;
+}
+
+PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool)
+{
+    pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr);
+    return hdr;
+}
+
+static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+    
+    copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"');
+    copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm);
+    copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"');
+    //Note: there's no dbl-quote in qop in Authorization header 
+    // (unlike WWW-Authenticate)
+    //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"');
+    copy_advance_pair(buf, ", qop=", 6, cred->qop);
+    copy_advance_pair(buf, ", nc=", 5, cred->nc);
+    copy_advance(buf, cred->other_param);
+
+    return (int) (buf-startbuf);
+}
+
+static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size)
+{
+    PJ_UNUSED_ARG(cred)
+    PJ_UNUSED_ARG(buf)
+    PJ_UNUSED_ARG(size)
+    return -1;
+}
+
+static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
+					  char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+
+    copy_advance(buf, hdr->name);
+    *buf++ = ':';
+    *buf++ = ' ';
+
+    copy_advance(buf, hdr->scheme);
+    *buf++ = ' ';
+
+    if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0)
+    {
+	printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf);
+    } 
+    else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0)
+    {
+	printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf);
+    } 
+    else {
+	pj_assert(0);
+	return -1;
+    }
+
+    if (printed == -1)
+	return -1;
+
+    buf += printed;
+    *buf = '\0';
+    return (int)(buf-startbuf);
+}
+
+static pjsip_authorization_hdr* pjsip_authorization_hdr_clone(  pj_pool_t *pool,
+								const pjsip_authorization_hdr *rhs)
+{
+    /* This function also serves Proxy-Authorization header. */
+    pjsip_authorization_hdr *hdr;
+    if (rhs->type == PJSIP_H_AUTHORIZATION)
+	hdr = pjsip_authorization_hdr_create(pool);
+    else
+	hdr = pjsip_proxy_authorization_hdr_create(pool);
+
+    pj_strdup(pool, &hdr->scheme, &rhs->scheme);
+
+    if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
+	pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username);
+	pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm);
+	pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce);
+	pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri);
+	pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response);
+	pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm);
+	pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce);
+	pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque);
+	pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop);
+	pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc);
+	pj_strdup(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param);
+    } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
+	pj_assert(0);
+	return NULL;
+    } else {
+	pj_assert(0);
+	return NULL;
+    }
+
+    return hdr;
+}
+
+static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
+								       const pjsip_authorization_hdr *rhs)
+{
+    /* This function also serves Proxy-Authorization header. */
+    pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Proxy-Authenticate and WWW-Authenticate header.
+ */
+static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
+					     char *buf, pj_size_t size);
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
+								     const pjsip_www_authenticate_hdr *hdr);
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
+									     const pjsip_www_authenticate_hdr *hdr);
+
+static pjsip_hdr_vptr www_authenticate_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print,
+};
+
+
+PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool)
+{
+    pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr);
+    return hdr;
+}
+
+
+PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool)
+{
+    pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr);
+    return hdr;
+}
+
+static int print_digest_challenge( pjsip_digest_challenge *chal,
+				   char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+
+    copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"');
+    copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"');
+    copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"');
+    copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"');
+    if (chal->stale) {
+	pj_str_t true_str = { "true", 4 };
+	copy_advance_pair(buf, ",stale=", 7, true_str);
+    }
+    copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm);
+    copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"');
+    copy_advance(buf, chal->other_param);
+
+    return (int)(buf-startbuf);
+}
+
+static int print_pgp_challenge( pjsip_pgp_challenge *chal,
+			        char *buf, pj_size_t size)
+{
+    PJ_UNUSED_ARG(chal)
+    PJ_UNUSED_ARG(buf)
+    PJ_UNUSED_ARG(size)
+    return -1;
+}
+
+static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
+					     char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+
+    copy_advance(buf, hdr->name);
+    *buf++ = ':';
+    *buf++ = ' ';
+
+    copy_advance(buf, hdr->scheme);
+    *buf++ = ' ';
+
+    if (pj_stricmp2(&hdr->scheme, "digest") == 0)
+	printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf);
+    else if (pj_stricmp2(&hdr->scheme, "pgp") == 0)
+	printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf);
+    else {
+	pj_assert(0);
+	return -1;
+    }
+
+    if (printed == -1)
+	return -1;
+
+    buf += printed;
+    *buf = '\0';
+    return (int)(buf-startbuf);
+}
+
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
+								     const pjsip_www_authenticate_hdr *rhs)
+{
+    /* This function also serves Proxy-Authenticate header. */
+    pjsip_www_authenticate_hdr *hdr;
+    if (rhs->type == PJSIP_H_WWW_AUTHENTICATE)
+	hdr = pjsip_www_authenticate_hdr_create(pool);
+    else
+	hdr = pjsip_proxy_authenticate_hdr_create(pool);
+
+    pj_strdup(pool, &hdr->scheme, &rhs->scheme);
+
+    if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
+	pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm);
+	pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain);
+	pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce);
+	pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque);
+	hdr->challenge.digest.stale = rhs->challenge.digest.stale;
+	pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm);
+	pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop);
+	pj_strdup(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param);
+    } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
+	pj_assert(0);
+	return NULL;
+    } else {
+	pj_assert(0);
+	return NULL;
+    }
+
+    return hdr;
+
+}
+
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
+									     const pjsip_www_authenticate_hdr *rhs)
+{
+    /* This function also serves Proxy-Authenticate header. */
+    pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_msg.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_msg.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_msg.h	(revision 1753)
@@ -0,0 +1,214 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_msg.h 7     6/19/05 6:12p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_MSG_H__
+#define __PJSIP_AUTH_SIP_AUTH_MSG_H__
+
+#include <pjsip/sip_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_MSG_AUTHORIZATION Header Field: Authorization and Proxy-Authorization
+ * @brief Authorization and Proxy-Authorization header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Common credential.
+ */
+struct pjsip_common_credential
+{
+    pj_str_t	realm;
+};
+
+typedef struct pjsip_common_credential pjsip_common_credential;
+
+
+/**
+ * This structure describe credential used in Authorization and
+ * Proxy-Authorization header for digest authentication scheme.
+ */
+struct pjsip_digest_credential
+{
+    pj_str_t	realm;
+    pj_str_t	username;
+    pj_str_t	nonce;
+    pj_str_t	uri;
+    pj_str_t	response;
+    pj_str_t	algorithm;
+    pj_str_t	cnonce;
+    pj_str_t	opaque;
+    pj_str_t	qop;
+    pj_str_t	nc;
+    pj_str_t	other_param;
+};
+
+typedef struct pjsip_digest_credential pjsip_digest_credential;
+
+/**
+ * This structure describe credential used in Authorization and
+ * Proxy-Authorization header for PGP authentication scheme.
+ */
+struct pjsip_pgp_credential
+{
+    pj_str_t	realm;
+    pj_str_t	version;
+    pj_str_t	signature;
+    pj_str_t	signed_by;
+    pj_str_t	nonce;
+};
+
+typedef struct pjsip_pgp_credential pjsip_pgp_credential;
+
+/**
+ * This structure describes SIP Authorization header (and also SIP
+ * Proxy-Authorization header).
+ */
+struct pjsip_authorization_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_authorization_hdr)
+    pj_str_t scheme;
+    union
+    {
+	pjsip_common_credential common;
+	pjsip_digest_credential digest;
+	pjsip_pgp_credential	pgp;
+    } credential;
+};
+
+typedef struct pjsip_authorization_hdr pjsip_authorization_hdr;
+
+/** SIP Proxy-Authorization header shares the same structure as SIP
+    Authorization header.
+ */
+typedef struct pjsip_authorization_hdr pjsip_proxy_authorization_hdr;
+
+/**
+ * Create SIP Authorization header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP Authorization header.
+ */
+PJ_DECL(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool);
+
+/**
+ * Create SIP Proxy-Authorization header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP Proxy-Authorization header.
+ */
+PJ_DECL(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup PJSIP_WWW_AUTH Header Field: Proxy-Authenticate and WWW-Authenticate
+ * @brief Proxy-Authenticate and WWW-Authenticate.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+struct pjsip_common_challenge
+{
+    pj_str_t	realm;
+};
+
+typedef struct pjsip_common_challenge pjsip_common_challenge;
+
+/**
+ * This structure describes authentication challenge used in Proxy-Authenticate
+ * or WWW-Authenticate for digest authentication scheme.
+ */
+struct pjsip_digest_challenge
+{
+    pj_str_t	realm;
+    pj_str_t	domain;
+    pj_str_t	nonce;
+    pj_str_t	opaque;
+    int		stale;
+    pj_str_t	algorithm;
+    pj_str_t	qop;
+    pj_str_t	other_param;
+};
+
+typedef struct pjsip_digest_challenge pjsip_digest_challenge;
+
+/**
+ * This structure describes authentication challenge used in Proxy-Authenticate
+ * or WWW-Authenticate for PGP authentication scheme.
+ */
+struct pjsip_pgp_challenge
+{
+    pj_str_t	realm;
+    pj_str_t	version;
+    pj_str_t	micalgorithm;
+    pj_str_t	pubalgorithm;
+    pj_str_t	nonce;
+};
+
+typedef struct pjsip_pgp_challenge pjsip_pgp_challenge;
+
+/**
+ * This structure describe SIP WWW-Authenticate header (Proxy-Authenticate
+ * header also uses the same structure).
+ */
+struct pjsip_www_authenticate_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_www_authenticate_hdr)
+    pj_str_t	scheme;
+    union
+    {
+	pjsip_common_challenge	common;
+	pjsip_digest_challenge	digest;
+	pjsip_pgp_challenge	pgp;
+    } challenge;
+};
+
+typedef struct pjsip_www_authenticate_hdr pjsip_www_authenticate_hdr;
+typedef struct pjsip_www_authenticate_hdr pjsip_proxy_authenticate_hdr;
+
+
+/**
+ * Create SIP WWW-Authenticate header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP WWW-Authenticate header.
+ */
+PJ_DECL(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool);
+
+/**
+ * Create SIP Proxy-Authenticate header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP Proxy-Authenticate header.
+ */
+PJ_DECL(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_AUTH_SIP_AUTH_MSG_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_parser.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_parser.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_parser.c	(revision 1753)
@@ -0,0 +1,268 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_parser.c 7     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_auth_parser.h>
+#include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pj/string.h>
+#include <pj/except.h>
+
+static pjsip_authorization_hdr*	      parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool);
+static pjsip_proxy_authorization_hdr* parse_hdr_proxy_authorization( pj_scanner *scanner, pj_pool_t *pool);
+static pjsip_www_authenticate_hdr*    parse_hdr_www_authenticate( pj_scanner *scanner, pj_pool_t *pool);
+static pjsip_proxy_authenticate_hdr*  parse_hdr_proxy_authenticate( pj_scanner *scanner, pj_pool_t *pool);
+
+static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred);
+static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred);
+static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal);
+static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal);
+
+const pj_str_t	pjsip_USERNAME_STR =	    { "username", 8 },
+		pjsip_REALM_STR =	    { "realm", 5},
+		pjsip_NONCE_STR =	    { "nonce", 5},
+		pjsip_URI_STR =		    { "uri", 3 },
+		pjsip_RESPONSE_STR =	    { "response", 8 },
+		pjsip_ALGORITHM_STR =	    { "algorithm", 9 },
+		pjsip_DOMAIN_STR =	    { "domain", 6 },
+		pjsip_STALE_STR =	    { "stale", 5},
+		pjsip_QOP_STR =		    { "qop", 3},
+		pjsip_CNONCE_STR =	    { "cnonce", 6},
+		pjsip_OPAQUE_STR =	    { "opaque", 6},
+		pjsip_NC_STR =		    { "nc", 2},
+		pjsip_TRUE_STR =	    { "true", 4},
+		pjsip_QUOTED_TRUE_STR =	    { "\"true\"", 6},
+		pjsip_FALSE_STR =	    { "false", 5},
+		pjsip_QUOTED_FALSE_STR =    { "\"false\"", 7},
+		pjsip_DIGEST_STR =	    { "Digest", 6},
+		pjsip_QUOTED_DIGEST_STR =   { "\"Digest\"", 8},
+		pjsip_PGP_STR =		    { "PGP", 3 },
+		pjsip_QUOTED_PGP_STR =	    { "\"PGP\"", 5 },
+		pjsip_MD5_STR =		    { "md5", 3 },
+		pjsip_QUOTED_MD5_STR =	    { "\"md5\"", 5},
+		pjsip_AUTH_STR =	    { "auth", 4},
+		pjsip_QUOTED_AUTH_STR =	    { "\"auth\"", 6 };
+
+
+static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred)
+{
+    for (;;) {
+	pj_str_t name, value;
+
+	pjsip_parse_param_imp(scanner, &name, &value, PJSIP_PARSE_REMOVE_QUOTE);
+
+	if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) {
+	    cred->username = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
+	    cred->realm = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
+	    cred->nonce = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_URI_STR)) {
+	    cred->uri = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) {
+	    cred->response = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
+	    cred->algorithm = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) {
+	    cred->cnonce = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
+	    cred->opaque = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
+	    cred->qop = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_NC_STR)) {
+	    cred->nc = value;
+
+	} else {
+	    pjsip_concat_param_imp(&cred->other_param, pool, &name, &value, ',');
+	}
+
+	/* Eat comma */
+	if (!pj_scan_is_eof(scanner) && *scanner->current == ',')
+	    pj_scan_get_char(scanner);
+	else
+	    break;
+    }
+}
+
+static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred)
+{
+    PJ_UNUSED_ARG(scanner)
+    PJ_UNUSED_ARG(pool)
+    PJ_UNUSED_ARG(cred)
+
+    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal)
+{
+    for (;;) {
+	pj_str_t name, value;
+
+	pjsip_parse_param_imp(scanner, &name, &value, PJSIP_PARSE_REMOVE_QUOTE);
+
+	if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
+	    chal->realm = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) {
+	    chal->domain = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
+	    chal->nonce = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
+	    chal->opaque = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_STALE_STR)) {
+	    if (!pj_stricmp(&value, &pjsip_TRUE_STR) || !pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR))
+		chal->stale = 1;
+
+	} else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
+	    chal->algorithm = value;
+
+
+	} else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
+	    chal->qop = value;
+
+	} else {
+	    pjsip_concat_param_imp(&chal->other_param, pool, &name, &value, ',');
+	}
+
+	/* Eat comma */
+	if (!pj_scan_is_eof(scanner) && *scanner->current == ',')
+	    pj_scan_get_char(scanner);
+	else
+	    break;
+    }
+}
+
+static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal)
+{
+    PJ_UNUSED_ARG(scanner)
+    PJ_UNUSED_ARG(pool)
+    PJ_UNUSED_ARG(chal)
+
+    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool, 
+					 pjsip_authorization_hdr *hdr)
+{
+    if (*scanner->current == '"') {
+	pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
+	hdr->scheme.ptr++;
+	hdr->scheme.slen -= 2;
+    } else {
+	pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->scheme);
+    }
+
+    if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+
+	parse_digest_credential(scanner, pool, &hdr->credential.digest);
+
+    } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
+
+	parse_pgp_credential( scanner, pool, &hdr->credential.pgp);
+
+    } else {
+	PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+    }
+
+    pjsip_parse_end_hdr_imp( scanner );
+}
+
+static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool, 
+					pjsip_www_authenticate_hdr *hdr)
+{
+    if (*scanner->current == '"') {
+	pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
+	hdr->scheme.ptr++;
+	hdr->scheme.slen -= 2;
+    } else {
+	pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->scheme);
+    }
+
+    if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+
+	parse_digest_challenge(scanner, pool, &hdr->challenge.digest);
+
+    } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
+
+	parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp);
+
+    } else {
+	PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+    }
+
+    pjsip_parse_end_hdr_imp( scanner );
+}
+
+
+static pjsip_authorization_hdr *parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(pool);
+    int_parse_hdr_authorization(scanner, pool, hdr);
+    return hdr;
+}
+
+static pjsip_proxy_authorization_hdr *parse_hdr_proxy_authorization( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_proxy_authorization_hdr *hdr = pjsip_proxy_authorization_hdr_create(pool);
+    int_parse_hdr_authorization(scanner, pool, hdr);
+    return hdr;
+}
+
+static pjsip_www_authenticate_hdr *parse_hdr_www_authenticate( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_www_authenticate_hdr *hdr = pjsip_www_authenticate_hdr_create(pool);
+    int_parse_hdr_authenticate(scanner, pool, hdr);
+    return hdr;
+}
+
+static pjsip_proxy_authenticate_hdr *parse_hdr_proxy_authenticate( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_proxy_authenticate_hdr *hdr = pjsip_proxy_authenticate_hdr_create(pool);
+    int_parse_hdr_authenticate(scanner, pool, hdr);
+    return hdr;
+}
+
+
+PJ_DEF(void) pjsip_auth_init_parser()
+{
+    pjsip_register_hdr_parser( "Authorization", NULL, (pjsip_parse_hdr_func*) &parse_hdr_authorization);
+    pjsip_register_hdr_parser( "Proxy-Authorization", NULL, (pjsip_parse_hdr_func*) &parse_hdr_proxy_authorization);
+    pjsip_register_hdr_parser( "WWW-Authenticate", NULL, (pjsip_parse_hdr_func*) &parse_hdr_www_authenticate);
+    pjsip_register_hdr_parser( "Proxy-Authenticate", NULL, (pjsip_parse_hdr_func*) &parse_hdr_proxy_authenticate);
+}
+
+PJ_DEF(void) pjsip_auth_deinit_parser()
+{
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_parser.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_parser.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_auth_parser.h	(revision 1753)
@@ -0,0 +1,90 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_parser.h 7     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_PARSER_H__
+#define __PJSIP_AUTH_SIP_AUTH_PARSER_H__
+
+/**
+ * @file pjsip_auth_parser.h
+ * @brief SIP Authorization Parser Module.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_AUTH_PARSER_MODULE Authorization Parser Module
+ * @ingroup PJSIP_AUTH
+ * @{
+ */
+
+/**
+ * Initialize and register authorization parser module.
+ * This will register parser handler for various Authorization related headers
+ * such as Authorization, WWW-Authenticate, Proxy-Authorizization, and 
+ * Proxy-Authenticate headers.
+ */
+PJ_DECL(void) pjsip_auth_init_parser();
+
+/**
+ * DeInitialize authorization parser module.
+ */
+PJ_DECL(void) pjsip_auth_deinit_parser();
+
+
+extern const pj_str_t	pjsip_USERNAME_STR,
+			pjsip_REALM_STR,
+			pjsip_NONCE_STR,
+			pjsip_URI_STR,
+			pjsip_RESPONSE_STR,
+			pjsip_ALGORITHM_STR,
+			pjsip_DOMAIN_STR,
+			pjsip_STALE_STR,
+			pjsip_QOP_STR,
+			pjsip_CNONCE_STR,
+			pjsip_OPAQUE_STR,
+			pjsip_NC_STR,
+			pjsip_TRUE_STR,
+			pjsip_FALSE_STR,
+			pjsip_DIGEST_STR,
+			pjsip_PGP_STR,
+			pjsip_MD5_STR,
+			pjsip_AUTH_STR;
+/*
+extern const pj_str_t	pjsip_QUOTED_TRUE_STR,
+			pjsip_QUOTED_FALSE_STR,
+			pjsip_QUOTED_DIGEST_STR,
+			pjsip_QUOTED_PGP_STR,
+			pjsip_QUOTED_MD5_STR,
+			pjsip_QUOTED_AUTH_STR;
+*/
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_AUTH_SIP_AUTH_PARSER_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_config.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_config.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_config.h	(revision 1753)
@@ -0,0 +1,160 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_config.h 8     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_CONFIG_H__
+#define __PJSIP_SIP_CONFIG_H__
+
+#include <pj/config.h>
+
+/* Endpoint. */
+#define PJSIP_MAX_TIMER_COUNT		(2*PJSIP_MAX_TSX_COUNT + 2*PJSIP_MAX_DIALOG_COUNT)
+#define PJSIP_POOL_LEN_ENDPT		(2048+64*PJSIP_MAX_TSX_COUNT)
+#define PJSIP_POOL_INC_ENDPT		(1024)
+
+/* Transport related constants. */
+#define PJSIP_MAX_TRANSPORTS		(PJ_IOQUEUE_MAX_HANDLES)
+#define PJSIP_MAX_PKT_LEN		1500
+#define PJSIP_POOL_LEN_RDATA		2500
+#define PJSIP_POOL_INC_RDATA		512
+#define PJSIP_POOL_LEN_TRANSPORT	512
+#define PJSIP_POOL_INC_TRANSPORT	512
+#define PJSIP_POOL_LEN_TDATA		2500
+#define PJSIP_POOL_INC_TDATA		512
+#define PJSIP_POOL_LEN_UA		(64 + 32*PJSIP_MAX_DIALOG_COUNT)
+#define PJSIP_POOL_INC_UA		0
+#define PJSIP_TRANSPORT_CLOSE_TIMEOUT	30
+#define PJSIP_MAX_TRANSPORT_USAGE	16
+
+#define PJSIP_MAX_FORWARDS_VALUE	70
+
+#define PJSIP_RFC3261_BRANCH_ID		"z9hG4bK"
+#define PJSIP_RFC3261_BRANCH_LEN	7
+
+/* Message/URL related constants. */
+#define PJSIP_MAX_CALL_ID_LEN		PJ_GUID_LENGTH
+#define PJSIP_MAX_TAG_LEN		PJ_GUID_LENGTH
+#define PJSIP_MAX_BRANCH_LEN		(PJSIP_RFC3261_BRANCH_LEN + PJ_GUID_LENGTH)
+#define PJSIP_MAX_URL_SIZE		256
+#define PJSIP_MAX_HNAME_LEN		64
+
+/* Transction related constants. */
+#define PJSIP_MAX_TSX_COUNT		(16*1024)
+#define PJSIP_POOL_LEN_TSX		1536 //768
+#define PJSIP_POOL_INC_TSX		256
+#define PJSIP_MAX_TSX_KEY_LEN		(PJSIP_MAX_URL_SIZE*2)
+
+/* Dialog related constants. */
+#define PJSIP_MAX_DIALOG_COUNT		(16*1024)
+#define PJSIP_POOL_LEN_DIALOG		1200
+#define PJSIP_POOL_INC_DIALOG		512
+
+/* Module related constants. */
+#define PJSIP_MAX_MODULE		8
+
+/*****************************************************************************
+ *  Default timeout settings, in miliseconds. 
+ */
+
+//#define PJSIP_T1_TIMEOUT	15000
+//#define PJSIP_T2_TIMEOUT	60000
+
+/* T1 timeout value. */
+#if !defined(PJSIP_T1_TIMEOUT)
+#  define PJSIP_T1_TIMEOUT	500
+#endif
+
+/* T2 timeout value. */
+#if !defined(PJSIP_T2_TIMEOUT)
+#  define PJSIP_T2_TIMEOUT	4000
+#endif
+
+/* Completed timer for non-INVITE */
+#if !defined(PJSIP_T4_TIMEOUT)
+#  define PJSIP_T4_TIMEOUT	5000
+#endif
+
+/* Completed timer for INVITE */
+#if !defined(PJSIP_TD_TIMEOUT)
+#  define PJSIP_TD_TIMEOUT	32000
+#endif
+
+
+/*****************************************************************************
+ *  Authorization
+ */
+
+/*
+ * If this flag is set, the stack will keep the Authorization/Proxy-Authorization
+ * headers that are sent in a cache. Future requests with the same realm and
+ * the same method will use the headers in the cache (as long as no qop is
+ * required by server).
+ *
+ * Turning on this flag will make authorization process goes faster, but
+ * will grow the memory usage undefinitely until the dialog/registration
+ * session is terminated.
+ *
+ * Default: 1
+ */
+#if !defined(PJSIP_AUTH_HEADER_CACHING)
+#   define PJSIP_AUTH_HEADER_CACHING	    1
+#endif
+
+/*
+ * If this flag is set, the stack will proactively send Authorization/Proxy-
+ * Authorization header for next requests. If next request has the same method
+ * with any of previous requests, then the last header which is saved in
+ * the cache will be used (if PJSIP_AUTH_CACHING is set). Otherwise a fresh
+ * header will be recalculated. If a particular server has requested qop, then
+ * a fresh header will always be calculated.
+ *
+ * If this flag is NOT set, then the stack will only send Authorization/Proxy-
+ * Authorization headers when it receives 401/407 response from server.
+ *
+ * Turning ON this flag will grow memory usage of a dialog/registration pool
+ * indefinitely until it is terminated, because the stack needs to keep the
+ * last WWW-Authenticate/Proxy-Authenticate challenge.
+ *
+ * Default: 1
+ */
+#if !defined(PJSIP_AUTH_AUTO_SEND_NEXT)
+#   define PJSIP_AUTH_AUTO_SEND_NEXT	    1
+#endif
+
+/*
+ * Support qop="auth" directive.
+ * This option also requires client to cache the last challenge offered by
+ * server.
+ *
+ * Default: 1
+ */
+#if !defined(PJSIP_AUTH_QOP_SUPPORT)
+#   define PJSIP_AUTH_QOP_SUPPORT	    1
+#endif
+
+
+#include <pj/config.h>
+#include <pj/compat.h>
+
+
+#endif	/* __PJSIP_SIP_CONFIG_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_endpoint.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_endpoint.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_endpoint.c	(revision 1753)
@@ -0,0 +1,1052 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_endpoint.c 24    6/23/05 12:20a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_private.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_misc.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/hash.h>
+
+
+#define PJSIP_EX_NO_MEMORY  PJ_NO_MEMORY_EXCEPTION
+#define LOG_THIS	    "endpoint..."
+
+#define MAX_METHODS   32
+
+/**
+ * The SIP endpoint.
+ */
+struct pjsip_endpoint
+{
+    /** Pool to allocate memory for the endpoint. */
+    pj_pool_t		*pool;
+
+    /** Mutex for the pool, hash table, and event list/queue. */
+    pj_mutex_t		*mutex;
+
+    /** Pool factory. */
+    pj_pool_factory	*pf;
+
+    /** Transaction table. */
+    pj_hash_table_t	*tsx_table;
+
+    /** Mutex for transaction table. */
+    pj_mutex_t		*tsx_table_mutex;
+
+    /** Timer heap. */
+    pj_timer_heap_t	*timer_heap;
+
+    /** Transport manager. */
+    pjsip_transport_mgr *transport_mgr;
+
+    /** DNS Resolver. */
+    pjsip_resolver_t	*resolver;
+
+    /** Number of modules registered. */
+    pj_uint32_t		 mod_count;
+
+    /** Modules. */
+    pjsip_module        *modules[PJSIP_MAX_MODULE];
+
+    /** Number of supported methods. */
+    unsigned		 method_cnt;
+
+    /** Array of supported methods. */
+    const pjsip_method	*methods[MAX_METHODS];
+
+    /** Allow header. */
+    pjsip_allow_hdr	*allow_hdr;
+
+    /** Route header list. */
+    pjsip_route_hdr	 route_hdr_list;
+
+    /** Additional request headers. */
+    pjsip_hdr		 req_hdr;
+};
+
+
+
+/*
+ * Prototypes.
+ */
+static void endpt_transport_callback( pjsip_endpoint *, pjsip_rx_data *rdata );
+
+
+/*
+ * Create transaction.
+ * Defined in sip_transaction.c
+ */
+pjsip_transaction * pjsip_tsx_create( pj_pool_t *pool, pjsip_endpoint *endpt);
+
+/*
+ * This is the global handler for memory allocation failure, for pools that
+ * are created by the endpoint (by default, all pools ARE allocated by 
+ * endpoint). The error is handled by throwing exception, and hopefully,
+ * the exception will be handled by the application (or this library).
+ */
+static void pool_callback( pj_pool_t *pool, pj_size_t size )
+{
+    PJ_UNUSED_ARG(pool)
+    PJ_UNUSED_ARG(size)
+
+    PJ_THROW(PJSIP_EX_NO_MEMORY);
+}
+
+
+/*
+ * Initialize modules.
+ */
+static pj_status_t init_modules( pjsip_endpoint *endpt )
+{
+    pj_status_t status;
+    unsigned i;
+    //pj_str_t str_COMMA = { ", ", 2 };
+    extern pjsip_module aux_tsx_module;
+
+    PJ_LOG(5, (LOG_THIS, "init_modules()"));
+
+    /* Load static modules. */
+    endpt->mod_count = PJSIP_MAX_MODULE;
+    status = register_static_modules( &endpt->mod_count, endpt->modules );
+    if (status != 0) {
+	return status;
+    }
+
+    /* Add mini aux module. */
+    endpt->modules[endpt->mod_count++] = &aux_tsx_module;
+
+    /* Load dynamic modules. */
+    // Not supported yet!
+
+    /* Sort modules on the priority. */
+    for (i=endpt->mod_count-1; i>0; --i) {
+	pj_uint32_t max = 0;
+	unsigned j;
+	for (j=1; j<=i; ++j) {
+	    if (endpt->modules[j]->priority > endpt->modules[max]->priority)
+		max = j;
+	}
+	if (max != i) {
+	    pjsip_module *temp = endpt->modules[max];
+	    endpt->modules[max] = endpt->modules[i];
+	    endpt->modules[i] = temp;
+	}
+    }
+
+    /* Initialize each module. */
+    for (i=0; i < endpt->mod_count; ++i) {
+	int j;
+
+	pjsip_module *mod = endpt->modules[i];
+	if (mod->init_module) {
+	    status = mod->init_module(endpt, mod, i);
+	    if (status != 0) {
+		return status;
+	    }
+	}
+
+	/* Collect all supported methods from modules. */
+	for (j=0; j<mod->method_cnt; ++j) {
+	    unsigned k;
+	    for (k=0; k<endpt->method_cnt; ++k) {
+		if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0)
+		    break;
+	    }
+	    if (k == endpt->method_cnt) {
+		if (endpt->method_cnt < MAX_METHODS) {
+		    endpt->methods[endpt->method_cnt++] = mod->methods[j];
+		} else {
+		    PJ_LOG(1,(LOG_THIS, "Too many methods"));
+		    return -1;
+		}
+	    }
+	}
+    }
+
+    /* Create Allow header. */
+    endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool );
+    endpt->allow_hdr->count = endpt->method_cnt;
+    for (i=0; i<endpt->method_cnt; ++i) {
+	endpt->allow_hdr->values[i] = endpt->methods[i]->name;
+    }
+
+    /* Start each module. */
+    for (i=0; i < endpt->mod_count; ++i) {
+	pjsip_module *mod = endpt->modules[i];
+	if (mod->start_module) {
+	    status = mod->start_module(mod);
+	    if (status != 0) {
+		return status;
+	    }
+	}
+    }
+
+    /* Done. */
+    return 0;
+}
+
+/*
+ * Unregister the transaction from the hash table, and destroy the resources
+ * from the transaction.
+ */
+PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
+				      pjsip_transaction *tsx)
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name));
+
+    pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED);
+
+    /* No need to lock transaction. 
+     * This function typically is called from the transaction callback, which
+     * means that transaction mutex is being held.
+     */
+    pj_assert( pj_mutex_is_locked(tsx->mutex) );
+
+    /* Lock endpoint. */
+    pj_mutex_lock( endpt->tsx_table_mutex );
+
+    /* Unregister from the hash table. */
+    pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr, 
+		 tsx->transaction_key.slen, NULL);
+
+    /* Unlock endpoint mutex. */
+    pj_mutex_unlock( endpt->tsx_table_mutex );
+
+    /* Destroy transaction mutex. */
+    pj_mutex_destroy( tsx->mutex );
+
+    /* Release the pool for the transaction. */
+    pj_pool_release(tsx->pool);
+
+    PJ_LOG(4, (LOG_THIS, "tsx%p destroyed", tsx));
+}
+
+
+/*
+ * Receive transaction events from transactions and dispatch them to the 
+ * modules.
+ */
+static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt)
+{
+    unsigned i;
+
+    /* Dispatch event to modules. */
+    for (i=0; i<endpt->mod_count; ++i) {
+	pjsip_module *mod = endpt->modules[i];
+	if (mod && mod->tsx_handler) {
+	    mod->tsx_handler( mod, evt );
+	}
+    }
+
+    /* Destroy transaction if it is terminated. */
+    if (evt->type == PJSIP_EVENT_TSX_STATE_CHANGED && 
+	evt->obj.tsx->state == PJSIP_TSX_STATE_DESTROYED) 
+    {
+	/* No need to lock mutex. Mutex is locked inside the destroy function */
+	pjsip_endpt_destroy_tsx( endpt, evt->obj.tsx );
+    }
+}
+
+/*
+ * Receive transaction events from transactions and put in the event queue
+ * to be processed later.
+ */
+void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt )
+{
+    endpt_do_event(endpt, evt);
+}
+
+/*
+ * Get "Allow" header.
+ */
+PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt )
+{
+    return endpt->allow_hdr;
+}
+
+/*
+ * Get additional headers to be put in outgoing request message.
+ */
+PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt)
+{
+    return &endpt->req_hdr;
+}
+
+PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
+					     int url_cnt, const pj_str_t url[])
+{
+    int i;
+    pjsip_route_hdr *hdr;
+    pj_str_t str_ROUTE = { "Route", 5 };
+
+    /* Lock endpoint mutex. */
+    pj_mutex_lock(endpt->mutex);
+
+    pj_list_init(&endpt->route_hdr_list);
+
+    for (i=0; i<url_cnt; ++i) {
+	int len = url[i].slen;
+	char *dup = pj_pool_alloc(endpt->pool, len + 1);
+	pj_memcpy(dup, url[i].ptr, len);
+	dup[len] = '\0';
+
+	hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL);
+	if (!hdr) {
+	    pj_mutex_unlock(endpt->mutex);
+	    PJ_LOG(4,(LOG_THIS, "Invalid URL %s in proxy URL", dup));
+	    return -1;
+	}
+
+	pj_assert(hdr->type == PJSIP_H_ROUTE);
+	pj_list_insert_before(&endpt->route_hdr_list, hdr);
+    }
+
+    /* Unlock endpoint mutex. */
+    pj_mutex_unlock(endpt->mutex);
+
+    return 0;
+}
+
+/*
+ * Get "Route" header list.
+ */
+PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt )
+{
+    return &endpt->route_hdr_list;
+}
+
+
+/*
+ * Initialize endpoint.
+ */
+PJ_DEF(pjsip_endpoint*) pjsip_endpt_create(pj_pool_factory *pf)
+{
+    pj_status_t status;
+    pj_pool_t *pool;
+    pjsip_endpoint *endpt;
+    pjsip_max_forwards_hdr *mf_hdr;
+
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create()"));
+
+    /* Create pool */
+    pool = pj_pool_create(pf, "pept%p", 
+			  PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT,
+			  &pool_callback);
+    if (!pool)
+	return NULL;
+
+    /* Create endpoint. */
+    endpt = pj_pool_calloc(pool, 1, sizeof(*endpt));
+    endpt->pool = pool;
+    endpt->pf = pf;
+
+    /* Create mutex for the events, etc. */
+    endpt->mutex = pj_mutex_create( endpt->pool, "ept%p", 0 );
+    if (!endpt->mutex) {
+	PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating endpoint mutex"));
+	goto on_error;
+    }
+
+    /* Create mutex for the transaction table. */
+    endpt->tsx_table_mutex = pj_mutex_create( endpt->pool, "mtbl%p", 0);
+    if (!endpt->tsx_table_mutex) {
+	PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating endpoint mutex(2)"));
+	goto on_error;
+    }
+
+    /* Create hash table for transaction. */
+    endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT );
+    if (!endpt->tsx_table) {
+	PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating tsx hash table"));
+	goto on_error;
+    }
+
+    /* Create timer heap to manage all timers within this endpoint. */
+    endpt->timer_heap = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT, 0);
+    if (!endpt->timer_heap) {
+	PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating timer heap"));
+	goto on_error;
+    }
+
+    /* Create transport manager. */
+    endpt->transport_mgr = pjsip_transport_mgr_create( endpt->pool,
+						       endpt,
+						       &endpt_transport_callback);
+    if (!endpt->transport_mgr) {
+	PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating transport mgr"));
+	goto on_error;
+    }
+
+    /* Create asynchronous DNS resolver. */
+    endpt->resolver = pjsip_resolver_create(endpt->pool);
+    if (!endpt->resolver) {
+	PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating resolver"));
+	goto on_error;
+    }
+
+    /* Initialize TLS ID for transaction lock. */
+    pjsip_tsx_lock_tls_id = pj_thread_local_alloc();
+    if (pjsip_tsx_lock_tls_id == -1) {
+	PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error allocating TLS"));
+	goto on_error;
+    }
+    pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
+
+    /* Initialize request headers. */
+    pj_list_init(&endpt->req_hdr);
+
+    /* Initialist "Route" header list. */
+    pj_list_init(&endpt->route_hdr_list);
+
+    /* Add "Max-Forwards" for request header. */
+    mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool);
+    mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE;
+    pj_list_insert_before( &endpt->req_hdr, mf_hdr);
+
+    /* Load and init modules. */
+    status = init_modules(endpt);
+    if (status != PJ_OK) {
+	PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error in init_modules()"));
+	return NULL;
+    }
+
+    /* Done. */
+    return endpt;
+
+on_error:
+    if (endpt->transport_mgr) {
+	pjsip_transport_mgr_destroy(endpt->transport_mgr);
+	endpt->transport_mgr = NULL;
+    }
+    if (endpt->mutex) {
+	pj_mutex_destroy(endpt->mutex);
+	endpt->mutex = NULL;
+    }
+    if (endpt->tsx_table_mutex) {
+	pj_mutex_destroy(endpt->tsx_table_mutex);
+	endpt->tsx_table_mutex = NULL;
+    }
+    pj_pool_release( endpt->pool );
+
+    PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init() failed"));
+    return NULL;
+}
+
+/*
+ * Destroy endpoint.
+ */
+PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt)
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_destroy()"));
+
+    /* Shutdown and destroy all transports. */
+    pjsip_transport_mgr_destroy(endpt->transport_mgr);
+
+    /* Delete endpoint mutex. */
+    pj_mutex_destroy(endpt->mutex);
+
+    /* Delete transaction table mutex. */
+    pj_mutex_destroy(endpt->tsx_table_mutex);
+
+    /* Finally destroy pool. */
+    pj_pool_release(endpt->pool);
+}
+
+/*
+ * Create new pool.
+ */
+PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
+					       const char *pool_name,
+					       pj_size_t initial,
+					       pj_size_t increment )
+{
+    pj_pool_t *pool;
+
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_pool()"));
+
+    /* Lock endpoint mutex. */
+    pj_mutex_lock(endpt->mutex);
+
+    /* Create pool */
+    pool = pj_pool_create( endpt->pf, pool_name,
+			   initial, increment, &pool_callback);
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(endpt->mutex);
+
+    if (pool) {
+	PJ_LOG(5, (LOG_THIS, "   pool %s created", pj_pool_getobjname(pool)));
+    } else {
+	PJ_LOG(4, (LOG_THIS, "Unable to create pool %s!", pool_name));
+    }
+
+    return pool;
+}
+
+/*
+ * Return back pool to endpoint's pool manager to be either destroyed or
+ * recycled.
+ */
+PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool )
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool)));
+
+    pj_mutex_lock(endpt->mutex);
+    pj_pool_release( pool );
+    pj_mutex_unlock(endpt->mutex);
+}
+
+/*
+ * Handle events.
+ */
+PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
+					const pj_time_val *max_timeout)
+{
+    pj_time_val timeout;
+    int i;
+
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_handle_events()"));
+
+    /* Poll the timer. The timer heap has its own mutex for better 
+     * granularity, so we don't need to lock end endpoint. We also keep
+     * polling the timer while we have events.
+     */
+    timeout.sec = timeout.msec = 0; /* timeout is 'out' var. This just to make compiler happy. */
+    for (i=0; i<10; ++i) {
+	if (pj_timer_heap_poll( endpt->timer_heap, &timeout ) < 1)
+	    break;
+    }
+
+    /* If caller specifies maximum time to wait, then compare the value with
+     * the timeout to wait from timer, and use the minimum value.
+     */
+    if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
+	timeout = *max_timeout;
+    }
+
+    /* Poll events in the transport manager. */
+    pjsip_transport_mgr_handle_events( endpt->transport_mgr, &timeout);
+}
+
+/*
+ * Schedule timer.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
+						pj_timer_entry *entry,
+						const pj_time_val *delay )
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)",
+			 entry, delay->sec, delay->msec));
+    return pj_timer_heap_schedule( endpt->timer_heap, entry, delay );
+}
+
+/*
+ * Cancel the previously registered timer.
+ */
+PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, 
+				       pj_timer_entry *entry )
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_cancel_timer(entry=%p)", entry));
+    pj_timer_heap_cancel( endpt->timer_heap, entry );
+}
+
+/*
+ * Create a new transaction.
+ * Endpoint must then initialize the new transaction as either UAS or UAC, and
+ * register it to the hash table.
+ */
+PJ_DEF(pjsip_transaction*) pjsip_endpt_create_tsx(pjsip_endpoint *endpt)
+{
+    pj_pool_t *pool;
+    pjsip_transaction *tsx;
+
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_tsx()"));
+
+    /* Request one pool for the transaction. Mutex is locked there. */
+    pool = pjsip_endpt_create_pool(endpt, "ptsx%p", 
+				      PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX);
+    if (pool == NULL) {
+	PJ_LOG(2, (LOG_THIS, "failed to create transaction (no pool)"));
+	return NULL;
+    }
+
+    /* Create the transaction. */
+    tsx = pjsip_tsx_create(pool, endpt);
+
+    /* Return */
+    return tsx;
+}
+
+/*
+ * Register the transaction to the endpoint.
+ * This will put the transaction to the transaction hash table. Before calling
+ * this function, the transaction must be INITIALIZED as either UAS or UAC, so
+ * that the transaction key is built.
+ */
+PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
+				       pjsip_transaction *tsx)
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_register_tsx(%s)", tsx->obj_name));
+
+    pj_assert(tsx->transaction_key.slen != 0);
+    //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
+
+    /* Lock hash table mutex. */
+    pj_mutex_lock(endpt->tsx_table_mutex);
+
+    /* Register the transaction to the hash table. */
+    pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr,
+		 tsx->transaction_key.slen, tsx);
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(endpt->tsx_table_mutex);
+}
+
+/*
+ * Find transaction by the key.
+ */
+PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
+					          const pj_str_t *key )
+{
+    pjsip_transaction *tsx;
+
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_find_tsx()"));
+
+    /* Start lock mutex in the endpoint. */
+    pj_mutex_lock(endpt->tsx_table_mutex);
+
+    /* Find the transaction in the hash table. */
+    tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen );
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(endpt->tsx_table_mutex);
+
+    return tsx;
+}
+
+/*
+ * Create key.
+ */
+static void rdata_create_key( pjsip_rx_data *rdata)
+{
+    pjsip_role_e role;
+    if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+	role = PJSIP_ROLE_UAS;
+    } else {
+	role = PJSIP_ROLE_UAC;
+    }
+    pjsip_tsx_create_key(rdata->pool, &rdata->key, role,
+			 &rdata->cseq->method, rdata);
+}
+
+/*
+ * This is the callback that is called by the transport manager when it 
+ * receives a message from the network.
+ */
+static void endpt_transport_callback( pjsip_endpoint *endpt,
+				      pjsip_rx_data *rdata )
+{
+    pjsip_msg *msg = rdata->msg;
+    pjsip_transaction *tsx;
+    pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
+
+    PJ_LOG(5, (LOG_THIS, "endpt_transport_callback(rdata=%p)", rdata));
+
+    /* For response, check that the value in Via sent-by match the transport.
+     * If not matched, silently drop the response.
+     * Ref: RFC3261 Section 18.1.2 Receiving Response
+     */
+    if (msg->type == PJSIP_RESPONSE_MSG) {
+	const pj_sockaddr_in *addr;
+	const char *addr_addr;
+	int port = rdata->via->sent_by.port;
+	pj_bool_t mismatch = PJ_FALSE;
+	if (port == 0) {
+	    int type;
+	    type = pjsip_transport_get_type(rdata->transport);
+	    port = pjsip_transport_get_default_port_for_type(type);
+	}
+	addr = pjsip_transport_get_addr_name(rdata->transport);
+	addr_addr = pj_sockaddr_get_str_addr(addr);
+	if (pj_strcmp2(&rdata->via->sent_by.host, addr_addr) != 0)
+	    mismatch = PJ_TRUE;
+	else if (port != pj_sockaddr_get_port(addr)) {
+	    /* Port or address mismatch, we should discard response */
+	    /* But we saw one implementation (we don't want to name it to 
+	     * protect the innocence) which put wrong sent-by port although
+	     * the "rport" parameter is correct.
+	     * So we discard the response only if the port doesn't match
+	     * both the port in sent-by and rport. We try to be lenient here!
+	     */
+	    if (rdata->via->rport_param != pj_sockaddr_get_port(addr))
+		mismatch = PJ_TRUE;
+	    else {
+		PJ_LOG(4,(LOG_THIS, "Response %p has mismatch port in sent-by"
+				    " but the rport parameter is correct",
+				    rdata));
+	    }
+	}
+
+	if (mismatch) {
+	    pjsip_event e;
+
+	    PJ_LOG(3, (LOG_THIS, "Response %p discarded: sent-by mismatch",
+				 rdata));
+
+	    e.type = PJSIP_EVENT_DISCARD_MSG;
+	    e.src_type = PJSIP_EVENT_RX_MSG;
+	    e.src.rdata = rdata;
+	    e.obj.ptr = NULL;
+	    endpt_do_event( endpt, &e );
+	    return;
+	}
+    } 
+
+    /* Create key for transaction lookup. */
+    rdata_create_key( rdata);
+
+    /* Find the transaction for the received message. */
+    PJ_LOG(5, (LOG_THIS, "finding tsx with key=%.*s", 
+			 rdata->key.slen, rdata->key.ptr));
+
+    /* Start lock mutex in the endpoint. */
+    pj_mutex_lock(endpt->tsx_table_mutex);
+
+    /* Find the transaction in the hash table. */
+    tsx = pj_hash_get( endpt->tsx_table, rdata->key.ptr, rdata->key.slen );
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(endpt->tsx_table_mutex);
+
+    /* If the transaction is not found... */
+    if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+	/* 
+	 * For response message, discard the message, except if the response is
+	 * an 2xx class response to INVITE, which in this case it must be
+	 * passed to TU to be acked.
+	 */
+	if (msg->type == PJSIP_RESPONSE_MSG) {
+
+	    /* Inform TU about the 200 message, only if it's INVITE. */
+	    if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
+		rdata->cseq->method.id == PJSIP_INVITE_METHOD) 
+	    {
+		pjsip_event e;
+
+		/* Should not happen for UA. Tsx theoritically lives until
+		 * all responses are absorbed.
+		 */
+		pj_assert(0);
+
+		e.type = PJSIP_EVENT_RX_200_RESPONSE;
+		e.src_type = PJSIP_EVENT_RX_MSG;
+		e.src.rdata = rdata;
+		e.obj.ptr = NULL;
+		endpt_do_event( endpt, &e );
+
+	    } else {
+		/* Just discard the response, inform TU. */
+		pjsip_event e;
+
+		PJ_LOG(3, (LOG_THIS, "Response %p discarded: transaction not found",
+			   rdata));
+
+		e.type = PJSIP_EVENT_DISCARD_MSG;
+		e.src_type = PJSIP_EVENT_RX_MSG;
+		e.src.rdata = rdata;
+		e.obj.ptr = NULL;
+		endpt_do_event( endpt, &e );
+	    }
+
+	/*
+	 * For non-ACK request message, create a new transaction.
+	 */
+	} else if (rdata->msg->line.req.method.id != PJSIP_ACK_METHOD) {
+	    /* Create transaction, mutex is locked there. */
+	    tsx = pjsip_endpt_create_tsx(endpt);
+	    if (!tsx)
+		return;
+
+	    /* Initialize transaction as UAS. */
+	    pjsip_tsx_init_uas( tsx, rdata );
+
+	    /* Register transaction, mutex is locked there. */
+	    pjsip_endpt_register_tsx( endpt, tsx );
+
+	    a_new_transaction_just_been_created = PJ_TRUE;
+	}
+    }
+
+    /* If transaction is found (or newly created), pass the message.
+     * Otherwise if it's an ACK request, pass directly to TU.
+     */
+    if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) {
+	/* Dispatch message to transaction. */
+	pjsip_tsx_on_rx_msg( tsx, rdata );
+
+    } else if (rdata->msg->line.req.method.id == PJSIP_ACK_METHOD) {
+	/*
+	 * This is an ACK message, but the INVITE transaction could not
+	 * be found (possibly because the branch parameter in Via in ACK msg
+	 * is different than the branch in original INVITE). This happens with
+	 * SER!
+	 */
+	pjsip_event event;
+
+	event.type = PJSIP_EVENT_RX_ACK_MSG;
+	event.src_type = PJSIP_EVENT_RX_MSG;
+	event.src.rdata = rdata;
+	event.obj.ptr = NULL;
+	endpt_do_event( endpt, &event );
+    }
+
+    /*
+     * If a new request message has just been receieved, but no modules
+     * seem to be able to handle the request message, then terminate the
+     * transaction.
+     *
+     * Ideally for cases like "unsupported method", we should be able to
+     * answer the request statelessly. But we can not do that since the
+     * endpoint shoule be able to be used as both user agent and proxy stack,
+     * and a proxy stack should be able to handle arbitrary methods.
+     */
+    if (a_new_transaction_just_been_created && tsx->status_code < 100) {
+	/* Certainly no modules has sent any response message.
+	 * Check that any modules has attached a module data.
+	 */
+	int i;
+	for (i=0; i<PJSIP_MAX_MODULE; ++i) {
+	    if (tsx->module_data[i] != NULL) {
+		break;
+	    }
+	}
+	if (i == PJSIP_MAX_MODULE) {
+	    /* No modules have attached itself to the transaction. 
+	     * Terminate the transaction with 501/Not Implemented.
+	     */
+	    pjsip_tx_data *tdata;
+	    
+	    if (tsx->method.id == PJSIP_OPTIONS_METHOD) {
+		tdata = pjsip_endpt_create_response(endpt, rdata, 200);
+	    } else {
+		tdata = pjsip_endpt_create_response(endpt, rdata, 
+						    PJSIP_SC_METHOD_NOT_ALLOWED);
+	    }
+	    if (endpt->allow_hdr) {
+		pjsip_msg_add_hdr( tdata->msg, 
+				   pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr));
+	    }
+	    pjsip_tsx_on_tx_msg( tsx, tdata );
+
+	} else {
+	    /*
+	     * If a module has registered itself in the transaction but it
+	     * hasn't responded the request, chances are the module wouldn't
+	     * respond to the request at all. We terminate the request here
+	     * with 500/Internal Server Error, to be safe.
+	     */
+	    pjsip_tx_data *tdata;
+	    tdata = pjsip_endpt_create_response(endpt, rdata, 500);
+	    pjsip_tsx_on_tx_msg(tsx, tdata);
+	}
+    }
+}
+
+/*
+ * Create transmit data buffer.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_tdata( pjsip_endpoint *endpt )
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_tdata()"));
+    return pjsip_tx_data_create(endpt->transport_mgr);
+}
+
+/*
+ * Resolve
+ */
+PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
+				  pj_pool_t *pool,
+				  pjsip_host_port *target,
+				  void *token,
+				  pjsip_resolver_callback *cb)
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_resolve()"));
+    pjsip_resolve( endpt->resolver, pool, target, token, cb);
+}
+
+/*
+ * Find/create transport.
+ */
+PJ_DEF(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt,
+					pj_pool_t *pool,
+					pjsip_transport_type_e type,
+					const pj_sockaddr_in *remote,
+					void *token,
+					pjsip_transport_completion_callback *cb)
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_get_transport()"));
+    pjsip_transport_get( endpt->transport_mgr, pool, type,
+			 remote, token, cb);
+}
+
+
+PJ_DEF(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt,
+						 pjsip_transport_type_e type,
+						 pj_sockaddr_in *addr,
+						 const pj_sockaddr_in *addr_name)
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_listener()"));
+    return pjsip_create_listener( endpt->transport_mgr, type, addr, addr_name );
+}
+
+PJ_DEF(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt,
+						     pj_sock_t sock,
+						     const pj_sockaddr_in *addr_name)
+{
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_udp_listener()"));
+    return pjsip_create_udp_listener( endpt->transport_mgr, sock, addr_name );
+}
+
+PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+    unsigned count;
+    pj_hash_iterator_t itr_val;
+    pj_hash_iterator_t *itr;
+
+    PJ_LOG(5, (LOG_THIS, "pjsip_endpt_dump()"));
+
+    /* Lock mutex. */
+    pj_mutex_lock(endpt->mutex);
+
+    PJ_LOG(3, (LOG_THIS, "Dumping endpoint %p:", endpt));
+    
+    /* Dumping pool factory. */
+    (*endpt->pf->dump_status)(endpt->pf, detail);
+
+    /* Pool health. */
+    PJ_LOG(3, (LOG_THIS," Endpoint pool capacity=%u, used_size=%u",
+	       pj_pool_get_capacity(endpt->pool),
+	       pj_pool_get_used_size(endpt->pool)));
+
+    /* Transaction tables. */
+    count = pj_hash_count(endpt->tsx_table);
+    PJ_LOG(3, (LOG_THIS, " Number of transactions: %u", count));
+
+    if (count && detail) {
+	pj_hash_iterator_t it_val;
+	pj_hash_iterator_t *it;
+	pj_time_val now;
+
+	PJ_LOG(3, (LOG_THIS, " Dumping transaction tables:"));
+
+	pj_gettimeofday(&now);
+	it = pj_hash_first(endpt->tsx_table, &it_val);
+
+	while (it != NULL) {
+	    int timeout_diff;
+
+	    /* Get the transaction. No need to lock transaction's mutex
+	     * since we already hold endpoint mutex, so that no transactions
+	     * will be deleted.
+	     */
+	    pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it);
+
+	    const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC");
+	
+	    if (tsx->timeout_timer._timer_id != -1) {
+		if (tsx->timeout_timer._timer_value.sec > now.sec) {
+		    timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec;
+		} else {
+		    timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec;
+		    timeout_diff = 0 - timeout_diff;
+		}
+	    } else {
+		timeout_diff = -1;
+	    }
+
+	    PJ_LOG(3, (LOG_THIS, "  %s %s %10.*s %.9u %s t=%ds", 
+		       tsx->obj_name, role, 
+		       tsx->method.name.slen, tsx->method.name.ptr,
+		       tsx->cseq,
+		       pjsip_tsx_state_str(tsx->state),
+		       timeout_diff));
+
+	    it = pj_hash_next(endpt->tsx_table, it);
+	}
+    }
+
+    /* Transports. 
+     * Note: transport is not properly locked in this function.
+     *       See pjsip_transport_first, pjsip_transport_next.
+     */
+    itr = pjsip_transport_first( endpt->transport_mgr, &itr_val );
+    if (itr) {
+	PJ_LOG(3, (LOG_THIS, " Dumping transports:"));
+
+	do {
+	    char src_addr[128], dst_addr[128];
+	    int src_port, dst_port;
+	    const pj_sockaddr_in *addr;
+	    pjsip_transport_t *t;
+
+	    t = pjsip_transport_this(endpt->transport_mgr, itr);
+	    addr = pjsip_transport_get_local_addr(t);
+	    strcpy(src_addr, pj_sockaddr_get_str_addr(addr));
+	    src_port = pj_sockaddr_get_port(addr);
+
+	    addr = pjsip_transport_get_remote_addr(t);
+	    strcpy(dst_addr, pj_sockaddr_get_str_addr(addr));
+	    dst_port = pj_sockaddr_get_port(addr);
+
+	    PJ_LOG(3, (LOG_THIS, "  %s %s %s:%d --> %s:%d (refcnt=%d)", 
+		       pjsip_transport_get_type_name(t),
+		       pjsip_transport_get_obj_name(t),
+		       src_addr, src_port,
+		       dst_addr, dst_port,
+		       pjsip_transport_get_ref_cnt(t)));
+
+	    itr = pjsip_transport_next(endpt->transport_mgr, itr);
+	} while (itr);
+    }
+
+    /* Timer. */
+    PJ_LOG(3,(LOG_THIS, " Timer heap has %u entries", 
+			pj_timer_heap_count(endpt->timer_heap)));
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(endpt->mutex);
+#else
+    PJ_LOG(3,(LOG_THIS, "pjsip_end_dump: can't dump because it's disabled."));
+#endif
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_endpoint.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_endpoint.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_endpoint.h	(revision 1753)
@@ -0,0 +1,370 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_endpoint.h 12    6/22/05 12:27a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_ENDPOINT_H__
+#define __PJSIP_SIP_ENDPOINT_H__
+
+/**
+ * @file sip_endpoint.h
+ * @brief SIP Endpoint.
+ */
+
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_resolve.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP SIP Stack Core
+ * Implementation of core SIP protocol stack processing.
+ */
+
+/**
+ * @defgroup PJSIP_ENDPT SIP Endpoint
+ * @ingroup PJSIP
+ * @brief
+ * Representation of SIP node instance.
+ *
+ * SIP Endpoint instance (pjsip_endpoint) can be viewed as the master/owner of
+ * all SIP objects in an application. It performs the following roles:
+ *  - it manages the allocation/deallocation of memory pools for all objects.
+ *  - it manages listeners and transports, and how they are used by transactions.
+ *  - it owns transaction hash table.
+ *  - it receives incoming messages from transport layer and automatically
+ *    dispatches them to the correct transaction (or create a new one).
+ *  - it has a single instance of timer management (timer heap).
+ *  - it manages modules, which is the primary means of extending the library.
+ *  - it provides single polling function for all objects and distributes events.
+ *  - it provides SIP policy such as which outbound proxy to use for all
+ *    outgoing SIP request messages.
+ *  - it automatically handles incoming requests which can not be handled by
+ *    existing modules (such as when incoming request has unsupported method).
+ *  - and so on..
+ *
+ * Theoritically application can have multiple instances of SIP endpoint, 
+ * although it's not clear why application may want to do it.
+ *
+ * @{
+ */
+
+/**
+ * Create an instance of SIP endpoint from the specified pool factory.
+ * The pool factory reference then will be kept by the endpoint, so that future
+ * memory allocations by SIP components will be taken from the same pool factory.
+ *
+ * @param pf	Pool factory that will be used for the lifetime of endpoint.
+ *
+ * @return the endpoint instance on success.
+ */
+PJ_DECL(pjsip_endpoint*) pjsip_endpt_create(pj_pool_factory *pf);
+
+/**
+ * Destroy endpoint instance. Application must make sure that all pending
+ * transactions have been terminated properly, because this function does not
+ * check for the presence of pending transactions.
+ *
+ * @param endpt		The SIP endpoint to be destroyed.
+ */
+PJ_DECL(void) pjsip_endpt_destroy(pjsip_endpoint *endpt);
+
+/**
+ * Poll for events. Application must call this function periodically to ensure
+ * that all events from both transports and timer heap are handled in timely
+ * manner.  This function, like all other endpoint functions, is thread safe, 
+ * and application may have more than one thread concurrently calling this function.
+ *
+ * @param endpt		The endpoint.
+ * @param max_timeout	Maximum time to wait for events, or NULL to wait forever
+ *			until event is received.
+ */
+PJ_DECL(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt, 
+					 const pj_time_val *max_timeout);
+
+/**
+ * Dump endpoint status to the log. This will print the status to the log
+ * with log level 3.
+ *
+ * @param endpt		The endpoint.
+ * @param detail	If non zero, then it will dump a detailed output.
+ *			BEWARE that this option may crash the system because
+ *			it tries to access all memory pools.
+ */
+PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail );
+
+/**
+ * Create pool from the endpoint. All SIP components should allocate their
+ * memory pool by calling this function, to make sure that the pools are
+ * allocated from the same pool factory. This function, like all other endpoint
+ * functions, is thread safe.
+ *
+ * @param endpt		The SIP endpoint.
+ * @param pool_name	Name to be assigned to the pool.
+ * @param initial	The initial size of the pool.
+ * @param increment	The resize size.
+ * @return		Memory pool, or NULL on failure.
+ *
+ * @see pj_pool_create
+ */
+PJ_DECL(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
+					     const char *pool_name,
+					     pj_size_t initial,
+					     pj_size_t increment );
+
+/**
+ * Return back pool to endpoint to be released back to the pool factory.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt	    The endpoint.
+ * @param pool	    The pool to be destroyed.
+ */
+PJ_DECL(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt,
+					pj_pool_t *pool );
+
+/**
+ * Schedule timer to endpoint's timer heap. Application must poll the endpoint
+ * periodically (by calling #pjsip_endpt_handle_events) to ensure that the
+ * timer events are handled in timely manner. When the timeout for the timer
+ * has elapsed, the callback specified in the entry argument will be called.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt	    The endpoint.
+ * @param entry	    The timer entry.
+ * @param delay	    The relative delay of the timer.
+ * @return	    PJ_OK (zero) if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
+						 pj_timer_entry *entry,
+						 const pj_time_val *delay );
+
+/**
+ * Cancel the previously registered timer.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt	    The endpoint.
+ * @param entry	    The timer entry previously registered.
+ */
+PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, 
+					pj_timer_entry *entry );
+
+/**
+ * Create a new transaction. After creating the transaction, application MUST
+ * initialize the transaction as either UAC or UAS (by calling
+ * #pjsip_tsx_init_uac or #pjsip_tsx_init_uas), then must register the 
+ * transaction to endpoint with #pjsip_endpt_register_tsx.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt	    The SIP endpoint.
+ * @return	    The new transaction, or NULL on failure.
+ */
+PJ_DECL(pjsip_transaction*) pjsip_endpt_create_tsx(pjsip_endpoint *endpt);
+
+/**
+ * Register the transaction to the endpoint's transaction table.
+ * Before the transaction is registered, it must have been initialized as
+ * either UAS or UAC by calling #pjsip_tsx_init_uac or #pjsip_tsx_init_uas.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt	    The SIP endpoint.
+ * @param tsx	    The transaction.
+ */
+PJ_DECL(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
+					pjsip_transaction *tsx);
+
+/**
+ * Forcefull destroy the transaction.
+ * The only time where application needs to call this function is when the
+ * transaction fails to initialize in #pjsip_tsx_init_uac or
+ * #pjsip_tsx_init_uas. For other cases. the transaction will be destroyed
+ * automaticly by endpoint.
+ *
+ * @param endpt	    The endpoint.
+ * @param tsx	    The transaction to destroy.
+ */
+PJ_DECL(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
+				      pjsip_transaction *tsx);
+
+/**
+ * Create a new transmit data buffer.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt the endpoint.
+ * @return new transmit data.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_tdata( pjsip_endpoint *endpt );
+
+/**
+ * Asynchronously resolve a SIP target host or domain according to rule 
+ * specified in RFC 3263 (Locating SIP Servers). When the resolving operation
+ * has completed, the callback will be called.
+ *
+ * Note: at the moment we don't have implementation of RFC 3263 yet!
+ *
+ * @param resolver  The resolver engine.
+ * @param pool	    The pool to allocate resolver job.
+ * @param target    The target specification to be resolved.
+ * @param token	    A user defined token to be passed back to callback function.
+ * @param cb	    The callback function.
+ */
+PJ_DECL(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
+				   pj_pool_t *pool,
+				   pjsip_host_port *target,
+				   void *token,
+				   pjsip_resolver_callback *cb);
+
+/**
+ * Find a SIP transport suitable for sending SIP message to the specified
+ * address. This function will complete asynchronously when the transport is
+ * ready (for example, when TCP socket is connected), and when it completes,
+ * the callback will be called with the status of the operation.
+ *
+ * @see pjsip_transport_get
+ */
+PJ_DECL(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt,
+					 pj_pool_t *pool,
+					 pjsip_transport_type_e type,
+					 const pj_sockaddr_in *remote,
+					 void *token,
+					 pjsip_transport_completion_callback *cb);
+
+/**
+ * Create listener a new transport listener. A listener is transport object
+ * that is capable of receiving SIP messages. For UDP listener, normally
+ * application should use #pjsip_endpt_create_udp_listener instead if the 
+ * application has already created the socket.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param type	    Transport type (eg. UDP, TCP, etc.)
+ * @param addr	    The bound address of the transport.
+ * @param addr_name The address to be advertised in SIP messages. For example,
+ *		    the bound address can be 0.0.0.0, but the advertised address
+ *		    normally will be the IP address of the host.
+ *
+ * @return	    Zero if listener is created successfully.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt,
+						  pjsip_transport_type_e type,
+						  pj_sockaddr_in *addr,
+						  const pj_sockaddr_in *addr_name);
+
+/**
+ * Create UDP listener. For UDP, normally the application would create the
+ * socket by itself (for STUN purpose), then it can register the socket as
+ * listener by calling this function.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param sock	    The socket handle.
+ * @param addr_name The address to be advertised in SIP message. If the socket
+ *		    has been resolved with STUN, then application may specify 
+ *		    the mapped address in this parameter.
+ *
+ * @return	    Zero if listener is created successfully.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt,
+						      pj_sock_t sock,
+						      const pj_sockaddr_in *addr_name);
+
+/**
+ * Get additional headers to be put in outgoing request message. 
+ * This function is normally called by transaction layer when sending outgoing
+ * requests.
+ * 
+ * @param endpt	    The endpoint.
+ *
+ * @return	    List of additional headers to be put in outgoing requests.
+ */
+PJ_DECL(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt);
+
+/**
+ * Get "Allow" header from endpoint. The endpoint builds the "Allow" header
+ * from the list of methods supported by modules.
+ *
+ * @param endpt	    The endpoint.
+ *
+ * @return	    "Allow" header, or NULL if endpoint doesn't have "Allow" header.
+ */
+PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt );
+
+
+/**
+ * Find transaction in endpoint's transaction table by the transaction's key.
+ * This function normally is only used by modules. The key for a transaction
+ * can be created by calling #pjsip_tsx_create_key.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param key	    Transaction key, as created with #pjsip_tsx_create_key.
+ *
+ * @return	    The transaction, or NULL if it's not found.
+ */
+PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
+					          const pj_str_t *key );
+
+/**
+ * Set list of SIP proxies to be visited for all outbound request messages.
+ * Application can call this function to specify how outgoing request messages
+ * should be routed. For example, if outgoing requests should go through an
+ * outbound proxy, then application can specify the URL of the proxy when
+ * calling this function. More than one proxy can be specified, and the
+ * order of which proxy is specified when calling this function specifies
+ * the order of which proxy will be visited first by the request messages.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param url_cnt   Number of proxies/URLs in the array.
+ * @param url	    Array of proxy URL, which specifies the order of which
+ *		    proxy will be visited first (e.g. url[0] will be visited
+ *		    before url[1]).
+ *
+ * @return	    Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
+					      int url_cnt, const pj_str_t url[]);
+
+/**
+ * Get the list of "Route" header that are configured for this endpoint.
+ * The "Route" header specifies how outbound request messages will be sent,
+ * and is built when application sets the outbound proxy.
+ *
+ * @param endpt	    The endpoint instance.
+ *
+ * @return	    List of "Route" header.
+ */
+PJ_DECL(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt );
+
+/**
+ * @}
+ */
+
+/*
+ * Internal functions.
+ */
+/*
+ * Receive transaction events from transactions and put in the event queue
+ * to be processed later.
+ */
+void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt );
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_SIP_ENDPOINT_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_event.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_event.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_event.h	(revision 1753)
@@ -0,0 +1,153 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_event.h 5     6/17/05 11:16p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_EVENT_H__
+#define __PJSIP_SIP_EVENT_H__
+
+/**
+ * @file sip_event.h
+ * @brief SIP Event
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_EVENT SIP Event
+ * @ingroup PJSIP
+ * @{
+ */
+#include <pj/types.h>
+
+
+/** 
+ * Event IDs.
+ */
+typedef enum pjsip_event_id_e
+{
+    /** Unidentified event. */
+    PJSIP_EVENT_UNIDENTIFIED,
+
+    /** Timer event, normally only used internally in transaction. */
+    PJSIP_EVENT_TIMER,
+
+    /** Message transmission event. */
+    PJSIP_EVENT_TX_MSG,
+
+    /** Message received event. */
+    PJSIP_EVENT_RX_MSG,
+
+    /** Transport error event. */
+    PJSIP_EVENT_TRANSPORT_ERROR,
+
+    /** Transaction state changed event. */
+    PJSIP_EVENT_TSX_STATE_CHANGED,
+
+    /** 2xx response received event. */
+    PJSIP_EVENT_RX_200_RESPONSE,
+
+    /** ACK request received event. */
+    PJSIP_EVENT_RX_ACK_MSG,
+
+    /** Message discarded event. */
+    PJSIP_EVENT_DISCARD_MSG,
+
+    /** Indicates that the event was triggered by user action. */
+    PJSIP_EVENT_USER,
+
+    /** On before transmitting message. */
+    PJSIP_EVENT_BEFORE_TX,
+
+} pjsip_event_id_e;
+
+
+/**
+ * \struct
+ * \brief Event descriptor to fully identify a SIP event.
+ *
+ * Events are the only way for a lower layer object to inform something
+ * to higher layer objects. Normally this is achieved by means of callback,
+ * i.e. the higher layer objects register a callback to handle the event on
+ * the lower layer objects.
+ *
+ * This event descriptor is used for example by transactions, to inform
+ * endpoint about events, and by transports, to inform endpoint about
+ * unexpected transport error.
+ */
+struct pjsip_event
+{
+    /** This is necessary so that we can put events as a list. */
+    PJ_DECL_LIST_MEMBER(struct pjsip_event)
+
+    /** The event type, can be any value of \b pjsip_event_id_e.
+     *  @see pjsip_event_id_e
+     */
+    pjsip_event_id_e type;
+
+    /** This field determines what is the content of \b src (source data). 
+     */
+    pjsip_event_id_e src_type;
+
+    /** Source data, which content is dependent on \b src_type.
+     *  - if src_type==PJSIP_EVENT_RX_MSG, src.rdata is valid.
+     *  - if src_type==PJSIP_EVENT_TX_MSG, src.tdata is valid.
+     *  - if src_type==PJSIP_EVENT_TIMER, src.timer is valid.
+     */
+    union
+    {
+	pjsip_rx_data	*rdata;
+	pjsip_tx_data	*tdata;
+	pj_timer_entry	*timer;
+	void		*data;
+	unsigned long	 udata;
+    } src;
+
+    /** The object that generates this event. */
+    union
+    {
+	pjsip_transaction  *tsx;
+	void		   *ptr;
+	unsigned long	    udata;
+    } obj;
+
+    /** Other data. */
+    union
+    {
+	long		    long_data;
+	void *		    ptr_data;
+    } data;
+};
+
+/**
+ * Get the event string from the event ID.
+ * @param e the event ID.
+ * @notes defined in sip_misc.c
+ */
+PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_SIP_EVENT_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_misc.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_misc.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_misc.c	(revision 1753)
@@ -0,0 +1,700 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_misc.c 13    8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_module.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/pool.h>
+#include <pj/except.h>
+
+#define LOG_THIS    "endpoint..."
+
+static const char *event_str[] = 
+{
+    "UNIDENTIFIED",
+    "TIMER",
+    "TX_MSG",
+    "RX_MSG",
+    "TRANSPORT_ERROR",
+    "TSX_STATE",
+    "RX_2XX_RESPONSE",
+    "RX_ACK",
+    "DISCARD_MSG",
+    "USER",
+    "BEFORE_TX",
+};
+
+static pj_str_t str_TEXT = { "text", 4},
+		str_PLAIN = { "plain", 5 };
+static int aux_mod_id;
+
+struct aux_tsx_data
+{
+    void *token;
+    void (*cb)(void*,pjsip_event*);
+};
+
+static pj_status_t aux_tsx_init( pjsip_endpoint *endpt,
+				 struct pjsip_module *mod, pj_uint32_t id )
+{
+    PJ_UNUSED_ARG(endpt)
+    PJ_UNUSED_ARG(mod)
+
+    aux_mod_id = id;
+    return 0;
+}
+
+static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+    pjsip_transaction *tsx = event->obj.tsx;
+    struct aux_tsx_data *tsx_data;
+
+    PJ_UNUSED_ARG(mod)
+
+    if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED)
+	return;
+    if (tsx == NULL)
+	return;
+    if (tsx->module_data[aux_mod_id] == NULL)
+	return;
+    if (tsx->status_code < 200)
+	return;
+
+    /* Call the callback, if any, and prevent the callback to be called again
+     * by clearing the transaction's module_data.
+     */
+    tsx_data = tsx->module_data[aux_mod_id];
+    tsx->module_data[aux_mod_id] = NULL;
+
+    if (tsx_data->cb) {
+	(*tsx_data->cb)(tsx_data->token, event);
+    }
+}
+
+pjsip_module aux_tsx_module = 
+{
+    { "Aux-Tsx", 7},	    /* Name.		*/
+    0,			    /* Flag		*/
+    128,		    /* Priority		*/
+    NULL,		    /* Arbitrary data.	*/
+    0,			    /* Number of methods supported (none). */
+    { 0 },		    /* Array of methods (none) */
+    &aux_tsx_init,	    /* init_module()	*/
+    NULL,		    /* start_module()	*/
+    NULL,		    /* deinit_module()	*/
+    &aux_tsx_handler,	    /* tsx_handler()	*/
+};
+
+PJ_DEF(pj_status_t) pjsip_endpt_send_request(  pjsip_endpoint *endpt,
+					       pjsip_tx_data *tdata,
+					       int timeout,
+					       void *token,
+					       void (*cb)(void*,pjsip_event*))
+{
+    pjsip_transaction *tsx;
+    struct aux_tsx_data *tsx_data;
+
+    tsx = pjsip_endpt_create_tsx(endpt);
+    if (!tsx) {
+	pjsip_tx_data_dec_ref(tdata);
+	return -1;
+    }
+
+    tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
+    tsx_data->token = token;
+    tsx_data->cb = cb;
+    tsx->module_data[aux_mod_id] = tsx_data;
+
+    if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
+	pjsip_endpt_destroy_tsx(endpt, tsx);
+	pjsip_tx_data_dec_ref(tdata);
+	return -1;
+    }
+
+    pjsip_endpt_register_tsx(endpt, tsx);
+    pjsip_tx_data_invalidate_msg(tdata);
+    pjsip_tsx_on_tx_msg(tsx, tdata);
+    pjsip_tx_data_dec_ref(tdata);
+    return 0;
+}
+
+/*
+ * Initialize transmit data (msg) with the headers and optional body.
+ * This will just put the headers in the message as it is. Be carefull
+ * when calling this function because once a header is put in a message, 
+ * it CAN NOT be put in other message until the first message is deleted, 
+ * because the way the header is put in the list.
+ * That's why the session will shallow_clone it's headers before calling
+ * this function.
+ */
+static void init_request_throw( pjsip_tx_data *tdata, 
+				pjsip_method *method,
+				pjsip_uri *param_target,
+				pjsip_from_hdr *param_from,
+				pjsip_to_hdr *param_to, 
+				pjsip_contact_hdr *param_contact,
+				pjsip_cid_hdr *param_call_id,
+				pjsip_cseq_hdr *param_cseq, 
+				const pj_str_t *param_text)
+{
+    pjsip_msg *msg;
+    pjsip_msg_body *body;
+
+    /* Create the message. */
+    msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+
+    /* Init request URI. */
+    pj_memcpy(&msg->line.req.method, method, sizeof(*method));
+    msg->line.req.uri = param_target;
+
+    /* Add From header. */
+    if (param_from->tag.slen == 0)
+	pj_create_unique_string(tdata->pool, &param_from->tag);
+    pjsip_msg_add_hdr(msg, (void*)param_from);
+
+    /* Add To header. */
+    pjsip_msg_add_hdr(msg, (void*)param_to);
+
+    /* Add Contact header. */
+    if (param_contact) {
+	pjsip_msg_add_hdr(msg, (void*)param_contact);
+    }
+
+    /* Add Call-ID header. */
+    pjsip_msg_add_hdr(msg, (void*)param_call_id);
+
+    /* Add CSeq header. */
+    pjsip_msg_add_hdr(msg, (void*)param_cseq);
+
+    /* Create message body. */
+    if (param_text) {
+	body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body));
+	body->content_type.type = str_TEXT;
+	body->content_type.subtype = str_PLAIN;
+	body->data = pj_pool_alloc(tdata->pool, param_text->slen );
+	pj_memcpy(body->data, param_text->ptr, param_text->slen);
+	body->len = param_text->slen;
+	body->print_body = &pjsip_print_text_body;
+	msg->body = body;
+    }
+}
+
+/*
+ * Create arbitrary request.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_request(  pjsip_endpoint *endpt, 
+						    const pjsip_method *method,
+						    const pj_str_t *param_target,
+						    const pj_str_t *param_from,
+						    const pj_str_t *param_to, 
+						    const pj_str_t *param_contact,
+						    const pj_str_t *param_call_id,
+						    int param_cseq, 
+						    const pj_str_t *param_text)
+{
+    pjsip_uri *target;
+    pjsip_tx_data *tdata;
+    pjsip_from_hdr *from;
+    pjsip_to_hdr *to;
+    pjsip_contact_hdr *contact;
+    pjsip_cseq_hdr *cseq = NULL;    /* = NULL, warning in VC6 */
+    pjsip_cid_hdr *call_id;
+    pj_str_t tmp;
+    PJ_USE_EXCEPTION;
+
+    PJ_LOG(5,(LOG_THIS, "Entering pjsip_endpt_create_request()"));
+
+    tdata = pjsip_endpt_create_tdata(endpt);
+    if (!tdata)
+	return NULL;
+
+    /* Init reference counter to 1. */
+    pjsip_tx_data_add_ref(tdata);
+
+    PJ_TRY {
+	/* Request target. */
+	pj_strdup_with_null(tdata->pool, &tmp, param_target);
+	target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0);
+	if (target == NULL) {
+	    PJ_LOG(4,(LOG_THIS, "Error creating request: invalid target %s", 
+		      tmp.ptr));
+	    goto on_error;
+	}
+
+	/* From */
+	from = pjsip_from_hdr_create(tdata->pool);
+	pj_strdup_with_null(tdata->pool, &tmp, param_from);
+	from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 
+				     PJSIP_PARSE_URI_AS_NAMEADDR);
+	if (from->uri == NULL) {
+	    PJ_LOG(4,(LOG_THIS, "Error creating request: invalid 'From' URI '%s'",
+				tmp.ptr));
+	    goto on_error;
+	}
+	pj_create_unique_string(tdata->pool, &from->tag);
+
+	/* To */
+	to = pjsip_to_hdr_create(tdata->pool);
+	pj_strdup_with_null(tdata->pool, &tmp, param_to);
+	to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 
+				   PJSIP_PARSE_URI_AS_NAMEADDR);
+	if (to->uri == NULL) {
+	    PJ_LOG(4,(LOG_THIS, "Error creating request: invalid 'To' URI '%s'",
+				tmp.ptr));
+	    goto on_error;
+	}
+
+	/* Contact. */
+	if (param_contact) {
+	    contact = pjsip_contact_hdr_create(tdata->pool);
+	    pj_strdup_with_null(tdata->pool, &tmp, param_contact);
+	    contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
+					    PJSIP_PARSE_URI_AS_NAMEADDR);
+	    if (contact->uri == NULL) {
+		PJ_LOG(4,(LOG_THIS, 
+			  "Error creating request: invalid 'Contact' URI '%s'",
+			  tmp.ptr));
+		goto on_error;
+	    }
+	} else {
+	    contact = NULL;
+	}
+
+	/* Call-ID */
+	call_id = pjsip_cid_hdr_create(tdata->pool);
+	if (param_call_id != NULL && param_call_id->slen)
+	    pj_strdup(tdata->pool, &call_id->id, param_call_id);
+	else
+	    pj_create_unique_string(tdata->pool, &call_id->id);
+
+	/* CSeq */
+	cseq = pjsip_cseq_hdr_create(tdata->pool);
+	if (param_cseq >= 0)
+	    cseq->cseq = param_cseq;
+	else
+	    cseq->cseq = pj_rand() & 0xFFFF;
+
+	/* Method */
+	pjsip_method_copy(tdata->pool, &cseq->method, method);
+
+	/* Create the request. */
+	init_request_throw( tdata, &cseq->method, target, from, to, contact, 
+			    call_id, cseq, param_text);
+    }
+    PJ_DEFAULT {
+	PJ_LOG(4,(LOG_THIS, "Caught exception %d when creating request", 
+			    PJ_GET_EXCEPTION()));
+	goto on_error;
+    }
+    PJ_END
+
+    PJ_LOG(4,(LOG_THIS, "Request %s (%d %.*s) created.", 
+		        tdata->obj_name, 
+			cseq->cseq, 
+			cseq->method.name.slen,
+			cseq->method.name.ptr));
+
+    return tdata;
+
+on_error:
+    pjsip_tx_data_dec_ref(tdata);
+    return NULL;
+}
+
+PJ_DEF(pjsip_tx_data*)
+pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+				     const pjsip_method *method,
+				     const pjsip_uri *param_target,
+				     const pjsip_from_hdr *param_from,
+				     const pjsip_to_hdr *param_to,
+				     const pjsip_contact_hdr *param_contact,
+				     const pjsip_cid_hdr *param_call_id,
+				     int param_cseq,
+				     const pj_str_t *param_text )
+{
+    pjsip_uri *target;
+    pjsip_tx_data *tdata;
+    pjsip_from_hdr *from;
+    pjsip_to_hdr *to;
+    pjsip_contact_hdr *contact;
+    pjsip_cid_hdr *call_id;
+    pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */
+    PJ_USE_EXCEPTION;
+
+    PJ_LOG(5,(LOG_THIS, "Entering pjsip_endpt_create_request_from_hdr()"));
+
+    tdata = pjsip_endpt_create_tdata(endpt);
+    if (!tdata)
+	return NULL;
+
+    pjsip_tx_data_add_ref(tdata);
+
+    PJ_TRY {
+	target = pjsip_uri_clone(tdata->pool, param_target);
+	from = pjsip_hdr_shallow_clone(tdata->pool, param_from);
+	pjsip_fromto_set_from(from);
+	to = pjsip_hdr_shallow_clone(tdata->pool, param_to);
+	pjsip_fromto_set_to(to);
+	if (param_contact)
+	    contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact);
+	else
+	    contact = NULL;
+	call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id);
+	cseq = pjsip_cseq_hdr_create(tdata->pool);
+	if (param_cseq >= 0)
+	    cseq->cseq = param_cseq;
+	else
+	    cseq->cseq = pj_rand() % 0xFFFF;
+	pjsip_method_copy(tdata->pool, &cseq->method, method);
+
+	init_request_throw(tdata, &cseq->method, target, from, to, contact, 
+			   call_id, cseq, param_text);
+    }
+    PJ_DEFAULT {
+	PJ_LOG(4,(LOG_THIS, "Caught exception %d when creating request", 
+			    PJ_GET_EXCEPTION()));
+	goto on_error;
+    }
+    PJ_END;
+
+    PJ_LOG(4,(LOG_THIS, "Request %s (%d %.*s) created.", 
+			tdata->obj_name, 
+			cseq->cseq, 
+			cseq->method.name.slen,
+			cseq->method.name.ptr));
+    return tdata;
+
+on_error:
+    pjsip_tx_data_dec_ref(tdata);
+    return NULL;
+}
+
+/*
+ * Construct a minimal response message for the received request.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_response( pjsip_endpoint *endpt,
+						    const pjsip_rx_data *rdata,
+						    int code)
+{
+    pjsip_tx_data *tdata;
+    pjsip_msg *msg, *req_msg;
+    pjsip_hdr *hdr;
+    pjsip_via_hdr *via;
+    pjsip_rr_hdr *rr;
+
+    /* rdata must be a request message. */
+    req_msg = rdata->msg;
+    pj_assert(req_msg->type == PJSIP_REQUEST_MSG);
+
+    /* Log this action. */
+    PJ_LOG(5,(LOG_THIS, "pjsip_endpt_create_response(rdata=%p, code=%d)", 
+		         rdata, code));
+
+    /* Create a new transmit buffer. */
+    tdata = pjsip_endpt_create_tdata( endpt );
+    if (!tdata)
+	return NULL;
+
+    /* Create new response message. */
+    tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
+
+    /* Set status code and reason text. */
+    msg->line.status.code = code;
+    msg->line.status.reason = *pjsip_get_status_text(code);
+
+    /* Set TX data attributes. */
+    tdata->rx_timestamp = rdata->timestamp;
+
+    /* Copy all the via headers, in order. */
+    via = rdata->via;
+    while (via) {
+	pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via));
+	via = via->next;
+	if (via != (void*)&req_msg->hdr)
+	    via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via);
+	else
+	    break;
+    }
+
+    /* Copy all Record-Route headers, in order. */
+    rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL);
+    while (rr) {
+	pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr));
+	rr = rr->next;
+	if (rr != (void*)&req_msg->hdr)
+	    rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr);
+	else
+	    break;
+    }
+
+    /* Copy Call-ID header. */
+    hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL);
+    pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr));
+
+    /* Copy From header. */
+    hdr = pjsip_hdr_clone(tdata->pool, rdata->from);
+    pjsip_msg_add_hdr( msg, hdr);
+
+    /* Copy To header. */
+    hdr = pjsip_hdr_clone(tdata->pool, rdata->to);
+    pjsip_msg_add_hdr( msg, hdr);
+
+    /* Copy CSeq header. */
+    hdr = pjsip_hdr_clone(tdata->pool, rdata->cseq);
+    pjsip_msg_add_hdr( msg, hdr);
+
+    /* All done. */
+    return tdata;
+}
+
+
+/*
+ * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of
+ * RFC3261). Note that the generation of ACK for 2xx response is different,
+ * and one must not use this function to generate such ACK.
+ */
+PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
+				    pjsip_tx_data *tdata,
+				    const pjsip_rx_data *rdata )
+{
+    pjsip_msg *ack_msg, *invite_msg;
+    pjsip_to_hdr *to;
+    pjsip_from_hdr *from;
+    pjsip_cseq_hdr *cseq;
+    pjsip_hdr *hdr;
+
+    /* Make compiler happy. */
+    PJ_UNUSED_ARG(endpt);
+
+    /* rdata must be a final response. */
+    pj_assert(rdata->msg->type==PJSIP_RESPONSE_MSG &&
+	      rdata->msg->line.status.code >= 300);
+
+    /* Log this action. */
+    PJ_LOG(5,(LOG_THIS, "pjsip_endpt_create_ack(rdata=%p)", rdata));
+
+    /* Create new request message. */
+    ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+    pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD );
+
+    /* The original INVITE message. */
+    invite_msg = tdata->msg;
+
+    /* Copy Request-Uri from the original INVITE. */
+    ack_msg->line.req.uri = invite_msg->line.req.uri;
+    
+    /* Copy Call-ID from the original INVITE */
+    hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL);
+    pjsip_msg_add_hdr( ack_msg, hdr );
+
+    /* Copy From header from the original INVITE. */
+    from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg, 
+						      PJSIP_H_FROM, NULL);
+    pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from );
+
+    /* Copy To header from the original INVITE. */
+    to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg, 
+						   PJSIP_H_TO, NULL);
+    pj_strdup(tdata->pool, &to->tag, &rdata->to_tag);
+    pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to );
+
+    /* Must contain single Via, just as the original INVITE. */
+    hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL);
+    pjsip_msg_insert_first_hdr( ack_msg, hdr );
+
+    /* Must have the same CSeq value as the original INVITE, but method 
+     * changed to ACK 
+     */
+    cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg, 
+							PJSIP_H_CSEQ, NULL);
+    pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD );
+    pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq );
+
+    /* If the original INVITE has Route headers, those header fields MUST 
+     * appear in the ACK.
+     */
+    hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+    while (hdr != NULL) {
+	pjsip_msg_add_hdr( ack_msg, hdr );
+	hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+    }
+
+    /* Set the message in the "tdata" to point to the ACK message. */
+    tdata->msg = ack_msg;
+
+    /* Reset transmit packet buffer, to force 're-printing' of message. */
+    tdata->buf.cur = tdata->buf.start;
+
+    /* We're done.
+     * "tdata" parameter now contains the ACK message.
+     */
+}
+
+
+/*
+ * Construct CANCEL request for the previously sent request, according to
+ * chapter 9.1 of RFC3261.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
+						  pjsip_tx_data *req_tdata )
+{
+    pjsip_msg *req_msg;	/* the original request. */
+    pjsip_tx_data *cancel_tdata;
+    pjsip_msg *cancel_msg;
+    pjsip_hdr *hdr;
+    pjsip_cseq_hdr *req_cseq, *cseq;
+    pjsip_uri *req_uri;
+
+    /* Log this action. */
+    PJ_LOG(5,(LOG_THIS, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata));
+
+    /* Get the original request. */
+    req_msg = req_tdata->msg;
+
+    /* The transmit buffer must INVITE request. */
+    pj_assert(req_msg->type == PJSIP_REQUEST_MSG &&
+	      req_msg->line.req.method.id == PJSIP_INVITE_METHOD );
+
+    /* Create new transmit buffer. */
+    cancel_tdata = pjsip_endpt_create_tdata( endpt );
+    if (!cancel_tdata) {
+	return NULL;
+    }
+
+    /* Create CANCEL request message. */
+    cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG);
+    cancel_tdata->msg = cancel_msg;
+
+    /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are
+     * copied from the original request.
+     */
+    /* Set request line. */
+    pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD);
+    req_uri = req_msg->line.req.uri;
+    cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri);
+
+    /* Copy Call-ID */
+    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL);
+    pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+    /* Copy From header. */
+    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL);
+    pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+    /* Copy To header. */
+    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL);
+    pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+    /* Create new CSeq with equal number, but method set to CANCEL. */
+    req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL);
+    cseq = pjsip_cseq_hdr_create(cancel_tdata->pool);
+    cseq->cseq = req_cseq->cseq;
+    pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD);
+    pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq);
+
+    /* Must only have single Via which matches the top-most Via in the 
+     * request being cancelled. 
+     */
+    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL);
+    pjsip_msg_insert_first_hdr(cancel_msg, 
+			       pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+    /* If the original request has Route header, the CANCEL request must also
+     * has exactly the same.
+     * Copy "Route" header from the request.
+     */
+    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL);
+    while (hdr != NULL) {
+	pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+	hdr = hdr->next;
+	if (hdr != &cancel_msg->hdr)
+	    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr);
+	else
+	    break;
+    }
+
+    /* Done.
+     * Return the transmit buffer containing the CANCEL request.
+     */
+    return cancel_tdata;
+}
+
+/* Get the address parameters (host, port, flag, TTL, etc) to send the
+ * response.
+ */
+PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
+					    const pjsip_transport_t *req_transport,
+					    const pjsip_via_hdr *via,
+					    pjsip_host_port *send_addr)
+{
+    /* Determine the destination address (section 18.2.2):
+     * - for TCP, SCTP, or TLS, send the response using the transport where
+     *   the request was received.
+     * - if maddr parameter is present, send to this address using the port
+     *   in sent-by or 5060. If multicast is used, the TTL in the Via must
+     *   be used, or 1 if ttl parameter is not present.
+     * - otherwise if received parameter is present, set to this address.
+     * - otherwise send to the address in sent-by.
+     */
+    send_addr->flag = pjsip_transport_get_flag(req_transport);
+    send_addr->type = pjsip_transport_get_type(req_transport);
+
+    if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
+	const pj_sockaddr_in *remote_addr;
+	remote_addr = pjsip_transport_get_remote_addr(req_transport);
+	pj_strdup2(pool, &send_addr->host, 
+		   pj_sockaddr_get_str_addr(remote_addr));
+	send_addr->port = pj_sockaddr_get_port(remote_addr);
+
+    } else {
+	/* Set the host part */
+	if (via->maddr_param.slen) {
+	    pj_strdup(pool, &send_addr->host, &via->maddr_param);
+	} else if (via->recvd_param.slen) {
+	    pj_strdup(pool, &send_addr->host, &via->recvd_param);
+	} else {
+	    pj_strdup(pool, &send_addr->host, &via->sent_by.host);
+	}
+
+	/* Set the port */
+	send_addr->port = via->sent_by.port;
+    }
+
+    return PJ_OK;
+}
+
+/*
+ * Get the event string from the event ID.
+ */
+PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e)
+{
+    return event_str[e];
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_misc.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_misc.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_misc.h	(revision 1753)
@@ -0,0 +1,190 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_misc.h 7     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_MISC_H__
+#define __PJSIP_SIP_MISC_H__
+
+#include <pjsip/sip_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_ENDPT SIP Endpoint
+ * @ingroup PJSIP
+ * @{
+ */
+
+/**
+ * Create an independent request message. This can be used to build any
+ * request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request
+ * inside a dialog, application should use #pjsip_dlg_create_request.
+ *
+ * Once a transmit data is created, the reference counter is initialized to 1.
+ *
+ * @param endpt	    Endpoint instance.
+ * @param method    SIP Method.
+ * @param target    Target URI.
+ * @param from	    URL to put in From header.
+ * @param to	    URL to put in To header.
+ * @param contact   URL to put in Contact header.
+ * @param call_id   Optional Call-ID (put NULL to generate unique Call-ID).
+ * @param cseq	    Optional CSeq (put -1 to generate random CSeq).
+ * @param text	    Optional text body (put NULL to omit body).
+ *
+ * @return	    The transmit data.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_request( pjsip_endpoint *endpt, 
+						    const pjsip_method *method,
+						    const pj_str_t *target,
+						    const pj_str_t *from,
+						    const pj_str_t *to, 
+						    const pj_str_t *contact,
+						    const pj_str_t *call_id,
+						    int cseq, 
+						    const pj_str_t *text);
+
+/**
+ * Create an independent request message from the specified headers. This
+ * function will shallow clone the headers and put them in the request.
+ *
+ * Once a transmit data is created, the reference counter is initialized to 1.
+ *
+ * @param endpt	    Endpoint instance.
+ * @param method    SIP Method.
+ * @param target    Target URI.
+ * @param from	    URL to put in From header.
+ * @param to	    URL to put in To header.
+ * @param contact   URL to put in Contact header.
+ * @param call_id   Optional Call-ID (put NULL to generate unique Call-ID).
+ * @param cseq	    Optional CSeq (put -1 to generate random CSeq).
+ * @param text	    Optional text body (put NULL to omit body).
+ *
+ * @return	    The transmit data.
+ */
+PJ_DECL(pjsip_tx_data*)
+pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+				     const pjsip_method *method,
+				     const pjsip_uri *target,
+				     const pjsip_from_hdr *from,
+				     const pjsip_to_hdr *to,
+				     const pjsip_contact_hdr *contact,
+				     const pjsip_cid_hdr *call_id,
+				     int cseq,
+				     const pj_str_t *text );
+
+/**
+ * Send outgoing request and initiate UAC transaction for the request.
+ * This is an auxiliary function to be used by application to send arbitrary
+ * requests outside a dialog. To send a request within a dialog, application
+ * should use #pjsip_dlg_send_msg instead.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param tdata	    The transmit data to be sent.
+ * @param timeout   Optional timeout for final response to be received, or -1 
+ *		    if the transaction should not have a timeout restriction.
+ * @param token	    Optional token to be associated with the transaction, and 
+ *		    to be passed to the callback.
+ * @param cb	    Optional callback to be called when the transaction has
+ *		    received a final response. The callback will be called with
+ *		    the previously registered token and the event that triggers
+ *		    the completion of the transaction.
+ *
+ * @return	    Zero if transaction is started successfully.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
+					       pjsip_tx_data *tdata,
+					       int timeout,
+					       void *token,
+					       void (*cb)(void*,pjsip_event*));
+
+/**
+ * Construct a minimal response message for the received request. This function
+ * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and 
+ * Call-ID headers from the request.
+ *
+ * Note: the txdata reference counter is set to ZERO!.
+ *
+ * @param endpt	    The endpoint.
+ * @param rdata	    The request receive data.
+ * @param code	    Status code to be put in the response.
+ *
+ * @return	    Transmit data.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_response(pjsip_endpoint *endpt,
+						    const pjsip_rx_data *rdata,
+						    int code);
+
+/**
+ * Construct a full ACK request for the received non-2xx final response.
+ * This utility function is normally called by the transaction to construct
+ * an ACK request to 3xx-6xx final response.
+ * The generation of ACK message for 2xx final response is different than
+ * this one.
+ * 
+ * @param endpt	    The endpoint.
+ * @param tdata	    On input, this contains the original INVITE request, and on
+ *		    output, it contains the ACK message.
+ * @param rdata	    The final response message.
+ */
+PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+				      pjsip_tx_data *tdata,
+				      const pjsip_rx_data *rdata );
+
+
+/**
+ * Construct CANCEL request for the previously sent request.
+ *
+ * @param endpt	    The endpoint.
+ * @param tdata	    The transmit buffer for the request being cancelled.
+ *
+ * @return	    Cancel request.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
+						   pjsip_tx_data *tdata );
+
+
+/**
+ * Get the address parameters (host, port, flag, TTL, etc) to send the
+ * response.
+ *
+ * @param pool	    The pool.
+ * @param tr	    The transport where the request was received.
+ * @param via	    The top-most Via header of the request.
+ * @param addr	    The send address concluded from the calculation.
+ *
+ * @return	    zero (PJ_OK) if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
+					     const pjsip_transport_t *tr,
+					     const pjsip_via_hdr *via,
+					     pjsip_host_port *addr);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_SIP_MISC_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_module.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_module.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_module.h	(revision 1753)
@@ -0,0 +1,145 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_module.h 6     6/17/05 11:16p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_MODULE_H__
+#define __PJSIP_SIP_MODULE_H__
+
+/**
+ * @file sip_module.h
+ * @brief Module helpers
+ */
+#include <pjsip/sip_types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_MOD SIP Modules
+ * @ingroup PJSIP
+ * @{
+ */
+
+/**
+ * Module registration structure, which is passed by the module to the
+ * endpoint during the module registration process. This structure enables
+ * the endpoint to query the module capability and to further communicate
+ * with the module.
+ */
+struct pjsip_module
+{
+    /**
+     * Module name.
+     */
+    pj_str_t name;
+
+    /**
+     * Flag to indicate the type of interfaces supported by the module.
+     */
+    pj_uint32_t flag;
+
+    /**
+     * Integer number to identify module initialization and start order with
+     * regard to other modules. Higher number will make the module gets
+     * initialized later.
+     */
+    pj_uint32_t priority;
+
+    /**
+     * Opaque data which can be used by a module to identify a resource within
+     * the module itself.
+     */
+    void *mod_data;
+
+    /**
+     * Number of methods supported by this module.
+     */
+    int method_cnt;
+
+    /**
+     * Array of methods supported by this module.
+     */
+    const pjsip_method *methods[8];
+
+    /**
+     * Pointer to function to be called to initialize the module.
+     *
+     * @param endpt	The endpoint instance.
+     * @param mod	The module.
+     * @param id	The unique module ID assigned to this module.
+     *
+     * @return		Module should return zero when initialization succeed.
+     */
+    pj_status_t (*init_module)(pjsip_endpoint *endpt,
+			       struct pjsip_module *mod, pj_uint32_t id);
+
+    /**
+     * Pointer to function to be called to start the module.
+     *
+     * @param mod	The module.
+     *
+     * @return		Module should return zero to indicate success.
+     */
+    pj_status_t (*start_module)(struct pjsip_module *mod);
+
+    /**
+     * Pointer to function to be called to deinitialize the module before
+     * it is unloaded.
+     *
+     * @param mod	The module.
+     *
+     * @return		Module should return zero to indicate success.
+     */
+    pj_status_t (*deinit_module)(struct pjsip_module *mod);
+
+    /**
+     * Pointer to function to receive transaction related events.
+     * If the module doesn't wish to receive such notification, this member
+     * must be set to NULL.
+     *
+     * @param mod	The module.
+     * @param event	The transaction event.
+     */
+    void (*tsx_handler)(struct pjsip_module *mod, pjsip_event *event);
+};
+
+
+/**
+ * Prototype of function to register static modules (eg modules that are
+ * linked staticly with the application). This function must be implemented 
+ * by any applications that use PJSIP library.
+ *
+ * @param count	    [input/output] On input, it contains the maximum number of
+ *		    elements in the array. On output, the function fills with 
+ *		    the number of modules to be registered.
+ * @param modules   [output] array of pointer to modules to be registered.
+ */
+pj_status_t register_static_modules( pj_size_t *count,
+				     pjsip_module **modules );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_SIP_MODULE_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_msg.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_msg.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_msg.c	(revision 1753)
@@ -0,0 +1,1437 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_msg.c 16    6/22/05 12:40a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_msg.h>
+#include <pjsip/print.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+/*
+ * Include inline definitions here if functions are NOT inlined.
+ */
+#if PJ_FUNCTIONS_ARE_INLINED==0
+#  include <pjsip/sip_msg_i.h>
+#endif
+
+static const pj_str_t method_names[] = 
+{
+    { "INVITE",	    6 },
+    { "CANCEL",	    6 },
+    { "ACK",	    3 },
+    { "BYE",	    3 },
+    { "REGISTER",   8 },
+    { "OPTIONS",    7 },
+};
+
+const pj_str_t pjsip_hdr_names[] = 
+{
+    { "Accept",		     6 },   // PJSIP_H_ACCEPT,
+    { "Accept-Encoding",    15 },   // PJSIP_H_ACCEPT_ENCODING,
+    { "Accept-Language",    15 },   // PJSIP_H_ACCEPT_LANGUAGE,
+    { "Alert-Info",	    10 },   // PJSIP_H_ALERT_INFO,
+    { "Allow",		     5 },   // PJSIP_H_ALLOW,
+    { "Authentication-Info",19 },   // PJSIP_H_AUTHENTICATION_INFO,
+    { "Authorization",	    13 },   // PJSIP_H_AUTHORIZATION,
+    { "Call-ID",	     7 },   // PJSIP_H_CALL_ID,
+    { "Call-Info",	     9 },   // PJSIP_H_CALL_INFO,
+    { "Contact",	     7 },   // PJSIP_H_CONTACT,
+    { "Content-Disposition",19 },   // PJSIP_H_CONTENT_DISPOSITION,
+    { "Content-Encoding",   16 },   // PJSIP_H_CONTENT_ENCODING,
+    { "Content-Language",   16 },   // PJSIP_H_CONTENT_LANGUAGE,
+    { "Content-Length",	    14 },   // PJSIP_H_CONTENT_LENGTH,
+    { "Content-Type",	    12 },   // PJSIP_H_CONTENT_TYPE,
+    { "CSeq",		     4 },   // PJSIP_H_CSEQ,
+    { "Date",		     4 },   // PJSIP_H_DATE,
+    { "Error-Info",	    10 },   // PJSIP_H_ERROR_INFO,
+    { "Expires",	     7 },   // PJSIP_H_EXPIRES,
+    { "From",		     4 },   // PJSIP_H_FROM,
+    { "In-Reply-To",	    11 },   // PJSIP_H_IN_REPLY_TO,
+    { "Max-Forwards",	    12 },   // PJSIP_H_MAX_FORWARDS,
+    { "MIME-Version",	    12 },   // PJSIP_H_MIME_VERSION,
+    { "Min-Expires",	    11 },   // PJSIP_H_MIN_EXPIRES,
+    { "Organization",	    12 },   // PJSIP_H_ORGANIZATION,
+    { "Priority",	     8 },   // PJSIP_H_PRIORITY,
+    { "Proxy-Authenticate", 18 },   // PJSIP_H_PROXY_AUTHENTICATE,
+    { "Proxy-Authorization",19 },   // PJSIP_H_PROXY_AUTHORIZATION,
+    { "Proxy-Require",	    13 },   // PJSIP_H_PROXY_REQUIRE,
+    { "Record-Route",	    12 },   // PJSIP_H_RECORD_ROUTE,
+    { "Reply-To",	     8 },   // PJSIP_H_REPLY_TO,
+    { "Require",	     7 },   // PJSIP_H_REQUIRE,
+    { "Retry-After",	    11 },   // PJSIP_H_RETRY_AFTER,
+    { "Route",		     5 },   // PJSIP_H_ROUTE,
+    { "Server",		     6 },   // PJSIP_H_SERVER,
+    { "Subject",	     7 },   // PJSIP_H_SUBJECT,
+    { "Supported",	     9 },   // PJSIP_H_SUPPORTED,
+    { "Timestamp",	     9 },   // PJSIP_H_TIMESTAMP,
+    { "To",		     2 },   // PJSIP_H_TO,
+    { "Unsupported",	    11 },   // PJSIP_H_UNSUPPORTED,
+    { "User-Agent",	    10 },   // PJSIP_H_USER_AGENT,
+    { "Via",		     3 },   // PJSIP_H_VIA,
+    { "Warning",	     7 },   // PJSIP_H_WARNING,
+    { "WWW-Authenticate",   16 },   // PJSIP_H_WWW_AUTHENTICATE,
+
+    { "_Unknown-Header",    15 },   // PJSIP_H_OTHER,
+};
+
+static pj_str_t status_phrase[710];
+static int print_media_type(char *buf, const pjsip_media_type *media);
+
+static int init_status_phrase()
+{
+    int i;
+    pj_str_t default_reason_phrase = { "Default status message", 22};
+
+    for (i=0; i<PJ_ARRAY_SIZE(status_phrase); ++i)
+	status_phrase[i] = default_reason_phrase;
+
+    pj_strset2( &status_phrase[100], "Trying");
+    pj_strset2( &status_phrase[180], "Ringing");
+    pj_strset2( &status_phrase[181], "Call Is Being Forwarded");
+    pj_strset2( &status_phrase[182], "Queued");
+    pj_strset2( &status_phrase[183], "Session Progress");
+
+    pj_strset2( &status_phrase[200], "OK");
+
+    pj_strset2( &status_phrase[300], "Multiple Choices");
+    pj_strset2( &status_phrase[301], "Moved Permanently");
+    pj_strset2( &status_phrase[302], "Moved Temporarily");
+    pj_strset2( &status_phrase[305], "Use Proxy");
+    pj_strset2( &status_phrase[380], "Alternative Service");
+
+    pj_strset2( &status_phrase[400], "Bad Request");
+    pj_strset2( &status_phrase[401], "Unauthorized");
+    pj_strset2( &status_phrase[402], "Payment Required");
+    pj_strset2( &status_phrase[403], "Forbidden");
+    pj_strset2( &status_phrase[404], "Not Found");
+    pj_strset2( &status_phrase[405], "Method Not Allowed");
+    pj_strset2( &status_phrase[406], "Not Acceptable");
+    pj_strset2( &status_phrase[407], "Proxy Authentication Required");
+    pj_strset2( &status_phrase[408], "Request Timeout");
+    pj_strset2( &status_phrase[410], "Gone");
+    pj_strset2( &status_phrase[413], "Request Entity Too Large");
+    pj_strset2( &status_phrase[414], "Request URI Too Long");
+    pj_strset2( &status_phrase[415], "Unsupported Media Type");
+    pj_strset2( &status_phrase[416], "Unsupported URI Scheme");
+    pj_strset2( &status_phrase[420], "Bad Extension");
+    pj_strset2( &status_phrase[421], "Extension Required");
+    pj_strset2( &status_phrase[423], "Interval Too Brief");
+    pj_strset2( &status_phrase[480], "Temporarily Unavailable");
+    pj_strset2( &status_phrase[481], "Call/Transaction Does Not Exist");
+    pj_strset2( &status_phrase[482], "Loop Detected");
+    pj_strset2( &status_phrase[483], "Too Many Hops");
+    pj_strset2( &status_phrase[484], "Address Incompleted");
+    pj_strset2( &status_phrase[485], "Ambiguous");
+    pj_strset2( &status_phrase[486], "Busy Here");
+    pj_strset2( &status_phrase[487], "Request Terminated");
+    pj_strset2( &status_phrase[488], "Not Acceptable Here");
+    pj_strset2( &status_phrase[491], "Request Pending");
+    pj_strset2( &status_phrase[493], "Undecipherable");
+
+    pj_strset2( &status_phrase[500], "Internal Server Error");
+    pj_strset2( &status_phrase[501], "Not Implemented");
+    pj_strset2( &status_phrase[502], "Bad Gateway");
+    pj_strset2( &status_phrase[503], "Service Unavailable");
+    pj_strset2( &status_phrase[504], "Server Timeout");
+    pj_strset2( &status_phrase[505], "Version Not Supported");
+    pj_strset2( &status_phrase[513], "Message Too Large");
+
+    pj_strset2( &status_phrase[600], "Busy Everywhere");
+    pj_strset2( &status_phrase[603], "Decline");
+    pj_strset2( &status_phrase[604], "Does Not Exist Anywhere");
+    pj_strset2( &status_phrase[606], "Not Acceptable");
+
+    pj_strset2( &status_phrase[701], "No response from destination server");
+    pj_strset2( &status_phrase[702], "Unable to resolve destination server");
+    pj_strset2( &status_phrase[703], "Error sending message to destination server");
+
+    return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Method.
+ */
+
+PJ_DEF(void) pjsip_method_init( pjsip_method *m, 
+			        pj_pool_t *pool, 
+			        const pj_str_t *str)
+{
+    pj_str_t dup;
+    pjsip_method_init_np(m, pj_strdup(pool, &dup, str));
+}
+
+PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me )
+{
+    m->id = me;
+    m->name = method_names[me];
+}
+
+PJ_DEF(void) pjsip_method_init_np(pjsip_method *m,
+				  pj_str_t *str)
+{
+    int i;
+    for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) {
+	if (pj_stricmp(str, &method_names[i])==0) {
+	    m->id = (pjsip_method_e)i;
+	    m->name = method_names[i];
+	    return;
+	}
+    }
+    m->id = PJSIP_OTHER_METHOD;
+    m->name = *str;
+}
+
+PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool,
+				pjsip_method *method,
+				const pjsip_method *rhs )
+{
+    method->id = rhs->id;
+    if (rhs->id != PJSIP_OTHER_METHOD) {
+	method->name = rhs->name;
+    } else {
+	pj_strdup(pool, &method->name, &rhs->name);
+    }
+}
+
+
+PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2)
+{
+    if (m1->id == m2->id) {
+	if (m1->id != PJSIP_OTHER_METHOD)
+	    return 0;
+	return pj_stricmp(&m1->name, &m2->name);
+    }
+    
+    return ( m1->id < m2->id ) ? -1 : 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Message.
+ */
+
+PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type)
+{
+    pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg));
+    pj_list_init(&msg->hdr);
+    msg->type = type;
+    msg->body = NULL;
+    return msg;
+}
+
+PJ_DEF(void*)  pjsip_msg_find_hdr( pjsip_msg *msg, 
+				   pjsip_hdr_e hdr_type, void *start)
+{
+    pjsip_hdr *hdr=start, *end=&msg->hdr;
+
+    if (hdr == NULL) {
+	hdr = msg->hdr.next;
+    }
+    for (; hdr!=end; hdr = hdr->next) {
+	if (hdr->type == hdr_type)
+	    return hdr;
+    }
+    return NULL;
+}
+
+PJ_DEF(void*)  pjsip_msg_find_hdr_by_name( pjsip_msg *msg, 
+					   const pj_str_t *name, void *start)
+{
+    pjsip_hdr *hdr=start, *end=&msg->hdr;
+
+    if (hdr == NULL) {
+	hdr = msg->hdr.next;
+    }
+    for (; hdr!=end; hdr = hdr->next) {
+	if (hdr->type < PJSIP_H_OTHER) {
+	    if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0)
+		return hdr;
+	} else {
+	    if (pj_stricmp(&hdr->name, name) == 0)
+		return hdr;
+	}
+    }
+    return NULL;
+}
+
+PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, 
+				         pjsip_hdr_e hdr_type, void *start)
+{
+    pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start);
+    if (hdr) {
+	pj_list_erase(hdr);
+    }
+    return hdr;
+}
+
+PJ_DEF(int) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size)
+{
+    char *p=buf, *end=buf+size;
+    int len;
+    pjsip_hdr *hdr;
+    pj_str_t clen_hdr =  { "Content-Length: ", 16};
+
+    /* Get a wild guess on how many bytes are typically needed.
+     * We'll check this later in detail, but this serves as a quick check.
+     */
+    if (size < 256)
+	return -1;
+
+    /* Print request line or status line depending on message type */
+    if (msg->type == PJSIP_REQUEST_MSG) {
+	pjsip_uri *uri;
+
+	/* Add method. */
+	len = msg->line.req.method.name.slen;
+	pj_memcpy(p, msg->line.req.method.name.ptr, len);
+	p += len;
+	*p++ = ' ';
+
+	/* Add URI */
+	uri = pjsip_uri_get_uri(msg->line.req.uri);
+	len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p);
+	if (len < 1)
+	    return -1;
+	p += len;
+
+	/* Add ' SIP/2.0' */
+	if (end-p < 16)
+	    return -1;
+	pj_memcpy(p, " SIP/2.0\r\n", 10);
+	p += 10;
+
+    } else {
+
+	/* Add 'SIP/2.0 ' */
+	pj_memcpy(p, "SIP/2.0 ", 8);
+	p += 8;
+
+	/* Add status code. */
+	len = pj_utoa(msg->line.status.code, p);
+	p += len;
+	*p++ = ' ';
+
+	/* Add reason text. */
+	len = msg->line.status.reason.slen;
+	pj_memcpy(p, msg->line.status.reason.ptr, len );
+	p += len;
+
+	/* Add newline. */
+	*p++ = '\r';
+	*p++ = '\n';
+    }
+
+    /* Print each of the headers. */
+    for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
+	len = (*hdr->vptr->print_on)(hdr, p, end-p);
+	if (len < 1)
+	    return -1;
+	p += len;
+
+	if (p+3 >= end)
+	    return -1;
+
+	*p++ = '\r';
+	*p++ = '\n';
+    }
+
+    /* Process message body. */
+    if (msg->body) {
+	pj_str_t ctype_hdr = { "Content-Type: ", 14};
+	int len;
+	const pjsip_media_type *media = &msg->body->content_type;
+	char *clen_pos;
+
+	/* Add Content-Type header. */
+	if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) {
+	    return -1;
+	}
+	pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
+	p += ctype_hdr.slen;
+	p += print_media_type(p, media);
+	*p++ = '\r';
+	*p++ = '\n';
+
+	/* Add Content-Length header. */
+	if ((end-p) < clen_hdr.slen+12+2) {
+	    return -1;
+	}
+	pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
+	p += clen_hdr.slen;
+	
+	/* Print blanks after "Content-Type:", this is where we'll put
+	 * the content length value after we know the length of the
+	 * body.
+	 */
+	pj_memset(p, ' ', 12);
+	clen_pos = p;
+	p += 12;
+	*p++ = '\r';
+	*p++ = '\n';
+	
+	/* Add blank newline. */
+	*p++ = '\r';
+	*p++ = '\n';
+
+	/* Print the message body itself. */
+	len = (*msg->body->print_body)(msg->body, p, end-p);
+	if (len < 0) {
+	    return -1;
+	}
+	p += len;
+
+	/* Now that we have the length of the body, print this to the
+	 * Content-Length header.
+	 */
+	len = pj_utoa(len, clen_pos);
+	clen_pos[len] = ' ';
+
+    } else {
+	/* There's no message body.
+	 * Add Content-Length with zero value.
+	 */
+	if ((end-p) < clen_hdr.slen+8) {
+	    return -1;
+	}
+	pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
+	p += clen_hdr.slen;
+	*p++ = '0';
+	*p++ = '\r';
+	*p++ = '\n';
+	*p++ = '\r';
+	*p++ = '\n';
+    }
+
+    *p = '\0';
+    return p-buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr )
+{
+    const pjsip_hdr *hdr = hdr_ptr;
+    return (*hdr->vptr->clone)(pool, hdr_ptr);
+}
+
+
+PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr )
+{
+    const pjsip_hdr *hdr = hdr_ptr;
+    return (*hdr->vptr->shallow_clone)(pool, hdr_ptr);
+}
+
+PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len)
+{
+    pjsip_hdr *hdr = hdr_ptr;
+    return (*hdr->vptr->print_on)(hdr_ptr, buf, len);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Status/Reason Phrase
+ */
+
+PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code)
+{
+    static int is_initialized;
+    if (is_initialized == 0) {
+	is_initialized = 1;
+	init_status_phrase();
+    }
+
+    return (code>=100 && code<(sizeof(status_phrase)/sizeof(status_phrase[0]))) ? 
+	&status_phrase[code] : &status_phrase[0];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic pjsip_hdr_names/hvalue header.
+ */
+
+static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, 
+				    char *buf, pj_size_t size);
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, 
+						   const pjsip_generic_string_hdr *hdr);
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
+							   const pjsip_generic_string_hdr *hdr );
+
+static pjsip_hdr_vptr generic_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_generic_string_hdr_print,
+};
+
+PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool,
+						     const pj_str_t *hnames )
+{
+    pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr));
+    init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr);
+    if (hnames) {
+	pj_strdup(pool, &hdr->name, hnames);
+	hdr->sname = hdr->name;
+    }
+    hdr->hvalue.ptr = NULL;
+    hdr->hvalue.slen = 0;
+    return hdr;
+}
+
+PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool,
+							       const pj_str_t *hname,
+							       const pj_str_t *hvalue)
+{
+    pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname);
+    pj_strdup(pool, &hdr->hvalue, hvalue);
+    return hdr;
+}
+
+static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, 
+				    char *buf, pj_size_t size)
+{
+    char *p = buf;
+    
+    if ((pj_ssize_t)size < hdr->name.slen + hdr->hvalue.slen + 5)
+	return -1;
+
+    pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+    p += hdr->name.slen;
+    *p++ = ':';
+    *p++ = ' ';
+    pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen);
+    p += hdr->hvalue.slen;
+    *p = '\0';
+
+    return p - buf;
+}
+
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, 
+					           const pjsip_generic_string_hdr *rhs)
+{
+    pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, &rhs->name);
+
+    hdr->type = rhs->type;
+    hdr->sname = hdr->name;
+    pj_strdup( pool, &hdr->hvalue, &rhs->hvalue);
+    return hdr;
+}
+
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
+							   const pjsip_generic_string_hdr *rhs )
+{
+    pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic pjsip_hdr_names/integer value header.
+ */
+
+static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, 
+					char *buf, pj_size_t size);
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, 
+						   const pjsip_generic_int_hdr *hdr);
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
+							   const pjsip_generic_int_hdr *hdr );
+
+static pjsip_hdr_vptr generic_int_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_generic_int_hdr_print,
+};
+
+PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool,
+						     const pj_str_t *hnames )
+{
+    pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr));
+    init_hdr(hdr, PJSIP_H_OTHER, &generic_int_hdr_vptr);
+    if (hnames) {
+	pj_strdup(pool, &hdr->name, hnames);
+	hdr->sname = hdr->name;
+    }
+    hdr->ivalue = 0;
+    return hdr;
+}
+
+PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool,
+							       const pj_str_t *hname,
+							       int value)
+{
+    pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname);
+    hdr->ivalue = value;
+    return hdr;
+}
+
+static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, 
+					char *buf, pj_size_t size)
+{
+    char *p = buf;
+
+    if ((pj_ssize_t)size < hdr->name.slen + 15)
+	return -1;
+
+    pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+    p += hdr->name.slen;
+    *p++ = ':';
+    *p++ = ' ';
+
+    p += pj_utoa(hdr->ivalue, p);
+
+    return p - buf;
+}
+
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, 
+					           const pjsip_generic_int_hdr *rhs)
+{
+    pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
+							   const pjsip_generic_int_hdr *rhs )
+{
+    pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic array header.
+ */
+static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, 
+						 const pjsip_generic_array_hdr *hdr);
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, 
+						 const pjsip_generic_array_hdr *hdr);
+
+static pjsip_hdr_vptr generic_array_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_generic_array_hdr_print,
+};
+
+PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_create( pj_pool_t *pool,
+							     const pj_str_t *hnames)
+{
+    pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr));
+    init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr);
+    if (hnames) {
+	pj_strdup(pool, &hdr->name, hnames);
+	hdr->sname = hdr->name;
+    }
+    hdr->count = 0;
+    return hdr;
+
+}
+
+static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, 
+					  char *buf, pj_size_t size)
+{
+    char *p = buf, *endbuf = buf+size;
+
+    copy_advance(p, hdr->name);
+    *p++ = ':';
+    *p++ = ' ';
+
+    if (hdr->count > 0) {
+	unsigned i;
+	int printed;
+	copy_advance(p, hdr->values[0]);
+	for (i=1; i<hdr->count; ++i) {
+	    copy_advance_pair(p, ", ", 2, hdr->values[i]);
+	}
+    }
+
+    return p - buf;
+}
+
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, 
+						 const pjsip_generic_array_hdr *rhs)
+{
+    unsigned i;
+    pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    for (i=0; i<rhs->count; ++i) {
+	pj_strdup(pool, &hdr->values[i], &rhs->values[i]);
+    }
+
+    return hdr;
+}
+
+
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, 
+						 const pjsip_generic_array_hdr *rhs)
+{
+    pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Accept header.
+ */
+PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool)
+{
+    pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr);
+    hdr->count = 0;
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Allow header.
+ */
+
+PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool)
+{
+    pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr);
+    hdr->count = 0;
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Call-ID header.
+ */
+
+PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool )
+{
+    pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr);
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Content-Length header.
+ */
+static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr);
+#define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone
+
+static pjsip_hdr_vptr clen_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print,
+};
+
+PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool )
+{
+    pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr));
+    init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr);
+    hdr->len = 0;
+    return hdr;
+}
+
+static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, 
+				 char *buf, pj_size_t size)
+{
+    char *p = buf;
+    int len;
+
+    if ((pj_ssize_t)size < hdr->name.slen + 14)
+	return -1;
+
+    pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+    p += hdr->name.slen;
+    *p++ = ':';
+    *p++ = ' ';
+
+    len = pj_utoa(hdr->len, p);
+    p += len;
+    *p = '\0';
+
+    return p-buf;
+}
+
+static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs)
+{
+    pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool);
+    hdr->len = rhs->len;
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * CSeq header.
+ */
+static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr);
+static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr );
+
+static pjsip_hdr_vptr cseq_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print,
+};
+
+PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool )
+{
+    pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr));
+    init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr);
+    hdr->cseq = 0;
+    hdr->method.id = PJSIP_OTHER_METHOD;
+    hdr->method.name.ptr = NULL;
+    hdr->method.name.slen = 0;
+    return hdr;
+}
+
+static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size)
+{
+    char *p = buf;
+    int len;
+
+    if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15)
+	return -1;
+
+    pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+    p += hdr->name.slen;
+    *p++ = ':';
+    *p++ = ' ';
+
+    len = pj_utoa(hdr->cseq, p);
+    p += len;
+    *p++ = ' ';
+
+    pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen);
+    p += hdr->method.name.slen;
+
+    *p = '\0';
+
+    return p-buf;
+}
+
+static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, 
+					     const pjsip_cseq_hdr *rhs)
+{
+    pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool);
+    hdr->cseq = rhs->cseq;
+    pjsip_method_copy(pool, &hdr->method, &rhs->method);
+    return hdr;
+}
+
+static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool,
+						     const pjsip_cseq_hdr *rhs )
+{
+    pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Contact header.
+ */
+static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr);
+static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *);
+
+static pjsip_hdr_vptr contact_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print,
+};
+
+PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool )
+{
+    pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr);
+    hdr->expires = -1;
+    return hdr;
+}
+
+static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, 
+				    pj_size_t size)
+{
+    if (hdr->star) {
+	char *p = buf;
+	if ((pj_ssize_t)size < hdr->name.slen + 6)
+	    return -1;
+	pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+	p += hdr->name.slen;
+	*p++ = ':';
+	*p++ = ' ';
+	*p++ = '*';
+	*p = '\0';
+	return p - buf;
+
+    } else {
+	int printed;
+	char *startbuf = buf;
+	char *endbuf = buf + size;
+
+	copy_advance(buf, hdr->name);
+	*buf++ = ':';
+	*buf++ = ' ';
+
+	printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri, 
+				  buf, endbuf-buf);
+	if (printed < 1)
+	    return -1;
+
+	buf += printed;
+
+	if (hdr->q1000) {
+	    if (buf+19 >= endbuf)
+		return -1;
+
+	    /*
+	    printed = sprintf(buf, ";q=%u.%03u",
+				   hdr->q1000/1000, hdr->q1000 % 1000);
+	     */
+	    pj_memcpy(buf, ";q=", 3);
+	    printed = pj_utoa(hdr->q1000/1000, buf+3);
+	    buf += printed + 3;
+	    *buf++ = '.';
+	    printed = pj_utoa(hdr->q1000 % 1000, buf);
+	    buf += printed;
+	}
+
+	if (hdr->expires >= 0) {
+	    if (buf+23 >= endbuf)
+		return -1;
+
+	    pj_memcpy(buf, ";expires=", 9);
+	    printed = pj_utoa(hdr->expires, buf+9);
+	    buf += printed + 9;
+	}
+
+	if (hdr->other_param.slen) {
+	    copy_advance(buf, hdr->other_param);
+	}
+
+	*buf = '\0';
+	return buf-startbuf;
+    }
+}
+
+static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, 
+					           const pjsip_contact_hdr *rhs)
+{
+    pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool);
+
+    hdr->star = rhs->star;
+    if (hdr->star)
+	return hdr;
+
+    hdr->uri = pjsip_uri_clone(pool, rhs->uri);
+    hdr->q1000 = rhs->q1000;
+    hdr->expires = rhs->expires;
+    pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+    return hdr;
+}
+
+static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool,
+							   const pjsip_contact_hdr *rhs)
+{
+    pjsip_contact_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Content-Type header..
+ */
+static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, const pjsip_ctype_hdr *hdr);
+#define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone
+
+static pjsip_hdr_vptr ctype_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print,
+};
+
+PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool )
+{
+    pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr);
+    return hdr;
+}
+
+static int print_media_type(char *buf, const pjsip_media_type *media)
+{
+    char *p = buf;
+
+    pj_memcpy(p, media->type.ptr, media->type.slen);
+    p += media->type.slen;
+    *p++ = '/';
+    pj_memcpy(p, media->subtype.ptr, media->subtype.slen);
+    p += media->subtype.slen;
+
+    if (media->param.slen) {
+	pj_memcpy(p, media->param.ptr, media->param.slen);
+	p += media->param.slen;
+    }
+
+    return p-buf;
+}
+
+static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, 
+				  char *buf, pj_size_t size)
+{
+    char *p = buf;
+    int len;
+
+    if ((pj_ssize_t)size < hdr->name.slen + 
+			   hdr->media.type.slen + hdr->media.subtype.slen + 
+			   hdr->media.param.slen + 8)
+    {
+	return -1;
+    }
+
+    pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+    p += hdr->name.slen;
+    *p++ = ':';
+    *p++ = ' ';
+
+    len = print_media_type(p, &hdr->media);
+    p += len;
+
+    *p = '\0';
+    return p-buf;
+}
+
+static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, 
+					       const pjsip_ctype_hdr *rhs)
+{
+    pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool);
+    pj_strdup(pool, &hdr->media.type, &rhs->media.type);
+    pj_strdup(pool, &hdr->media.param, &rhs->media.param);
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Expires header.
+ */
+PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool )
+{
+    pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr);
+    hdr->ivalue = 0;
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * To or From header.
+ */
+static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, 
+				   char *buf, pj_size_t size);
+static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, 
+					         const pjsip_fromto_hdr *hdr);
+static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
+							 const pjsip_fromto_hdr *hdr);
+
+
+static pjsip_hdr_vptr fromto_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print,
+};
+
+PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool )
+{
+    pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr);
+    return hdr;
+}
+
+PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool )
+{
+    pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr);
+    return hdr;
+}
+
+PJ_DEF(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr )
+{
+    hdr->type = PJSIP_H_FROM;
+    hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_FROM];
+    return hdr;
+}
+
+PJ_DEF(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr )
+{
+    hdr->type = PJSIP_H_TO;
+    hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO];
+    return hdr;
+}
+
+static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, 
+				   char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+
+    copy_advance(buf, hdr->name);
+    *buf++ = ':';
+    *buf++ = ' ';
+
+    printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri, buf, endbuf-buf);
+    if (printed < 1)
+	return -1;
+
+    buf += printed;
+
+    copy_advance_pair(buf, ";tag=", 5, hdr->tag);
+    if (hdr->other_param.slen)
+	copy_advance(buf, hdr->other_param);
+
+    return buf-startbuf;
+}
+
+static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, 
+					         const pjsip_fromto_hdr *rhs)
+{
+    pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool);
+
+    hdr->type = rhs->type;
+    hdr->name = rhs->name;
+    hdr->sname = rhs->sname;
+    hdr->uri = pjsip_uri_clone(pool, rhs->uri);
+    pj_strdup( pool, &hdr->tag, &rhs->tag);
+    pj_strdup( pool, &hdr->other_param, &rhs->other_param);
+
+    return hdr;
+}
+
+static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
+							 const pjsip_fromto_hdr *rhs)
+{
+    pjsip_fromto_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Max-Forwards header.
+ */
+PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool)
+{
+    pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr);
+    hdr->ivalue = 0;
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Min-Expires header.
+ */
+PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool)
+{
+    pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr);
+    hdr->ivalue = 0;
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Record-Route and Route header.
+ */
+static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size );
+static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
+static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
+
+static pjsip_hdr_vptr routing_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print,
+};
+
+PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool )
+{
+    pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr);
+    pjsip_name_addr_init(&hdr->name_addr);
+    pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param));
+    return hdr;
+}
+
+PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool )
+{
+    pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr);
+    pjsip_name_addr_init(&hdr->name_addr);
+    pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param));
+    return hdr;
+}
+
+PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr )
+{
+    hdr->type = PJSIP_H_RECORD_ROUTE;
+    hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE];
+    return hdr;
+}
+
+PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr )
+{
+    hdr->type = PJSIP_H_ROUTE;
+    hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_ROUTE];
+    return hdr;
+}
+
+static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr,
+				    char *buf, pj_size_t size )
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+
+    copy_advance(buf, hdr->name);
+    *buf++ = ':';
+    *buf++ = ' ';
+
+    printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, endbuf-buf);
+    if (printed < 1)
+	return -1;
+    buf += printed;
+
+    if (hdr->other_param.slen) {
+	copy_advance(buf, hdr->other_param);
+    }
+
+    return buf-startbuf;
+}
+
+static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool,
+						   const pjsip_routing_hdr *rhs )
+{
+    pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+
+    init_hdr(hdr, rhs->type, rhs->vptr);
+    pjsip_name_addr_init(&hdr->name_addr);
+    pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr);
+    pj_strdup( pool, &hdr->other_param, &rhs->other_param);
+    return hdr;
+}
+
+static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool,
+							   const pjsip_routing_hdr *rhs )
+{
+    pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Require header.
+ */
+PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool)
+{
+    pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr);
+    hdr->count = 0;
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Retry-After header.
+ */
+PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool)
+{
+    pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr);
+    hdr->ivalue = 0;
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Supported header.
+ */
+PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool)
+{
+    pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr);
+    hdr->count = 0;
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Unsupported header.
+ */
+PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool)
+{
+    pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr);
+    hdr->count = 0;
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Via header.
+ */
+static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr);
+static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr );
+
+static pjsip_hdr_vptr via_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_via_hdr_print,
+};
+
+PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool )
+{
+    pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr);
+    hdr->sent_by.port = 5060;
+    hdr->ttl_param = -1;
+    hdr->rport_param = -1;
+    return hdr;
+}
+
+static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, 
+				char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+    pj_str_t sip_ver = { "SIP/2.0/", 8 };
+
+    if ((pj_ssize_t)size < hdr->name.slen + sip_ver.slen + 
+			   hdr->transport.slen + hdr->sent_by.host.slen + 12)
+    {
+	return -1;
+    }
+
+    /* pjsip_hdr_names */
+    copy_advance(buf, hdr->name);
+    *buf++ = ':';
+    *buf++ = ' ';
+
+    /* SIP/2.0/transport host:port */
+    pj_memcpy(buf, sip_ver.ptr, sip_ver.slen);
+    buf += sip_ver.slen;
+    pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen);
+    buf += hdr->transport.slen;
+    *buf++ = ' ';
+    pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen);
+    buf += hdr->sent_by.host.slen;
+    *buf++ = ':';
+    printed = pj_utoa(hdr->sent_by.port, buf);
+    buf += printed;
+
+    if (hdr->ttl_param >= 0) {
+	size = endbuf-buf;
+	if (size < 14)
+	    return -1;
+	pj_memcpy(buf, ";ttl=", 5);
+	printed = pj_utoa(hdr->ttl_param, buf+5);
+	buf += printed + 5;
+    }
+
+    if (hdr->rport_param >= 0) {
+	size = endbuf-buf;
+	if (size < 14)
+	    return -1;
+	pj_memcpy(buf, ";rport", 6);
+	buf += 6;
+	if (hdr->rport_param > 0) {
+	    *buf++ = '=';
+	    buf += pj_utoa(hdr->rport_param, buf);
+	}
+    }
+
+
+    copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param);
+    copy_advance_pair(buf, ";received=", 10, hdr->recvd_param);
+    copy_advance_pair(buf, ";branch=", 8, hdr->branch_param);
+    copy_advance(buf, hdr->other_param);
+    
+    *buf = '\0';
+    return buf-startbuf;
+}
+
+static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, 
+					   const pjsip_via_hdr *rhs)
+{
+    pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool);
+    pj_strdup(pool, &hdr->transport, &rhs->transport);
+    pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host);
+    hdr->sent_by.port = rhs->sent_by.port;
+    hdr->ttl_param = rhs->ttl_param;
+    hdr->rport_param = rhs->rport_param;
+    pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param);
+    pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param);
+    pj_strdup(pool, &hdr->branch_param, &rhs->branch_param);
+    pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+    return hdr;
+}
+
+static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool,
+						   const pjsip_via_hdr *rhs )
+{
+    pjsip_via_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * General purpose function to textual data in a SIP body. 
+ */
+PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
+{
+    if (size < msg_body->len)
+	return -1;
+    pj_memcpy(buf, msg_body->data, msg_body->len);
+    return msg_body->len;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_msg.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_msg.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_msg.h	(revision 1753)
@@ -0,0 +1,1532 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_msg.h 13    6/22/05 12:27a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_MSG_H__
+#define __PJSIP_SIP_MSG_H__
+
+/**
+ * @file sip_msg.h
+ * @brief SIP Message Structure.
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_uri.h>
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_MSG SIP Message Structure
+ * @ingroup PJSIP
+ * @{
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_METHOD Methods
+ * @brief Method names and manipulation.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * This enumeration declares SIP methods as described by RFC3261. Additional
+ * methods do exist, and they are described by corresponding RFCs for the SIP
+ * extentensions. Since they won't alter the characteristic of the processing
+ * of the message, they don't need to be explicitly mentioned here.
+ */
+typedef enum pjsip_method_e
+{
+    /** INVITE method, for establishing dialogs. */
+    PJSIP_INVITE_METHOD,
+
+    /** CANCEL method, for cancelling request. */
+    PJSIP_CANCEL_METHOD,
+
+    /** ACK method, for acknowledging final response to INVITE. */
+    PJSIP_ACK_METHOD,
+
+    /** BYE method, for terminating dialog. */
+    PJSIP_BYE_METHOD,
+
+    /** REGISTER method. */
+    PJSIP_REGISTER_METHOD,
+
+    /** OPTIONS method, for querying remote capabilities. */
+    PJSIP_OPTIONS_METHOD,
+
+    /** Other method, which means that the method name itself will be stored
+        elsewhere. */
+    PJSIP_OTHER_METHOD,
+
+} pjsip_method_e;
+
+
+
+/**
+ * This structure represents a SIP method.
+ * Application must always use either #pjsip_method_init or #pjsip_method_set
+ * to make sure that method name is initialized correctly. This way, the name
+ * member will always contain a valid method string regardless whether the ID
+ * is recognized or not.
+ */
+typedef struct pjsip_method
+{
+    pjsip_method_e id;	    /**< Method ID, from \a pjsip_method_e. */
+    pj_str_t	   name;    /**< Method name, which will always contain the 
+			         method string. */
+} pjsip_method;
+
+
+/** 
+ * Initialize the method structure from a string. 
+ * This function will check whether the method is a known method then set
+ * both the id and name accordingly.
+ *
+ * @param m	The method to initialize.
+ * @param pool	Pool where memory allocation will be allocated from, if required.
+ * @param str	The method string.
+ */
+PJ_DECL(void) pjsip_method_init( pjsip_method *m, 
+				 pj_pool_t *pool, 
+				 const pj_str_t *str);
+
+/** 
+ * Initialize the method structure from a string, without cloning the string.
+ * See #pjsip_method_init.
+ *
+ * @param m	The method structure to be initialized.
+ * @param str	The method string.
+ */
+PJ_DECL(void) pjsip_method_init_np( pjsip_method *m,
+				    pj_str_t *str);
+
+/** 
+ * Set the method with the predefined method ID. 
+ * This function will also set the name member of the structure to the correct
+ * string according to the method.
+ *
+ * @param m	The method structure.
+ * @param id	The method ID.
+ */
+PJ_DECL(void) pjsip_method_set( pjsip_method *m, pjsip_method_e id );
+
+
+/** 
+ * Copy one method structure to another. If the method is of the known methods,
+ * then memory allocation is not required.
+ *
+ * @param pool	    Pool to allocate memory from, if required.
+ * @param method    The destination method to copy to.
+ * @param rhs	    The source method to copy from.
+ */
+PJ_DECL(void) pjsip_method_copy( pj_pool_t *pool,
+				 pjsip_method *method,
+				 const pjsip_method *rhs );
+
+/** 
+ * Compare one method with another, and conveniently determine whether the 
+ * first method is equal, less than, or greater than the second method.
+ *
+ * @param m1	The first method.
+ * @param m2	The second method.
+ *
+ * @return	Zero if equal, otherwise will return -1 if less or +1 if greater.
+ */
+PJ_DECL(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/** 
+ * @defgroup PJSIP_MSG_HDR Header Fields General Structure.
+ * @brief General Header Fields Structure.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Header types, as defined by RFC3261.
+ */
+typedef enum pjsip_hdr_e
+{
+    /*
+     * These are the headers documented in RFC3261. Headers not documented
+     * there must have type PJSIP_H_OTHER, and the header type itself is 
+     * recorded in the header name string.
+     *
+     * DO NOT CHANGE THE VALUE/ORDER OF THE HEADER IDs!!!.
+     */
+    PJSIP_H_ACCEPT,
+    PJSIP_H_ACCEPT_ENCODING_UNIMP,
+    PJSIP_H_ACCEPT_LANGUAGE_UNIMP,
+    PJSIP_H_ALERT_INFO_UNIMP,
+    PJSIP_H_ALLOW,
+    PJSIP_H_AUTHENTICATION_INFO_UNIMP,
+    PJSIP_H_AUTHORIZATION,
+    PJSIP_H_CALL_ID,
+    PJSIP_H_CALL_INFO_UNIMP,
+    PJSIP_H_CONTACT,
+    PJSIP_H_CONTENT_DISPOSITION_UNIMP,
+    PJSIP_H_CONTENT_ENCODING_UNIMP,
+    PJSIP_H_CONTENT_LANGUAGE_UNIMP,
+    PJSIP_H_CONTENT_LENGTH,
+    PJSIP_H_CONTENT_TYPE,
+    PJSIP_H_CSEQ,
+    PJSIP_H_DATE_UNIMP,
+    PJSIP_H_ERROR_INFO_UNIMP,
+    PJSIP_H_EXPIRES,
+    PJSIP_H_FROM,
+    PJSIP_H_IN_REPLY_TO_UNIMP,
+    PJSIP_H_MAX_FORWARDS,
+    PJSIP_H_MIME_VERSION_UNIMP,
+    PJSIP_H_MIN_EXPIRES,
+    PJSIP_H_ORGANIZATION_UNIMP,
+    PJSIP_H_PRIORITY_UNIMP,
+    PJSIP_H_PROXY_AUTHENTICATE,
+    PJSIP_H_PROXY_AUTHORIZATION,
+    PJSIP_H_PROXY_REQUIRE_UNIMP,
+    PJSIP_H_RECORD_ROUTE,
+    PJSIP_H_REPLY_TO_UNIMP,
+    PJSIP_H_REQUIRE,
+    PJSIP_H_RETRY_AFTER,
+    PJSIP_H_ROUTE,
+    PJSIP_H_SERVER_UNIMP,
+    PJSIP_H_SUBJECT_UNIMP,
+    PJSIP_H_SUPPORTED,
+    PJSIP_H_TIMESTAMP_UNIMP,
+    PJSIP_H_TO,
+    PJSIP_H_UNSUPPORTED,
+    PJSIP_H_USER_AGENT_UNIMP,
+    PJSIP_H_VIA,
+    PJSIP_H_WARNING_UNIMP,
+    PJSIP_H_WWW_AUTHENTICATE,
+
+    PJSIP_H_OTHER,
+
+} pjsip_hdr_e;
+
+/**
+ * This structure provides the pointer to basic functions that are needed
+ * for generic header operations. All header fields will have pointer to
+ * this structure, so that they can be manipulated uniformly.
+ */
+typedef struct pjsip_hdr_vptr
+{
+    /** 
+     * Function to clone the header. 
+     *
+     * @param pool  Memory pool to allocate the new header.
+     * @param hdr   Header to clone.
+     *
+     * @return A new instance of the header.
+     */
+    void *(*clone)(pj_pool_t *pool, const void *hdr);
+
+    /** 
+     * Pointer to function to shallow clone the header. 
+     * Shallow cloning will just make a memory copy of the original header,
+     * thus all pointers in original header will be kept intact. Because the
+     * function does not need to perform deep copy, the operation should be
+     * faster, but the application must make sure that the original header
+     * is still valid throughout the lifetime of new header.
+     *
+     * @param pool  Memory pool to allocate the new header.
+     * @param hdr   The header to clone.
+     */
+    void *(*shallow_clone)(pj_pool_t *pool, const void *hdr);
+
+    /** Pointer to function to print the header to the specified buffer.
+     *	Returns the length of string written, or -1 if the remaining buffer
+     *	is not enough to hold the header.
+     *
+     *  @param hdr  The header to print.
+     *  @param buf  The buffer.
+     *  @param len  The size of the buffer.
+     *
+     *  @return	    The size copied to buffer, or -1 if there's not enough space.
+     */
+    int (*print_on)(void *hdr, char *buf, pj_size_t len);
+
+} pjsip_hdr_vptr;
+
+
+/**
+ * Generic fields for all SIP headers are declared using this macro, to make
+ * sure that all headers will have exactly the same layout in their start of
+ * the storage. This behaves like C++ inheritance actually.
+ */
+#define PJSIP_DECL_HDR_MEMBER(hdr)   \
+    /** List members. */	\
+    PJ_DECL_LIST_MEMBER(hdr)	\
+    /** Header type */		\
+    pjsip_hdr_e	    type;	\
+    /** Header name. */		\
+    pj_str_t	    name;	\
+    /** Header short name version. */	\
+    pj_str_t	    sname;		\
+    /** Virtual function table. */	\
+    pjsip_hdr_vptr *vptr;
+
+
+/**
+ * Generic SIP header structure, for generic manipulation for headers in the
+ * message. All header fields can be typecasted to this type.
+ */
+typedef struct pjsip_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_hdr)
+} pjsip_hdr;
+
+
+/**
+ * This generic function will clone any header, by calling "clone" function
+ * in header's virtual function table.
+ *
+ * @param pool	    The pool to allocate memory from.
+ * @param hdr	    The header to clone.
+ *
+ * @return	    A new instance copied from the original header.
+ */
+PJ_DECL(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr );
+
+
+/**
+ * This generic function will clone any header, by calling "shallow_clone" 
+ * function in header's virtual function table.
+ *
+ * @param pool	    The pool to allocate memory from.
+ * @param hdr	    The header to clone.
+ *
+ * @return	    A new instance copied from the original header.
+ */
+PJ_DECL(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr );
+
+/**
+ * This generic function will print any header, by calling "print" 
+ * function in header's virtual function table.
+ *
+ * @param hdr  The header to print.
+ * @param buf  The buffer.
+ * @param len  The size of the buffer.
+ *
+ * @return	The size copied to buffer, or -1 if there's not enough space.
+ */
+PJ_DECL(int) pjsip_hdr_print_on( void *hdr, char *buf, pj_size_t len);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_LINE Request and Status Line.
+ * @brief Request and status line structures and manipulation.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * This structure describes SIP request line.
+ */
+typedef struct pjsip_request_line 
+{
+    pjsip_method    method; /**< Method for this request line. */
+    pjsip_uri *uri;    /**< URI for this request line. */
+} pjsip_request_line;
+
+
+/**
+ * This structure describes SIP status line.
+ */
+typedef struct pjsip_status_line 
+{
+    int		code;	    /**< Status code. */
+    pj_str_t	reason;	    /**< Reason string. */
+} pjsip_status_line;
+
+
+/**
+ * This enumeration lists standard SIP status codes according to RFC 3261.
+ * In addition, it also declares new status class 7xx for errors generated
+ * by the stack. This status class however should not get transmitted on the 
+ * wire.
+ */
+typedef enum pjsip_status_code
+{
+    PJSIP_SC_TRYING = 100,
+    PJSIP_SC_RINGING = 180,
+    PJSIP_SC_CALL_BEING_FORWARDED = 181,
+    PJSIP_SC_QUEUED = 182,
+    PJSIP_SC_PROGRESS = 183,
+
+    PJSIP_SC_OK = 200,
+
+    PJSIP_SC_MULTIPLE_CHOICES = 300,
+    PJSIP_SC_MOVED_PERMANENTLY = 301,
+    PJSIP_SC_MOVED_TEMPORARILY = 302,
+    PJSIP_SC_USE_PROXY = 305,
+    PJSIP_SC_ALTERNATIVE_SERVICE = 380,
+
+    PJSIP_SC_BAD_REQUEST = 400,
+    PJSIP_SC_UNAUTHORIZED = 401,
+    PJSIP_SC_PAYMENT_REQUIRED = 402,
+    PJSIP_SC_FORBIDDEN = 403,
+    PJSIP_SC_NOT_FOUND = 404,
+    PJSIP_SC_METHOD_NOT_ALLOWED = 405,
+    PJSIP_SC_NOT_ACCEPTABLE = 406,
+    PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407,
+    PJSIP_SC_REQUEST_TIMEOUT = 408,
+    PJSIP_SC_GONE = 410,
+    PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413,
+    PJSIP_SC_REQUEST_URI_TOO_LONG = 414,
+    PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415,
+    PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416,
+    PJSIP_SC_BAD_EXTENSION = 420,
+    PJSIP_SC_EXTENSION_REQUIRED = 421,
+    PJSIP_SC_INTERVAL_TOO_BRIEF = 423,
+    PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480,
+    PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481,
+    PJSIP_SC_LOOP_DETECTED = 482,
+    PJSIP_SC_TOO_MANY_HOPS = 483,
+    PJSIP_SC_ADDRESS_INCOMPLETE = 484,
+    PJSIP_AC_AMBIGUOUS = 485,
+    PJSIP_SC_BUSY_HERE = 486,
+    PJSIP_SC_REQUEST_TERMINATED = 487,
+    PJSIP_SC_NOT_ACCEPTABLE_HERE = 488,
+    PJSIP_SC_REQUEST_PENDING = 491,
+    PJSIP_SC_UNDECIPHERABLE = 493,
+
+    PJSIP_SC_INTERNAL_SERVER_ERROR = 500,
+    PJSIP_SC_NOT_IMPLEMENTED = 501,
+    PJSIP_SC_BAD_GATEWAY = 502,
+    PJSIP_SC_SERVICE_UNAVAILABLE = 503,
+    PJSIP_SC_SERVER_TIMEOUT = 504,
+    PJSIP_SC_VERSION_NOT_SUPPORTED = 505,
+    PJSIP_SC_MESSAGE_TOO_LARGE = 513,
+
+    PJSIP_SC_BUSY_EVERYWHERE = 600,
+    PJSIP_SC_DECLINE = 603,
+    PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604,
+    PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606,
+
+    PJSIP_SC_TSX_TIMEOUT = 701,
+    PJSIP_SC_TSX_RESOLVE_ERROR = 702,
+    PJSIP_SC_TSX_TRANSPORT_ERROR = 703,
+
+} pjsip_status_code;
+
+/**
+ * Get the default status text for the status code.
+ *
+ * @param status_code	    SIP Status Code
+ *
+ * @return		    textual message for the status code.
+ */ 
+PJ_DECL(const pj_str_t*) pjsip_get_status_text(int status_code);
+
+/**
+ * This macro returns non-zero (TRUE) if the specified status_code is
+ * in the same class as the code_class.
+ *
+ * @param status_code	The status code.
+ * @param code_class	The status code in the class (for example 100, 200).
+ */
+#define PJSIP_IS_STATUS_IN_CLASS(status_code, code_class)    \
+	    (status_code/100 == code_class/100)
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJSIP_MSG_MEDIA Media Type
+ * @brief Media type definitions and manipulations.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * This structure describes SIP media type, as used for example in 
+ * Accept and Content-Type header..
+ */
+typedef struct pjsip_media_type
+{
+    pj_str_t type;	    /**< Media type. */
+    pj_str_t subtype;	    /**< Media subtype. */
+    pj_str_t param;	    /**< Media type parameters (concatenated). */
+} pjsip_media_type;
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJSIP_MSG_BODY Message Body
+ * @brief SIP message body structures and manipulation.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Generic abstraction to message body.
+ * When an incoming message is parsed (pjsip_parse_msg()), the parser fills in
+ * all members with the appropriate value. The 'data' and 'len' member will
+ * describe portion of incoming packet which denotes the message body.
+ * When application needs to attach message body to outgoing SIP message, it
+ * must fill in all members of this structure. 
+ */
+typedef struct pjsip_msg_body
+{
+    /** MIME content type. 
+     *  For incoming messages, the parser will fill in this member with the
+     *  content type found in Content-Type header.
+     *
+     *  For outgoing messages, application must fill in this member with
+     *  appropriate value, because the stack will generate Content-Type header
+     *  based on the value specified here.
+     */
+    pjsip_media_type content_type;
+
+    /** Pointer to buffer which holds the message body data. 
+     *  For incoming messages, the parser will fill in this member with the
+     *  pointer to the body string.
+     *
+     *  When sending outgoing message, this member doesn't need to point to the
+     *  actual message body string. It can be assigned with arbitrary pointer,
+     *  because the value will only need to be understood by the print_body()
+     *  function. The stack itself will not try to interpret this value, but
+     *  instead will always call the print_body() whenever it needs to get the
+     *  actual body string.
+     */
+    void *data;
+
+    /** The length of the data. 
+     *  For incoming messages, the parser will fill in this member with the
+     *  actual length of message body.
+     *
+     *  When sending outgoing message, again just like the "data" member, the
+     *  "len" member doesn't need to point to the actual length of the body 
+     *  string.
+     */
+    unsigned len;
+
+    /** Pointer to function to print this message body. 
+     *  Application must set a proper function here when sending outgoing 
+     *  message.
+     *
+     *  @param msg_body	    This structure itself.
+     *  @param buf	    The buffer.
+     *  @param size	    The buffer size.
+     *
+     *  @return		    The length of the string printed, or -1 if there is
+     *			    not enough space in the buffer to print the whole
+     *			    message body.
+     */
+    int (*print_body)(struct pjsip_msg_body *msg_body, 
+		      char *buf, pj_size_t size);
+
+} pjsip_msg_body;
+
+/**
+ * General purpose function to textual data in a SIP body. Attach this function
+ * in a SIP message body only if the data in pjsip_msg_body is a textual 
+ * message ready to be embedded in a SIP message. If the data in the message
+ * body is not a textual body, then application must supply a custom function
+ * to print that body.
+ *
+ * @param msg_body	The message body.
+ * @param buf		Buffer to copy the message body to.
+ * @param size		The size of the buffer.
+ *
+ * @return		The length copied to the buffer, or -1.
+ */
+PJ_DECL(int) pjsip_print_text_body( pjsip_msg_body *msg_body, 
+				    char *buf, pj_size_t size);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_MSG Message Structure
+ * @brief Message structure and operations.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Message type (request or response).
+ */
+typedef enum pjsip_msg_type_e
+{
+    PJSIP_REQUEST_MSG,	    /**< Indicates request message. */
+    PJSIP_RESPONSE_MSG,	    /**< Indicates response message. */
+} pjsip_msg_type_e;
+
+
+/**
+ * This structure describes a SIP message.
+ */
+struct pjsip_msg
+{
+    /** Message type (ie request or response). */
+    pjsip_msg_type_e  type;
+
+    /** The first line of the message can be either request line for request
+     *	messages, or status line for response messages. It is represented here
+     *  as a union.
+     */
+    union
+    {
+	/** Request Line. */
+	struct pjsip_request_line   req;
+
+	/** Status Line. */
+	struct pjsip_status_line    status;
+    } line;
+
+    /** List of message headers. */
+    pjsip_hdr hdr;
+
+    /** Pointer to message body, or NULL if no message body is attached to
+     *	this mesage. 
+     */
+    pjsip_msg_body *body;
+};
+
+
+/** 
+ * Create new request or response message.
+ *
+ * @param pool	    The pool.
+ * @param type	    Message type.
+ * @return	    New message, or THROW exception if failed.
+ */
+PJ_DECL(pjsip_msg*)  pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type);
+
+/** 
+ * Find a header in the message by the header type.
+ *
+ * @param msg	    The message.
+ * @param type	    The header type to find.
+ * @param start	    The first header field where the search should begin.
+ *		    If NULL is specified, then the search will begin from the
+ *		    first header, otherwise the search will begin at the
+ *		    specified header.
+ *
+ * @return	    The header field, or NULL if no header with the specified 
+ *		    type is found.
+ */
+PJ_DECL(void*)  pjsip_msg_find_hdr( pjsip_msg *msg, 
+				    pjsip_hdr_e type, void *start);
+
+/** 
+ * Find a header in the message by its name.
+ *
+ * @param msg	    The message.
+ * @param name	    The header name to find.
+ * @param start	    The first header field where the search should begin.
+ *		    If NULL is specified, then the search will begin from the
+ *		    first header, otherwise the search will begin at the
+ *		    specified header.
+ *
+ * @return	    The header field, or NULL if no header with the specified 
+ *		    type is found.
+ */
+PJ_DECL(void*)  pjsip_msg_find_hdr_by_name( pjsip_msg *msg, 
+					    const pj_str_t *name, void *start);
+
+/** 
+ * Find and remove a header in the message. 
+ *
+ * @param msg	    The message.
+ * @param hdr	    The header type to find.
+ * @param start	    The first header field where the search should begin,
+ *		    or NULL to search from the first header in the message.
+ *
+ * @return	    The header field, or NULL if not found.
+ */
+PJ_DECL(void*)  pjsip_msg_find_remove_hdr( pjsip_msg *msg, 
+					   pjsip_hdr_e hdr, void *start);
+
+/** 
+ * Add a header to the message, putting it last in the header list.
+ *
+ * @param msg	    The message.
+ * @param hdr	    The header to add.
+ *
+ * @bug Once the header is put in a list (or message), it can not be put in 
+ *      other list (or message). Otherwise Real Bad Thing will happen.
+ */
+PJ_IDECL(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr );
+
+/** 
+ * Add header field to the message, putting it in the front of the header list.
+ *
+ * @param msg	The message.
+ * @param hdr	The header to add.
+ *
+ * @bug Once the header is put in a list (or message), it can not be put in 
+ *      other list (or message). Otherwise Real Bad Thing will happen.
+ */
+PJ_IDECL(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr );
+
+/** 
+ * Print the message to the specified buffer. 
+ *
+ * @param msg	The message to print.
+ * @param buf	The buffer
+ * @param size	The size of the buffer.
+ *
+ * @return	The length of the printed characters (in bytes), or NEGATIVE
+ *		value if the message is too large for the specified buffer.
+ */
+PJ_DECL(int) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJSIP_MSG_HDR_GEN Header Field: Generic
+ * @brief Generic header field which contains header name and value.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Generic SIP header, which contains hname and a string hvalue.
+ * Note that this header is not supposed to be used as 'base' class for headers.
+ */
+typedef struct pjsip_generic_string_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_string_hdr) /**< Standard header field. */
+    pj_str_t hvalue;				    /**< hvalue */
+} pjsip_generic_string_hdr;
+
+
+/**
+ * Create a new instance of generic header. A generic header can have an
+ * arbitrary header name.
+ *
+ * @param pool	    The pool.
+ * @param hname	    The header name to be assigned to the header, or NULL to
+ *		    assign the header name with some string.
+ *
+ * @return	    The header, or THROW exception.
+ */
+PJ_DECL(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool, 
+						      const pj_str_t *hname );
+
+/**
+ * Create a generic header along with the text content.
+ *
+ * @param pool	    The pool.
+ * @param hname	    The header name.
+ * @param hvalue    The header text content.
+ *
+ * @return	    The header instance.
+ */
+PJ_DECL(pjsip_generic_string_hdr*) 
+pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool,
+					   const pj_str_t *hname,
+					   const pj_str_t *hvalue);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJSIP_MSG_HDR_GEN_INT Header Field: Generic Integer
+ * @brief Generic header field which contains header name and value.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Generic SIP header, which contains hname and a string hvalue.
+ */
+typedef struct pjsip_generic_int_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_int_hdr) /**< Standard header field. */
+    pj_int32_t ivalue;				    /**< ivalue */
+} pjsip_generic_int_hdr;
+
+
+/**
+ * Create a new instance of generic header. A generic header can have an
+ * arbitrary header name.
+ *
+ * @param pool	    The pool.
+ * @param hname	    The header name to be assigned to the header, or NULL to
+ *		    assign the header name with some string.
+ *
+ * @return	    The header, or THROW exception.
+ */
+PJ_DECL(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, 
+						      const pj_str_t *hname );
+
+/**
+ * Create a generic header along with the value.
+ *
+ * @param pool	    The pool.
+ * @param hname	    The header name.
+ * @param value     The header value content.
+ *
+ * @return	    The header instance.
+ */
+PJ_DECL(pjsip_generic_int_hdr*) 
+pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool,
+					 const pj_str_t *hname,
+					 int value);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_GENERIC_LIST Header Field: Generic string list.
+ * @brief Header with list of strings separated with comma
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/** Maximum elements in the header array. */
+#define PJSIP_GENERIC_ARRAY_MAX_COUNT	32
+
+typedef struct pjsip_generic_array_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_array_hdr)
+    unsigned	count;					/**< Number of elements. */
+    pj_str_t	values[PJSIP_GENERIC_ARRAY_MAX_COUNT];	/**< Elements.		 */
+} pjsip_generic_array_hdr;
+
+/**
+ * Create generic array header.
+ *
+ * @param pool	    Pool to allocate memory from.
+ *
+ * @return	    New generic array header.
+ */
+PJ_DECL(pjsip_generic_array_hdr*) pjsip_generic_array_create(pj_pool_t *pool,
+							     const pj_str_t *hnames);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_ACCEPT Header Field: Accept
+ * @brief Accept header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/** Accept header. */
+typedef pjsip_generic_array_hdr pjsip_accept_hdr;
+
+/** Maximum fields in Accept header. */
+#define PJSIP_MAX_ACCEPT_COUNT	PJSIP_GENERIC_ARRAY_MAX_COUNT
+
+/**
+ * Create new Accept header instance.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New Accept header instance.
+ */
+PJ_DECL(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_ALLOW Header Field: Allow
+ * @brief Allow header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_array_hdr pjsip_allow_hdr;
+
+/**
+ * Create new Allow header instance.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New Allow header instance.
+ */
+PJ_DECL(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CID Header Field: Call-ID
+ * @brief Call-ID header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Call-ID header.
+ */
+typedef struct pjsip_cid_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_cid_hdr)
+    pj_str_t id;	    /**< Call-ID string. */
+} pjsip_cid_hdr;
+
+
+/**
+ * Create new Call-ID header.
+ *
+ * @param pool	The pool.
+ *
+ * @return	new Call-ID header.
+ */
+PJ_DECL(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool );
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CLEN Header Field: Content-Length
+ * @brief Content-Length header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Content-Length header.
+ */
+typedef struct pjsip_clen_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_clen_hdr)
+    int len;	/**< Content length. */
+} pjsip_clen_hdr;
+
+/**
+ * Create new Content-Length header.
+ *
+ * @param pool	the pool.
+ * @return	A new Content-Length header instance.
+ */
+PJ_DECL(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CSEQ Header Field: CSeq
+ * @brief CSeq header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * CSeq header.
+ */
+typedef struct pjsip_cseq_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_cseq_hdr)
+    int		    cseq;	/**< CSeq number. */
+    pjsip_method    method;	/**< CSeq method. */
+} pjsip_cseq_hdr;
+
+
+/** Create new  CSeq header. 
+ *
+ *  @param pool	The pool.
+ *  @return A new CSeq header instance.
+ */
+PJ_DECL(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CONTACT Header Field: Contact
+ * @brief Contact header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Contact header.
+ * In this library, contact header only contains single URI. If a message has
+ * multiple URI in the Contact header, the URI will be put in separate Contact
+ * headers.
+ */
+typedef struct pjsip_contact_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_contact_hdr)
+    int		    star;	    /**< The contact contains only a '*' character */
+    pjsip_uri *uri;	    /**< URI in the contact. */
+    int		    q1000;	    /**< The "q" value times 1000 (to avoid float) */
+    pj_int32_t	    expires;	    /**< Expires parameter, otherwise -1 if not present. */
+    pj_str_t	    other_param;    /**< Other parameters, concatenated in a single string. */
+} pjsip_contact_hdr;
+
+
+/**
+ * Create a new Contact header.
+ *
+ * @param pool	The pool.
+ * @return	A new instance of Contact header.
+ */
+PJ_DECL(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CTYPE Header Field: Content-Type
+ * @brief Content-Type header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Content-Type.
+ */
+typedef struct pjsip_ctype_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_ctype_hdr)
+    pjsip_media_type media; /**< Media type. */
+} pjsip_ctype_hdr;
+
+
+/**
+ * Create a nwe Content Type header.
+ *
+ * @param pool	The pool.
+ * @return	A new Content-Type header.
+ */
+PJ_DECL(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_EXPIRES Header Field: Expires
+ * @brief Expires header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/** Expires header. */
+typedef pjsip_generic_int_hdr pjsip_expires_hdr;
+
+/**
+ * Create a new Expires header.
+ *
+ * @param pool	The pool.
+ * @return	A new Expires header.
+ */
+PJ_DECL(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_FROMTO Header Field: From/To
+ * @brief From and To header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * To or From header.
+ */
+typedef struct pjsip_fromto_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_fromto_hdr)
+    pjsip_uri  *uri;	    /**< URI in From/To header. */
+    pj_str_t	     tag;	    /**< Header "tag" parameter. */
+    pj_str_t	     other_param;   /**< Other params, concatenated as a single string. */
+} pjsip_fromto_hdr;
+
+/** Alias for From header. */
+typedef pjsip_fromto_hdr pjsip_from_hdr;
+
+/** Alias for To header. */
+typedef pjsip_fromto_hdr pjsip_to_hdr;
+
+/**
+ * Create a From header.
+ *
+ * @param pool	The pool.
+ * @return	New instance of From header.
+ */
+PJ_DECL(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool );
+
+/**
+ * Create a To header.
+ *
+ * @param pool	The pool.
+ * @return	New instance of To header.
+ */
+PJ_DECL(pjsip_to_hdr*)   pjsip_to_hdr_create( pj_pool_t *pool );
+
+/**
+ * Convert the header to a From header.
+ *
+ * @param pool	The pool.
+ * @return	"From" header.
+ */
+PJ_DECL(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr );
+
+/**
+ * Convert the header to a To header.
+ *
+ * @param pool	The pool.
+ * @return	"To" header.
+ */
+PJ_DECL(pjsip_to_hdr*)   pjsip_fromto_set_to( pjsip_fromto_hdr *hdr );
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_MAX_FORWARDS Header Field: Max-Forwards
+ * @brief Max-Forwards header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_int_hdr pjsip_max_forwards_hdr;
+
+/**
+ * Create new Max-Forwards header instance.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New Max-Forwards header instance.
+ */
+PJ_DECL(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_MIN_EXPIRES Header Field: Min-Expires
+ * @brief Min-Expires header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_int_hdr pjsip_min_expires_hdr;
+
+/**
+ * Create new Max-Forwards header instance.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New Max-Forwards header instance.
+ */
+PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_ROUTING Header Field: Record-Route/Route
+ * @brief Record-Route and Route header fields.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Record-Route and Route headers.
+ */
+typedef struct pjsip_routing_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_routing_hdr)  /**< Generic header fields. */
+    pjsip_name_addr  name_addr;	  /**< The URL in the Route/Record-Route header. */
+    pj_str_t	     other_param; /** Other parameter. */
+} pjsip_routing_hdr;
+
+/** Alias for Record-Route header. */
+typedef pjsip_routing_hdr pjsip_rr_hdr;
+
+/** Alias for Route header. */
+typedef pjsip_routing_hdr pjsip_route_hdr;
+
+
+/** 
+ * Create new Record-Route header from the pool. 
+ *
+ * @param pool	The pool.
+ * @return	A new instance of Record-Route header.
+ */
+PJ_DECL(pjsip_rr_hdr*)	    pjsip_rr_hdr_create( pj_pool_t *pool );
+
+/** 
+ * Create new Route header from the pool. 
+ *
+ * @param pool	The pool.
+ * @return	A new instance of "Route" header.
+ */
+PJ_DECL(pjsip_route_hdr*)   pjsip_route_hdr_create( pj_pool_t *pool );
+
+/** 
+ * Convert generic routing header to Record-Route header. 
+ *
+ * @param r	The generic routing header, or a "Routing" header.
+ * @return	Record-Route header.
+ */
+PJ_DECL(pjsip_rr_hdr*)	    pjsip_routing_hdr_set_rr( pjsip_routing_hdr *r );
+
+/** 
+ * Convert generic routing header to "Route" header. 
+ *
+ * @param r	The generic routing header, or a "Record-Route" header.
+ * @return	"Route" header.
+ */
+PJ_DECL(pjsip_route_hdr*)   pjsip_routing_hdr_set_route( pjsip_routing_hdr *r );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_REQUIRE Header Field: Require
+ * @brief Require header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_array_hdr pjsip_require_hdr;
+
+/**
+ * Create new Require header instance.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New Require header instance.
+ */
+PJ_DECL(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_RETRY_AFTER Header Field: Retry-After
+ * @brief Retry-After header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_int_hdr pjsip_retry_after_hdr;
+
+/**
+ * Create new Retry-After header instance.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New Retry-After header instance.
+ */
+PJ_DECL(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_SUPPORTED Header Field: Supported
+ * @brief Supported header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_array_hdr pjsip_supported_hdr;
+
+/**
+ * Create new Supported header instance.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New Supported header instance.
+ */
+PJ_DECL(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_UNSUPPORTED Header Field: Unsupported
+ * @brief Unsupported header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_array_hdr pjsip_unsupported_hdr;
+
+/**
+ * Create new Unsupported header instance.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New Unsupported header instance.
+ */
+PJ_DECL(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_VIA Header Field: Via
+ * @brief Via header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * SIP Via header.
+ * In this implementation, Via header can only have one element in each header.
+ * If a message arrives with multiple elements in a single Via, then they will
+ * be split up into multiple Via headers.
+ */
+typedef struct pjsip_via_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_via_hdr)
+    pj_str_t	     transport;	    /**< Transport type. */
+    pjsip_host_port  sent_by;	    /**< Host and optional port */
+    int		     ttl_param;	    /**< TTL parameter, or -1 if it's not specified. */
+    int		     rport_param;   /**< "rport" parameter, 0 to specify without
+					 port number, -1 means doesn't exist. */
+    pj_str_t	     maddr_param;   /**< "maddr" parameter. */
+    pj_str_t	     recvd_param;   /**< "received" parameter. */
+    pj_str_t	     branch_param;  /**< "branch" parameter. */
+    pj_str_t	     other_param;   /**< Other parameters, concatenated as single string. */
+    pj_str_t	     comment;	    /**< Comment. */
+} pjsip_via_hdr;
+
+/**
+ * Create a new Via header.
+ *
+ * @param pool	    The pool.
+ * @return	    A new "Via" header instance.
+ */
+PJ_DECL(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+/**
+ * @bug Once a header is put in the message, the header CAN NOT be put in
+ *      other list. Solution:
+ *	- always clone header in the message.
+ *	- create a list node for each header in the message.
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_UNIMP Unimplemented Header Fields
+ * @brief Unimplemented header fields.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/** Accept-Encoding header. */
+typedef pjsip_generic_string_hdr pjsip_accept_encoding_hdr;
+
+/** Create Accept-Encoding header. */
+#define pjsip_accept_encoding_hdr_create pjsip_generic_string_hdr_create
+
+/** Accept-Language header. */
+typedef pjsip_generic_string_hdr pjsip_accept_lang_hdr;
+
+/** Create Accept-Language header. */
+#define pjsip_accept_lang_hdr_create pjsip_generic_string_hdr_create
+
+/** Alert-Info header. */
+typedef pjsip_generic_string_hdr pjsip_alert_info_hdr;
+
+/** Create Alert-Info header. */
+#define pjsip_alert_info_hdr_create pjsip_generic_string_hdr_create
+
+/** Authentication-Info header. */
+typedef pjsip_generic_string_hdr pjsip_auth_info_hdr;
+
+/** Create Authentication-Info header. */
+#define pjsip_auth_info_hdr_create pjsip_generic_string_hdr_create
+
+/** Call-Info header. */
+typedef pjsip_generic_string_hdr pjsip_call_info_hdr;
+
+/** Create Call-Info header. */
+#define pjsip_call_info_hdr_create pjsip_generic_string_hdr_create
+
+/** Content-Disposition header. */
+typedef pjsip_generic_string_hdr pjsip_content_disposition_hdr;
+
+/** Create Content-Disposition header. */
+#define pjsip_content_disposition_hdr_create pjsip_generic_string_hdr_create
+
+/** Content-Encoding header. */
+typedef pjsip_generic_string_hdr pjsip_content_encoding_hdr;
+
+/** Create Content-Encoding header. */
+#define pjsip_content_encoding_hdr_create pjsip_generic_string_hdr_create
+
+/** Content-Language header. */
+typedef pjsip_generic_string_hdr pjsip_content_lang_hdr;
+
+/** Create Content-Language header. */
+#define pjsip_content_lang_hdr_create pjsip_generic_string_hdr_create
+
+/** Date header. */
+typedef pjsip_generic_string_hdr pjsip_date_hdr;
+
+/** Create Date header. */
+#define pjsip_date_hdr_create pjsip_generic_string_hdr_create
+
+/** Error-Info header. */
+typedef pjsip_generic_string_hdr pjsip_err_info_hdr;
+
+/** Create Error-Info header. */
+#define pjsip_err_info_hdr_create pjsip_generic_string_hdr_create
+
+/** In-Reply-To header. */
+typedef pjsip_generic_string_hdr pjsip_in_reply_to_hdr;
+
+/** Create In-Reply-To header. */
+#define pjsip_in_reply_to_hdr_create pjsip_generic_string_hdr_create
+
+/** MIME-Version header. */
+typedef pjsip_generic_string_hdr pjsip_mime_version_hdr;
+
+/** Create MIME-Version header. */
+#define pjsip_mime_version_hdr_create pjsip_generic_string_hdr_create
+
+/** Organization header. */
+typedef pjsip_generic_string_hdr pjsip_organization_hdr;
+
+/** Create Organization header. */
+#define pjsip_organization_hdr_create pjsip_genric_string_hdr_create
+
+/** Priority header. */
+typedef pjsip_generic_string_hdr pjsip_priority_hdr;
+
+/** Create Priority header. */
+#define pjsip_priority_hdr_create pjsip_generic_string_hdr_create
+
+/** Proxy-Require header. */
+typedef pjsip_generic_string_hdr pjsip_proxy_require_hdr;
+
+/** Reply-To header. */
+typedef pjsip_generic_string_hdr pjsip_reply_to_hdr;
+
+/** Create Reply-To header. */
+#define pjsip_reply_to_hdr_create pjsip_generic_string_hdr_create
+
+/** Server header. */
+typedef pjsip_generic_string_hdr pjsip_server_hdr;
+
+/** Create Server header. */
+#define pjsip_server_hdr_create pjsip_generic_string_hdr_create
+
+/** Subject header. */
+typedef pjsip_generic_string_hdr pjsip_subject_hdr;
+
+/** Create Subject header. */
+#define pjsip_subject_hdr_create pjsip_generic_string_hdr_create
+
+/** Timestamp header. */
+typedef pjsip_generic_string_hdr pjsip_timestamp_hdr;
+
+/** Create Timestamp header. */
+#define pjsip_timestamp_hdr_create pjsip_generic_string_hdr_create
+
+/** User-Agent header. */
+typedef pjsip_generic_string_hdr pjsip_user_agent_hdr;
+
+/** Create User-Agent header. */
+#define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create
+
+/** Warning header. */
+typedef pjsip_generic_string_hdr pjsip_warning_hdr;
+
+/** Create Warning header. */
+#define pjsip_warning_hdr_create pjsip_generic_string_hdr_create
+
+/**
+ * @}
+ */
+
+/**
+ * @}  // PJSIP_MSG
+ */
+
+/*
+ * Include inline definitions.
+ */
+#if PJ_FUNCTIONS_ARE_INLINED
+#  include <pjsip/sip_msg_i.h>
+#endif
+
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_SIP_MSG_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_msg_i.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_msg_i.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_msg_i.h	(revision 1753)
@@ -0,0 +1,34 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_msg_i.h 2     2/24/05 10:46a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+
+PJ_IDEF(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr )
+{
+    pj_list_insert_before(&msg->hdr, hdr);
+}
+
+PJ_IDEF(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr )
+{
+    pj_list_insert_after(&msg->hdr, hdr);
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_parser.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_parser.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_parser.c	(revision 1753)
@@ -0,0 +1,1576 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_parser.c 18    12/02/05 1:16p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_auth_parser.h>
+#include <pj/scanner.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/hash.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <ctype.h>	/* tolower() */
+
+#define RESERVED    ";/?:@&=+$,"
+#define MARK	    "-_.!~*'()"
+#define ESCAPED	    "%"
+#define USER	    "&=+$,;?/"
+#define PASS	    "&=+$,"
+#define TOKEN	    "-.!%*_=`'~+"   /* '+' is because of application/pidf+xml in Content-Type! */
+#define HOST	    "_-."
+#define HEX_DIGIT   "abcdefABCDEF"
+#define PARAM_CHAR  "[]/:&+$" MARK "%"
+
+#define PJSIP_VERSION		"SIP/2.0"
+#define PJSIP_SYN_ERR_EXCEPTION	1
+
+#define UNREACHED(expr)
+
+typedef struct handler_rec
+{
+    char		  hname[PJSIP_MAX_HNAME_LEN+1];
+    pj_size_t		  hname_len;
+    pj_uint32_t		  hname_hash;
+    pjsip_parse_hdr_func *handler;
+} handler_rec;
+
+static handler_rec handler[127];
+static unsigned handler_count;
+static int parser_is_initialized;
+
+
+/*
+ * Global vars (also extern).
+ */
+const pj_str_t  pjsip_USER_STR = { "user", 4};
+const pj_str_t  pjsip_METHOD_STR = { "method", 6};
+const pj_str_t  pjsip_TRANSPORT_STR = { "transport", 9};
+const pj_str_t  pjsip_MADDR_STR = { "maddr", 5 };
+const pj_str_t  pjsip_LR_STR = { "lr", 2 };
+const pj_str_t  pjsip_SIP_STR = { "sip", 3 };
+const pj_str_t  pjsip_SIPS_STR = { "sips", 4 };
+const pj_str_t  pjsip_TEL_STR = { "tel", 3 };
+const pj_str_t  pjsip_BRANCH_STR = { "branch", 6 };
+const pj_str_t  pjsip_TTL_STR = { "ttl", 3 };
+const pj_str_t  pjsip_PNAME_STR = { "received", 8 };
+const pj_str_t  pjsip_Q_STR = { "q", 1 };
+const pj_str_t  pjsip_EXPIRES_STR = { "expires", 7 };
+const pj_str_t  pjsip_TAG_STR = { "tag", 3 };
+const pj_str_t  pjsip_RPORT_STR = { "rport", 5};
+
+pj_char_spec	pjsip_HOST_SPEC,	    /* For scanning host part. */
+		pjsip_DIGIT_SPEC,	    /* Decimal digits */
+		pjsip_ALPHA_SPEC,	    /* Alpha (A-Z, a-z) */
+		pjsip_ALNUM_SPEC,	    /* Decimal + Alpha. */
+		pjsip_TOKEN_SPEC,	    /* Token. */
+		pjsip_HEX_SPEC,		    /* Hexadecimal digits. */
+		pjsip_PARAM_CHAR_SPEC,	    /* For scanning pname (or pvalue when it's not quoted.) */
+		pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */
+		pjsip_PASSWD_SPEC,	    /* Password. */
+		pjsip_USER_SPEC,	    /* User */
+		pjsip_ARRAY_ELEMENTS,	    /* Array separator. */
+		pjsip_NEWLINE_OR_EOF_SPEC,  /* For eating up header.*/
+		pjsip_DISPLAY_SCAN_SPEC;    /* Used when searching for display name in URL. */
+
+
+/*
+ * Forward decl.
+ */
+static pjsip_msg *	  int_parse_msg( pj_scanner *scanner, 
+					 pj_pool_t *pool, 
+					 pjsip_parser_err_report *err_list);
+static void		  int_parse_param( pj_scanner *scanner, 
+					   pj_str_t *pname, 
+					   pj_str_t *pvalue);
+static void		  int_parse_req_line( pj_scanner *scanner, 
+					      pj_pool_t *pool,
+					      pjsip_request_line *req_line);
+static int		  int_is_next_user( pj_scanner *scanner);
+static void		  int_parse_status_line( pj_scanner *scanner, 
+						 pjsip_status_line *line);
+static void		  int_parse_user_pass( pj_scanner *scanner, 
+					       pj_str_t *user, 
+					       pj_str_t *pass);
+static void		  int_parse_uri_host_port( pj_scanner *scanner, 
+						   pj_str_t *p_host, 
+						   int *p_port);
+static pjsip_uri *	  int_parse_uri_or_name_addr( pj_scanner *scanner, 
+					    pj_pool_t *pool, unsigned option);
+static pjsip_url *	  int_parse_sip_url( pj_scanner *scanner, 
+					     pj_pool_t *pool,
+					     pj_bool_t parse_params);
+static pjsip_name_addr*	  int_parse_name_addr( pj_scanner *scanner, 
+					       pj_pool_t *pool );
+static void		  parse_hdr_end( pj_scanner *scanner );
+static pjsip_accept_hdr*  parse_hdr_accept( pj_scanner *scanner, 
+					    pj_pool_t *pool);
+static pjsip_allow_hdr*   parse_hdr_allow( pj_scanner *scanner, 
+					   pj_pool_t *pool);
+static pjsip_cid_hdr*	  parse_hdr_call_id( pj_scanner *scanner, 
+					     pj_pool_t *pool);
+static pjsip_contact_hdr* parse_hdr_contact( pj_scanner *scanner, 
+					     pj_pool_t *pool);
+static pjsip_clen_hdr*    parse_hdr_content_length( pj_scanner *scanner, 
+						    pj_pool_t *pool);
+static pjsip_ctype_hdr*   parse_hdr_content_type( pj_scanner *scanner, 
+						  pj_pool_t *pool);
+static pjsip_cseq_hdr*    parse_hdr_cseq( pj_scanner *scanner, 
+					  pj_pool_t *pool);
+static pjsip_expires_hdr* parse_hdr_expires( pj_scanner *scanner, 
+					  pj_pool_t *pool);
+static pjsip_from_hdr*    parse_hdr_from( pj_scanner *scanner, 
+					  pj_pool_t *pool);
+static pjsip_max_forwards_hdr*  parse_hdr_max_forwards( pj_scanner *scanner, 
+				        pj_pool_t *pool);
+static pjsip_min_expires_hdr*  parse_hdr_min_expires( pj_scanner *scanner, 
+				        pj_pool_t *pool);
+static pjsip_rr_hdr*      parse_hdr_rr( pj_scanner *scanner, 
+				        pj_pool_t *pool);
+static pjsip_route_hdr*   parse_hdr_route( pj_scanner *scanner, 
+					   pj_pool_t *pool);
+static pjsip_require_hdr* parse_hdr_require( pj_scanner *scanner, 
+				        pj_pool_t *pool);
+static pjsip_retry_after_hdr* parse_hdr_retry_after( pj_scanner *scanner, 
+				        pj_pool_t *pool);
+static pjsip_supported_hdr* parse_hdr_supported( pj_scanner *scanner, 
+				        pj_pool_t *pool);
+static pjsip_to_hdr*	  parse_hdr_to( pj_scanner *scanner, 
+				        pj_pool_t *pool);
+static pjsip_unsupported_hdr* parse_hdr_unsupported( pj_scanner *scanner, 
+				        pj_pool_t *pool);
+static pjsip_via_hdr*	  parse_hdr_via( pj_scanner *scanner, 
+					 pj_pool_t *pool);
+static pjsip_generic_string_hdr* parse_hdr_generic_string( pj_scanner *scanner, 
+					     pj_pool_t *pool);
+
+/* Convert non NULL terminated string to integer. */
+static unsigned long pj_strtoul_mindigit(const pj_str_t *str, unsigned mindig)
+{
+    unsigned long value;
+    unsigned i;
+
+    value = 0;
+    for (i=0; i<(unsigned)str->slen; ++i) {
+	value = value * 10 + (str->ptr[i] - '0');
+    }
+    for (; i<mindig; ++i) {
+	value = value * 10;
+    }
+    return value;
+}
+
+/* Case insensitive comparison */
+#define parser_stricmp(str1, str2)  \
+	    (str1.slen != str2.slen ? -1 : \
+		!(memcmp(str1.ptr, str2.ptr, str1.slen)==0 || stricmp(str1.ptr, str2.ptr)==0))
+
+/* Syntax error handler for parser. */
+static void on_syntax_error(pj_scanner *scanner)
+{
+    PJ_UNUSED_ARG(scanner)
+    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+/* Concatenate unrecognized params into single string. */
+void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool, 
+			     const pj_str_t *pname, const pj_str_t *pvalue, int sepchar)
+{
+    char *new_param, *p;
+    int len;
+
+    len = param->slen + pname->slen + pvalue->slen + 3;
+    p = new_param = pj_pool_alloc(pool, len);
+    
+    if (param->slen) {
+	int old_len = param->slen;
+	pj_memcpy(p, param->ptr, old_len);
+	p += old_len;
+    }
+    *p++ = (char)sepchar;
+    pj_memcpy(p, pname->ptr, pname->slen);
+    p += pname->slen;
+
+    if (pvalue->slen) {
+	*p++ = '=';
+	pj_memcpy(p, pvalue->ptr, pvalue->slen);
+	p += pvalue->slen;
+    }
+
+    *p = '\0';
+    
+    param->ptr = new_param;
+    param->slen = p - new_param;
+}
+
+/* Concatenate unrecognized params into single string. */
+static void concat_param( pj_str_t *param, pj_pool_t *pool, 
+			  const pj_str_t *pname, const pj_str_t *pvalue )
+{
+    pjsip_concat_param_imp(param, pool, pname, pvalue, ';');
+}
+
+/* Initialize static properties of the parser. */
+static void init_parser()
+{
+    static int initialized;
+
+    if (initialized)
+	return;
+
+    initialized = 1;
+
+    pj_cs_add_num( pjsip_DIGIT_SPEC );
+    pj_cs_add_alpha( pjsip_ALPHA_SPEC );
+    
+    pj_cs_add_alpha( pjsip_ALNUM_SPEC );
+    pj_cs_add_num( pjsip_ALNUM_SPEC );
+
+    pj_cs_add_str(pjsip_NEWLINE_OR_EOF_SPEC, "\r\n");
+    //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0);
+
+    pj_cs_add_str( pjsip_ARRAY_ELEMENTS, ",\r\n");
+
+    pj_memcpy(pjsip_TOKEN_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+    pj_cs_add_str( pjsip_TOKEN_SPEC, TOKEN);
+
+    pj_memcpy(pjsip_HOST_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+    pj_cs_add_str( pjsip_HOST_SPEC, HOST);
+
+    pj_memcpy(pjsip_HEX_SPEC, pjsip_DIGIT_SPEC, sizeof(pj_char_spec));
+    pj_cs_add_str( pjsip_HEX_SPEC, HEX_DIGIT);
+
+    pj_memcpy(pjsip_PARAM_CHAR_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+    pj_cs_add_str( pjsip_PARAM_CHAR_SPEC, PARAM_CHAR);
+
+    pj_memcpy(pjsip_USER_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+    pj_cs_add_str( pjsip_USER_SPEC, MARK ESCAPED USER );
+
+    pj_memcpy(pjsip_PASSWD_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+    pj_cs_add_str( pjsip_PASSWD_SPEC, MARK ESCAPED PASS);
+
+    pj_cs_add_str( pjsip_PROBE_USER_HOST_SPEC, "@ \n>");
+    pj_cs_invert( pjsip_PROBE_USER_HOST_SPEC );
+
+    pj_cs_add_str( pjsip_DISPLAY_SCAN_SPEC, ":\r\n<");
+
+    pjsip_register_hdr_parser( "Accept", NULL, (pjsip_parse_hdr_func*) &parse_hdr_accept);
+    pjsip_register_hdr_parser( "Allow", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow);
+    pjsip_register_hdr_parser( "Call-ID", NULL, (pjsip_parse_hdr_func*) &parse_hdr_call_id);
+    pjsip_register_hdr_parser( "Contact", "m", (pjsip_parse_hdr_func*) &parse_hdr_contact);
+    pjsip_register_hdr_parser( "Content-Length", NULL, (pjsip_parse_hdr_func*) &parse_hdr_content_length);
+    pjsip_register_hdr_parser( "Content-Type", NULL, (pjsip_parse_hdr_func*) &parse_hdr_content_type);
+    pjsip_register_hdr_parser( "CSeq", NULL, (pjsip_parse_hdr_func*) &parse_hdr_cseq);
+    pjsip_register_hdr_parser( "Expires", NULL, (pjsip_parse_hdr_func*) &parse_hdr_expires);
+    pjsip_register_hdr_parser( "From", "f", (pjsip_parse_hdr_func*) &parse_hdr_from);
+    pjsip_register_hdr_parser( "Max-Forwards", NULL, (pjsip_parse_hdr_func*) &parse_hdr_max_forwards);
+    pjsip_register_hdr_parser( "Min-Expires", NULL, (pjsip_parse_hdr_func*) &parse_hdr_min_expires);
+    pjsip_register_hdr_parser( "Record-Route", NULL, (pjsip_parse_hdr_func*) &parse_hdr_rr);
+    pjsip_register_hdr_parser( "Route", NULL, (pjsip_parse_hdr_func*) &parse_hdr_route);
+    pjsip_register_hdr_parser( "Require", NULL, (pjsip_parse_hdr_func*) &parse_hdr_require);
+    pjsip_register_hdr_parser( "Retry-After", NULL, (pjsip_parse_hdr_func*) &parse_hdr_retry_after);
+    pjsip_register_hdr_parser( "Supported", "k", (pjsip_parse_hdr_func*) &parse_hdr_supported);
+    pjsip_register_hdr_parser( "To", "t", (pjsip_parse_hdr_func*) &parse_hdr_to);
+    pjsip_register_hdr_parser( "Unsupported", NULL, (pjsip_parse_hdr_func*) &parse_hdr_unsupported);
+    pjsip_register_hdr_parser( "Via", NULL, (pjsip_parse_hdr_func*) &parse_hdr_via);
+
+    /* Register auth parser. */
+    pjsip_auth_init_parser();
+
+}
+
+static void init_sip_parser()
+{
+    if (!parser_is_initialized) {
+	/* Prevent race cond. */
+	pj_enter_critical_section();
+	if (!parser_is_initialized) {
+	    init_parser();
+	    parser_is_initialized = 1;
+	}
+	pj_leave_critical_section();
+    }
+}
+
+/* Compare the handler record with header name, and return:
+ * - 0  if handler match.
+ * - <0 if handler is 'less' than the header name.
+ * - >0 if handler is 'greater' than header name.
+ */
+static int compare_handler( const handler_rec *r1, 
+			    const char *name, 
+			    pj_size_t name_len,
+			    pj_uint32_t hash )
+{
+    /* Compare length. */
+    if (r1->hname_len < name_len)
+	return -1;
+    if (r1->hname_len > name_len)
+	return 1;
+
+    /* Length is equal, compare hashed value. */
+    if (r1->hname_hash < hash)
+	return -1;
+    if (r1->hname_hash > hash)
+	return 1;
+
+    /* Equal length and equal hash. compare the strings. */
+    return strcmp(r1->hname, name);
+}
+
+/* Register one handler for one header name. */
+static int int_register_parser( const char *name, pjsip_parse_hdr_func *fptr )
+{
+    unsigned	pos;
+    handler_rec rec;
+    unsigned	i;
+
+    if (handler_count >= PJ_ARRAY_SIZE(handler)) {
+	return -1;
+    }
+
+    /* Initialize temporary handler. */
+    rec.handler = fptr;
+    rec.hname_len = strlen(name);
+    if (rec.hname_len >= sizeof(rec.hname)) {
+	return -1;
+    }
+    /* Name is copied in lowercase. */
+    for (i=0; i<rec.hname_len; ++i) {
+	rec.hname[i] = (char)tolower(name[i]);
+    }
+    rec.hname[i] = '\0';
+    /* Hash value is calculated from the lowercase name. */
+    rec.hname_hash = pj_hash_calc(0, rec.hname, PJ_HASH_KEY_STRING);
+
+    /* Get the pos to insert the new handler. */
+    for (pos=0; pos < handler_count; ++pos) {
+	int d;
+	d = compare_handler(&handler[pos], rec.hname, rec.hname_len, rec.hname_hash);
+	if (d == 0) {
+	    pj_assert(0);
+	    return -1;
+	}
+	if (d > 0) {
+	    break;
+	}
+    }
+
+    /* Shift handlers. */
+    if (pos != handler_count) {
+	pj_memmove( &handler[pos+1], &handler[pos], (handler_count-pos)*sizeof(handler_rec));
+    }
+    /* Add new handler. */
+    pj_memcpy( &handler[pos], &rec, sizeof(handler_rec));
+    ++handler_count;
+
+    return 0;
+}
+
+/* Register parser handler. If both header name and short name are valid,
+ * then two instances of handler will be registered.
+ */
+PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname,
+					       const char *hshortname,
+					       pjsip_parse_hdr_func *fptr)
+{
+    if (int_register_parser(hname, fptr)) {
+	return -1;
+    }
+    if (hshortname && int_register_parser(hshortname, fptr)) {
+	return -1;
+    }
+    return 0;
+}
+
+/* Find handler to parse the header name. */
+static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname)
+{
+    handler_rec *first;
+    char	 hname_copy[PJSIP_MAX_HNAME_LEN];
+    pj_uint32_t  hash;
+    int		 comp;
+    unsigned	 i, n;
+
+    /* Calculate hash value while converting the header to lowercase. 
+     * Don't assume that 'hname' is NULL terminated.
+     */
+    hash = 0;
+    for (i=0; i<(unsigned)hname->slen; ++i) {
+	hname_copy[i] = (char)tolower(hname->ptr[i]);
+	hash = hash * PJ_HASH_MULTIPLIER + hname_copy[i];
+    }
+    hname_copy[i] = '\0';
+
+    /* Binary search for the handler. */
+    comp = -1;
+    first = &handler[0];
+    n = handler_count;
+    for (; n > 0; ) {
+	unsigned half = n / 2;
+	handler_rec *mid = first + half;
+
+	comp = compare_handler(mid, hname_copy, hname->slen, hash);
+	if (comp < 0) {
+	    first = ++mid;
+	    n -= half + 1;
+	} else if (comp==0) {
+	    first = mid;
+	    break;
+	} else {
+	    n = half;
+	}
+    }
+
+    return comp==0 ? first->handler : NULL;
+}
+
+/* Public function to parse SIP message. */
+PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool, char *buf, pj_size_t size,
+				    pjsip_parser_err_report *err_list)
+{
+    pjsip_msg *msg = NULL;
+    pj_scanner scanner;
+    PJ_USE_EXCEPTION;
+
+    init_sip_parser();
+
+    pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
+
+    PJ_TRY {
+	msg = int_parse_msg(&scanner, pool, err_list);
+    } 
+    PJ_DEFAULT {
+	msg = NULL;
+    }
+    PJ_END
+
+    pj_scan_fini(&scanner);
+    return msg;
+}
+
+/* Determine if a message has been received. */
+PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, 
+				  pj_bool_t is_datagram, pj_size_t *msg_size)
+{
+#if PJ_HAS_TCP
+    const char *hdr_end;
+    const char *body_start;
+    const char *pos;
+    const char *line;
+    int content_length = -1;
+
+    *msg_size = size;
+
+    /* For datagram, the whole datagram IS the message. */
+    if (is_datagram) {
+	return PJ_TRUE;
+    }
+
+
+    /* Find the end of header area by finding an empty line. */
+    if ((pos = strstr(buf, "\n\r\n")) == NULL) {
+	return PJ_FALSE;
+    }
+
+    hdr_end = pos+1;
+    body_start = pos+3;
+
+    /* Find "Content-Length" header the hard way. */
+    line = strchr(buf, '\n');
+    while (line && line < hdr_end-14) {
+	++line;
+	if ( ((*line=='C' || *line=='c') && strncasecmp(line, "Content-Length", 14) == 0) ||
+	     ((*line=='l' || *line=='L') && (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':')))
+	{
+	    /* Try to parse the header. */
+	    pj_scanner scanner;
+	    PJ_USE_EXCEPTION;
+
+	    init_sip_parser();
+
+	    pj_scan_init(&scanner, (char*)line, hdr_end-line, 
+			 PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
+
+	    PJ_TRY {
+		pj_str_t str_clen;
+
+		/* Get "Content-Length" or "L" name */
+		if (*line=='C' || *line=='c')
+		    pj_scan_advance_n(&scanner, 14, PJ_TRUE);
+		else if (*line=='l' || *line=='L')
+		    pj_scan_advance_n(&scanner, 1, PJ_TRUE);
+
+		/* Get colon */
+		if (pj_scan_get_char(&scanner) != ':') {
+		    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+		}
+
+		/* Get number */
+		pj_scan_get(&scanner, pjsip_DIGIT_SPEC, &str_clen);
+
+		/* Get newline. */
+		pj_scan_get_newline(&scanner);
+
+		/* Found a valid Content-Length header. */
+		content_length = pj_strtoul(&str_clen);
+	    } 
+	    PJ_END
+
+	    pj_scan_fini(&scanner);
+	}
+
+	/* Found valid Content-Length? */
+	if (content_length != -1)
+	    break;
+
+	/* Go to next line. */
+	line = strchr(line, '\n');
+    }
+
+    /* Found Content-Length? */
+    if (content_length == -1) {
+	PJ_LOG(4, ("sipparser", "pjsip_find_msg: incoming TCP packet has missing "
+			     "Content-Length header, treated as incomplete."));
+	return PJ_FALSE;
+    }
+
+    /* Enough packet received? */
+    *msg_size = (body_start - buf) + content_length;
+    return (*msg_size) <= size;
+#else
+    PJ_UNUSED_ARG(buf)
+    PJ_UNUSED_ARG(is_datagram)
+    *msg_size = size;
+    return 1;
+#endif
+}
+
+/* Public function to parse URI */
+PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, 
+					 char *buf, pj_size_t size,
+					 unsigned option)
+{
+    PJ_USE_EXCEPTION;
+    pj_scanner scanner;
+    pjsip_uri *uri = NULL;
+
+    if (!parser_is_initialized) {
+	init_parser();
+	parser_is_initialized = 1;
+    }
+
+    pj_scan_init(&scanner, buf, size, 0, &on_syntax_error);
+
+    
+    PJ_TRY {
+	uri = int_parse_uri_or_name_addr(&scanner, pool, option);
+    }
+    PJ_END;
+
+    /* Must have exhausted all inputs. */
+    if (pj_scan_is_eof(&scanner) || *scanner.current=='\r' || *scanner.current=='\n') {
+	/* Success. */
+	pj_scan_fini(&scanner);
+	return uri;
+    }
+
+    /* Still have some characters unparsed. */
+    pj_scan_fini(&scanner);
+    return NULL;
+}
+
+/* Generic function to print message body.
+ * This assumes that the 'data' member points to a contigous memory where the 
+ * actual body is laid.
+ */
+static int generic_print_body (pjsip_msg_body *msg_body, char *buf, pj_size_t size)
+{
+    pjsip_msg_body *body = msg_body;
+    if (size < body->len)
+	return 0;
+
+    pj_memcpy (buf, body->data, body->len);
+    return body->len;
+}
+
+/* Internal function to parse SIP message */
+static pjsip_msg *int_parse_msg( pj_scanner *scanner, pj_pool_t *pool, 
+				 pjsip_parser_err_report *err_list)
+{
+    PJ_USE_EXCEPTION;
+    int ch;
+    pjsip_msg *msg;
+    pjsip_ctype_hdr *ctype_hdr = NULL;
+
+    /* Skip leading newlines. */
+    ch = *scanner->current;
+    while (ch=='\r' || ch=='\n') {
+	pj_scan_get_char(scanner);
+	ch = *scanner->current;
+    }
+
+    msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
+
+    /* Parse request or status line */
+    if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) {
+	msg->type = PJSIP_RESPONSE_MSG;
+	int_parse_status_line( scanner, &msg->line.status );
+    } else {
+	msg->type = PJSIP_REQUEST_MSG;
+	int_parse_req_line(scanner, pool, &msg->line.req );
+    }
+
+    /* Parse headers. */
+    do {
+	pj_str_t hname;
+	pjsip_parse_hdr_func * handler;
+	pjsip_hdr *hdr = NULL;
+
+	/* Get hname. */
+	pj_scan_get( scanner, pjsip_TOKEN_SPEC, &hname);
+	ch = pj_scan_get_char( scanner );
+	if (ch != ':') {
+	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+	}
+
+	/* Find handler. */
+	handler = find_handler(&hname);
+
+	PJ_TRY {
+	    /* Call the handler if found.
+	     * If no handler is found, then treat the header as generic
+	     * hname/hvalue pair.
+	     */
+	    if (handler) {
+		hdr = (*handler)(scanner, pool);
+	    } else {
+		pjsip_generic_string_hdr *ghdr = parse_hdr_generic_string(scanner, pool);
+		ghdr->type = PJSIP_H_OTHER;
+		ghdr->name = ghdr->sname = hname;
+		hdr = (pjsip_hdr*)ghdr;
+	    }
+
+	    /* Check if we've just parsed a Content-Type header. 
+	     * We will check for a message body if we've got Content-Type header.
+	     */
+	    if (hdr->type == PJSIP_H_CONTENT_TYPE) {
+		ctype_hdr = (pjsip_ctype_hdr*)hdr;
+	    }
+
+	}
+	PJ_DEFAULT {
+	    /* Exception was thrown during parsing. 
+	     * Skip until newline, and parse next header. 
+	     */
+	    pj_str_t token;
+	    hdr = NULL;
+
+	    PJ_LOG(4,("sipparser", "Syntax error in line %d col %d (hname=%.*s)",
+		      scanner->line, scanner->col, hname.slen, hname.ptr));
+
+	    if (err_list) {
+		pjsip_parser_err_report *err_info;
+		
+		err_info = pj_pool_alloc(pool, sizeof(*err_info));
+		err_info->exception_code = PJ_GET_EXCEPTION();
+		err_info->line = scanner->line;
+		err_info->col = scanner->col;
+		err_info->hname = hname;
+
+		pj_list_insert_before(err_list, err_info);
+	    }
+
+	    if (!pj_scan_is_eof(scanner)) {
+		pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &token);
+		parse_hdr_end(scanner);
+	    }
+	}
+	PJ_END;
+
+	if (hdr) {
+	    /* Single parse of header line can produce multiple headers.
+	     * For example, if one Contact: header contains Contact list
+	     * separated by comma, then these Contacts will be split into
+	     * different Contact headers.
+	     * So here we must insert list instead of just insert one header.
+	     */
+	    pj_list_insert_nodes_before(&msg->hdr, hdr);
+	}
+
+	/* Parse until EOF or an empty line is found. */
+    } while (!pj_scan_is_eof(scanner) && 
+	      *scanner->current != '\r' && *scanner->current != '\n');
+
+    /* If empty line is found, eat it. */
+    if (!pj_scan_is_eof(scanner)) {
+	if (*scanner->current=='\r' || *scanner->current=='\n') {
+	    pj_scan_get_newline(scanner);
+	}
+    }
+
+    /* If we have Content-Type header, treat the rest of the message as body. */
+    if (ctype_hdr) {
+	pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
+	pj_strdup (pool, &body->content_type.type, &ctype_hdr->media.type);
+	pj_strdup (pool, &body->content_type.subtype, &ctype_hdr->media.subtype);
+	pj_strdup (pool, &body->content_type.param, &ctype_hdr->media.param);
+	body->data = scanner->current;
+	body->len = scanner->end - scanner->current;
+	body->print_body = &generic_print_body;
+
+	msg->body = body;
+    }
+
+    return msg;
+}
+
+/* Parse parameter (pname ["=" pvalue]). */
+void pjsip_parse_param_imp(  pj_scanner *scanner,
+			     pj_str_t *pname, pj_str_t *pvalue,
+			     unsigned option)
+{
+    /* pname */
+    pj_scan_get(scanner, pjsip_PARAM_CHAR_SPEC, pname);
+
+    /* pvalue, if any */
+    if (*scanner->current == '=') {
+	pj_scan_get_char(scanner);
+	/* pvalue can be a quoted string. */
+	if (*scanner->current == '"') {
+	    pj_scan_get_quote( scanner, '"', '"', pvalue);
+	    if (option & PJSIP_PARSE_REMOVE_QUOTE) {
+		pvalue->ptr++;
+		pvalue->slen -= 2;
+	    }
+	} else if (pj_cs_match(pjsip_PARAM_CHAR_SPEC, *scanner->current)) {
+	    pj_scan_get(scanner, pjsip_PARAM_CHAR_SPEC, pvalue);
+	} else {
+	    pvalue->ptr = NULL;
+	    pvalue->slen = 0;
+	}
+    } else {
+	pvalue->ptr = NULL;
+	pvalue->slen = 0;
+    }
+}
+
+/* Parse parameter (";" pname ["=" pvalue]). */
+static void int_parse_param( pj_scanner *scanner,
+			     pj_str_t *pname, pj_str_t *pvalue)
+{
+    /* Get ';' character */
+    pj_scan_get_char(scanner);
+
+    /* Get pname and optionally pvalue */
+    pjsip_parse_param_imp(scanner, pname, pvalue, 0);
+}
+
+/* Parse host:port in URI. */
+static void int_parse_uri_host_port( pj_scanner *scanner, 
+				     pj_str_t *host, int *p_port)
+{
+    pj_scan_get( scanner, pjsip_HOST_SPEC, host);
+    if (*scanner->current == ':') {
+	pj_str_t port;
+	pj_scan_get_char(scanner);
+	pj_scan_get(scanner, pjsip_DIGIT_SPEC, &port);
+	*p_port = pj_strtoul(&port);
+    } else {
+	*p_port = 0;
+    }
+}
+
+/* Determine if the next token in an URI is a user specification. */
+static int int_is_next_user(pj_scanner *scanner)
+{
+    pj_str_t dummy;
+    int is_user;
+
+    /* Find character '@'. If this character exist, then the token
+     * must be a username.
+     */
+    if (pj_scan_peek( scanner, pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@')
+	is_user = 1;
+    else
+	is_user = 0;
+
+    return is_user;
+}
+
+/* Parse user:pass tokens in an URI. */
+static void int_parse_user_pass( pj_scanner *scanner, 
+				 pj_str_t *user, pj_str_t *pass)
+{
+    pj_scan_get( scanner, pjsip_USER_SPEC, user);
+    if ( *scanner->current == ':') {
+	pj_scan_get_char( scanner );
+	pj_scan_get( scanner, pjsip_PASSWD_SPEC, pass);
+    } else {
+	pass->ptr = NULL;
+	pass->slen = 0;
+    }
+
+    /* Get the '@' */
+    pj_scan_get_char( scanner );
+}
+
+/* Parse all types of URI. */
+static pjsip_uri *int_parse_uri_or_name_addr(pj_scanner *scanner, pj_pool_t *pool,
+						  unsigned option)
+{
+    pjsip_uri *uri;
+    int is_name_addr = 0;
+
+    if (*scanner->current=='"' || *scanner->current=='<') {
+	uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
+	is_name_addr = 1;
+    } else {
+	pj_scan_state backtrack;
+	pj_str_t scheme;
+	int colon;
+
+	pj_scan_save_state( scanner, &backtrack);
+	pj_scan_get( scanner, pjsip_TOKEN_SPEC, &scheme);
+	colon = pj_scan_get_char( scanner );
+	pj_scan_restore_state( scanner, &backtrack);
+
+	if (colon==':' && (parser_stricmp(scheme, pjsip_SIP_STR)==0 || parser_stricmp(scheme, pjsip_SIPS_STR)==0)) 
+	{
+	    uri = (pjsip_uri*)int_parse_sip_url( scanner, pool, 
+					(option & PJSIP_PARSE_URI_IN_FROM_TO_HDR) == 0 );
+
+	} else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) {
+
+	    /* Not supported. */
+	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+	    UNREACHED({return NULL; /* Not reached. */});
+
+	} else {
+	    uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
+	    is_name_addr = 1;
+	}
+    }
+
+    /* Should we return the URI object as name address? */
+    if (option & PJSIP_PARSE_URI_AS_NAMEADDR) {
+	if (is_name_addr == 0) {
+	    pjsip_name_addr *name_addr;
+
+	    name_addr = pjsip_name_addr_create(pool);
+	    name_addr->uri = uri;
+
+	    uri = (pjsip_uri*)name_addr;
+	}
+    }
+
+    return uri;
+}
+
+/* Parse URI. */
+static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool, 
+				pj_bool_t parse_params)
+{
+    if (*scanner->current=='"' || *scanner->current=='<') {
+	return (pjsip_uri*)int_parse_name_addr( scanner, pool );
+    } else {
+	pj_str_t scheme;
+	int colon;
+
+	/* Get scheme. */
+	colon = pj_scan_peek(scanner, pjsip_TOKEN_SPEC, &scheme);
+	if (colon != ':') {
+	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+	}
+
+	if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 || parser_stricmp(scheme, pjsip_SIPS_STR)==0)) 
+	{
+	    return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params );
+
+	} else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) {
+	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+	    UNREACHED({ return NULL; /* Not reached. */ })
+
+	} else {
+	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+	    UNREACHED({ return NULL; /* Not reached. */ })
+	}
+    }
+}
+
+/* Parse "sip:" and "sips:" URI. */
+static pjsip_url *int_parse_sip_url( pj_scanner *scanner, 
+				     pj_pool_t *pool,
+				     pj_bool_t parse_params)
+{
+    pj_str_t scheme;
+    pjsip_url *url;
+    int colon;
+    int skip_ws = scanner->skip_ws;
+    scanner->skip_ws = 0;
+
+    pj_scan_get(scanner, pjsip_TOKEN_SPEC, &scheme);
+    colon = pj_scan_get_char(scanner);
+    if (colon != ':') {
+	PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+    }
+
+    if (parser_stricmp(scheme, pjsip_SIP_STR)==0) {
+	url = pjsip_url_create(pool, 0);
+
+    } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) {
+	url = pjsip_url_create(pool, 1);
+
+    } else {
+	PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+	/* should not reach here */
+	UNREACHED({
+	    pj_assert(0);
+	    return 0;
+	})
+    }
+
+    if (int_is_next_user(scanner)) {
+	int_parse_user_pass(scanner, &url->user, &url->passwd);
+    }
+
+    /* Get host:port */
+    int_parse_uri_host_port(scanner, &url->host, &url->port);
+
+    /* Get URL parameters. */
+    while ( parse_params && *scanner->current == ';' ) {
+	pj_str_t pname, pvalue;
+
+	int_parse_param( scanner, &pname, &pvalue);
+
+	if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) {
+	    url->user_param = pvalue;
+
+	} else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) {
+	    url->method_param = pvalue;
+
+	} else if (!parser_stricmp(pname, pjsip_TRANSPORT_STR) && pvalue.slen) {
+	    url->transport_param = pvalue;
+
+	} else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
+	    url->ttl_param = pj_strtoul(&pvalue);
+
+	} else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
+	    url->maddr_param = pvalue;
+
+	} else if (!parser_stricmp(pname, pjsip_LR_STR)) {
+	    url->lr_param = 1;
+
+	} else {
+	    concat_param(&url->other_param, pool, &pname, &pvalue);
+	}
+    }
+
+    /* Get header params. */
+    if (parse_params && *scanner->current == '?') {
+	pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &url->header_param);
+    }
+
+    scanner->skip_ws = skip_ws;
+    pj_scan_skip_whitespace(scanner);
+    return url;
+}
+
+/* Parse nameaddr. */
+static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner, 
+					     pj_pool_t *pool )
+{
+    int has_bracket;
+    pjsip_name_addr *name_addr;
+
+    name_addr = pjsip_name_addr_create(pool);
+
+    if (*scanner->current == '"') {
+	pj_scan_get_quote( scanner, '"', '"', &name_addr->display);
+
+    } else if (*scanner->current != '<') {
+	int next;
+	pj_str_t dummy;
+
+	/* This can be either the start of display name,
+	 * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char.
+	 * We're only interested in display name, because SIP URL
+	 * will be parser later.
+	 */
+	next = pj_scan_peek_until(scanner, pjsip_DISPLAY_SCAN_SPEC, &dummy);
+	if (next == '<') {
+	    /* Ok, this is what we're looking for, a display name. */
+	    pj_scan_get_until_ch( scanner, '<', &name_addr->display);
+	    pj_strtrim(&name_addr->display);
+	}
+    }
+
+    /* Manually skip whitespace. */
+    pj_scan_skip_whitespace(scanner);
+
+    /* Get the SIP-URL */
+    has_bracket = (*scanner->current == '<');
+    if (has_bracket)
+	pj_scan_get_char(scanner);
+    name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE );
+    if (has_bracket)
+	pj_scan_get_char(scanner);
+
+    return name_addr;
+}
+
+
+/* Parse SIP request line. */
+static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool,
+				pjsip_request_line *req_line)
+{
+    pj_str_t token;
+
+    pj_scan_get( scanner, pjsip_TOKEN_SPEC, &token);
+    pjsip_method_init_np( &req_line->method, &token);
+
+    req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE);
+    if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0)
+	PJ_THROW( PJSIP_SYN_ERR_EXCEPTION);
+    pj_scan_advance_n (scanner, 7, 1);
+    pj_scan_get_newline( scanner );
+}
+
+/* Parse status line. */
+static void int_parse_status_line( pj_scanner *scanner, 
+				   pjsip_status_line *status_line)
+{
+    pj_str_t token;
+
+    if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0)
+	PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+    pj_scan_advance_n( scanner, 7, 1);
+
+    pj_scan_get( scanner, pjsip_DIGIT_SPEC, &token);
+    status_line->code = pj_strtoul(&token);
+    pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &status_line->reason);
+    pj_scan_get_newline( scanner );
+}
+
+/* Parse ending of header. */
+static void parse_hdr_end( pj_scanner *scanner )
+{
+    if (pj_scan_is_eof(scanner)) {
+	;   /* Do nothing. */
+    } else if (*scanner->current == '&') {
+	pj_scan_get_char(scanner);
+    } else {
+	pj_scan_get_newline(scanner);
+    }
+}
+
+/* Parse ending of header. */
+void pjsip_parse_end_hdr_imp( pj_scanner *scanner )
+{
+    parse_hdr_end(scanner);
+}
+
+/* Parse generic array header. */
+static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr,
+				     pj_scanner *scanner)
+{
+    pj_scan_get_until( scanner, pjsip_ARRAY_ELEMENTS, &hdr->values[0]);
+    hdr->count++;
+
+    while (*scanner->current == ',') {
+	pj_scan_get_char(scanner);
+	pj_scan_get_until( scanner, pjsip_ARRAY_ELEMENTS, &hdr->values[hdr->count]);
+	hdr->count++;
+    }
+    parse_hdr_end(scanner);
+}
+
+/* Parse generic string header. */
+static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr,
+				      pj_scanner *scanner )
+{
+    pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue);
+    parse_hdr_end(scanner);
+}
+
+/* Parse generic integer header. */
+static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr,
+				   pj_scanner *scanner )
+{
+    pj_str_t tmp;
+    pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &tmp);
+    hdr->ivalue = pj_strtoul(&tmp);
+    parse_hdr_end(scanner);
+}
+
+
+/* Parse Accept header. */
+static pjsip_accept_hdr* parse_hdr_accept( pj_scanner *scanner, 
+					    pj_pool_t *pool)
+{
+    pjsip_accept_hdr *accept = pjsip_accept_hdr_create(pool);
+    parse_generic_array_hdr(accept, scanner);
+    return accept;
+}
+
+/* Parse Allow header. */
+static pjsip_allow_hdr*   parse_hdr_allow( pj_scanner *scanner, 
+					   pj_pool_t *pool)
+{
+    pjsip_allow_hdr *allow = pjsip_allow_hdr_create(pool);
+    parse_generic_array_hdr(allow, scanner);
+    return allow;
+}
+
+/* Parse Call-ID header. */
+static pjsip_cid_hdr* parse_hdr_call_id( pj_scanner *scanner, 
+					  pj_pool_t *pool)
+{
+    pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(pool);
+    pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id);
+    parse_hdr_end(scanner);
+    return hdr;
+}
+
+/* Parse and interpret Contact param. */
+static void int_parse_contact_param( pjsip_contact_hdr *hdr, 
+				     pj_scanner *scanner,
+				     pj_pool_t *pool)
+{
+    while ( *scanner->current == ';' ) {
+	pj_str_t pname, pvalue;
+
+	int_parse_param( scanner, &pname, &pvalue);
+	if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) {
+	    char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen);
+	    if (!dot_pos) {
+		hdr->q1000 = pj_strtoul(&pvalue);
+	    } else {
+		pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1);
+		pvalue.ptr = dot_pos + 1;
+		hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3);
+	    }    
+	} else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) {
+	    hdr->expires = pj_strtoul(&pvalue);
+
+	} else {
+	    concat_param(&hdr->other_param, pool, &pname, &pvalue);
+	}
+    }
+}
+
+/* Parse Contact header. */
+PJ_DEF(pjsip_contact_hdr*) parse_hdr_contact( pj_scanner *scanner, 
+					      pj_pool_t *pool)
+{
+    pjsip_contact_hdr *first = NULL;
+    
+    do {
+	pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool);
+	if (first == NULL)
+	    first = hdr;
+	else
+	    pj_list_insert_before(first, hdr);
+
+	if (*scanner->current == '*') {
+	    pj_scan_get_char(scanner);
+	    hdr->star = 1;
+
+	} else {
+	    hdr->star = 0;
+	    hdr->uri = int_parse_uri_or_name_addr(scanner, pool, PJSIP_PARSE_URI_AS_NAMEADDR);
+
+	    int_parse_contact_param(hdr, scanner, pool);
+	}
+
+	if (*scanner->current != ',')
+	    break;
+
+	pj_scan_get_char(scanner);
+
+    } while (1);
+
+    parse_hdr_end(scanner);
+    return first;
+}
+
+/* Parse Content-Length header. */
+PJ_DEF(pjsip_clen_hdr*) parse_hdr_content_length( pj_scanner *scanner, 
+					          pj_pool_t *pool)
+{
+    pj_str_t digit;
+    pjsip_clen_hdr *hdr;
+
+    hdr = pjsip_clen_hdr_create(pool);
+    pj_scan_get(scanner, pjsip_DIGIT_SPEC, &digit);
+    hdr->len = pj_strtoul(&digit);
+    parse_hdr_end(scanner);
+    return hdr;
+}
+
+/* Parse Content-Type header. */
+PJ_DEF(pjsip_ctype_hdr*) parse_hdr_content_type( pj_scanner *scanner, 
+					         pj_pool_t *pool)
+{
+    pjsip_ctype_hdr *hdr;
+
+    hdr = pjsip_ctype_hdr_create(pool);
+    
+    /* Parse media type and subtype. */
+    pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->media.type);
+    pj_scan_get_char(scanner);
+    pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->media.subtype);
+
+    /* Parse media parameters */
+    while (*scanner->current == ';') {
+	pj_str_t pname, pvalue;
+	int_parse_param(scanner, &pname, &pvalue);
+	concat_param(&hdr->media.param, pool, &pname, &pvalue);
+    }
+
+    parse_hdr_end(scanner);
+    return hdr;
+}
+
+/* Parse CSeq header. */
+PJ_DEF(pjsip_cseq_hdr*) parse_hdr_cseq( pj_scanner *scanner, 
+				        pj_pool_t *pool)
+{
+    pj_str_t cseq, method;
+    pjsip_cseq_hdr *hdr;
+
+    hdr = pjsip_cseq_hdr_create(pool);
+    pj_scan_get( scanner, pjsip_DIGIT_SPEC, &cseq);
+    hdr->cseq = pj_strtoul(&cseq);
+
+    pj_scan_get( scanner, pjsip_TOKEN_SPEC, &method);
+    pjsip_method_init_np(&hdr->method, &method);
+
+    parse_hdr_end( scanner );
+    return hdr;
+}
+
+/* Parse Expires header. */
+static pjsip_expires_hdr* parse_hdr_expires(pj_scanner *scanner, 
+					    pj_pool_t *pool)
+{
+    pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(pool);
+    parse_generic_int_hdr(hdr, scanner);
+    return hdr;
+}
+
+/* Parse From: or To: header. */
+static void parse_hdr_fromto( pj_scanner *scanner, 
+			      pj_pool_t *pool, 
+			      pjsip_from_hdr *hdr)
+{
+    hdr->uri = int_parse_uri_or_name_addr(scanner, pool, 
+					  PJSIP_PARSE_URI_AS_NAMEADDR |
+					  PJSIP_PARSE_URI_IN_FROM_TO_HDR);
+
+    while ( *scanner->current == ';' ) {
+	pj_str_t pname, pvalue;
+
+	int_parse_param( scanner, &pname, &pvalue);
+
+	if (!parser_stricmp(pname, pjsip_TAG_STR)) {
+	    hdr->tag = pvalue;
+	    
+	} else {
+	    concat_param(&hdr->other_param, pool, &pname, &pvalue);
+	}
+    }
+
+    parse_hdr_end(scanner);
+}
+
+/* Parse From: header. */
+PJ_DEF(pjsip_from_hdr*) parse_hdr_from( pj_scanner *scanner, 
+				        pj_pool_t *pool)
+{
+    pjsip_from_hdr *hdr = pjsip_from_hdr_create(pool);
+    parse_hdr_fromto(scanner, pool, hdr);
+    return hdr;
+}
+
+/* Parse Require: header. */
+static pjsip_require_hdr* parse_hdr_require( pj_scanner *scanner, 
+					     pj_pool_t *pool)
+{
+    pjsip_require_hdr *hdr = pjsip_require_hdr_create(pool);
+    parse_generic_array_hdr(hdr, scanner);
+    return hdr;
+}
+
+/* Parse Retry-After: header. */
+static pjsip_retry_after_hdr* parse_hdr_retry_after(pj_scanner *scanner, 
+						    pj_pool_t *pool)
+{
+    pjsip_retry_after_hdr *hdr;
+    hdr = pjsip_retry_after_hdr_create(pool);
+    parse_generic_int_hdr(hdr, scanner);
+    return hdr;
+}
+
+/* Parse Supported: header. */
+static pjsip_supported_hdr* parse_hdr_supported(pj_scanner *scanner, 
+						pj_pool_t *pool)
+{
+    pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(pool);
+    parse_generic_array_hdr(hdr, scanner);
+    return hdr;
+}
+
+
+/* Parse To: header. */
+PJ_DEF(pjsip_to_hdr*) parse_hdr_to( pj_scanner *scanner, 
+				    pj_pool_t *pool)
+{
+    pjsip_to_hdr *hdr = pjsip_to_hdr_create(pool);
+    parse_hdr_fromto(scanner, pool, hdr);
+    return hdr;
+}
+
+/* Parse Unsupported: header. */
+static pjsip_unsupported_hdr* parse_hdr_unsupported(pj_scanner *scanner, 
+						    pj_pool_t *pool)
+{
+    pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(pool);
+    parse_generic_array_hdr(hdr, scanner);
+    return hdr;
+}
+
+/* Parse and interpret Via parameters. */
+static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner,
+				 pj_pool_t *pool)
+{
+    while ( *scanner->current == ';' ) {
+	pj_str_t pname, pvalue;
+
+	int_parse_param( scanner, &pname, &pvalue);
+
+	if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) {
+	    hdr->branch_param = pvalue;
+
+	} else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
+	    hdr->ttl_param = pj_strtoul(&pvalue);
+	    
+	} else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
+	    hdr->maddr_param = pvalue;
+
+	} else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) {
+	    hdr->recvd_param = pvalue;
+
+	} else if (!parser_stricmp(pname, pjsip_RPORT_STR)) {
+	    if (pvalue.slen)
+		hdr->rport_param = pj_strtoul(&pvalue);
+	    else
+		hdr->rport_param = 0;
+	} else {
+	    concat_param( &hdr->other_param, pool, &pname, &pvalue);
+	}
+    }
+
+}
+
+/* Parse Max-Forwards header. */
+static pjsip_max_forwards_hdr* parse_hdr_max_forwards( pj_scanner *scanner, 
+						       pj_pool_t *pool)
+{
+    pjsip_max_forwards_hdr *hdr;
+    hdr = pjsip_max_forwards_hdr_create(pool);
+    parse_generic_int_hdr(hdr, scanner);
+    return hdr;
+}
+
+/* Parse Min-Expires header. */
+static pjsip_min_expires_hdr*  parse_hdr_min_expires(pj_scanner *scanner, 
+						     pj_pool_t *pool)
+{
+    pjsip_min_expires_hdr *hdr;
+    hdr = pjsip_min_expires_hdr_create(pool);
+    parse_generic_int_hdr(hdr, scanner);
+    return hdr;
+}
+
+
+/* Parse Route: or Record-Route: header. */
+static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool,
+				pjsip_routing_hdr *hdr )
+{
+    pjsip_name_addr *temp=int_parse_name_addr(scanner, pool);
+
+    pj_memcpy(&hdr->name_addr, temp, sizeof(*temp));
+    if (*scanner->current == ';') {
+	pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->other_param);
+    }
+}
+
+/* Parse Record-Route header. */
+PJ_DEF(pjsip_rr_hdr*) parse_hdr_rr( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_rr_hdr *first = NULL;
+
+    do {
+	pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(pool);
+	if (!first) {
+	    first = hdr;
+	} else {
+	    pj_list_insert_before(first, hdr);
+	}
+	parse_hdr_rr_route(scanner, pool, hdr);
+	if (*scanner->current == ',') {
+	    pj_scan_get_char(scanner);
+	} else {
+	    break;
+	}
+    } while (1);
+    parse_hdr_end(scanner);
+    return first;
+}
+
+/* Parse Route: header. */
+PJ_DEF(pjsip_route_hdr*) parse_hdr_route( pj_scanner *scanner, 
+					  pj_pool_t *pool)
+{
+    pjsip_route_hdr *first = NULL;
+
+    do {
+	pjsip_route_hdr *hdr = pjsip_route_hdr_create(pool);
+	if (!first) {
+	    first = hdr;
+	} else {
+	    pj_list_insert_before(first, hdr);
+	}
+	parse_hdr_rr_route(scanner, pool, hdr);
+	if (*scanner->current == ',') {
+	    pj_scan_get_char(scanner);
+	} else {
+	    break;
+	}
+    } while (1);
+    parse_hdr_end(scanner);
+    return first;
+}
+
+/* Parse Via: header. */
+PJ_DEF(pjsip_via_hdr*) parse_hdr_via( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_via_hdr *first = NULL;
+
+    do {
+	pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool);
+	if (!first)
+	    first = hdr;
+	else
+	    pj_list_insert_before(first, hdr);
+
+	if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0)
+	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+
+	pj_scan_advance_n( scanner, 8, 1);
+
+	pj_scan_get( scanner, pjsip_TOKEN_SPEC, &hdr->transport);
+	pj_scan_get( scanner, pjsip_HOST_SPEC, &hdr->sent_by.host);
+
+	if (*scanner->current==':') {
+	    pj_str_t digit;
+	    pj_scan_get_char(scanner);
+	    pj_scan_get(scanner, pjsip_DIGIT_SPEC, &digit);
+	    hdr->sent_by.port = pj_strtoul(&digit);
+	} else {
+	    hdr->sent_by.port = 5060;
+	}
+	
+	int_parse_via_param(hdr, scanner, pool);
+
+	if (*scanner->current == '(') {
+	    pj_scan_get_char(scanner);
+	    pj_scan_get_until_ch( scanner, ')', &hdr->comment);
+	    pj_scan_get_char( scanner );
+	}
+
+	if (*scanner->current != ',')
+	    break;
+
+	pj_scan_get_char(scanner);
+
+    } while (1);
+
+    parse_hdr_end(scanner);
+    return first;
+}
+
+/* Parse generic header. */
+PJ_DEF(pjsip_generic_string_hdr*) parse_hdr_generic_string( pj_scanner *scanner, 
+						pj_pool_t *pool)
+{
+    pjsip_generic_string_hdr *hdr;
+
+    hdr = pjsip_generic_string_hdr_create(pool, NULL);
+    parse_generic_string_hdr(hdr, scanner);
+    return hdr;
+
+}
+
+/* Public function to parse a header value. */
+PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
+			       char *buf, pj_size_t size, int *parsed_len )
+{
+    pj_scanner scanner;
+    pjsip_hdr *hdr = NULL;
+    PJ_USE_EXCEPTION;
+
+    init_sip_parser();
+
+    pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
+
+    PJ_TRY {
+	pjsip_parse_hdr_func *handler = find_handler(hname);
+	if (handler) {
+	    hdr = (*handler)(&scanner, pool);
+	} else {
+	    pjsip_generic_string_hdr *ghdr = parse_hdr_generic_string(&scanner, pool);
+	    ghdr->type = PJSIP_H_OTHER;
+	    pj_strdup(pool, &ghdr->name, hname);
+	    ghdr->sname = ghdr->name;
+	    hdr = (pjsip_hdr*)ghdr;
+	}
+
+    } 
+    PJ_DEFAULT {
+	hdr = NULL;
+    }
+    PJ_END
+
+    if (parsed_len) {
+	*parsed_len = (scanner.current - scanner.begin);
+    }
+
+    pj_scan_fini(&scanner);
+
+    return hdr;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_parser.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_parser.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_parser.h	(revision 1753)
@@ -0,0 +1,323 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_parser.h 10    9/11/05 9:28a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_PARSER_H__
+#define __PJSIP_SIP_PARSER_H__
+
+/**
+ * @file sip_parser.h
+ * @brief SIP Message Parser
+ */
+
+#include <pjsip/sip_types.h>
+#include <pj/scanner.h>
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_PARSER SIP Message Parser
+ * @ingroup PJSIP
+ * @{
+ */
+
+/**
+ * URI Parsing options.
+ */
+enum
+{
+    /** If this option is specified, function #pjsip_parse_uri will return
+     *  the URI object as pjsip_name_addr instead of the corresponding
+     *  URI object.
+     */
+    PJSIP_PARSE_URI_AS_NAMEADDR = 1,
+
+    /** If this option is specified, function #pjsip_parse_uri and other
+     *  internal functions that this function calls will parse URI according
+     *  to convention for parsing From/To header. For example, when the URI
+     *  is not enclosed in brackets ("<" and ">"), all parameters will not
+     *  be stored to the URI (it will be stored to the header).
+     */
+    PJSIP_PARSE_URI_IN_FROM_TO_HDR = 2,
+};
+
+/**
+ * Parser syntax error exception value.
+ */
+#define PJSIP_SYN_ERR_EXCEPTION	1
+
+/**
+ * This structure is used to get error reporting from parser.
+ */
+typedef struct pjsip_parser_err_report
+{
+    PJ_DECL_LIST_MEMBER(struct pjsip_parser_err_report)
+    int		exception_code;	/**< Error exception (e.g. PJSIP_SYN_ERR_EXCEPTION) */
+    int		line;		/**< Line number. */
+    int		col;		/**< Column number. */
+    pj_str_t	hname;		/**< Header name, if any. */
+} pjsip_parser_err_report;
+
+
+/**
+ * Type of function to parse header. The parsing function must follow these
+ * specification:
+ *   - It must not modify the input text.
+ *   - The hname and HCOLON has been parsed prior to invoking the handler.
+ *   - It returns the header instance on success.
+ *   - For error reporting, it must throw PJSIP_SYN_ERR_EXCEPTION exception 
+ *     instead of just returning NULL. 
+ *     When exception is thrown, the return value is ignored.
+ *   - It must read the header separator after finished reading the header
+ *     body. The separator types are described below, and if they don't exist,
+ *     exception must be thrown. Header separator can be a:
+ *	- newline, such as when the header is part of a SIP message.
+ *	- ampersand, such as when the header is part of an URI.
+ *	- for the last header, these separator is optional since parsing
+ *        can be terminated when seeing EOF.
+ */
+typedef void* (pjsip_parse_hdr_func)(pj_scanner *scanner, pj_pool_t *pool);
+
+/**
+ * Type of function to parse URI scheme.
+ * Most of the specification of header parser handler (pjsip_parse_hdr_func)
+ * also applies here (except the separator part).
+ */
+typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool);
+
+/**
+ * Register header parser handler. The parser handler MUST follow the 
+ * specification of header parser handler function. New registration 
+ * overwrites previous registration with the same name.
+ *
+ * @param hname		The header name.
+ * @param hshortname	The short header name or NULL.
+ * @param fptr		The pointer to function to parser the header.
+ *
+ * @return		zero if success.
+ * @see pjsip_parse_hdr_func
+ */
+PJ_DECL(pj_status_t) pjsip_register_hdr_parser( const char *hname,
+						const char *hshortname,
+						pjsip_parse_hdr_func *fptr);
+
+/**
+ * Unregister previously registered header parser handler.
+ * All the arguments MUST exactly equal to the value specified upon 
+ * registration of the handler.
+ *
+ * @param hname		The header name registered.
+ * @param hshortname	The short header name registered, or NULL.
+ *
+ * @return		zero if unregistration was successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_unregister_hdr_parser( const char *hname,
+						  const char *hshortname,
+						  pjsip_parse_hdr_func *fptr);
+
+/**
+ * Register URI scheme parser handler.
+ *
+ * @param scheme	The URI scheme registered.
+ * @param func		The URI parser function.
+ *
+ * @return		zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_register_uri_parser( const char *scheme,
+					        pjsip_parse_uri_func *func);
+
+/**
+ * Unregister URI scheme parser handler.
+ * All the arguments MUST exactly equal to the value specified upon 
+ * registration of the handler.
+ *
+ * @param scheme	The URI scheme as registered previously.
+ * @param func		The function handler as registered previously.
+ *
+ * @return		zero if the registration was successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_unregister_uri_parser( const char *scheme,
+						  pjsip_parse_uri_func *func);
+
+/**
+ * Parse an URI in the input and return the correct instance of URI.
+ *
+ * @param pool		The pool to get memory allocations.
+ * @param buf		The input buffer, which size must be at least (size+1)
+ *			because the function will temporarily put NULL 
+ *			termination at the end of the buffer during parsing.
+ * @param size		The length of the string (not counting NULL terminator).
+ * @param options	If no options are given (value is zero), the object 
+ *			returned is dependent on the syntax of the URI, 
+ *			eg. basic SIP URL, TEL URL, or name address. 
+ *			If option PJSIP_PARSE_URI_AS_NAMEADDR is given,
+ *			then the returned object is always name address object,
+ *			with the relevant URI object contained in the name 
+ *			address object.
+ * @return		The URI or NULL when failed. No exception is thrown by 
+ *			this function (or any public parser functions).
+ */
+PJ_DECL(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, 
+				     char *buf, pj_size_t size,
+				     unsigned option);
+
+/**
+ * Parse a packet buffer and build a full SIP message from the packet. This
+ * function parses all parts of the message, including request/status line,
+ * all headers, and the message body. The message body however is only 
+ * treated as a text block, ie. the function will not try to parse the content
+ * of the body.
+ *
+ * @param pool		The pool to allocate memory.
+ * @param buf		The input buffer, which size must be at least (size+1)
+ *			because the function will temporarily put NULL 
+ *			termination at the end of the buffer during parsing.
+ * @param size		The length of the string (not counting NULL terminator).
+ * @param err_list	If this parameter is not NULL, then the parser will
+ *			put error messages during parsing in this list.
+ *
+ * @return		The message or NULL when failed. No exception is thrown
+ *			by this function (or any public parser functions).
+ */
+PJ_DECL(pjsip_msg *) pjsip_parse_msg( pj_pool_t *pool, 
+				      char *buf, pj_size_t size,
+				      pjsip_parser_err_report *err_list);
+
+
+/**
+ * Check incoming packet to see if a (probably) valid SIP message has been 
+ * received.
+ *
+ * @param buf		The input buffer, which must be NULL terminated.
+ * @param size		The buffer size.
+ * @param msg_size	[out] If message is valid, this parameter will contain
+ *			the size of the SIP message (including body, if any).
+ *
+ * @return		PJ_TRUE (1) if a message is found.
+ */
+PJ_DECL(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, 
+				   pj_bool_t is_datagram, pj_size_t *msg_size);
+
+/**
+ * Parse the content of a header and return the header instance.
+ * This function parses the content of a header (ie. part after colon) according
+ * to the expected name, and will return the correct instance of header.
+ *
+ * @param pool		Pool to allocate memory for the header.
+ * @param hname		Header name which is used to find the correct function
+ *			to parse the header.
+ * @param line		Header content, which size must be at least size+1.
+ * @param size		The length of the string (not counting NULL terminator,
+ *			if any).
+ * @param parsed_len	If the value is not NULL, then upon return the function
+ *			will fill the pointer with the length of the string
+ *			that has been parsed. This is usefull for two purposes,
+ *			one is when the string may contain more than one header
+ *			lines, and two when an error happen the value can
+ *			pinpoint the location of the error in the buffer.
+ *
+ * @return		The instance of the header if parsing was successfull,
+ *			or otherwise a NULL pointer will be returned.
+ */
+PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
+				char *line, pj_size_t size,
+				int *parsed_len);
+
+/**
+ * Parse header line(s). Multiple headers can be parsed by this function.
+ * When there are multiple headers, the headers MUST be separated by either
+ * a newline (as in SIP message) or ampersand mark (as in URI). This separator
+ * however is optional for the last header.
+ *
+ * @param pool the pool.
+ * @param buf the input text to parse.
+ * @param size the text length.
+ * @param hlist the header list to store the parsed headers. This list must
+ *              have been initialized before calling this function.
+ * @return zero if successfull, or -1 if error is encountered. Upon error,
+ *              the \a hlist argument MAY contain successfully parsed headers.
+ */
+PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool,
+					  char *input, pj_size_t size,
+					  pj_list *hlist );
+
+
+/*
+ * Various specification used in parsing, exported here as extern for other
+ * parsers.
+ */
+extern
+pj_char_spec	pjsip_HOST_SPEC,	    /* For scanning host part. */
+		pjsip_DIGIT_SPEC,	    /* Decimal digits */
+		pjsip_ALPHA_SPEC,	    /* Alpha (A-Z, a-z) */
+		pjsip_ALNUM_SPEC,	    /* Decimal + Alpha. */
+		pjsip_TOKEN_SPEC,	    /* Token. */
+		pjsip_HEX_SPEC,		    /* Hexadecimal digits. */
+		pjsip_PARAM_CHAR_SPEC,	    /* For scanning pname (or pvalue when it's not quoted.) */
+		pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */
+		pjsip_PASSWD_SPEC,	    /* Password. */
+		pjsip_USER_SPEC,	    /* User */
+		pjsip_NEWLINE_OR_EOF_SPEC,  /* For eating up header.*/
+		pjsip_DISPLAY_SCAN_SPEC;    /* Used when searching for display name in URL. */
+
+/*
+ * Various string constants.
+ */
+extern const pj_str_t pjsip_USER_STR,
+		      pjsip_METHOD_STR,
+		      pjsip_TRANSPORT_STR,
+		      pjsip_MADDR_STR,
+		      pjsip_LR_STR,
+		      pjsip_SIP_STR,
+		      pjsip_SIPS_STR,
+		      pjsip_TEL_STR,
+		      pjsip_BRANCH_STR,
+		      pjsip_TTL_STR,
+		      pjsip_PNAME_STR,
+		      pjsip_Q_STR,
+		      pjsip_EXPIRES_STR,
+		      pjsip_TAG_STR;
+
+/*
+ * Parser utilities.
+ */
+enum
+{
+    PJSIP_PARSE_REMOVE_QUOTE = 1,
+};
+
+void pjsip_parse_param_imp(  pj_scanner *scanner,
+			     pj_str_t *pname, pj_str_t *pvalue,
+			     unsigned opt);
+void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool, 
+			 const pj_str_t *pname, const pj_str_t *pvalue, int sepchar);
+void pjsip_parse_end_hdr_imp ( pj_scanner *scanner );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_SIP_PARSER_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_private.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_private.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_private.h	(revision 1753)
@@ -0,0 +1,104 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_private.h 5     6/17/05 11:16p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_PRIVATE_H__
+#define __PJSIP_SIP_PRIVATE_H__
+
+/**
+ * @file sip_private.h
+ * @brief Private structures and functions for PJSIP Library.
+ */ 
+
+#include <pjsip/sip_types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_PRIVATE Private structures and functions (PJSIP internals)
+ * @ingroup PJSIP
+ * @{
+ */
+
+
+/** 
+ * Create a new transport manager.
+ * @param pool The pool
+ * @param endpt The endpoint
+ * @param cb Callback to be called to receive messages from transport.
+ */
+PJ_DECL(pjsip_transport_mgr*) pjsip_transport_mgr_create( pj_pool_t *pool,
+							  pjsip_endpoint *endpt,
+							  void (*cb)(pjsip_endpoint *,pjsip_rx_data *));
+
+
+/**
+ * Destroy transport manager and release all transports.
+ * @param mgr Transport manager to be destroyed.
+ */
+PJ_DECL(void) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr );
+
+/**
+ * Poll for transport events.
+ * Incoming messages will be parsed by the transport manager, and the callback
+ * will be called for each of this message.
+ * @param endpt The endpoint.
+ * @param timeout Timeout value, or NULL to wait forever.
+ */
+PJ_DECL(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr,
+					        const pj_time_val *timeout );
+
+/**
+ * Get the pointer to the first transport iterator.
+ * @param mgr The transport manager.
+ * @param it  The iterator used for iterating the hash element.
+ * @return the iterator to the first transport, or NULL.
+ */
+PJ_DECL(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr,
+						    pj_hash_iterator_t *it );
+
+
+/**
+ * Get the next transport iterator.
+ * @param itr the iterator to the transport.
+ * @return the iterator pointed to the next transport, or NULL.
+ */
+PJ_DECL(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr,
+						   pj_hash_iterator_t *itr );
+
+/**
+ * Get the value of transport iterator.
+ * @param mgr the transport manager.
+ * @param itr the transport iterator.
+ * @return the transport associated with the iterator.
+ */
+PJ_DECL(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr,
+						  pj_hash_iterator_t *itr );
+
+/** 
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_PRIVATE_I_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_resolve.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_resolve.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_resolve.c	(revision 1753)
@@ -0,0 +1,128 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_resolve.c 3     4/17/05 11:59a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+
+#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_transport.h>
+#include <pj/pool.h>
+#include <ctype.h>
+
+struct pjsip_resolver_t
+{
+    void *dummy;
+};
+
+PJ_DEF(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool)
+{
+    pjsip_resolver_t *resolver;
+    resolver = (pjsip_resolver_t*) pj_pool_calloc(pool, 1, sizeof(*resolver));
+    return resolver;
+}
+
+PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver)
+{
+    PJ_UNUSED_ARG(resolver)
+}
+
+static int is_str_ip(const pj_str_t *host)
+{
+    const char *p = host->ptr;
+    const char *end = ((const char*)host->ptr) + host->slen;
+
+    while (p != end) {
+	if (isdigit(*p) || *p=='.') {
+	    ++p;
+	} else {
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
+			    pj_pool_t *pool,
+			    pjsip_host_port *target,
+			    void *token,
+			    pjsip_resolver_callback *cb)
+{
+    struct pjsip_server_addresses svr_addr;
+    pj_status_t status;
+    int is_ip_addr;
+    pjsip_transport_type_e type = target->type;
+
+    PJ_UNUSED_ARG(resolver)
+    PJ_UNUSED_ARG(pool)
+
+    /* We only do synchronous resolving at this moment. */
+    PJ_TODO(SUPPORT_RFC3263_SERVER_RESOLUTION)
+
+    /* Is it IP address or hostname?. */
+    is_ip_addr = is_str_ip(&target->host);
+
+    /* Set the transport type if not explicitly specified. 
+     * RFC 3263 section 4.1 specify rules to set up this.
+     */
+    if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
+	if (is_ip_addr || (target->port != 0)) {
+#if PJ_HAS_TCP
+	    if (target->flag & PJSIP_TRANSPORT_SECURE) 
+	    {
+		type = PJSIP_TRANSPORT_TLS;
+	    } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) 
+	    {
+		type = PJSIP_TRANSPORT_TCP;
+	    } else 
+#endif
+	    {
+		type = PJSIP_TRANSPORT_UDP;
+	    }
+	} else {
+	    /* No type or explicit port is specified, and the address is
+	     * not IP address.
+	     * In this case, full resolution must be performed.
+	     * But we don't support it (yet).
+	     */
+	    type = PJSIP_TRANSPORT_UDP;
+	}
+
+    }
+
+    /* Set the port number if not specified. */
+    if (target->port == 0) {
+	target->port = pjsip_transport_get_default_port_for_type(type);
+    }
+
+    /* Resolve hostname. */
+    if (!is_ip_addr) {
+	status = pj_sockaddr_init(&svr_addr.entry[0].addr, &target->host, target->port);
+    } else {
+	status = pj_sockaddr_init(&svr_addr.entry[0].addr, &target->host, target->port);
+	pj_assert(status == PJ_OK);
+    }
+
+    /* Call the callback. */
+    svr_addr.count = (status == PJ_OK) ? 1 : 0;
+    svr_addr.entry[0].type = type;
+    (*cb)(status, token, &svr_addr);
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_resolve.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_resolve.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_resolve.h	(revision 1753)
@@ -0,0 +1,125 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_resolve.h 5     6/17/05 11:16p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_RESOLVE_H__
+#define __PJSIP_SIP_RESOLVE_H__
+
+/**
+ * @file sip_resolve.h
+ * @brief 
+ * This module contains the mechanism to resolve server address as specified by
+ * RFC 3263 - Locating SIP Servers
+ */
+
+#include <pjsip/sip_types.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_RESOLVE SIP Server Resolver
+ * @ingroup PJSIP
+ * @{
+ */
+
+/** 
+ * Maximum number of addresses returned by the resolver. 
+ */
+#define PJSIP_MAX_RESOLVED_ADDRESSES	8
+
+typedef struct pjsip_server_addresses pjsip_server_addresses;
+
+/**
+ * The server addresses returned by the resolver.
+ */
+struct pjsip_server_addresses
+{
+    /** Number of address records. */
+    unsigned	count;
+
+    /** Address records. */
+    struct
+    {
+	/** Preferable transport to be used to contact this address. */
+	pjsip_transport_type_e	type;
+
+	/** The server's address. */
+	pj_sockaddr_in		addr;
+
+    } entry[PJSIP_MAX_RESOLVED_ADDRESSES];
+
+};
+
+/**
+ * The type of callback function to be called when resolver finishes the job.
+ *
+ * @param status    The status of the operation, which is zero on success.
+ * @param token	    The token that was associated with the job when application
+ *		    call the resolve function.
+ * @param addr	    The addresses resolved by the operation.
+ */
+typedef void pjsip_resolver_callback(pj_status_t status,
+				     void *token,
+				     const struct pjsip_server_addresses *addr);
+
+/**
+ * Create resolver engine.
+ *
+ * @param pool	The Pool.
+ * @return	The resolver engine.
+ */
+PJ_DECL(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool);
+
+/**
+ * Destroy resolver engine.
+ *
+ * @param resolver The resolver.
+ */
+PJ_DECL(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver);
+
+/**
+ * Asynchronously resolve a SIP target host or domain according to rule 
+ * specified in RFC 3263 (Locating SIP Servers). When the resolving operation
+ * has completed, the callback will be called.
+ *
+ * Note: at the moment we don't have implementation of RFC 3263 yet!
+ *
+ * @param resolver	The resolver engine.
+ * @param pool		The pool to allocate resolver job.
+ * @param target	The target specification to be resolved.
+ * @param token		A user defined token to be passed back to callback function.
+ * @param cb		The callback function.
+ */
+PJ_DECL(void) pjsip_resolve( pjsip_resolver_t *resolver,
+			     pj_pool_t *pool,
+			     pjsip_host_port *target,
+			     void *token,
+			     pjsip_resolver_callback *cb);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_SIP_RESOLVE_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transaction.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transaction.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transaction.c	(revision 1753)
@@ -0,0 +1,1951 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_transaction.c 21    12/02/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_config.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_endpoint.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/guid.h>
+#include <pj/pool.h>
+
+/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
+int pjsip_tsx_lock_tls_id;
+
+/* State names */
+static const char *state_str[] = 
+{
+    "Null",
+    "Calling",
+    "Trying",
+    "Proceeding",
+    "Completed",
+    "Confirmed",
+    "Terminated",
+    "Destroyed",
+};
+
+/* Role names */
+static const char *role_name[] = 
+{
+    "Client",
+    "Server"
+};
+
+/* Transaction lock. */
+typedef struct tsx_lock_data {
+    struct tsx_lock_data *prev;
+    pjsip_transaction    *tsx;
+    int			  is_alive;
+} tsx_lock_data;
+
+/* Timer timeout value constants */
+static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000, PJSIP_T1_TIMEOUT%1000 };
+static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000, PJSIP_T4_TIMEOUT%1000 };
+static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000, PJSIP_TD_TIMEOUT%1000 };
+static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000,
+					       (64*PJSIP_T1_TIMEOUT)%1000 };
+
+/* Internal timer IDs */
+enum Transaction_Timer_Id
+{
+    TSX_TIMER_RETRANSMISSION,
+    TSX_TIMER_TIMEOUT,
+};
+
+/* Function Prototypes */
+static int  pjsip_tsx_on_state_null( pjsip_transaction *tsx, 
+				     pjsip_event *event);
+static int  pjsip_tsx_on_state_calling( pjsip_transaction *tsx, 
+				        pjsip_event *event);
+static int  pjsip_tsx_on_state_trying( pjsip_transaction *tsx, 
+				       pjsip_event *event);
+static int  pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, 
+					       pjsip_event *event);
+static int  pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx, 
+					       pjsip_event *event);
+static int  pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, 
+					      pjsip_event *event);
+static int  pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, 
+					      pjsip_event *event);
+static int  pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx, 
+					  pjsip_event *event);
+static int  pjsip_tsx_on_state_terminated( pjsip_transaction *tsx, 
+					   pjsip_event *event);
+static int  pjsip_tsx_on_state_destroyed( pjsip_transaction *tsx, 
+					  pjsip_event *event);
+static void tsx_timer_callback( pj_timer_heap_t *theap, 
+			        pj_timer_entry *entry);
+static int  tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata);
+static void lock_tsx( pjsip_transaction *tsx, struct tsx_lock_data *lck );
+static pj_status_t unlock_tsx( pjsip_transaction *tsx, struct tsx_lock_data *lck );
+
+/* State handlers for UAC, indexed by state */
+static int  (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *tsx,
+							  pjsip_event *event ) = 
+{
+    &pjsip_tsx_on_state_null,
+    &pjsip_tsx_on_state_calling,
+    &pjsip_tsx_on_state_trying,
+    &pjsip_tsx_on_state_proceeding_uac,
+    &pjsip_tsx_on_state_completed_uac,
+    &pjsip_tsx_on_state_confirmed,
+    &pjsip_tsx_on_state_terminated,
+    &pjsip_tsx_on_state_destroyed,
+};
+
+/* State handlers for UAS */
+static int  (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *tsx, 
+							  pjsip_event *event ) = 
+{
+    &pjsip_tsx_on_state_null,
+    &pjsip_tsx_on_state_calling,
+    &pjsip_tsx_on_state_trying,
+    &pjsip_tsx_on_state_proceeding_uas,
+    &pjsip_tsx_on_state_completed_uas,
+    &pjsip_tsx_on_state_confirmed,
+    &pjsip_tsx_on_state_terminated,
+    &pjsip_tsx_on_state_destroyed,
+};
+
+/*
+ * Get transaction state name.
+ */
+PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state)
+{
+    return state_str[state];
+}
+
+/*
+ * Get the role name.
+ */
+PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role)
+{
+    return role_name[role];
+}
+
+
+/*
+ * Create transaction key for RFC2543 compliant messages, which don't have
+ * unique branch parameter in the top most Via header.
+ *
+ * INVITE requests matches a transaction if the following attributes
+ * match the original request:
+ *	- Request-URI
+ *	- To tag
+ *	- From tag
+ *	- Call-ID
+ *	- CSeq
+ *	- top Via header
+ *
+ * CANCEL matching is done similarly as INVITE, except:
+ *	- CSeq method will differ
+ *	- To tag is not matched.
+ *
+ * ACK matching is done similarly, except that:
+ *	- method of the CSeq will differ,
+ *	- To tag is matched to the response sent by the server transaction.
+ *
+ * The transaction key is constructed from the common components of above
+ * components. Additional comparison is needed to fully match a transaction.
+ */
+void create_tsx_key_2543( pj_pool_t *pool,
+			  pj_str_t *str,
+			  pjsip_role_e role,
+			  const pjsip_method *method,
+			  const pjsip_rx_data *rdata )
+{
+#define SEPARATOR   '$'
+    char *key, *p, *end;
+    int len;
+    pj_size_t len_required;
+    pjsip_uri *req_uri;
+    pj_str_t *host;
+
+    host = &rdata->via->sent_by.host;
+    req_uri = (pjsip_uri*)rdata->msg->line.req.uri;
+
+    /* Calculate length required. */
+    len_required = PJSIP_MAX_URL_SIZE +	    /* URI */
+		   9 +			    /* CSeq number */
+		   rdata->from_tag.slen +   /* From tag. */
+		   rdata->call_id.slen +    /* Call-ID */
+		   host->slen +		    /* Via host. */
+		   9 +			    /* Via port. */
+		   32;			    /* Separator+Allowance. */
+    key = p = pj_pool_alloc(pool, len_required);
+    end = p + len_required;
+
+    /* Add role. */
+    *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
+    *p++ = SEPARATOR;
+
+    /* Add Request-URI */
+    /* This is BUG!
+     * Response doesn't have Request-URI!
+     *
+    len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p );
+    p += len;
+    *p++ = SEPARATOR;
+     */
+
+    /* Add method, except when method is INVITE or ACK. */
+    if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
+	pj_memcpy(p, method->name.ptr, method->name.slen);
+	p += method->name.slen;
+	*p++ = '$';
+    }
+
+    /* Add CSeq (only the number). */
+    len = pj_utoa(rdata->cseq->cseq, p);
+    p += len;
+    *p++ = SEPARATOR;
+
+    /* Add From tag. */
+    len = rdata->from->tag.slen;
+    pj_memcpy( p, rdata->from->tag.ptr, len);
+    p += len;
+    *p++ = SEPARATOR;
+
+    /* Add Call-ID. */
+    len = rdata->call_id.slen;
+    pj_memcpy( p, rdata->call_id.ptr, len );
+    p += len;
+    *p++ = SEPARATOR;
+
+    /* Add top Via header. 
+     * We don't really care whether the port contains the real port (because
+     * it can be omited if default port is used). Anyway this function is 
+     * only used to match request retransmission, and we expect that the 
+     * request retransmissions will contain the same port.
+     */
+    if ((end-p) < host->slen + 12) {
+	goto on_error;
+    }
+    pj_memcpy(p, host->ptr, host->slen);
+    p += host->slen;
+    *p++ = ':';
+
+    len = pj_utoa(rdata->via->sent_by.port, p);
+    p += len;
+    *p++ = SEPARATOR;
+    
+    *p++ = '\0';
+
+    /* Done. */
+    str->ptr = key;
+    str->slen = p-key;
+
+    return;
+
+on_error:
+    PJ_LOG(2,("tsx........", "Not enough buffer (%d) for transaction key",
+	      len_required));
+    pj_assert(0);
+    str->ptr = NULL;
+    str->slen = 0;
+}
+
+/*
+ * Create transaction key for RFC3161 compliant system.
+ */
+void create_tsx_key_3261( pj_pool_t *pool,
+			  pj_str_t *key,
+			  pjsip_role_e role,
+			  const pjsip_method *method,
+			  const pj_str_t *branch )
+{
+    char *p;
+
+    p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 );
+    
+    /* Add role. */
+    *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
+    *p++ = SEPARATOR;
+
+    /* Add method, except when method is INVITE or ACK. */
+    if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
+	pj_memcpy(p, method->name.ptr, method->name.slen);
+	p += method->name.slen;
+	*p++ = '$';
+    }
+
+    /* Add branch ID. */
+    pj_memcpy(p, branch->ptr, branch->slen);
+    p += branch->slen;
+
+    /* Set length */
+    key->slen = p - key->ptr;
+}
+
+/*
+ * Create key from the incoming data, to be used to search the transaction
+ * in the transaction hash table.
+ */
+PJ_DEF(void) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key, 
+				   pjsip_role_e role, 
+				   const pjsip_method *method, 
+				   const pjsip_rx_data *rdata )
+{
+    pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN};
+
+    /* Get the branch parameter in the top-most Via.
+     * If branch parameter is started with "z9hG4bK", then the message was
+     * generated by agent compliant with RFC3261. Otherwise, it will be
+     * handled as RFC2543.
+     */
+    const pj_str_t *branch = &rdata->via->branch_param;
+
+    if (pj_strncmp(branch, &rfc3261_branch, PJSIP_RFC3261_BRANCH_LEN) == 0) {
+
+	/* Create transaction key. */
+	create_tsx_key_3261(pool, key, role, method, branch);
+
+    } else {
+	/* Create the key for the message. This key will be matched up with the
+	 * transaction key. For RFC2563 transactions, the transaction key
+	 * was created by the same function, so it will match the message.
+	 */
+	create_tsx_key_2543( pool, key, role, method, rdata );
+    }
+}
+
+
+/*
+ * Create new transaction.
+ */
+PJ_DEF(pjsip_transaction *) pjsip_tsx_create(pj_pool_t *pool,
+					     pjsip_endpoint *endpt)
+{
+    pjsip_transaction *tsx;
+
+    tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction));
+
+    tsx->pool = pool;
+    tsx->endpt = endpt;
+    tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION;
+    tsx->retransmit_timer._timer_id = -1;
+    tsx->retransmit_timer.user_data = tsx;
+    tsx->retransmit_timer.cb = &tsx_timer_callback;
+    tsx->timeout_timer.id = TSX_TIMER_TIMEOUT;
+    tsx->timeout_timer._timer_id = -1;
+    tsx->timeout_timer.user_data = tsx;
+    tsx->timeout_timer.cb = &tsx_timer_callback;
+    sprintf(tsx->obj_name, "tsx%p", tsx);
+    tsx->mutex = pj_mutex_create(pool, "mtsx%p", 0);
+    if (!tsx->mutex) {
+	return NULL;
+    }
+
+    return tsx;
+}
+
+/*
+ * Lock transaction and set the value of Thread Local Storage.
+ */
+static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck)
+{
+    struct tsx_lock_data *prev_data;
+
+    pj_mutex_lock(tsx->mutex);
+    prev_data = (struct tsx_lock_data *) pj_thread_local_get(pjsip_tsx_lock_tls_id);
+    lck->prev = prev_data;
+    lck->tsx = tsx;
+    lck->is_alive = 1;
+    pj_thread_local_set(pjsip_tsx_lock_tls_id, lck);
+}
+
+
+/*
+ * Unlock transaction.
+ * This will selectively unlock the mutex ONLY IF the transaction has not been 
+ * destroyed. The function knows whether the transaction has been destroyed
+ * because when transaction is destroyed the is_alive flag for the transaction
+ * will be set to zero.
+ */
+static pj_status_t unlock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck)
+{
+    pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck);
+    pj_assert( lck->tsx == tsx );
+    pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev);
+    if (lck->is_alive)
+	pj_mutex_unlock(tsx->mutex);
+
+    return lck->is_alive ? 0 : -1;
+}
+
+/*
+ * Set transaction state, and inform TU about the transaction state change.
+ */
+static void tsx_set_state( pjsip_transaction *tsx,
+			   pjsip_tsx_state_e state,
+			   const pjsip_event *event )
+{
+    pjsip_event e;
+
+    PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, ev=%s (src:%s)", 
+	       state_str[tsx->state], state_str[state], pjsip_event_str(event->type),
+	       pjsip_event_str(event->src_type)));
+
+    /* Change state. */
+    tsx->state = state;
+
+    /* Update the state handlers. */
+    if (tsx->role == PJSIP_ROLE_UAC) {
+	tsx->state_handler = tsx_state_handler_uac[state];
+    } else {
+	tsx->state_handler = tsx_state_handler_uas[state];
+    }
+
+    /* Inform TU */
+    pj_memcpy(&e, event, sizeof(*event));
+    e.type = PJSIP_EVENT_TSX_STATE_CHANGED;
+    e.obj.tsx = tsx;
+    pjsip_endpt_send_tsx_event( tsx->endpt, &e  );
+
+    /* When the transaction is terminated, release transport, and free the
+     * saved last transmitted message.
+     */
+    if (state == PJSIP_TSX_STATE_TERMINATED) {
+
+	/* Decrement transport reference counter. */
+	if (tsx->transport && tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
+	    pjsip_transport_dec_ref( tsx->transport );
+	    tsx->transport = NULL;
+	}
+	/* Free last transmitted message. */
+	if (tsx->last_tx) {
+	    pjsip_tx_data_dec_ref( tsx->last_tx );
+	    tsx->last_tx = NULL;
+	}
+	/* Cancel timeout timer. */
+	if (tsx->timeout_timer._timer_id != -1) {
+	    pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
+	    tsx->timeout_timer._timer_id = -1;
+	}
+	/* Cancel retransmission timer. */
+	if (tsx->retransmit_timer._timer_id != -1) {
+	    pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
+	    tsx->retransmit_timer._timer_id = -1;
+	}
+
+	/* If transport is not pending, reschedule timeout timer to
+	 * destroy this transaction.
+	 */
+	if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
+	    pj_time_val timeout = {0, 0};
+	    pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, 
+					&timeout);
+	}
+
+    } else if (state == PJSIP_TSX_STATE_DESTROYED) {
+
+	/* Clear TLS, so that mutex will not be unlocked */
+	struct tsx_lock_data *lck = pj_thread_local_get(pjsip_tsx_lock_tls_id);
+	while (lck) {
+	    if (lck->tsx == tsx) {
+		lck->is_alive = 0;
+	    }
+	    lck = lck->prev;
+	}
+    }
+}
+
+/*
+ * Look-up destination address and select which transport to be used to send
+ * the request message. The procedure used here follows the guidelines on 
+ * sending the request in RFC3261 chapter 8.1.2.
+ *
+ * This function also modifies the message (request line and Route headers)
+ * accordingly.
+ */
+static pj_status_t tsx_process_route( pjsip_transaction *tsx,
+				      pjsip_tx_data *tdata,
+				      pjsip_host_port *send_addr )
+{
+    const pjsip_uri *new_request_uri, *target_uri;
+    const pjsip_name_addr *topmost_route_uri;
+    pjsip_route_hdr *first_route_hdr, *last_route_hdr;
+    
+    pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
+
+    /* Get the first "Route" header from the message. If the message doesn't
+     * have any "Route" headers but the endpoint has, then copy the "Route"
+     * headers from the endpoint first.
+     */
+    last_route_hdr = first_route_hdr = 
+	pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
+    if (first_route_hdr) {
+	topmost_route_uri = &first_route_hdr->name_addr;
+	while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
+	    pjsip_route_hdr *hdr;
+	    hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, last_route_hdr->next);
+	    if (!hdr)
+		break;
+	    last_route_hdr = hdr;
+	}
+    } else {
+	const pjsip_route_hdr *hdr_list;
+	hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt);
+	if (hdr_list->next != hdr_list) {
+	    const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next;
+	    first_route_hdr = NULL;
+	    topmost_route_uri = &hdr->name_addr;
+	    do {
+		last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
+		if (first_route_hdr == NULL)
+		    first_route_hdr = last_route_hdr;
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr);
+		hdr = hdr->next;
+	    } while (hdr != hdr_list);
+	} else {
+	    topmost_route_uri = NULL;
+	}
+    }
+
+    /* If Route headers exist, and the first element indicates loose-route,
+     * the URI is taken from the Request-URI, and we keep all existing Route
+     * headers intact.
+     * If Route headers exist, and the first element DOESN'T indicate loose
+     * route, the URI is taken from the first Route header, and remove the
+     * first Route header from the message.
+     * Otherwise if there's no Route headers, the URI is taken from the
+     * Request-URI.
+     */
+    if (topmost_route_uri) {
+	pj_bool_t has_lr_param;
+
+	if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
+	    PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
+	{
+	    const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
+	    has_lr_param = url->lr_param;
+	} else {
+	    has_lr_param = 0;
+	}
+
+	if (has_lr_param) {
+	    new_request_uri = tdata->msg->line.req.uri;
+	    /* We shouldn't need to delete topmost Route if it has lr param.
+	     * But seems like it breaks some proxy implementation, so we
+	     * delete it anyway.
+	     */
+	    /*
+	    pj_list_erase(first_route_hdr);
+	    if (first_route_hdr == last_route_hdr)
+		last_route_hdr = NULL;
+	    */
+	} else {
+	    new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
+	    pj_list_erase(first_route_hdr);
+	    if (first_route_hdr == last_route_hdr)
+		last_route_hdr = NULL;
+	}
+
+	target_uri = (pjsip_uri*)topmost_route_uri;
+
+    } else {
+	target_uri = new_request_uri = tdata->msg->line.req.uri;
+    }
+
+    /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
+     * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
+     */
+    pj_memset(send_addr, 0, sizeof(*send_addr));
+
+    if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
+	pjsip_uri *uri = (pjsip_uri*) target_uri;
+	const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+	send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
+	pj_strdup(tdata->pool, &send_addr->host, &url->host);
+        send_addr->port = url->port;
+	send_addr->type = pjsip_transport_get_type_from_name(&url->transport_param);
+
+    } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
+	pjsip_uri *uri = (pjsip_uri*) target_uri;
+	const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+	pj_strdup(tdata->pool, &send_addr->host, &url->host);
+	send_addr->port = url->port;
+	send_addr->type = pjsip_transport_get_type_from_name(&url->transport_param);
+#if PJ_HAS_TCP
+	if (send_addr->type == PJSIP_TRANSPORT_TCP || 
+	    send_addr->type == PJSIP_TRANSPORT_SCTP) 
+	{
+	    send_addr->flag |= PJSIP_TRANSPORT_RELIABLE;
+	}
+#endif
+    } else {
+	PJ_LOG(2, (tsx->obj_name, "Unable to lookup destination address for "
+				  "non SIP-URL"));   
+	return -1;
+    }
+
+    /* If target URI is different than request URI, replace 
+     * request URI add put the original URI in the last Route header.
+     */
+    if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
+	pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
+	route->name_addr.uri = tdata->msg->line.req.uri;
+	if (last_route_hdr)
+	    pj_list_insert_after(last_route_hdr, route);
+	else
+	    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
+	tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
+    }
+
+    /* Success. */
+    return 0;  
+}
+
+
+/*
+ * Callback from the transport job.
+ * This callback is called when asychronous transport connect() operation
+ * has completed, with or without error.
+ */
+static void tsx_transport_callback(pjsip_transport_t *tr, 
+				   void *token, 
+				   pj_status_t status)
+{
+    char addr[PJ_MAX_HOSTNAME];
+    pjsip_transaction *tsx = token;
+    struct tsx_lock_data lck;
+
+    pj_memcpy(addr, tsx->dest_name.host.ptr, tsx->dest_name.host.slen);
+    addr[tsx->dest_name.host.slen] = '\0';
+
+
+    if (status == PJ_OK) {
+	PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d",
+				  pjsip_transport_get_type_name(tr),
+				  addr, tsx->dest_name.port));
+    } else {
+	PJ_LOG(3, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d", 
+				  pjsip_transport_get_type_name(tr),
+				  addr, tsx->dest_name.port, status));
+    }
+
+    /* Lock transaction. */
+    lock_tsx(tsx, &lck);
+
+    if (status != PJ_OK) {
+	pjsip_event event;
+	
+	event.type = PJSIP_EVENT_TRANSPORT_ERROR;
+	event.src_type = PJSIP_EVENT_TX_MSG;
+	event.src.tdata = tsx->last_tx;
+
+	tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+	tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+	tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event);
+
+	/* Unlock transaction. */
+	unlock_tsx(tsx, &lck);
+	return;
+    }
+
+    /* See if transaction has already been terminated. If so, schedule to destroy
+     * the transaction.
+     */
+    if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+	pj_time_val timeout = {0, 0};
+	pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, 
+				    &timeout);
+
+	/* Unlock transaction. */
+	unlock_tsx(tsx, &lck);
+	return;
+    }
+
+    /* Add reference counter to the transport. */
+    pjsip_transport_add_ref(tr);
+
+    /* Mark transport as ready. */
+    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+    tsx->transport = tr;
+
+    /* If there's a pending message to send, send it now. */
+    if (tsx->has_unsent_msg) {
+	tsx_send_msg( tsx, tsx->last_tx );
+    }
+
+    /* Unlock transaction. */
+    unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Callback from the resolver job.
+ */
+static void tsx_resolver_callback(pj_status_t status,
+				  void *token,
+				  const struct pjsip_server_addresses *addr)
+{
+    pjsip_transaction *tsx = token;
+    struct tsx_lock_data lck;
+
+    PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status));
+
+    if (status != PJ_OK || addr->count == 0) {
+	pjsip_event event;
+	
+	event.type = PJSIP_EVENT_TRANSPORT_ERROR;
+	event.src_type = PJSIP_EVENT_TX_MSG;
+	event.src.tdata = tsx->last_tx;
+
+	lock_tsx(tsx, &lck);
+	tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR;
+	tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event);
+	unlock_tsx(tsx, &lck);
+	return;
+    }
+
+    /* Lock transaction. */
+    lock_tsx(tsx, &lck);
+
+    /* Copy server addresses. */
+    pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr));
+
+    /* Create/find the transport for the remote address. */
+    PJ_LOG(5,(tsx->obj_name, "tsx getting transport for %s:%d",
+			     pj_sockaddr_get_str_addr(&addr->entry[0].addr),
+			     pj_sockaddr_get_port(&addr->entry[0].addr)));
+
+    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING;
+    pjsip_endpt_get_transport(tsx->endpt, tsx->pool,
+			      addr->entry[0].type, &addr->entry[0].addr,
+			      tsx,
+			      &tsx_transport_callback);
+
+    /* Unlock transaction */
+    unlock_tsx(tsx, &lck);
+
+    /* There should be nothing to do after this point.
+     * Execution for the transaction will resume when the callback for the 
+     * transport is called.
+     */
+}
+
+/*
+ * Initialize the transaction as UAC transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, 
+					pjsip_tx_data *tdata)
+{
+    pjsip_msg *msg;
+    pjsip_cseq_hdr *cseq;
+    pjsip_via_hdr *via;
+    struct tsx_lock_data lck;
+    const pjsip_hdr *endpt_hdr;
+
+    PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata));
+
+    /* Lock transaction. */
+    lock_tsx(tsx, &lck);
+
+    /* Keep shortcut */
+    msg = tdata->msg;
+
+    /* Role is UAC. */
+    tsx->role = PJSIP_ROLE_UAC;
+
+    /* Save method. */
+    pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
+
+    /* Generate branch parameter if it doesn't exist. */
+    via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
+    if (via == NULL) {
+	via = pjsip_via_hdr_create(tdata->pool);
+	pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via);
+    }
+
+    if (via->branch_param.slen == 0) {
+	pj_str_t tmp;
+	via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN);
+	via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
+	pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, 
+		  PJSIP_RFC3261_BRANCH_LEN);
+
+	tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN;
+	pj_generate_unique_string( &tmp );
+    }
+
+    /* Copy branch parameter. */
+    tsx->branch = via->branch_param;
+
+    /* Add additional request headers from endpoint. */
+    endpt_hdr = pjsip_endpt_get_request_headers(tsx->endpt)->next;
+    while (endpt_hdr != pjsip_endpt_get_request_headers(tsx->endpt)) {
+	pjsip_hdr *hdr = pjsip_hdr_shallow_clone(tdata->pool, endpt_hdr);
+	pjsip_msg_add_hdr( tdata->msg, hdr );
+	endpt_hdr = endpt_hdr->next;
+    }
+
+    /* Generate transaction key. */
+    create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
+			 PJSIP_ROLE_UAC, &tsx->method, 
+			 &via->branch_param);
+
+    PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
+	       tsx->transaction_key.ptr));
+
+    /* Save CSeq. */
+    cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
+    if (!cseq) {
+	PJ_LOG(4,(tsx->obj_name, "CSeq header not present in outgoing message!"));
+	return -1;
+    }
+    tsx->cseq = cseq->cseq;
+
+
+    /* Begin with State_Null.
+     * Manually set-up the state becase we don't want to call the callback.
+     */
+    tsx->state = PJSIP_TSX_STATE_NULL;
+    tsx->state_handler = pjsip_tsx_on_state_null;
+
+    /* Get destination name from the message. */
+    if (tsx_process_route(tsx, tdata, &tsx->dest_name) != 0) {
+	pjsip_event event;
+	PJ_LOG(3,(tsx->obj_name, "Error: unable to get destination address for request"));
+	
+	event.type = PJSIP_EVENT_TRANSPORT_ERROR;
+	event.src_type = PJSIP_EVENT_TX_MSG;
+	event.src.tdata = tsx->last_tx;
+
+	tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+	tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+	tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event);
+	unlock_tsx(tsx, &lck);
+	return -1;
+    }
+
+    /* Resolve destination.
+     * This will start asynchronous resolver job, and when it finishes, 
+     * the callback will be called.
+     */
+    PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
+			     tsx->dest_name.host.slen, 
+			     tsx->dest_name.host.ptr,
+			     tsx->dest_name.port));
+
+    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+    pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, 
+			 tsx, &tsx_resolver_callback);
+
+    /* There should be nothing to do after this point. 
+     * Execution for the transaction will resume when the resolver callback is
+     * called.
+     */
+
+    /* Unlock transaction and return.
+     * If transaction has been destroyed WITHIN the current thread, the 
+     * unlock_tsx() function will return -1.
+     */
+    return unlock_tsx(tsx, &lck);
+}
+
+
+/*
+ * Initialize the transaction as UAS transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx, 
+					pjsip_rx_data *rdata)
+{
+    pjsip_msg *msg = rdata->msg;
+    pj_str_t *branch;
+    pjsip_cseq_hdr *cseq;
+    struct tsx_lock_data lck;
+
+    PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata));
+
+    /* Lock transaction. */
+    lock_tsx(tsx, &lck);
+
+    /* Keep shortcut to message */
+    msg = rdata->msg;
+
+    /* Role is UAS */
+    tsx->role = PJSIP_ROLE_UAS;
+
+    /* Save method. */
+    pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
+
+    /* Get transaction key either from branch for RFC3261 message, or
+     * create transaction key.
+     */
+    pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key, PJSIP_ROLE_UAS, 
+			 &tsx->method, rdata);
+
+    /* Duplicate branch parameter for transaction. */
+    branch = &rdata->via->branch_param;
+    pj_strdup(tsx->pool, &tsx->branch, branch);
+
+    PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
+	       tsx->transaction_key.ptr));
+
+    /* Save CSeq */
+    cseq = rdata->cseq;
+    tsx->cseq = cseq->cseq;
+
+    /* Begin with state NULL
+     * Manually set-up the state becase we don't want to call the callback.
+     */
+    tsx->state = PJSIP_TSX_STATE_NULL; 
+    tsx->state_handler = &pjsip_tsx_on_state_null;
+
+    /* Get the transport to send the response. 
+     * According to section 18.2.2 of RFC3261, if the transport is reliable
+     * then the response must be sent using that transport.
+     */
+    /* In addition, RFC 3581 says, if Via has "rport" parameter specified,
+     * then return the response using the same transport.
+     */
+    if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->transport) || 
+	rdata->via->rport_param >= 0) 
+    {
+	tsx->transport = rdata->transport;
+	pjsip_transport_add_ref(tsx->transport);
+	tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+
+	tsx->current_addr = 0;
+	tsx->remote_addr.count = 1;
+	tsx->remote_addr.entry[0].type = pjsip_transport_get_type(tsx->transport);
+	pj_memcpy(&tsx->remote_addr.entry[0].addr, 
+		  &rdata->addr, rdata->addr_len);
+	
+    } else {
+	pj_status_t status;
+
+	status = pjsip_get_response_addr(tsx->pool, rdata->transport,
+					 rdata->via, &tsx->dest_name);
+	if (status != PJ_OK) {
+	    pjsip_event event;
+	    PJ_LOG(2,(tsx->obj_name, "Unable to get destination address "
+				     "for response"));
+	    
+	    event.type = PJSIP_EVENT_TRANSPORT_ERROR;
+	    event.src_type = PJSIP_EVENT_TX_MSG;
+	    event.src.tdata = tsx->last_tx;
+
+	    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+	    tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+	    tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event);
+	    unlock_tsx(tsx, &lck);
+	    return status;
+	}
+
+	/* Resolve destination.
+	 * This will start asynchronous resolver job, and when it finishes, 
+	 * the callback will be called.
+	 */
+	PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
+				 tsx->dest_name.host.slen, tsx->dest_name.host.ptr,
+				 tsx->dest_name.port));
+
+	tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+	pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, 
+			     tsx, &tsx_resolver_callback);
+    }
+    
+    /* There should be nothing to do after this point. 
+     * Execution for the transaction will resume when the resolver callback is
+     * called.
+     */
+
+    /* Unlock transaction and return.
+     * If transaction has been destroyed WITHIN the current thread, the 
+     * unlock_tsx() function will return -1.
+     */
+    return unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Callback when timer expires.
+ */
+static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
+{
+    pjsip_event event;
+    pjsip_transaction *tsx = entry->user_data;
+    struct tsx_lock_data lck;
+
+    PJ_UNUSED_ARG(theap);
+
+    PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)", 
+	     (entry->id == TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout")));
+
+    event.type = event.src_type = PJSIP_EVENT_TIMER;
+    event.src.timer = (entry->id == TSX_TIMER_RETRANSMISSION ? 
+	&tsx->retransmit_timer : &tsx->timeout_timer);
+
+    /* Dispatch event to transaction. */
+    lock_tsx(tsx, &lck);
+    (*tsx->state_handler)(tsx, &event);
+    unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
+ * non-2xx/INVITE is automatically sent by the transaction.
+ * This operation is only valid if the transaction is configured to handle ACK
+ * (tsx->handle_ack is non-zero). If this attribute is not set, then the
+ * transaction will comply with RFC-3261, i.e. it will set itself to 
+ * TERMINATED state when it receives 2xx/INVITE.
+ */
+PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata)
+{
+    pjsip_msg *msg;
+    pjsip_host_port dest_addr;
+    pjsip_event event;
+    pjsip_via_hdr *via;
+    struct tsx_lock_data lck;
+
+    lock_tsx(tsx, &lck);
+
+    pj_assert(tsx->handle_ack != 0);
+    
+    msg = tdata->msg;
+
+    /* Generate branch parameter if it doesn't exist. */
+    via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
+    if (via == NULL) {
+	via = pjsip_via_hdr_create(tdata->pool);
+	pjsip_msg_add_hdr(msg, (pjsip_hdr*) via);
+    }
+
+    if (via->branch_param.slen == 0) {
+	via->branch_param = tsx->branch;
+    } else {
+	pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 );
+    }
+
+    /* Get destination name from the message. */
+    if (tsx_process_route(tsx, tdata, &dest_addr) != 0){
+	PJ_LOG(2,(tsx->obj_name, "Unable to get destination address for request"));
+	goto on_error;
+    }
+
+    /* Compare message's destination name with transaction's destination name.
+     * If NOT equal, then we'll have to resolve the destination.
+     */
+    if (dest_addr.type == tsx->dest_name.type &&
+	dest_addr.flag == tsx->dest_name.flag &&
+	dest_addr.port == tsx->dest_name.port &&
+	pj_stricmp(&dest_addr.host, &tsx->dest_name.host) == 0)
+    {
+	/* Equal destination. We can use current transport. */
+	pjsip_tsx_on_tx_msg(tsx, tdata);
+	unlock_tsx(tsx, &lck);
+	return;
+
+    }
+
+    /* New destination; we'll have to resolve host and create new transport. */
+    pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr));
+    pj_strdup(tsx->pool, &tsx->dest_name.host, &dest_addr.host);
+
+    PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
+			     tsx->dest_name.host.slen, 
+			     tsx->dest_name.host.ptr,
+			     tsx->dest_name.port));
+
+    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+    pjsip_transport_dec_ref(tsx->transport);
+    tsx->transport = NULL;
+
+    /* Put the message in queue. */
+    pjsip_tsx_on_tx_msg(tsx, tdata);
+
+    /* This is a bug!
+     * We shouldn't change transaction's state before actually sending the
+     * message. Otherwise transaction will terminate before message is sent,
+     * and timeout timer will be scheduled.
+     */
+    PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK)
+
+    /* 
+     * This will start asynchronous resolver job, and when it finishes, 
+     * the callback will be called.
+     */
+
+    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+    pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, 
+			 tsx, &tsx_resolver_callback);
+
+    unlock_tsx(tsx, &lck);
+
+    /* There should be nothing to do after this point. 
+     * Execution for the transaction will resume when the resolver callback is
+     * called.
+     */
+    return;
+
+on_error:
+    /* Failure condition. 
+     * Send TERMINATED event.
+     */
+    tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+    event.type = PJSIP_EVENT_TRANSPORT_ERROR;
+    event.src_type = PJSIP_EVENT_TX_MSG;
+    event.src.tdata = tdata;
+    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, &event);
+
+    unlock_tsx(tsx, &lck);
+}
+
+
+/*
+ * This function is called by TU to send a message.
+ */
+PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
+				  pjsip_tx_data *tdata )
+{
+    pjsip_event event;
+    struct tsx_lock_data lck;
+
+    PJ_LOG(5,(tsx->obj_name, "on transmit msg (tdata=%p)", tdata));
+
+    event.type = event.src_type = PJSIP_EVENT_TX_MSG;
+    event.src.tdata = tdata;
+
+    PJ_LOG(5,(tsx->obj_name, "on state %s (ev=%s, src=%s, data=%p)", 
+	      state_str[tsx->state], pjsip_event_str(event.type), 
+	      pjsip_event_str(event.src_type), event.src.data));
+
+    /* Dispatch to transaction. */
+    lock_tsx(tsx, &lck);
+    (*tsx->state_handler)(tsx, &event);
+    unlock_tsx(tsx, &lck);
+}
+
+/*
+ * This function is called by endpoint when incoming message for the 
+ * transaction is received.
+ */
+PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
+				  pjsip_rx_data *rdata)
+{
+    pjsip_event event;
+    struct tsx_lock_data lck;
+
+    event.type = event.src_type = PJSIP_EVENT_RX_MSG;
+    event.src.rdata = rdata;
+
+    PJ_LOG(5,(tsx->obj_name, "on state %s (ev=%s, src=%s, data=%p)", 
+	      state_str[tsx->state], pjsip_event_str(event.type), 
+	      pjsip_event_str(event.src_type), event.src.data));
+
+    /* Dispatch to transaction. */
+    lock_tsx(tsx, &lck);
+    (*tsx->state_handler)(tsx, &event);
+    unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Forcely terminate transaction.
+ */
+PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
+{
+    pjsip_event event;
+    struct tsx_lock_data lck;
+
+    lock_tsx(tsx, &lck);
+
+    tsx->status_code = code;
+    event.type = PJSIP_EVENT_USER;
+    event.src_type = PJSIP_EVENT_USER;
+    event.src.data = 0;
+    event.obj.tsx = tsx;
+    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, &event);
+
+    unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Send message to the transport.
+ * If transport is not yet available, then do nothing. The message will be
+ * transmitted when transport connection completion callback is called.
+ */
+static int tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata)
+{
+    pjsip_event event;
+
+    PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata));
+
+    if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
+	int sent;
+	pjsip_event before_tx_event;
+
+	pj_assert(tsx->transport != NULL);
+
+	/* Make sure Via transport info is filled up properly for
+	 * requests. 
+	 */
+	if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+	    const pj_sockaddr_in *addr_name;
+	    pjsip_via_hdr *via = (pjsip_via_hdr*) 
+		pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+
+	    /* For request message, set "rport" parameter by default. */
+	    if (tdata->msg->type == PJSIP_REQUEST_MSG)
+		via->rport_param = 0;
+
+	    /* Don't update Via sent-by on retransmission. */
+	    if (via->sent_by.host.slen == 0) {
+		addr_name = pjsip_transport_get_addr_name(tsx->transport);
+		pj_strdup2(tdata->pool, &via->transport, 
+			pjsip_transport_get_type_name(tsx->transport));
+		pj_strdup2(tdata->pool, &via->sent_by.host, 
+			pj_sockaddr_get_str_addr(addr_name));
+		via->sent_by.port = pj_sockaddr_get_port(addr_name);
+	    }
+	}
+
+	/* Notify everybody we're about to send message. */
+	before_tx_event.type = PJSIP_EVENT_BEFORE_TX;
+	before_tx_event.src_type = PJSIP_EVENT_TX_MSG;
+	before_tx_event.obj.tsx = tsx;
+	before_tx_event.src.tdata = tdata;
+	before_tx_event.data.long_data = tsx->retransmit_count;
+	pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event  );
+
+	tsx->has_unsent_msg = 0;
+	sent = pjsip_transport_send_msg(
+		tsx->transport, tdata,
+		&tsx->remote_addr.entry[tsx->current_addr].addr
+		);
+	if (sent < 1) {
+	    goto on_error;
+	}
+    } else {
+	tsx->has_unsent_msg = 1;
+    }
+
+    return 0;
+
+on_error:
+    tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+    event.type = PJSIP_EVENT_TRANSPORT_ERROR;
+    event.src_type = PJSIP_EVENT_TX_MSG;
+    event.src.tdata = tdata;
+    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, &event);
+    return -1;
+}
+
+/*
+ * Retransmit last message sent.
+ */
+static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx,
+					 int should_restart_timer)
+{
+    PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)", 
+	      tsx->last_tx, tsx->retransmit_count, should_restart_timer));
+
+    pj_assert(tsx->last_tx != NULL);
+
+    ++tsx->retransmit_count;
+
+    if (tsx_send_msg( tsx, tsx->last_tx) != 0) {
+	return -1;
+    }
+    
+    /* Restart timer T1. */
+    if (should_restart_timer) {
+	pj_time_val timeout;
+	int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;
+
+	if (tsx->method.id != PJSIP_INVITE_METHOD && msec_time > PJSIP_T2_TIMEOUT) 
+	    msec_time = PJSIP_T2_TIMEOUT;
+
+	timeout.sec = msec_time / 1000;
+	timeout.msec = msec_time % 1000;
+	pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, &timeout);
+    }
+
+    return PJ_OK;
+}
+
+/*
+ * Handler for events in state Null.
+ */
+static int  pjsip_tsx_on_state_null( pjsip_transaction *tsx, pjsip_event *event )
+{
+    pj_assert( tsx->state == PJSIP_TSX_STATE_NULL);
+    pj_assert( tsx->last_tx == NULL );
+    pj_assert( tsx->has_unsent_msg == 0);
+
+    if (tsx->role == PJSIP_ROLE_UAS) {
+
+	/* Set state to Trying. */
+	pj_assert(event->type == PJSIP_EVENT_RX_MSG);
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, event);
+
+    } else {
+	pjsip_tx_data *tdata = event->src.tdata;
+
+	/* Save the message for retransmission. */
+	tsx->last_tx = tdata;
+	pjsip_tx_data_add_ref(tdata);
+
+	/* Send the message. */
+	if (tsx_send_msg( tsx, tdata) != 0) {
+	    return -1;
+	}
+
+	/* Start Timer B (or called timer F for non-INVITE) for transaction 
+	 * timeout.
+	 */
+	pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout_timer_val);
+
+	/* Start Timer A (or timer E) for retransmission only if unreliable 
+	 * transport is being used.
+	 */
+	if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL &&
+	    PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) 
+	{
+	    pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer, &t1_timer_val);
+	    tsx->retransmit_count = 0;
+	}
+
+	/* Move state. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, event);
+    }
+
+    return PJ_OK;
+}
+
+/*
+ * State Calling is for UAC after it sends request but before any responses
+ * is received.
+ */
+static int  pjsip_tsx_on_state_calling( pjsip_transaction *tsx, 
+				        pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING);
+    pj_assert(tsx->role == PJSIP_ROLE_UAC);
+
+    if (event->type == PJSIP_EVENT_TIMER && 
+	event->src.timer == &tsx->retransmit_timer) 
+    {
+
+	/* Retransmit the request. */
+	if (pjsip_tsx_retransmit( tsx, 1 ) != 0) {
+	    return -1;
+	}
+
+    } else if (event->type == PJSIP_EVENT_TIMER && 
+	      event->src.timer == &tsx->timeout_timer) 
+    {
+
+	/* Cancel retransmission timer. */
+	if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+	    pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
+	}
+
+	/* Set status code */
+	tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+
+	/* Inform TU. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event );
+
+	/* Transaction is destroyed */
+	return -1;
+
+    } else if (event->type == PJSIP_EVENT_RX_MSG) {
+
+	/* Cancel retransmission timer A. */
+	if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
+	    pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
+
+	/* Cancel timer B (transaction timeout) */
+	pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
+
+	/* Discard retransmission message if it is not INVITE.
+	 * The INVITE tdata is needed in case we have to generate ACK for
+	 * the final response.
+	 */
+	/* Keep last_tx for authorization. */
+	/* SOME PROXIES THEY ALWAYS SEND 100 RESPONSE, CAUSING
+	 * tdata TO BE DESTROYED!!
+	code = event->src.rdata->msg->line.status.code;
+	if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
+	    pjsip_tx_data_dec_ref(tsx->last_tx);
+	    tsx->last_tx = NULL;
+	}
+	*/
+
+	/* Processing is similar to state Proceeding. */
+	pjsip_tsx_on_state_proceeding_uac( tsx, event);
+
+    } else {
+	pj_assert(0);
+    }
+
+    return PJ_OK;
+}
+
+/*
+ * State Trying is for UAS after it received request but before any responses
+ * is sent.
+ * Note: this is different than RFC3261, which can use Trying state for
+ *	 non-INVITE client transaction (bug in RFC?).
+ */
+static int  pjsip_tsx_on_state_trying( pjsip_transaction *tsx, pjsip_event *event)
+{
+    int result;
+
+    pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING);
+
+    /* This state is only for UAS */
+    pj_assert(tsx->role == PJSIP_ROLE_UAS);
+
+    /* Better be transmission of response message.
+     * If we've got request retransmission, this means that the TU hasn't
+     * transmitted any responses within 500 ms, which is not allowed. If
+     * this happens, just ignore the event (we couldn't retransmit last
+     * response because we haven't sent any!).
+     */
+    //pj_assert(event->type == PJSIP_EVENT_TX_MSG);
+    if (event->type != PJSIP_EVENT_TX_MSG) {
+	return PJ_OK;
+    }
+
+    /* The rest of the processing of the event is exactly the same as in
+     * "Proceeding" state.
+     */
+    result = pjsip_tsx_on_state_proceeding_uas( tsx, event);
+
+    /* Inform the TU of the state transision if state is still State_Trying */
+    if (result==0 && tsx->state == PJSIP_TSX_STATE_TRYING) {
+	tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, event);
+    }
+
+    return result;
+}
+
+/*
+ * Handler for events in Proceeding for UAS
+ * This state happens after the TU sends provisional response.
+ */
+static int pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || 
+	      tsx->state == PJSIP_TSX_STATE_TRYING);
+
+    /* This state is only for UAS. */
+    pj_assert(tsx->role == PJSIP_ROLE_UAS);
+
+    /* Receive request retransmission. */
+    if (event->type == PJSIP_EVENT_RX_MSG) {
+
+	/* Send last response. */
+	if (pjsip_tsx_retransmit( tsx, 0 ) != 0) {
+	    return -1;
+	}
+	
+    } else if (event->type == PJSIP_EVENT_TX_MSG ) {
+	pjsip_tx_data *tdata = event->src.tdata;
+
+	/* The TU sends response message to the request. Save this message so
+	 * that we can retransmit the last response in case we receive request
+	 * retransmission.
+	 */
+	pjsip_msg *msg = tdata->msg;
+
+	/* This can only be a response message. */
+	pj_assert(msg->type == PJSIP_RESPONSE_MSG);
+
+	/* Status code must be higher than last sent. */
+	pj_assert(msg->line.status.code >= tsx->status_code);
+
+	/* Update last status */
+	tsx->status_code = msg->line.status.code;
+
+	/* Discard the saved last response (it will be updated later as
+	 * necessary).
+	 */
+	if (tsx->last_tx && tsx->last_tx != tdata) {
+	    pjsip_tx_data_dec_ref( tsx->last_tx );
+	    tsx->last_tx = NULL;
+	}
+
+	/* Send the message. */
+	if (tsx_send_msg(tsx, tdata) != 0) {
+	    return -1;
+	}
+
+	// Update To tag header for RFC2543 transaction.
+	// TODO:
+
+	/* Update transaction state */
+	if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
+
+	    if (tsx->last_tx != tdata) {
+		tsx->last_tx = tdata;
+		pjsip_tx_data_add_ref( tdata );
+	    }
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, event);
+
+	} else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+
+	    if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) {
+
+		/* 2xx class message is not saved, because retransmission is handled
+		 * by the TU.
+		 */
+		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event);
+
+		/* Transaction is destroyed. */
+		return -1;
+
+	    } else {
+		pj_time_val timeout;
+
+		if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		    tsx->retransmit_count = 0;
+		    pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, 
+						&t1_timer_val);
+		}
+
+		/* Save last response sent for retransmission when request 
+		 * retransmission is received.
+		 */
+		if (tsx->last_tx != tdata) {
+		    tsx->last_tx = tdata;
+		    pjsip_tx_data_add_ref(tdata);
+		}
+
+		/* Setup timeout timer: */
+		
+		if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		    
+		    /* Start Timer H at 64*T1 for INVITE server transaction,
+		     * regardless of transport.
+		     */
+		    timeout = timeout_timer_val;
+		    
+		} else if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+		    
+		    /* For non-INVITE, start timer J at 64*T1 for unreliable
+		     * transport.
+		     */
+		    timeout = timeout_timer_val;
+		    
+		} else {
+		    
+		    /* Transaction terminates immediately for non-INVITE when
+		     * reliable transport is used.
+		     */
+		    timeout.sec = timeout.msec = 0;
+		}
+
+		pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, 
+                                            &timeout);
+
+		pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout);
+
+		/* Set state to "Completed" */
+		tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event);
+	    }
+
+	} else if (tsx->status_code >= 300) {
+
+	    /* 3xx-6xx class message causes transaction to move to "Completed" state. */
+	    if (tsx->last_tx != tdata) {
+		tsx->last_tx = tdata;
+		pjsip_tx_data_add_ref( tdata );
+	    }
+
+	    /* Start timer H for transaction termination */
+	    pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,&timeout_timer_val);
+
+	    /* For INVITE, if unreliable transport is used, retransmission 
+	     * timer G will be scheduled (retransmission).
+	     */
+	    if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+		pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL);
+		if (cseq->method.id == PJSIP_INVITE_METHOD) {
+		    tsx->retransmit_count = 0;
+		    pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer, 
+					       &t1_timer_val);
+		}
+	    }
+
+	    /* Inform TU */
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event);
+
+	} else {
+	    pj_assert(0);
+	}
+
+
+    } else if (event->type == PJSIP_EVENT_TIMER && 
+	       event->src.timer == &tsx->retransmit_timer) {
+	/* Retransmission timer elapsed. */
+
+	/* Must have last response to retransmit. */
+	pj_assert(tsx->last_tx != NULL);
+
+	/* Retransmit the last response. */
+	if (pjsip_tsx_retransmit( tsx, 1 ) != 0) {
+	    return -1;
+	}
+
+    } else if (event->type == PJSIP_EVENT_TIMER && 
+	       event->src.timer == &tsx->timeout_timer) {
+
+	/* Timeout timer. should not happen? */
+	pj_assert(0);
+
+	tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event);
+
+	return -1;
+
+    } else {
+	pj_assert(0);
+    }
+
+    return 0;
+}
+
+/*
+ * Handler for events in Proceeding for UAC
+ * This state happens after provisional response(s) has been received from
+ * UAS.
+ */
+static int pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx, pjsip_event *event)
+{
+
+    pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || 
+	      tsx->state == PJSIP_TSX_STATE_CALLING);
+
+    if (event->type != PJSIP_EVENT_TIMER) {
+	/* Must be incoming response, because we should not retransmit
+	 * request once response has been received.
+	 */
+	pj_assert(event->type == PJSIP_EVENT_RX_MSG);
+	if (event->type != PJSIP_EVENT_RX_MSG) {
+	    return 0;
+	}
+
+	tsx->status_code = event->src.rdata->msg->line.status.code;
+    } else {
+	tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+    }
+
+    if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
+
+	/* Inform the message to TU. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, event);
+
+    } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
+
+	/* Stop timeout timer B/F. */
+	pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+
+	/* For INVITE, the state moves to Terminated state (because ACK is
+	 * handled in TU). For non-INVITE, state moves to Completed.
+	 */
+	if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) {
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event);
+	    return -1;
+
+	} else {
+	    pj_time_val timeout;
+
+	    /* For unreliable transport, start timer D (for INVITE) or 
+	     * timer K for non-INVITE. */
+	    if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
+		if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		    timeout = td_timer_val;
+		} else {
+		    timeout = t4_timer_val;
+		}
+	    } else {
+		timeout.sec = timeout.msec = 0;
+	    }
+	    pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout);
+
+	    /* Move state to Completed, inform TU. */
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event);
+	}
+
+    } else if (tsx->status_code >= 300 && tsx->status_code <= 699) {
+	pj_time_val timeout;
+	pjsip_tx_data *ack_tdata = NULL;
+
+	/* Stop timer B. */
+	pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+
+	/* Generate ACK now (for INVITE) but send it later because
+	 * dialog need to use last_tx.
+	 */
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    const pjsip_hdr *hdr;
+
+	    ack_tdata = pjsip_endpt_create_tdata( tsx->endpt );
+
+	    /* Create msg */
+	    ack_tdata->msg = pjsip_msg_create( ack_tdata->pool, PJSIP_REQUEST_MSG);
+
+	    /* Init request line. */
+	    pjsip_method_set( &ack_tdata->msg->line.req.method, PJSIP_ACK_METHOD );
+	    ack_tdata->msg->line.req.uri = pjsip_uri_clone( ack_tdata->pool, tsx->last_tx->msg->line.req.uri);
+
+	    /* Duplicate headers. */
+	    hdr = tsx->last_tx->msg->hdr.next;
+	    while (hdr != &tsx->last_tx->msg->hdr) {
+		pjsip_hdr *new_hdr;
+		new_hdr = pjsip_hdr_clone(ack_tdata->pool, hdr);
+		pjsip_msg_add_hdr( ack_tdata->msg, (pjsip_hdr*)new_hdr);
+		hdr = hdr->next;
+	    }
+
+	    pjsip_endpt_create_ack( tsx->endpt, ack_tdata, event->src.rdata );
+	}
+
+	/* Inform TU. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event);
+
+	/* Generate and send ACK for INVITE. */
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    if (tsx_send_msg( tsx, ack_tdata) != 0) {
+		return -1;
+	    }
+	}
+
+	/* Start Timer D with TD/T4 timer if unreliable transport is used. */
+	if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
+	    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		timeout = td_timer_val;
+	    } else {
+		timeout = t4_timer_val;
+	    }
+	} else {
+	    timeout.sec = timeout.msec = 0;
+	}
+	pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout);
+
+    } else {
+	// Shouldn't happen because there's no timer for this state.
+	pj_assert(0);
+    }
+
+    return PJ_OK;
+}
+
+/*
+ * Handler for events in Completed state for UAS
+ */
+static int pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
+
+    if (event->type == PJSIP_EVENT_RX_MSG) {
+	pjsip_msg *msg = event->src.rdata->msg;
+	pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL );
+
+	/* On receive request retransmission, retransmit last response. */
+	if (cseq->method.id != PJSIP_ACK_METHOD) {
+	    if (pjsip_tsx_retransmit( tsx, 0 ) != 0) {
+		return -1;
+	    }
+
+	} else {
+	    /* Process incoming ACK request. */
+
+	    /* Cease retransmission. */
+	    pjsip_endpt_cancel_timer( tsx->endpt, &tsx->retransmit_timer );
+
+	    /* Start timer I in T4 interval (transaction termination). */
+	    pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+	    pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &t4_timer_val);
+
+	    /* Move state to "Confirmed" */
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED, event);
+	}	
+
+    } else if (event->type == PJSIP_EVENT_TIMER) {
+
+	if (event->src.timer == &tsx->retransmit_timer) {
+	    /* Retransmit message. */
+	    if (pjsip_tsx_retransmit( tsx, 1 ) != 0) {
+		return -1;
+	    }
+
+	} else {
+	    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+
+		/* For INVITE, this means that ACK was never received.
+		 * Set state to Terminated, and inform TU.
+		 */
+
+		tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+
+		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event);
+
+		return -1;
+
+	    } else {
+		/* Transaction terminated, it can now be deleted. */
+		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event);
+		return -1;
+	    }
+	}
+
+    } else {
+	/* Ignore request to transmit. */
+	pj_assert(event->src.tdata == tsx->last_tx);
+    }
+
+    return PJ_OK;
+}
+
+/*
+ * Handler for events in Completed state for UAC transaction.
+ */
+static int pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
+
+    if (event->type == PJSIP_EVENT_TIMER) {
+	/* Must be the timeout timer. */
+	pj_assert(event->src.timer == &tsx->timeout_timer);
+
+	/* Move to Terminated state. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event);
+
+	/* Transaction has been destroyed. */
+	return -1;
+
+    } else if (event->type == PJSIP_EVENT_RX_MSG) {
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    /* On received of final response retransmission, retransmit the ACK.
+	     * TU doesn't need to be informed.
+	     */
+	    pjsip_msg *msg = event->src.rdata->msg;
+	    pj_assert(msg->type == PJSIP_RESPONSE_MSG);
+	    if (msg->type==PJSIP_RESPONSE_MSG &&
+		msg->line.status.code >= 200)
+	    {
+		if (pjsip_tsx_retransmit( tsx, 0 ) != 0) {
+		    return -1;
+		}
+	    } else {
+		/* Very late retransmission of privisional response. */
+		pj_assert( msg->type == PJSIP_RESPONSE_MSG );
+	    }
+	} else {
+	    /* Just drop the response. */
+	}
+    } else if (tsx->method.id == PJSIP_INVITE_METHOD &&
+	       event->type == PJSIP_EVENT_TX_MSG &&
+	       event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) {
+
+	/* Set last transmitted message. */
+	if (tsx->last_tx != event->src.tdata) {
+	    pjsip_tx_data_dec_ref( tsx->last_tx );
+	    tsx->last_tx = event->src.tdata;
+	    pjsip_tx_data_add_ref( tsx->last_tx );
+	}
+
+	/* No state changed, but notify app. 
+	 * Must notify now, so app has chance to put SDP in outgoing ACK msg.
+	 */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event );
+
+	/* Send msg */
+	tsx_send_msg(tsx, event->src.tdata);
+
+    } else {
+	pj_assert(0);
+    }
+
+    return 0;
+}
+
+/*
+ * Handler for events in state Confirmed.
+ */
+static int pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx, pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED);
+
+    /* This state is only for UAS for INVITE. */
+    pj_assert(tsx->role == PJSIP_ROLE_UAS);
+    pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+    /* Absorb any ACK received. */
+    if (event->type == PJSIP_EVENT_RX_MSG) {
+	/* Must be a request message. */
+	pj_assert(event->src.rdata->msg->type == PJSIP_REQUEST_MSG);
+
+	/* Must be an ACK request or a late INVITE retransmission. */
+	pj_assert(event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD ||
+		  event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD);
+
+    } else if (event->type == PJSIP_EVENT_TIMER) {
+	/* Must be from timeout_timer_. */
+	pj_assert(event->src.timer == &tsx->timeout_timer);
+
+	/* Move to Terminated state. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event);
+
+	/* Transaction has been destroyed. */
+	return -1;
+
+    } else {
+	pj_assert(0);
+    }
+
+    return PJ_OK;
+}
+
+/*
+ * Handler for events in state Terminated.
+ */
+static int pjsip_tsx_on_state_terminated( pjsip_transaction *tsx, pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED);
+
+    PJ_UNUSED_ARG(event)
+
+    /* Destroy this transaction */
+    tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, event);
+
+    return PJ_OK;
+}
+
+
+static int pjsip_tsx_on_state_destroyed( pjsip_transaction *tsx, pjsip_event *event)
+{
+    PJ_UNUSED_ARG(tsx)
+    PJ_UNUSED_ARG(event)
+    return PJ_OK;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transaction.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transaction.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transaction.h	(revision 1753)
@@ -0,0 +1,211 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_transaction.h 5     6/17/05 11:16p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_TRANSACTION_H__
+#define __PJSIP_SIP_TRANSACTION_H__
+
+/**
+ * @file sip_transaction.h
+ * @brief SIP Transaction
+ */
+
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_resolve.h>
+//#include <pjsip/sip_config.h>
+//#include <pjsip/sip_endpoint.h>
+#include <pj/timer.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_TRANSACT SIP Transaction
+ * @ingroup PJSIP
+ * @{
+ */
+
+struct pjsip_transaction;
+
+
+/**
+ * Transaction state.
+ */
+typedef enum pjsip_tsx_state_e
+{
+    PJSIP_TSX_STATE_NULL,
+    PJSIP_TSX_STATE_CALLING,
+    PJSIP_TSX_STATE_TRYING,
+    PJSIP_TSX_STATE_PROCEEDING,
+    PJSIP_TSX_STATE_COMPLETED,
+    PJSIP_TSX_STATE_CONFIRMED,
+    PJSIP_TSX_STATE_TERMINATED,
+    PJSIP_TSX_STATE_DESTROYED,
+    PJSIP_TSX_STATE_MAX,
+} pjsip_tsx_state_e;
+
+
+/**
+ * State of the transport in the transaction.
+ * The transport is progressing independently of the transaction.
+ */
+typedef enum pjsip_tsx_transport_state_e
+{
+    PJSIP_TSX_TRANSPORT_STATE_NULL,
+    PJSIP_TSX_TRANSPORT_STATE_RESOLVING,
+    PJSIP_TSX_TRANSPORT_STATE_CONNECTING,
+    PJSIP_TSX_TRANSPORT_STATE_FINAL,
+} pjsip_tsx_transport_state_e;
+
+
+/**
+ * Transaction state.
+ */
+struct pjsip_transaction
+{
+    pj_pool_t		       *pool;
+    pjsip_endpoint	       *endpt;
+    char			obj_name[PJ_MAX_OBJ_NAME];
+    pjsip_role_e		role;
+    int				status_code;
+    pjsip_tsx_state_e		state;
+    int (*state_handler)(struct pjsip_transaction *, pjsip_event *);
+
+    pj_mutex_t		       *mutex;
+    pjsip_method		method;
+    int				cseq;
+    pj_str_t			transaction_key;
+    pj_str_t			branch;
+
+    pjsip_tsx_transport_state_e	transport_state;
+    pjsip_host_port		dest_name;
+    int				current_addr;
+    pjsip_server_addresses	remote_addr;
+    pjsip_transport_t	       *transport;
+
+    pjsip_tx_data	       *last_tx;
+    int				has_unsent_msg;
+    int				handle_ack;
+    int				retransmit_count;
+
+    pj_timer_entry		retransmit_timer;
+    pj_timer_entry		timeout_timer;
+    void		       *module_data[PJSIP_MAX_MODULE];
+};
+
+
+/** 
+ * Init transaction as UAC.
+ * @param tsx the transaction.
+ * @param tdata the transmit data.
+ * @return PJ_OK if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, 
+					 pjsip_tx_data *tdata);
+
+/**
+ * Init transaction as UAS.
+ * @param tsx the transaction to be initialized.
+ * @param rdata the received incoming request.
+ * @return PJ_OK if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx,
+					 pjsip_rx_data *rdata);
+
+/**
+ * Process incoming message for this transaction.
+ * @param tsx the transaction.
+ * @param rdata the incoming message.
+ */
+PJ_DECL(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
+				   pjsip_rx_data *rdata);
+
+/**
+ * Transmit message with this transaction.
+ * @param tsx the transaction.
+ * @param tdata the outgoing message.
+ */
+PJ_DECL(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
+				   pjsip_tx_data *tdata);
+
+
+/**
+ * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
+ * non-2xx/INVITE is automatically sent by the transaction.
+ * This operation is only valid if the transaction is configured to handle ACK
+ * (tsx->handle_ack is non-zero). If this attribute is not set, then the
+ * transaction will comply with RFC-3261, i.e. it will set itself to 
+ * TERMINATED state when it receives 2xx/INVITE.
+ * @param tsx The transaction.
+ * @param tdata The ACK request.
+ */
+PJ_DECL(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx,
+				   pjsip_tx_data *tdata);
+
+/**
+ * Forcely terminate transaction.
+ * @param tsx the transaction.
+ * @param code the status code to report.
+ */
+PJ_DECL(void) pjsip_tsx_terminate( pjsip_transaction *tsx,
+				   int code );
+
+/**
+ * Create transaction key, which is used to match incoming requests 
+ * or response (retransmissions) against transactions.
+ * @param pool The pool
+ * @param key Output key.
+ * @param role The role of the transaction.
+ * @param method The method to be put as a key. 
+ * @param rdata The received data to calculate.
+ */
+PJ_DECL(void) pjsip_tsx_create_key( pj_pool_t *pool,
+				    pj_str_t *key,
+				    pjsip_role_e role,
+				    const pjsip_method *method,
+				    const pjsip_rx_data *rdata );
+
+/**
+ * @}
+ */
+
+/*
+ * Internal.
+ */
+
+/*
+ * Get the string name for the state.
+ */
+PJ_DECL(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state);
+
+/*
+ * Get the role name.
+ */
+PJ_DECL(const char *) pjsip_role_name(pjsip_role_e role);
+
+
+/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
+extern int pjsip_tsx_lock_tls_id;
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_TRANSACT_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transport.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transport.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transport.c	(revision 1753)
@@ -0,0 +1,1630 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_transport.c 16    6/21/05 12:37a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_private.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/ioqueue.h>
+#include <pj/hash.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+#define MGR_IDLE_CHECK_INTERVAL	30
+#define MGR_HASH_TABLE_SIZE	PJSIP_MAX_DIALOG_COUNT
+#define BACKLOG			5
+#define DEFAULT_SO_SNDBUF	(8 * 1024 * 1024)
+#define DEFAULT_SO_RCVBUF	(8 * 1024 * 1024)
+
+#define LOG_TRANSPORT_MGR	"trmgr"	
+#define THIS_FILE		"sip_transport"
+
+static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr );
+
+
+/**
+ * New TCP socket for accept.
+ */
+typedef struct incoming_socket_rec
+{
+    pj_sock_t		sock;
+    pj_sockaddr_in	remote;
+    pj_sockaddr_in	local;
+    int			addrlen;
+} incoming_socket_rec;
+
+/**
+ * SIP Transport.
+ */
+struct pjsip_transport_t
+{
+    /** Standard list members, for chaining the transport in the 
+     *  listener list. 
+     */
+    PJ_DECL_LIST_MEMBER(struct pjsip_transport_t)
+
+    /** Transport's pool. */
+    pj_pool_t		*pool;
+
+    /** Mutex */
+    pj_mutex_t		*tr_mutex;
+
+    /** Transport name for logging purpose */
+    char		 obj_name[PJ_MAX_OBJ_NAME];
+
+    /** Socket handle */
+    pj_sock_t		 sock;
+
+    /** Transport type. */
+    pjsip_transport_type_e type;
+
+    /** Flags to keep various states (see pjsip_transport_flags_e). */
+    pj_uint32_t		 flag;
+
+    /** I/O Queue key */
+    pj_ioqueue_key_t	*key;
+
+    /** Receive data buffer */
+    pjsip_rx_data	*rdata;
+
+    /** Pointer to transport manager */
+    pjsip_transport_mgr *mgr;
+
+    /** Reference counter, to prevent this transport from being closed while
+     *  it's being used. 
+     */
+    pj_atomic_t		 *ref_cnt;
+
+    /** Local address. */
+    pj_sockaddr_in	 local_addr;
+
+    /** Address name (what to put in Via address field). */
+    pj_sockaddr_in	 addr_name;
+
+    /** Remote address (can be zero for UDP and for listeners). UDP listener
+     *  bound to local loopback interface (127.0.0.1) has remote address set
+     *  to 127.0.0.1 to prevent client from using it to send to remote hosts,
+     *  because remote host then will receive 127.0.0.1 as the packet's 
+     *  source address.
+     */
+    pj_sockaddr_in	 remote_addr;
+
+    /** Struct to save incoming socket information. */
+    incoming_socket_rec	 accept_data;
+
+    /** When this transport should be closed. */
+    pj_time_val		 close_time;
+
+    /** List of callbacks to be called when client attempt to use this 
+     *  transport while it's not connected (i.e. still connecting). 
+     */
+    pj_list		 cb_list;
+};
+
+
+/*
+ * Transport manager.
+ */
+struct pjsip_transport_mgr 
+{
+    pj_hash_table_t *transport_table;
+    pj_mutex_t	    *mutex;
+    pjsip_endpoint  *endpt;
+    pj_ioqueue_t    *ioqueue;
+    pj_time_val	     next_idle_check;
+    void           (*message_callback)(pjsip_endpoint*, pjsip_rx_data *rdata);
+};
+
+/*
+ * Transport role.
+ */
+typedef enum transport_role_e
+{
+    TRANSPORT_ROLE_LISTENER,
+    TRANSPORT_ROLE_TRANSPORT,
+} transport_role_e;
+
+/*
+ * Transport key for indexing in the hash table.
+ * WATCH OUT FOR ALIGNMENT PROBLEM HERE!
+ */
+typedef struct transport_key
+{
+    pj_uint8_t	type;
+    pj_uint8_t	zero;
+    pj_uint16_t	port;
+    pj_uint32_t	addr;
+} transport_key;
+
+/*
+ * Transport callback.
+ */
+struct transport_callback
+{
+    PJ_DECL_LIST_MEMBER(struct transport_callback)
+
+    /** User defined token to be passed to the callback. */
+    void *token;
+
+    /** The callback function. */
+    void (*cb)(pjsip_transport_t *tr, void *token, pj_status_t status);
+
+};
+
+/*
+ * Transport names.
+ */
+const struct
+{
+    pjsip_transport_type_e type;
+    pj_uint16_t		   port;
+    pj_str_t		   name;
+} transport_names[] = 
+{
+    { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}},
+    { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}},
+#if PJ_HAS_TCP
+    { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}},
+    { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}},
+    { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}}
+#endif
+};
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read);
+static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent);
+static void on_ioqueue_accept(pj_ioqueue_key_t *key, int status);
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status);
+
+static pj_ioqueue_callback ioqueue_transport_callback = 
+{
+    &on_ioqueue_read,
+    &on_ioqueue_write,
+    &on_ioqueue_accept,
+    &on_ioqueue_connect
+};
+
+static void init_key_from_transport(transport_key *key, 
+				    const pjsip_transport_t *tr)
+{
+    /* This is to detect alignment problems. */
+    pj_assert(sizeof(transport_key) == 8);
+
+    key->type = (pj_uint8_t)tr->type;
+    key->zero = 0;
+    key->addr = pj_sockaddr_get_addr(&tr->remote_addr);
+    key->port = pj_sockaddr_get_port(&tr->remote_addr);
+    /*
+    if (key->port == 0) {
+	key->port = pj_sockaddr_get_port(&tr->local_addr);
+    }
+    */
+}
+
+#if PJ_HAS_TCP
+static void init_tcp_key(transport_key *key, pjsip_transport_type_e type,
+			 const pj_sockaddr_in *addr)
+{
+    /* This is to detect alignment problems. */
+    pj_assert(sizeof(transport_key) == 8);
+
+    key->type = (pj_uint8_t)type;
+    key->zero = 0;
+    key->addr = pj_sockaddr_get_addr(addr);
+    key->port = pj_sockaddr_get_port(addr);
+}
+#endif
+
+static void init_udp_key(transport_key *key, pjsip_transport_type_e type,
+			  const pj_sockaddr_in *addr)
+{
+    PJ_UNUSED_ARG(addr)
+
+    /* This is to detect alignment problems. */
+    pj_assert(sizeof(transport_key) == 8);
+
+    pj_memset(key, 0, sizeof(*key));
+    key->type = (pj_uint8_t)type;
+
+#if 0	/* Not sure why we need to make 127.0.0.1 a special case */
+    if (addr->sin_addr.s_addr == inet_addr("127.0.0.1")) {
+	/* This looks more complicated than it is because key->addr uses
+	 * the host version of the address (i.e. converted with ntohl()).
+	 */
+	pj_str_t localaddr = pj_str("127.0.0.1");
+	pj_sockaddr_in addr;
+	pj_sockaddr_set_str_addr(&addr, &localaddr);
+	key->addr = pj_sockaddr_get_addr(&addr);
+    }
+#endif
+}
+
+/*
+ * Get type format name (for pool name).
+ */
+static const char *transport_get_name_format( int type )
+{
+    switch (type) {
+    case PJSIP_TRANSPORT_UDP:
+	return " udp%p";
+#if PJ_HAS_TCP
+    case PJSIP_TRANSPORT_TCP:
+	return " tcp%p";
+    case PJSIP_TRANSPORT_TLS:
+	return " tls%p";
+    case PJSIP_TRANSPORT_SCTP:
+	return "sctp%p";
+#endif
+    }
+    pj_assert(0);
+    return 0;
+}
+
+/*
+ * Get the default SIP port number for the specified type.
+ */
+PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
+{
+    return transport_names[type].port;
+}
+
+/*
+ * Get transport name.
+ */
+static const char *get_type_name(int type)
+{
+    return transport_names[type].name.ptr;
+}
+
+/*
+ * Get transport type from name.
+ */
+PJ_DEF(pjsip_transport_type_e) 
+pjsip_transport_get_type_from_name(const pj_str_t *name)
+{
+    unsigned i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+	if (pj_stricmp(name, &transport_names[i].name) == 0) {
+	    return transport_names[i].type;
+	}
+    }
+    return PJSIP_TRANSPORT_UNSPECIFIED;
+}
+
+/*
+ * Create new transmit buffer.
+ */
+pjsip_tx_data* pjsip_tx_data_create( pjsip_transport_mgr *mgr )
+{
+    pj_pool_t *pool;
+    pjsip_tx_data *tdata;
+
+    PJ_LOG(5, ("", "pjsip_tx_data_create"));
+
+    pool = pjsip_endpt_create_pool( mgr->endpt, "ptdt%p",
+				    PJSIP_POOL_LEN_TDATA,
+				    PJSIP_POOL_INC_TDATA );
+    if (!pool) {
+	return NULL;
+    }
+    tdata = pj_pool_calloc(pool, 1, sizeof(pjsip_tx_data));
+    tdata->pool = pool;
+    tdata->mgr = mgr;
+    sprintf(tdata->obj_name,"txd%p", tdata);
+
+    tdata->ref_cnt = pj_atomic_create(tdata->pool, 0);
+    if (!tdata->ref_cnt) {
+	pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
+	return NULL;
+    }
+    
+    return tdata;
+}
+
+/*
+ * Add reference to tx buffer.
+ */
+PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
+{
+    pj_atomic_inc(tdata->ref_cnt);
+}
+
+/*
+ * Decrease transport data reference, destroy it when the reference count
+ * reaches zero.
+ */
+PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
+{
+    pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
+    if (pj_atomic_dec(tdata->ref_cnt) <= 0) {
+	PJ_LOG(6,(tdata->obj_name, "destroying txdata"));
+	pj_atomic_destroy( tdata->ref_cnt );
+	pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
+    }
+}
+
+/*
+ * Invalidate the content of the print buffer to force the message to be
+ * re-printed when sent.
+ */
+PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
+{
+    tdata->buf.cur = tdata->buf.start;
+}
+
+/*
+ * Get the transport type.
+ */
+PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type( const pjsip_transport_t * tr)
+{
+    return tr->type;
+}
+
+/*
+ * Get transport type from transport flag.
+ */
+PJ_DEF(pjsip_transport_type_e) pjsip_get_transport_type_from_flag(unsigned flag)
+{
+#if PJ_HAS_TCP
+    if (flag & PJSIP_TRANSPORT_SECURE) {
+	return PJSIP_TRANSPORT_TLS;
+    } else if (flag & PJSIP_TRANSPORT_RELIABLE) {
+	return PJSIP_TRANSPORT_TCP;
+    } else 
+#else
+    PJ_UNUSED_ARG(flag)
+#endif
+    {
+	return PJSIP_TRANSPORT_UDP;
+    }
+}
+
+/*
+ * Get the transport type name.
+ */
+PJ_DEF(const char *) pjsip_transport_get_type_name( const pjsip_transport_t * tr)
+{
+    return get_type_name(tr->type);
+}
+
+/*
+ * Get the transport's object name.
+ */
+PJ_DEF(const char*) pjsip_transport_get_obj_name( const pjsip_transport_t *tr )
+{
+    return tr->obj_name;
+}
+
+/*
+ * Get the transport's reference counter.
+ */
+PJ_DEF(int) pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr )
+{
+    return pj_atomic_get(tr->ref_cnt);
+}
+
+/*
+ * Get transport local address.
+ */
+PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_local_addr( pjsip_transport_t *tr )
+{
+    return &tr->local_addr;
+}
+
+/*
+ * Get address name.
+ */
+PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_addr_name (pjsip_transport_t *tr)
+{
+    return &tr->addr_name;
+}
+
+/*
+ * Get transport remote address.
+ */
+PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_remote_addr( const pjsip_transport_t *tr )
+{
+    return &tr->remote_addr;
+}
+
+/*
+ * Get transport flag.
+ */
+PJ_DEF(unsigned) pjsip_transport_get_flag( const pjsip_transport_t * tr )
+{
+    return tr->flag;
+}
+
+/*
+ * Add reference to the specified transport.
+ */
+PJ_DEF(void) pjsip_transport_add_ref( pjsip_transport_t * tr )
+{
+    pj_atomic_inc(tr->ref_cnt);
+}
+
+/*
+ * Decrease the reference time of the transport.
+ */
+PJ_DEF(void) pjsip_transport_dec_ref( pjsip_transport_t *tr )
+{
+    pj_assert(tr->ref_cnt > 0);
+    if (pj_atomic_dec(tr->ref_cnt) == 0) {
+	pj_gettimeofday(&tr->close_time);
+	tr->close_time.sec += PJSIP_TRANSPORT_CLOSE_TIMEOUT;
+    }
+}
+
+/*
+ * Open the underlying transport.
+ */
+static pj_sock_t create_socket( pjsip_transport_type_e type,
+				pj_sockaddr_in *local )
+{
+    int sock_family;
+    int sock_type;
+    int sock_proto;
+    int len;
+    pj_sock_t sock;
+
+    /* Set socket parameters */
+    if (type == PJSIP_TRANSPORT_UDP) {
+	sock_family = PJ_AF_INET;
+	sock_type = PJ_SOCK_DGRAM;
+	sock_proto = 0;
+
+#if PJ_HAS_TCP
+    } else if (type == PJSIP_TRANSPORT_TCP) {
+	sock_family = PJ_AF_INET;
+	sock_type = PJ_SOCK_STREAM;
+	sock_proto = 0;
+#endif
+    } else {
+	PJ_LOG(2,("", "create_socket: unsupported transport type %s",
+		  get_type_name(type)));
+	return PJ_INVALID_SOCKET;
+    }
+
+    /* Create socket. */
+    sock = pj_sock_socket( sock_family, sock_type, sock_proto, PJ_SOCK_ASYNC);
+    if (sock == PJ_INVALID_SOCKET) {
+	PJ_PERROR((THIS_FILE, "%s socket()", get_type_name(type)));
+	return PJ_INVALID_SOCKET;
+    }
+
+    /* Bind the socket to the requested address, or if no address is
+     * specified, let the operating system chooses the address.
+     */
+    if (/*local->sin_addr.s_addr != 0 &&*/ local->sin_port != 0) {
+	/* Bind to the requested address. */
+	if (pj_sock_bind(sock, local, sizeof(*local)) != 0) {
+	    PJ_PERROR((THIS_FILE, "bind() to %s %s:%d",
+				  get_type_name(type),
+				  pj_sockaddr_get_str_addr(local),
+				  pj_sockaddr_get_port(local)));
+	    pj_sock_close(sock);
+	    return PJ_INVALID_SOCKET;
+	}
+    } else if (type == PJSIP_TRANSPORT_UDP) {
+	/* Only for UDP sockets: bind to any address so that the operating 
+	 * system allocates the port for us. For TCP, let the OS implicitly
+	 * bind the socket with connect() syscall (if we bind now, then we'll
+	 * get 0.0.0.0 as local address).
+	 */
+	pj_memset(local, 0, sizeof(*local));
+	local->sin_family = PJ_AF_INET;
+	if (pj_sock_bind(sock, local, sizeof(*local)) != 0) {
+	    PJ_PERROR((THIS_FILE, "bind() to %s 0.0.0.0:0", get_type_name(type)));
+	    pj_sock_close(sock);
+	    return PJ_INVALID_SOCKET;
+	}
+
+	/* Get the local address. */
+	len = sizeof(pj_sockaddr_in);
+	if (pj_sock_getsockname(sock, local, &len)) {
+	    PJ_PERROR((THIS_FILE, "getsockname()"));
+	    pj_sock_close(sock);
+	    return -1;
+	}
+    }
+
+    return sock;
+}
+
+/*
+ * Close the transport.
+ */
+static void destroy_socket( pjsip_transport_t * tr)
+{
+    pj_assert( pj_atomic_get(tr->ref_cnt) == 0);
+    pj_sock_close(tr->sock);
+    tr->sock = -1;
+}
+
+/*
+ * Create a new transport object.
+ */
+static pjsip_transport_t* create_transport( pjsip_transport_mgr *mgr,
+					    pjsip_transport_type_e type,
+					    pj_sock_t sock_hnd,
+					    const pj_sockaddr_in *local_addr,
+					    const pj_sockaddr_in *addr_name)
+{
+    pj_pool_t *tr_pool=NULL, *rdata_pool=NULL;
+    pjsip_transport_t *tr = NULL;
+
+    /* Allocate pool for transport from endpoint. */
+    tr_pool = pjsip_endpt_create_pool( mgr->endpt,
+				       transport_get_name_format(type),
+				       PJSIP_POOL_LEN_TRANSPORT,
+				       PJSIP_POOL_INC_TRANSPORT );
+    if (!tr_pool) {
+	goto on_error;
+    }
+
+    /* Allocate pool for rdata from endpoint. */
+    rdata_pool = pjsip_endpt_create_pool( mgr->endpt,
+					     "prdt%p",
+					     PJSIP_POOL_LEN_RDATA,
+					     PJSIP_POOL_INC_RDATA );
+    if (!rdata_pool) {
+	goto on_error;
+    }
+
+    /* Allocate and initialize the transport. */
+    tr = pj_pool_calloc(tr_pool, 1, sizeof(*tr));
+    tr->pool = tr_pool;
+    tr->type = type;
+    tr->mgr = mgr;
+    tr->sock = sock_hnd;
+    pj_memcpy(&tr->local_addr, local_addr, sizeof(pj_sockaddr_in));
+    pj_list_init(&tr->cb_list);
+    sprintf(tr->obj_name, transport_get_name_format(type), tr);
+
+    if (type != PJSIP_TRANSPORT_UDP) {
+	tr->flag |= PJSIP_TRANSPORT_RELIABLE;
+    }
+
+    /* Address name. */
+    if (addr_name == NULL) {
+	addr_name = &tr->local_addr;
+    } 
+    pj_memcpy(&tr->addr_name, addr_name, sizeof(*addr_name));
+
+    /* Create atomic */
+    tr->ref_cnt = pj_atomic_create(tr_pool, 0);
+    if (!tr->ref_cnt) {
+	goto on_error;
+    }
+    
+    /* Init rdata in the transport. */
+    tr->rdata = pj_pool_alloc(rdata_pool, sizeof(*tr->rdata));
+    tr->rdata->pool = rdata_pool;
+    tr->rdata->len = 0;
+    tr->rdata->transport = tr;
+    
+    /* Init transport mutex. */
+    tr->tr_mutex = pj_mutex_create(tr_pool, "mtr%p", 0);
+    if (!tr->tr_mutex) {
+	PJ_PERROR((tr->obj_name, "pj_mutex_create()"));
+	goto on_error;
+    }
+
+    /* Register to I/O Queue */
+    tr->key = pj_ioqueue_register(tr_pool, mgr->ioqueue, 
+				  (pj_oshandle_t)tr->sock, tr,
+				  &ioqueue_transport_callback);
+    if (tr->key == NULL) {
+	PJ_PERROR((tr->obj_name, "pj_ioqueue_register()"));
+	goto on_error;
+    }
+
+    return tr;
+
+on_error:
+    if (tr && tr->tr_mutex) {
+	pj_mutex_destroy(tr->tr_mutex);
+    }
+    if (tr_pool) {
+	pjsip_endpt_destroy_pool(mgr->endpt, tr_pool);
+    }
+    if (rdata_pool) {
+	pjsip_endpt_destroy_pool(mgr->endpt, rdata_pool);
+    }
+    return NULL;
+}
+
+/*
+ * Destroy transport.
+ */
+static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr )
+{
+    transport_key hash_key;
+
+    /* Remove from I/O queue. */
+    pj_ioqueue_unregister( mgr->ioqueue, tr->key );
+
+    /* Remove from hash table */
+    init_key_from_transport(&hash_key, tr);
+    pj_hash_set(NULL, mgr->transport_table, &hash_key, sizeof(hash_key), NULL);
+
+    /* Close transport. */
+    destroy_socket(tr);
+
+    /* Destroy the transport mutex. */
+    pj_mutex_destroy(tr->tr_mutex);
+
+    /* Destroy atomic */
+    pj_atomic_destroy( tr->ref_cnt );
+
+    /* Release the pool associated with the rdata. */
+    pjsip_endpt_destroy_pool(mgr->endpt, tr->rdata->pool );
+
+    /* Release the pool associated with the transport. */
+    pjsip_endpt_destroy_pool(mgr->endpt, tr->pool );
+}
+
+
+static int transport_send_msg( pjsip_transport_t *tr, pjsip_tx_data *tdata,
+			       const pj_sockaddr_in *addr)
+{
+    const char *buf = tdata->buf.start;
+    int sent;
+    int len;
+
+    /* Allocate buffer if necessary. */
+    if (tdata->buf.start == NULL) {
+	tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN);
+	tdata->buf.cur = tdata->buf.start;
+	tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
+    }
+
+    /* Print the message if it's not printed */
+    if (tdata->buf.cur <= tdata->buf.start) {
+	len = pjsip_msg_print(tdata->msg, tdata->buf.start, 
+			      tdata->buf.end - tdata->buf.start);
+	if (len < 1) {
+	    return len;
+	}
+	tdata->buf.cur += len;
+	tdata->buf.cur[len] = '\0';
+    }
+
+    /* BUG BUG BUG */
+    /* MUST CHECK THAT THE SOCKET IS READY TO SEND (IOQueue)! */
+    PJ_TODO(BUG_BUG_BUG___SENDING_DATAGRAM_WHILE_SOCKET_IS_PENDING__)
+
+    /* Send the message. */
+    buf = tdata->buf.start;
+    len = tdata->buf.cur - tdata->buf.start;
+
+    if (tr->type == PJSIP_TRANSPORT_UDP) {
+	PJ_LOG(4,(tr->obj_name, "sendto %s:%d, %d bytes, data:\n"
+		  "----------- begin msg ------------\n"
+		  "%s"
+		  "------------ end msg -------------",
+		  pj_sockaddr_get_str_addr(addr), 
+		  pj_sockaddr_get_port(addr),
+		  len, buf));
+
+	sent = pj_ioqueue_sendto( tr->mgr->ioqueue, tr->key,
+				  buf, len, addr, sizeof(*addr));
+    } 
+#if PJ_HAS_TCP
+    else {
+	PJ_LOG(4,(tr->obj_name, "sending %d bytes, data:\n"
+		  "----------- begin msg ------------\n"
+		  "%s"
+		  "------------ end msg -------------",
+		  len, buf));
+
+	sent = pj_ioqueue_write (tr->mgr->ioqueue, tr->key, buf, len);
+    }
+#else
+    else {
+	pj_assert(0);
+	sent = -1;
+    }
+#endif
+
+    if (sent == len || sent == PJ_IOQUEUE_PENDING) {
+	return len;
+    }
+
+    /* On error, clear the flag. */
+    PJ_PERROR((tr->obj_name, tr->type == PJSIP_TRANSPORT_UDP ? "pj_ioqueue_sendto()" : "pj_ioqueue_write()"));
+    return -1;
+}
+
+/*
+ * Send a SIP message using the specified transport, to the address specified
+ * in the outgoing data.
+ */
+PJ_DEF(int) pjsip_transport_send_msg( pjsip_transport_t *tr, 
+				      pjsip_tx_data *tdata,
+				      const pj_sockaddr_in *addr)
+{
+    int sent;
+
+    PJ_LOG(5, (tr->obj_name, "pjsip_transport_send_msg(tdata=%s)", tdata->obj_name));
+
+    sent = transport_send_msg(tr, tdata, addr );
+    return sent;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Create a new transport manager.
+ */
+PJ_DEF(pjsip_transport_mgr*) 
+pjsip_transport_mgr_create( pj_pool_t *pool,
+			    pjsip_endpoint * endpt,
+			    void (*cb)(pjsip_endpoint*,pjsip_rx_data *) )
+{
+    pjsip_transport_mgr *mgr;
+
+    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_create()"));
+
+    mgr = pj_pool_alloc(pool, sizeof(*mgr));
+    mgr->endpt = endpt;
+    mgr->message_callback = cb;
+    
+    mgr->transport_table = pj_hash_create(pool, MGR_HASH_TABLE_SIZE);
+    if (!mgr->transport_table) {
+	PJ_LOG(3, (LOG_TRANSPORT_MGR, "error creating transport manager hash table"));
+	return NULL;
+    }
+    mgr->ioqueue = pj_ioqueue_create(pool, PJSIP_MAX_TRANSPORTS);
+    if (!mgr->ioqueue) {
+	PJ_LOG(3, (LOG_TRANSPORT_MGR, "error creating IO queue"));
+	return NULL;
+    }
+    mgr->mutex = pj_mutex_create(pool, "tmgr%p", 0);
+    if (!mgr->mutex) {
+	PJ_LOG(3, (LOG_TRANSPORT_MGR, "error creating mutex"));
+	pj_ioqueue_destroy(mgr->ioqueue);
+	return NULL;
+    }
+    pj_gettimeofday(&mgr->next_idle_check);
+    mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL;
+    return mgr;
+}
+
+/*
+ * Destroy transport manager.
+ */
+PJ_DEF(void) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr )
+{
+    pj_hash_iterator_t itr_val;
+    pj_hash_iterator_t *itr;
+    
+    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_destroy()"));
+
+    pj_mutex_lock(mgr->mutex);
+
+    itr = pjsip_transport_first(mgr, &itr_val);
+    while (itr != NULL) {
+	pj_hash_iterator_t *next;
+	pjsip_transport_t *transport;
+	
+	transport = pjsip_transport_this(mgr, itr);
+
+	next = pjsip_transport_next(mgr, itr);
+
+	pj_atomic_set(transport->ref_cnt, 0);
+	destroy_transport( mgr, transport);
+
+	itr = next;
+    }
+    pj_ioqueue_destroy(mgr->ioqueue);
+
+    pj_mutex_unlock(mgr->mutex);
+}
+
+/*
+ * Create listener
+ */
+static pj_status_t create_listener( pjsip_transport_mgr *mgr,
+				    pjsip_transport_type_e type,
+				    pj_sock_t sock_hnd,
+				    pj_sockaddr_in *local_addr,
+				    const pj_sockaddr_in *addr_name)
+{
+    pjsip_transport_t *tr;
+    struct transport_key *hash_key;
+    int opt_val;
+
+    opt_val = DEFAULT_SO_SNDBUF;
+    if (pj_sock_setsockopt( sock_hnd, SOL_SOCKET, SO_SNDBUF, &opt_val, sizeof(opt_val)) != PJ_OK) {
+	PJ_LOG(3, (LOG_TRANSPORT_MGR, "create listener: error setting SNDBUF to %d", DEFAULT_SO_SNDBUF));
+	// Just ignore the error.
+    }
+
+    opt_val = DEFAULT_SO_RCVBUF;
+    if (pj_sock_setsockopt( sock_hnd, SOL_SOCKET, SO_RCVBUF, &opt_val, sizeof(opt_val)) != PJ_OK) {
+	PJ_LOG(3, (LOG_TRANSPORT_MGR, "create listener: error setting RCVBUF to %d", DEFAULT_SO_SNDBUF));
+	// Just ignore the error
+    }
+
+    tr = create_transport(mgr, type, sock_hnd, local_addr, addr_name);
+    if (!tr) {
+	pj_sock_close(sock_hnd);
+	return -1;
+    }
+#if PJ_HAS_TCP
+    if (type == PJSIP_TRANSPORT_TCP) {
+	pj_status_t status;
+
+	if (pj_sock_listen(tr->sock, BACKLOG) != 0) {
+	    PJ_PERROR((tr->obj_name, "listen()"));
+	    destroy_transport(mgr, tr);
+	    return -1;
+	}
+	tr->accept_data.addrlen = sizeof(tr->accept_data.local);
+	status = pj_ioqueue_accept(mgr->ioqueue, tr->key, 
+				   &tr->accept_data.sock, 
+				   &tr->accept_data.local, 
+				   &tr->accept_data.remote,
+				   &tr->accept_data.addrlen);
+	if (status != PJ_IOQUEUE_PENDING) {
+	    PJ_PERROR((tr->obj_name, "pj_ioqueue_accept()"));
+	    destroy_transport(mgr, tr);
+	    return -1;
+	}
+
+    } else 
+#endif
+    if (type == PJSIP_TRANSPORT_UDP) {
+	pj_status_t status;
+
+	tr->rdata->addr_len = sizeof(tr->rdata->addr);
+	status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, 
+				      tr->rdata->packet, PJSIP_MAX_PKT_LEN,
+				      &tr->rdata->addr, 
+				      &tr->rdata->addr_len);
+	if (status != PJ_IOQUEUE_PENDING) {
+	    PJ_PERROR((tr->obj_name, "pj_ioqueue_recvfrom()"));
+	    destroy_transport(mgr, tr);
+	    return -1;
+	}
+    }
+
+    pj_atomic_set(tr->ref_cnt, 1);
+
+    /* Listeners normally have no remote address */
+    pj_memset(&tr->remote_addr, 0, sizeof(tr->remote_addr));
+
+    /* Set remote address to 127.0.0.1 for UDP socket bound to 127.0.0.1. 
+     * See further comments on struct pjsip_transport_t definition.
+     */
+    if (type == PJSIP_TRANSPORT_UDP && local_addr->sin_addr.s_addr == inet_addr("127.0.0.1")) {
+	pj_str_t localaddr = pj_str("127.0.0.1");
+	pj_sockaddr_set_str_addr( &tr->remote_addr, &localaddr);
+    }
+    hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
+    init_key_from_transport(hash_key, tr);
+
+    pj_mutex_lock(mgr->mutex);
+    pj_hash_set(tr->pool, mgr->transport_table, hash_key, sizeof(transport_key), tr);
+    pj_mutex_unlock(mgr->mutex);
+
+    PJ_LOG(4,(tr->obj_name, "Listening at %s %s:%d", 
+			 get_type_name(tr->type), 
+			 pj_sockaddr_get_str_addr(&tr->local_addr),
+			 pj_sockaddr_get_port(&tr->local_addr)));
+    PJ_LOG(4,(tr->obj_name, "Listener public address is at %s %s:%d", 
+			 get_type_name(tr->type), 
+			 pj_sockaddr_get_str_addr(&tr->addr_name),
+			 pj_sockaddr_get_port(&tr->addr_name)));
+    return PJ_OK;
+}
+
+/*
+ * Create listener.
+ */
+PJ_DEF(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr,
+					   pjsip_transport_type_e type,
+					   pj_sockaddr_in *local_addr,
+					   const pj_sockaddr_in *addr_name)
+{
+    pj_sock_t sock_hnd;
+
+    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_create_listener(type=%d)", type));
+
+    sock_hnd = create_socket(type, local_addr);
+    if (sock_hnd == PJ_INVALID_SOCKET) {
+	return -1;
+    }
+
+    return create_listener(mgr, type, sock_hnd, local_addr, addr_name);
+}
+
+/*
+ * Create UDP listener.
+ */
+PJ_DEF(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr,
+					       pj_sock_t sock,
+					       const pj_sockaddr_in *addr_name)
+{
+    pj_sockaddr_in local_addr;
+    int addrlen = sizeof(local_addr);
+
+    if (pj_sock_getsockname(sock, (pj_sockaddr_t*)&local_addr, &addrlen) != 0)
+	return -1;
+
+    return create_listener(mgr, PJSIP_TRANSPORT_UDP, sock, &local_addr, addr_name);
+}
+
+/*
+ * Find transport to be used to send message to remote destination. If no
+ * suitable transport is found, a new one will be created.
+ */
+PJ_DEF(void) pjsip_transport_get( pjsip_transport_mgr *mgr,
+			          pj_pool_t *pool,
+				  pjsip_transport_type_e type,
+				  const pj_sockaddr_in *remote,
+				  void *token,
+				  pjsip_transport_completion_callback *cb)
+{
+    transport_key search_key, *hash_key;
+    pjsip_transport_t *tr;
+    pj_sockaddr_in local;
+    int sock_hnd;
+    pj_status_t status;
+    struct transport_callback *cb_rec;
+
+    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_get()"));
+
+    /* Create the callback record.
+     */
+    cb_rec = pj_pool_calloc(pool, 1, sizeof(*cb_rec));
+    cb_rec->token = token;
+    cb_rec->cb = cb;
+
+    /* Create key for hash table look-up.
+     * The key creation is different for TCP and UDP.
+     */
+#if PJ_HAS_TCP
+    if (type==PJSIP_TRANSPORT_TCP) {
+	init_tcp_key(&search_key, type, remote);
+    } else 
+#endif
+    if (type==PJSIP_TRANSPORT_UDP) {
+	init_udp_key(&search_key, type, remote);
+    }
+
+    /* Start lock the manager. */
+    pj_mutex_lock(mgr->mutex);
+
+    /* Lookup the transport in the hash table. */
+    tr = pj_hash_get(mgr->transport_table, &search_key, sizeof(transport_key));
+
+    if (tr) {
+	/* Transport found. If the transport is still busy (i.e. connecting
+	 * is in progress), then just register the callback. Otherwise
+	 * report via the callback if callback is specified. 
+	 */
+	pj_mutex_unlock(mgr->mutex);
+	pj_mutex_lock(tr->tr_mutex);
+
+	if (tr->flag & PJSIP_TRANSPORT_IOQUEUE_BUSY) {
+	    /* Transport is busy. Just register the callback. */
+	    pj_list_insert_before(&tr->cb_list, cb_rec);
+
+	} else {
+	    /* Transport is ready. Call callback now.
+	     */
+	    (*cb_rec->cb)(tr, cb_rec->token, PJ_OK);
+	}
+	pj_mutex_unlock(tr->tr_mutex);
+
+	return;
+    }
+
+
+    /* Transport not found. Create new one. */
+    pj_memset(&local, 0, sizeof(local));
+    local.sin_family = PJ_AF_INET;
+    sock_hnd = create_socket(type, &local);
+    if (sock_hnd == PJ_INVALID_SOCKET) {
+	pj_mutex_unlock(mgr->mutex);
+	(*cb_rec->cb)(NULL, cb_rec->token, -1);
+	return;
+    }
+    tr = create_transport(mgr, type, sock_hnd, &local, NULL);
+    if (!tr) {
+	pj_mutex_unlock(mgr->mutex);
+	(*cb_rec->cb)(NULL, cb_rec->token, -1);
+	return;
+    }
+
+#if PJ_HAS_TCP
+    if (type == PJSIP_TRANSPORT_TCP) {
+	pj_memcpy(&tr->remote_addr, remote, sizeof(pj_sockaddr_in));
+	status = pj_ioqueue_connect(mgr->ioqueue, tr->key, 
+				    &tr->remote_addr, sizeof(pj_sockaddr_in));
+	pj_assert(status != 0);
+	if (status != PJ_IOQUEUE_PENDING) {
+	    PJ_PERROR((tr->obj_name, "pj_ioqueue_connect()"));
+	    destroy_transport(mgr, tr);
+	    pj_mutex_unlock(mgr->mutex);
+	    (*cb_rec->cb)(NULL, cb_rec->token, -1);
+	    return;
+	}
+    } else 
+#endif
+    if (type == PJSIP_TRANSPORT_UDP) {
+	int len;
+
+	do {
+	    tr->rdata->addr_len = sizeof(tr->rdata->addr);
+	    len = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, 
+				       tr->rdata->packet, PJSIP_MAX_PKT_LEN,
+				       &tr->rdata->addr, 
+				       &tr->rdata->addr_len);
+	    pj_assert(len < 0);
+	    if (len != PJ_IOQUEUE_PENDING) {
+		PJ_PERROR((tr->obj_name, "pj_ioqueue_recvfrom()"));
+		destroy_transport(mgr, tr);
+		pj_mutex_unlock(mgr->mutex);
+		(*cb_rec->cb)(NULL, cb_rec->token, -1);
+		return;
+	    }
+
+	    /* Bug here.
+	     * If data is immediately available, although not likely, it will
+	     * be dropped because we don't expect to have data right after
+	     * the socket is created, do we ?!
+	     */
+	    PJ_TODO(FIXED_BUG_ON_IMMEDIATE_TRANSPORT_DATA);
+
+	} while (len != PJ_IOQUEUE_PENDING);
+
+	//Bug: cb will never be called!
+	//     Must force status to PJ_OK;
+	//status = PJ_IOQUEUE_PENDING;
+
+	status = PJ_OK;
+
+    } else {
+	pj_mutex_unlock(mgr->mutex);
+	(*cb_rec->cb)(NULL, cb_rec->token, -1);
+	return;
+    }
+
+    pj_assert(status==PJ_IOQUEUE_PENDING || status==PJ_OK);
+    pj_mutex_lock(tr->tr_mutex);
+    hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
+    pj_memcpy(hash_key, &search_key, sizeof(transport_key));
+    pj_hash_set(tr->pool, mgr->transport_table, hash_key, sizeof(transport_key), tr);
+    if (status == PJ_OK) {
+	pj_mutex_unlock(tr->tr_mutex);
+	pj_mutex_unlock(mgr->mutex);
+	(*cb_rec->cb)(tr, cb_rec->token, PJ_OK);
+    } else {
+	pj_list_insert_before(&tr->cb_list, cb_rec);
+	pj_mutex_unlock(tr->tr_mutex);
+	pj_mutex_unlock(mgr->mutex);
+    }
+
+}
+
+#if PJ_HAS_TCP
+/*
+ * Handle completion of asynchronous accept() operation.
+ * This function is called by handle_events() function.
+ */
+static void handle_new_connection( pjsip_transport_mgr *mgr,
+				   pjsip_transport_t *listener,
+				   pj_status_t status )
+{
+    pjsip_transport_t *tr;
+    transport_key *hash_key;
+
+    pj_assert (listener->type == PJSIP_TRANSPORT_TCP);
+
+    if (status != PJ_OK) {
+	PJ_PERROR((listener->obj_name, "accept() returned error"));
+	return;
+    }
+
+    PJ_LOG(4,(listener->obj_name, "incoming tcp connection from %s:%d",
+	      pj_sockaddr_get_str_addr(&listener->accept_data.remote),
+	      pj_sockaddr_get_port(&listener->accept_data.remote)));
+
+    tr = create_transport(mgr, listener->type, 
+			  listener->accept_data.sock,
+			  &listener->accept_data.local,
+			  NULL);
+    if (!tr) {
+	goto on_return;
+    }
+
+    /*
+    tr->rdata->addr_len = sizeof(tr->rdata->addr);
+    status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, 
+				  tr->rdata->packet, PJSIP_MAX_PKT_LEN,
+				  &tr->rdata->addr, 
+				  &tr->rdata->addr_len);
+    */
+    tr->rdata->addr = listener->accept_data.remote;
+    tr->rdata->addr_len = listener->accept_data.addrlen;
+
+    status = pj_ioqueue_read (mgr->ioqueue, tr->key, tr->rdata->packet, PJSIP_MAX_PKT_LEN);
+    if (status != PJ_IOQUEUE_PENDING) {
+	PJ_PERROR((tr->obj_name, "pj_ioqueue_read()"));
+	destroy_transport(mgr, tr);
+	goto on_return;
+    }
+
+    pj_memcpy(&tr->remote_addr, &listener->accept_data.remote, listener->accept_data.addrlen);
+    hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
+    init_key_from_transport(hash_key, tr);
+
+    pj_mutex_lock(mgr->mutex);
+    pj_hash_set(tr->pool, mgr->transport_table, hash_key, sizeof(transport_key), tr);
+    pj_mutex_unlock(mgr->mutex);
+
+on_return:
+    /* Re-initiate asynchronous accept() */
+    listener->accept_data.addrlen = sizeof(listener->accept_data.local);
+    status = pj_ioqueue_accept(mgr->ioqueue, listener->key, 
+			       &listener->accept_data.sock, 
+			       &listener->accept_data.local, 
+			       &listener->accept_data.remote,
+			       &listener->accept_data.addrlen);
+    if (status != PJ_IOQUEUE_PENDING) {
+	PJ_PERROR((listener->obj_name, "pj_ioqueue_accept()"));
+	return;
+    }
+}
+
+/*
+ * Handle completion of asynchronous connect() function.
+ * This function is called by the handle_events() function.
+ */
+static void handle_connect_completion( pjsip_transport_mgr *mgr,
+				       pjsip_transport_t *tr,
+				       pj_status_t status )
+{
+    struct transport_callback new_list;
+    struct transport_callback *cb_rec;
+
+    PJ_UNUSED_ARG(mgr)
+
+    /* On connect completion, we must call all registered callbacks in
+     * the transport.
+     */
+
+    /* Initialize new list. */
+    pj_list_init(&new_list);
+
+    /* Hold transport's mutex. We don't want other thread to register a 
+     * callback while we're dealing with it. 
+    */
+    pj_mutex_lock(tr->tr_mutex);
+
+    /* Copy callback list to new list so that we can call the callbacks
+     * without holding the mutex.
+     */
+    pj_list_merge_last(&new_list, &tr->cb_list);
+
+    /* Clear transport's busy flag. */
+    tr->flag &= ~PJSIP_TRANSPORT_IOQUEUE_BUSY;
+
+    /* If success, update local address. 
+     * Local address is only available after connect() has returned.
+     */
+    if (status == PJ_OK) {
+	int addrlen = sizeof(tr->local_addr);
+	int rc;
+	if ((rc=pj_sock_getsockname(tr->sock, (pj_sockaddr_t*)&tr->local_addr, &addrlen)) == 0) {
+	    pj_memcpy(&tr->addr_name, &tr->local_addr, sizeof(tr->addr_name));
+	} else {
+	    PJ_LOG(4,(tr->obj_name, "Unable to get local address (getsockname=%d)", rc));
+	}
+    }
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(tr->tr_mutex);
+
+    /* Call all registered callbacks. */
+    cb_rec = new_list.next;
+    while (cb_rec != &new_list) {
+	struct transport_callback *next;
+	next = cb_rec->next;
+	(*cb_rec->cb)(tr, cb_rec->token, status);
+	cb_rec = next;
+    }
+
+    /* Success? */
+    if (status != PJ_OK) {
+	destroy_transport(mgr, tr);
+	return;
+    }
+
+    /* Initiate read operation to socket. */
+    status = pj_ioqueue_read (mgr->ioqueue, tr->key, tr->rdata->packet, PJSIP_MAX_PKT_LEN);
+    if (status != PJ_IOQUEUE_PENDING) {
+	PJ_PERROR((tr->obj_name, "pj_ioqueue_read()"));
+	destroy_transport(mgr, tr);
+	return;
+    }
+}
+#endif /* PJ_HAS_TCP */
+
+/*
+ * Handle incoming data.
+ * This function is called when the transport manager receives 'notification'
+ * from the I/O Queue that the receive operation has completed.
+ * This function will then attempt to parse the message, and hands over the
+ * message to the endpoint.
+ */
+static void handle_received_data( pjsip_transport_mgr *mgr,
+				  pjsip_transport_t *tr,
+				  pj_ssize_t size )
+{
+    pjsip_msg *msg;
+    pjsip_cid_hdr *call_id;
+    pjsip_rx_data *rdata = tr->rdata;
+    pj_pool_t *rdata_pool;
+    pjsip_hdr *hdr;
+    pj_str_t s;
+    char *src_addr;
+    int src_port;
+    pj_size_t msg_fragment_size = 0;
+
+    /* Check size. */
+    if (size < 1) {
+	if (tr->type != PJSIP_TRANSPORT_UDP) {
+	    /* zero bytes indicates transport has been closed for TCP.
+	     * But alas, we can't destroy it now since transactions may still
+	     * have reference to it. In that case, just do nothing, the 
+	     * transaction will receive error when it tries to send anything.
+	     * But alas!! UAC transactions wont send anything!!.
+	     * So this is a bug!
+	     */
+	    if (pj_atomic_get(tr->ref_cnt)==0) {
+		PJ_LOG(4,(tr->obj_name, "connection closed"));
+		destroy_transport(mgr, tr);
+	    } else {
+		PJ_TODO(HANDLE_TCP_TRANSPORT_CLOSED);
+		//PJ_TODO(SIGNAL_TRANSACTIONS_ON_TRANSPORT_CLOSED);
+	    }
+	    return;
+	} else {
+	    /* On Windows machines, UDP recv() will return zero upon receiving
+	     * ICMP port unreachable message.
+	     */
+	    PJ_LOG(4,(tr->obj_name, "Ignored zero length UDP packet (port unreachable?)"));
+	    goto on_return;
+	}
+    }
+    
+    /* Save received time. */
+    pj_gettimeofday(&rdata->timestamp);
+    
+    /* Update length. */
+    rdata->len += size;
+
+    /* Null terminate packet, this is the requirement of the parser. */
+    rdata->packet[rdata->len] = '\0';
+
+    /* Get source address and port for logging purpose. */
+    src_addr = pj_sockaddr_get_str_addr(&rdata->addr);
+    src_port = pj_sockaddr_get_port(&rdata->addr);
+
+    /* Print the whole data to the log. */
+    PJ_LOG(4,(tr->obj_name, "%d bytes recvfrom %s:%d:\n"
+		    "----------- begin msg ------------\n"
+		    "%s"
+		    "------------ end msg -------------", 
+	       rdata->len, src_addr, src_port, rdata->packet));
+
+
+    /* Process all message fragments. */
+    while (rdata->len > 0) {
+
+	msg_fragment_size = rdata->len;
+#if PJ_HAS_TCP
+	/* For TCP transport, check if the whole message has been received. */
+	if (tr->type != PJSIP_TRANSPORT_UDP) {
+	    pj_bool_t is_complete;
+	    is_complete = pjsip_find_msg(rdata->packet, rdata->len, PJ_FALSE, &msg_fragment_size);
+	    if (!is_complete) {
+		if (rdata->len == PJSIP_MAX_PKT_LEN) {
+		    PJ_LOG(1,(tr->obj_name, 
+			      "Transport buffer full (%d bytes) for TCP socket %s:%d "
+			      "(probably too many invalid fragments received). "
+			      "Buffer will be discarded.",
+			      PJSIP_MAX_PKT_LEN, src_addr, src_port));
+		    goto on_return;
+		} else {
+		    goto tcp_read_packet;
+		}
+	    }
+	}
+#endif
+
+	/* Clear parser error report */
+	pj_list_init(&rdata->parse_err);
+
+	/* Parse the message. */
+	PJ_LOG(5,(tr->obj_name, "Parsing %d bytes from %s:%d", msg_fragment_size,
+		src_addr, src_port));
+
+	msg = pjsip_parse_msg( rdata->pool, rdata->packet, msg_fragment_size, 
+			       &rdata->parse_err);
+	if (msg == NULL) {
+	    PJ_LOG(3,(tr->obj_name, "Bad message (%d bytes from %s:%d)", msg_fragment_size,
+		    src_addr, src_port));
+	    goto finish_process_fragment;
+	}
+
+	/* Attach newly created message to rdata. */
+	rdata->msg = msg;
+
+	/* Extract Call-ID, From and To header and tags, topmost Via, and CSeq
+	* header from the message.
+	*/
+	call_id = pjsip_msg_find_hdr( msg, PJSIP_H_CALL_ID, NULL);
+	rdata->from = pjsip_msg_find_hdr( msg, PJSIP_H_FROM, NULL);
+	rdata->to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL);
+	rdata->via = pjsip_msg_find_hdr( msg, PJSIP_H_VIA, NULL);
+	rdata->cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL);
+
+	if (call_id == NULL || rdata->from == NULL || rdata->to == NULL ||
+	    rdata->via == NULL || rdata->cseq == NULL) 
+	{
+	    PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: missing some header", 
+		    src_addr, src_port));
+	    goto finish_process_fragment;
+	}
+	rdata->call_id = call_id->id;
+	rdata->from_tag = rdata->from->tag;
+	rdata->to_tag = rdata->to->tag;
+
+	/* If message is received from address that's different from the sent-by,
+  	 * MUST add received parameter to the via.
+	 * In our case, we add Via receive param for EVERY received message, 
+	 * because it saves us from resolving the host HERE in case sent-by is in
+	 * FQDN format. And it doesn't hurt either.
+	 */
+	s = pj_str(src_addr);
+	pj_strdup(rdata->pool, &rdata->via->recvd_param, &s);
+
+	/* RFC 3581:
+	 * If message contains "rport" param, put the received port there.
+	 */
+	if (rdata->via->rport_param == 0) {
+	    rdata->via->rport_param = pj_sockaddr_get_port(&rdata->addr);
+	}
+
+	/* Drop response message if it has more than one Via.
+	*/
+	if (msg->type == PJSIP_RESPONSE_MSG) {
+	    hdr = (pjsip_hdr*)rdata->via->next;
+	    if (hdr) {
+		hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
+		if (hdr) {
+		    PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: "
+					    "multiple Via in response message",
+					    src_addr, src_port));
+		    goto finish_process_fragment;
+		}
+	    }
+	}
+
+	/* Call the transport manager's upstream message callback.
+	*/
+	(*mgr->message_callback)(mgr->endpt, rdata);
+
+finish_process_fragment:
+	rdata->len -= msg_fragment_size;
+	if (rdata->len > 0) {
+	    pj_memmove(rdata->packet, rdata->packet+msg_fragment_size, rdata->len);
+	    PJ_LOG(4,(tr->obj_name, "Processing next fragment, size=%d bytes", rdata->len));
+	}
+
+    }	/* while (rdata->len > 0) */
+
+on_return:
+    /* Reset the pool and rdata */
+    rdata_pool = rdata->pool;
+    pj_pool_reset(rdata_pool);
+    rdata = pj_pool_alloc( rdata_pool, sizeof(*rdata) );
+    rdata->len = 0;
+    rdata->transport = tr;
+    rdata->pool = rdata_pool;
+    tr->rdata = rdata;
+
+    /* Read the next packet. */
+    rdata->addr_len = sizeof(rdata->addr);
+    if (tr->type == PJSIP_TRANSPORT_UDP) {
+	pj_ioqueue_recvfrom( tr->mgr->ioqueue, tr->key,
+			    tr->rdata->packet, PJSIP_MAX_PKT_LEN,
+			    &rdata->addr, &rdata->addr_len);
+    }
+
+#if PJ_HAS_TCP
+    /* The next 'if' should have been 'else if', but we need to put the
+       label inside the '#if PJ_HAS_TCP' block to avoid 'unreferenced label' warning.
+     */
+tcp_read_packet:
+    if (tr->type == PJSIP_TRANSPORT_TCP) {
+	pj_ioqueue_read( tr->mgr->ioqueue, tr->key, 
+			 tr->rdata->packet + tr->rdata->len,
+			 PJSIP_MAX_PKT_LEN - tr->rdata->len);
+    }
+#endif
+}
+
+static void transport_mgr_on_idle( pjsip_transport_mgr *mgr )
+{
+    pj_time_val now;
+    pj_hash_iterator_t itr_val;
+    pj_hash_iterator_t *itr;
+
+
+    /* Get time for comparing transport's close time. */
+    pj_gettimeofday(&now);
+    if (now.sec < mgr->next_idle_check.sec) {
+	return;
+    }
+
+    /* Acquire transport manager's lock. */
+    pj_mutex_lock(mgr->mutex);
+
+    /* Update next idle check. */
+    mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL;
+
+    /* Iterate all transports, and close transports that are not used for
+       some periods.
+     */
+    itr = pjsip_transport_first(mgr, &itr_val);
+    while (itr != NULL) {
+	pj_hash_iterator_t *next;
+	pjsip_transport_t *transport;
+	
+	transport = pjsip_transport_this(mgr, itr);
+
+	next = pjsip_transport_next(mgr, itr);
+
+	if (pj_atomic_get(transport->ref_cnt)==0 && 
+	    PJ_TIME_VAL_LTE(transport->close_time, now))
+	{
+	    destroy_transport(mgr, transport);
+	}
+
+	itr = next;
+    }
+
+    /* Release transport manager's lock. */
+    pj_mutex_unlock(mgr->mutex);
+}
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read)
+{
+    pjsip_transport_t *t;
+    t = pj_ioqueue_get_user_data(key);
+
+    handle_received_data( t->mgr, t, bytes_read );
+}
+
+static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent)
+{
+    PJ_UNUSED_ARG(key)
+    PJ_UNUSED_ARG(bytes_sent)
+
+    /* Completion of write operation. 
+     * Do nothing.
+     */
+}
+
+static void on_ioqueue_accept(pj_ioqueue_key_t *key, int status)
+{
+#if PJ_HAS_TCP
+    pjsip_transport_t *t;
+    t = pj_ioqueue_get_user_data(key);
+
+    handle_new_connection( t->mgr, t, status );
+#else
+    PJ_UNUSED_ARG(key)
+    PJ_UNUSED_ARG(status)
+#endif
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+#if PJ_HAS_TCP
+    pjsip_transport_t *t;
+    t = pj_ioqueue_get_user_data(key);
+
+    handle_connect_completion( t->mgr, t, status);
+#else
+    PJ_UNUSED_ARG(key)
+    PJ_UNUSED_ARG(status)
+#endif
+}
+
+
+/*
+ * Poll for events.
+ */
+PJ_DEF(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr,
+					       const pj_time_val *req_timeout )
+{
+    int event_count;
+    int break_loop;
+    int result;
+    pj_time_val timeout;
+
+    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_handle_events()"));
+
+    event_count = 0;
+    break_loop = 0;
+    timeout = *req_timeout;
+    do {
+	result = pj_ioqueue_poll( mgr->ioqueue, &timeout);
+	if (result == 1) {
+	    ++event_count;
+
+	    /* Break the loop. */
+	    //if (timeout.msec==0 && timeout.sec==0) {
+	    	break_loop = 1;
+	    //}
+
+	} else {
+	    /* On idle, cleanup transport. */
+	    transport_mgr_on_idle(mgr);
+
+	    break_loop = 1;
+	}
+	timeout.sec = timeout.msec = 0;
+    } while (!break_loop);
+
+    return event_count;
+}
+
+
+PJ_DEF(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr,
+						   pj_hash_iterator_t *it )
+{
+    return pj_hash_first(mgr->transport_table, it);
+}
+
+PJ_DEF(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr,
+						  pj_hash_iterator_t *itr )
+{
+    return pj_hash_next(mgr->transport_table, itr);
+}
+
+PJ_DEF(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr,
+						 pj_hash_iterator_t *itr )
+{
+    return pj_hash_this(mgr->transport_table, itr);
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transport.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transport.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_transport.h	(revision 1753)
@@ -0,0 +1,479 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_transport.h 9     6/17/05 11:16p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_TRANSPORT_H__
+#define __PJSIP_SIP_TRANSPORT_H__
+
+/**
+ * @file sip_transport.h
+ * @brief SIP Transport
+ */
+
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pj/sock.h>
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_TRANSPORT SIP Transport
+ * @ingroup PJSIP
+ *
+ * This is the low-level transport layer. Application normally won't need to 
+ * use this function, but instead can use transaction or higher layer API to
+ * send and receive messages.
+ *
+ * @{
+ */
+
+/**
+ * Incoming message buffer.
+ * This structure keep all the information regarding the received message. This
+ * buffer lifetime is only very short, normally after the transaction has been
+ * called, this buffer will be deleted/recycled. So care must be taken when
+ * allocating storage from the pool of this buffer.
+ */
+struct pjsip_rx_data
+{
+    PJ_DECL_LIST_MEMBER(struct pjsip_rx_data)
+
+    /** Memory pool for this buffer. */
+    pj_pool_t		*pool;
+
+    /** Time when the message was received. */
+    pj_time_val		 timestamp;
+
+    /** The packet buffer. */
+    char		 packet[PJSIP_MAX_PKT_LEN];
+
+    /** The length of the packet received. */
+    int			 len;
+
+    /** The source address from which the packet was received. */
+    pj_sockaddr_in	 addr;
+
+    /** The length of the source address. */
+    int			 addr_len;
+
+    /** The transport object which received this packet. */
+    pjsip_transport_t	*transport;
+
+    /** The parsed message, if any. */
+    pjsip_msg		*msg;
+
+    /** This the transaction key generated from the message. This key is only
+     *  available after the rdata has reached the endpoint. 
+     */
+    pj_str_t		 key;
+
+    /** The Call-ID header as found in the message. */
+    pj_str_t		 call_id;
+
+    /** The From header as found in the message. */
+    pjsip_from_hdr	*from;
+
+    /** The tag in the From header as found in the message. */
+    pj_str_t		 from_tag;
+
+    /** The To header as found in the message. */
+    pjsip_to_hdr	*to;
+
+    /** The To tag header as found in the message. */
+    pj_str_t		 to_tag;
+
+    /** The topmost Via header as found in the message. */
+    pjsip_via_hdr	*via;
+
+    /** The CSeq header as found in the message. */
+    pjsip_cseq_hdr	*cseq;
+
+    /** The list of error generated by the parser when parsing this message. */
+    pjsip_parser_err_report parse_err;
+};
+
+
+/**
+ * Data structure for sending outgoing message. Application normally creates
+ * this buffer by calling #pjsip_endpt_create_tdata.
+ *
+ * The lifetime of this buffer is controlled by the reference counter in this
+ * structure, which is manipulated by calling #pjsip_tx_data_add_ref and
+ * #pjsip_tx_data_dec_ref. When the reference counter has reached zero, then
+ * this buffer will be destroyed.
+ *
+ * A transaction object normally will add reference counter to this buffer
+ * when application calls #pjsip_tsx_on_tx_msg, because it needs to keep the
+ * message for retransmission. The transaction will release the reference
+ * counter once its state has reached final state.
+ */
+struct pjsip_tx_data
+{
+    PJ_DECL_LIST_MEMBER(struct pjsip_tx_data)
+
+    /** Memory pool for this buffer. */
+    pj_pool_t		*pool;
+
+    /** A name to identify this buffer. */
+    char		 obj_name[PJ_MAX_OBJ_NAME];
+
+    /** For response message, this contains the reference to timestamp when 
+     *  the original request message was received. The value of this field
+     *  is set when application creates response message to a request by
+     *  calling #pjsip_endpt_create_response.
+     */
+    pj_time_val		 rx_timestamp;
+
+    /** The transport manager for this buffer. */
+    pjsip_transport_mgr *mgr;
+
+    /** The message in this buffer. */
+    pjsip_msg 		*msg;
+
+    /** Buffer to the printed text representation of the message. When the
+     *  content of this buffer is set, then the transport will send the content
+     *  of this buffer instead of re-printing the message structure. If the
+     *  message structure has changed, then application must invalidate this
+     *  buffer by calling #pjsip_tx_data_invalidate_msg.
+     */
+    pjsip_buffer	 buf;
+
+    /** Reference counter. */
+    pj_atomic_t		*ref_cnt;
+};
+
+
+/**
+ * Add reference counter to the transmit buffer. The reference counter controls
+ * the life time of the buffer, ie. when the counter reaches zero, then it 
+ * will be destroyed.
+ *
+ * @param tdata	    The transmit buffer.
+ */
+PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata );
+
+/**
+ * Decrement reference counter of the transmit buffer.
+ * When the transmit buffer is no longer used, it will be destroyed.
+ *
+ * @param tdata	    The transmit buffer data.
+ */
+PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
+
+/**
+ * Invalidate the print buffer to force message to be re-printed. Call
+ * when the message has changed after it has been printed to buffer. The
+ * message is printed to buffer normally by transport when it is about to be 
+ * sent to the wire. Subsequent sending of the message will not cause
+ * the message to be re-printed, unless application invalidates the buffer
+ * by calling this function.
+ *
+ * @param tdata	    The transmit buffer.
+ */
+PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata );
+
+
+/**
+ * Flags for SIP transports.
+ */
+enum pjsip_transport_flags_e
+{
+    PJSIP_TRANSPORT_RELIABLE	    = 1,    /**< Transport is reliable. */
+    PJSIP_TRANSPORT_SECURE	    = 2,    /**< Transport is secure. */
+    PJSIP_TRANSPORT_IOQUEUE_BUSY    = 4,    /**< WTH?? */
+};
+
+/**
+ * Get the transport type from the transport name.
+ *
+ * @param name	    Transport name, such as "TCP", or "UDP".
+ *
+ * @return	    The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if 
+ *		    the name is not recognized as the name of supported 
+ *		    transport.
+ */
+PJ_DECL(pjsip_transport_type_e) 
+pjsip_transport_get_type_from_name(const pj_str_t *name);
+
+/**
+ * Get the transport type for the specified flags.
+ *
+ * @param flag	    The transport flag.
+ *
+ * @return	    Transport type.
+ */
+PJ_DECL(pjsip_transport_type_e) 
+pjsip_transport_get_type_from_flag(unsigned flag);
+
+/**
+ * Get the default SIP port number for the specified type.
+ *
+ * @param type	    Transport type.
+ *
+ * @return	    The port number, which is the default SIP port number for
+ *		    the specified type.
+ */
+PJ_DECL(int) 
+pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type);
+
+
+/**
+ * Add reference to transport.
+ * Transactions or dialogs that uses a particular transport must call this 
+ * function to indicate that the transport is being used, thus preventing the
+ * transport from being closed.
+ *
+ * @param transport	The transport.
+ */
+PJ_DECL(void) 
+pjsip_transport_add_ref( pjsip_transport_t *transport );
+
+/**
+ * Decrease reference to transport.
+ * When the transport reference counter becomes zero, a timer will be started
+ * and when this timer expires and the reference counter is still zero, the
+ * transport will be released.
+ *
+ * @param transport	The transport
+ */
+PJ_DECL(void) 
+pjsip_transport_dec_ref( pjsip_transport_t *transport );
+
+
+/**
+ * Macro to check whether the transport is reliable.
+ *
+ * @param transport	The transport
+ *
+ * @return		non-zero (not necessarily 1) if transport is reliable.
+ */
+#define PJSIP_TRANSPORT_IS_RELIABLE(transport)	\
+	(pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_RELIABLE)
+
+
+/**
+ * Macro to check whether the transport is secure.
+ *
+ * @param transport	The transport
+ *
+ * @return		non-zero (not necessarily one) if transport is secure.
+ */
+#define PJSIP_TRANSPORT_IS_SECURE(transport)	\
+	(pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_SECURE)
+
+/**
+ * Get the transport type.
+ *
+ * @param tr		The transport.
+ *
+ * @return		Transport type.
+ */
+PJ_DECL(pjsip_transport_type_e) 
+pjsip_transport_get_type( const pjsip_transport_t * tr);
+
+/**
+ * Get the transport type name (ie "UDP", or "TCP").
+ *
+ * @param tr		The transport.
+ * @return		The string type.
+ */
+PJ_DECL(const char *) 
+pjsip_transport_get_type_name( const pjsip_transport_t * tr);
+
+/**
+ * Get the transport's object name.
+ *
+ * @param tr		The transport.
+ * @return		The object name.
+ */
+PJ_DECL(const char*) 
+pjsip_transport_get_obj_name( const pjsip_transport_t *tr );
+
+/**
+ * Get the transport's reference counter.
+ *
+ * @param tr		The transport.
+ * @return		The reference count value.
+ */
+PJ_DECL(int) 
+pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr );
+
+/**
+ * Get transport flag.
+ *
+ * @param tr		The transport.
+ * @return		Transport flag.
+ */
+PJ_DECL(unsigned) 
+pjsip_transport_get_flag( const pjsip_transport_t * tr );
+
+/**
+ * Get the local address of the transport, ie. the address which the socket
+ * is bound.
+ *
+ * @param tr		The transport.
+ * @return		The address.
+ */
+PJ_DECL(const pj_sockaddr_in *) 
+pjsip_transport_get_local_addr( pjsip_transport_t * tr );
+
+/**
+ * Get the address name of the transport. Address name can be an arbitrary
+ * address assigned to a transport. This is usefull for example when STUN
+ * is used, then the address name of an UDP transport can specify the public
+ * address of the transport. When the address name is not set, then value
+ * will be equal to the local/bound address. Application should normally
+ * prefer to use the address name instead of the local address.
+ *
+ * @param tr		The transport.
+ * @return		The address name.
+ */
+PJ_DECL(const pj_sockaddr_in*) 
+pjsip_transport_get_addr_name (pjsip_transport_t *tr);
+
+/**
+ * Get the remote address of the transport. Not all transports will have 
+ * a valid remote address. UDP transports, for example, will likely to have
+ * zero has their remote address, because UDP transport can be used to send
+ * and receive messages from multiple destinations.
+ *
+ * @param tr		The transport.
+ * @return		The address.
+ */
+PJ_DECL(const pj_sockaddr_in *) 
+pjsip_transport_get_remote_addr( const pjsip_transport_t * tr );
+
+/**
+ * Send a SIP message using the specified transport, to the address specified
+ * in the outgoing data. This function is only usefull for application when it
+ * wants to handle the message statelessly, because otherwise it should create
+ * a transaction and let the transaction handles the transmission of the 
+ * message.
+ *
+ * This function will send the message immediately, so application must be
+ * sure that the transport is ready to do so before calling this function.
+ *
+ * @param tr		The transport to send the message.
+ * @param tdata		The outgoing message buffer.
+ * @param addr		The remote address.
+ *
+ * @return		The number of bytes sent, or zero if the connection 
+ *			has closed, or -1 on error.
+ */
+PJ_DECL(int) pjsip_transport_send_msg( pjsip_transport_t *tr, 
+				       pjsip_tx_data *tdata,
+				       const pj_sockaddr_in *addr);
+
+
+/**
+ * @}
+ */
+
+/*
+ * PRIVATE FUNCTIONS!!!
+ *
+ * These functions are normally to be used by endpoint. Application should
+ * use the variant provided by the endpoint instance.
+ *
+ * Application normally wouldn't be able to call these functions because it
+ * has no reference of the transport manager (the instance of the transport
+ * manager is hidden by endpoint!).
+ */
+
+/*
+ * Create a new transmit buffer.
+ *
+ * @param mgr		The transport manager.
+ * @return		The transmit buffer data, or NULL on error.
+ */
+pjsip_tx_data* pjsip_tx_data_create( pjsip_transport_mgr *mgr );
+
+
+/**
+ * Create listener.
+ *
+ * @param mgr		The transport manager.
+ * @param type		Transport type.
+ * @param local_addr	The address to bind.
+ * @param addr_name	If not null, sets the address name. If NULL, 
+ *			then the local address will be used.
+ *
+ * @return		PJ_OK if listener was successfully created.
+ */
+PJ_DECL(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr,
+					    pjsip_transport_type_e type,
+					    pj_sockaddr_in *local_addr,
+					    const pj_sockaddr_in *addr_name);
+
+
+/**
+ * Create UDP listener.
+ *
+ * @param mgr		The transport manager.
+ * @param sock		The UDP socket.
+ * @param addr_name	If not null, sets the address name. If NULL, 
+ *			then the local address will be used.
+ *
+ * @return		PJ_OK if listener was successfully created.
+ */
+PJ_DECL(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr,
+						pj_sock_t sock,
+						const pj_sockaddr_in *addr_name);
+
+/** 
+ * Type of function to receive asynchronous transport completion for
+ * pjsip_transport_get() operation.
+ *
+ * @param tr		The transport.
+ * @param token		Token registered previously.
+ * @param status	Status of operation.
+ */
+typedef void pjsip_transport_completion_callback(pjsip_transport_t *tr, 
+						 void *token, 
+						 pj_status_t status);
+
+/**
+ * Find transport to be used to send message to remote destination. If no
+ * suitable transport is found, a new one will be created. If transport
+ * can not be available immediately (for example, an outgoing TCP connec()),
+ * then the caller will be notified later via the callback.
+ *
+ * @param mgr		The transport manager.
+ * @param pool		Pool to allocate asychronous job (if required).
+ * @param type		The transport type.
+ * @param remote	The remote address.
+ * @param token		The token that will be passed to the callback.
+ * @param cb		The callback to be called to report the completion of 
+ *			the operation.
+ */
+PJ_DECL(void) pjsip_transport_get( pjsip_transport_mgr *mgr,
+				   pj_pool_t *pool,
+				   pjsip_transport_type_e type,
+				   const pj_sockaddr_in *remote,
+				   void *token,
+				   pjsip_transport_completion_callback *cb);
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_SIP_TRANSPORT_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_types.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_types.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_types.h	(revision 1753)
@@ -0,0 +1,158 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_types.h 5     6/19/05 6:12p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_TYPES_H__
+#define __PJSIP_SIP_TYPES_H__
+
+#include <pjsip/sip_config.h>
+#include <pj/types.h>
+
+/**
+ * Opaque data structure for transports (sip_transport.h).
+ */
+typedef struct pjsip_transport_t pjsip_transport_t;
+
+/**
+ * Opaque data type for transport manager (sip_transport.h).
+ */
+typedef struct pjsip_transport_mgr pjsip_transport_mgr;
+
+/**
+ * Transport types.
+ */
+typedef enum pjsip_transport_type_e
+{
+    /** Unspecified. */
+    PJSIP_TRANSPORT_UNSPECIFIED,
+
+    /** UDP. */
+    PJSIP_TRANSPORT_UDP,
+
+#if PJ_HAS_TCP
+    /** TCP. */
+    PJSIP_TRANSPORT_TCP,
+
+    /** TLS. */
+    PJSIP_TRANSPORT_TLS,
+
+    /** SCTP. */
+    PJSIP_TRANSPORT_SCTP,
+#endif
+
+} pjsip_transport_type_e;
+
+
+/**
+ * Forward declaration for endpoint (sip_endpoint.h).
+ */
+typedef struct pjsip_endpoint pjsip_endpoint;
+
+/**
+ * Forward declaration for transactions (sip_transaction.h).
+ */
+typedef struct pjsip_transaction pjsip_transaction;
+
+/**
+ * Forward declaration for events (sip_event.h).
+ */
+typedef struct pjsip_event pjsip_event;
+
+/**
+ * Forward declaration for transmit data/buffer (sip_transport.h).
+ */
+typedef struct pjsip_tx_data pjsip_tx_data;
+
+/**
+ * Forward declaration for receive data/buffer (sip_transport.h).
+ */
+typedef struct pjsip_rx_data pjsip_rx_data;
+
+/**
+ * Forward declaration for message (sip_msg.h).
+ */
+typedef struct pjsip_msg pjsip_msg;
+
+/**
+ * Forward declaration for URI (sip_uri.h).
+ */
+typedef struct pjsip_uri pjsip_uri;
+
+/**
+ * Opaque data type for the resolver engine (sip_resolve.h).
+ */
+typedef struct pjsip_resolver_t pjsip_resolver_t;
+
+/**
+ * Forward declaration for credential.
+ */
+typedef struct pjsip_cred_info pjsip_cred_info;
+
+
+/**
+ * Forward declaration for module (sip_module.h).
+ */
+typedef struct pjsip_module pjsip_module;
+
+/**
+ * Transaction role.
+ */
+typedef enum pjsip_role_e
+{
+    PJSIP_ROLE_UAC,	/**< Transaction role is UAC. */
+    PJSIP_ROLE_UAS,	/**< Transaction role is UAS. */
+} pjsip_role_e;
+
+
+/**
+ * General purpose buffer.
+ */
+typedef struct pjsip_buffer
+{
+    /** The start of the buffer. */
+    char *start;
+
+    /** Pointer to current end of the buffer, which also indicates the position
+        of subsequent buffer write.
+     */
+    char *cur;
+
+    /** The absolute end of the buffer. */
+    char *end;
+
+} pjsip_buffer;
+
+
+/**
+ * General host:port pair, used for example as Via sent-by.
+ */
+typedef struct pjsip_host_port
+{
+    unsigned flag;	/**< Flags of pjsip_transport_flags_e (not used in Via). */
+    unsigned type;	/**< Transport type (pjsip_transport_type_e), or zero. */
+    pj_str_t host;	/**< Host part. */
+    int	     port;	/**< Port number. */
+} pjsip_host_port;
+
+
+#endif	/* __PJSIP_SIP_TYPES_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_uri.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_uri.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_uri.c	(revision 1753)
@@ -0,0 +1,418 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_uri.c 8     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/print.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+#define IS_SIPS(url)	((url)->vptr==&sips_url_vptr)
+
+static const pj_str_t *pjsip_url_get_scheme( const pjsip_url* );
+static const pj_str_t *pjsips_url_get_scheme( const pjsip_url* );
+static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * );
+static void *pjsip_get_uri( pjsip_uri *uri );
+static void *pjsip_name_addr_get_uri( pjsip_name_addr *name );
+
+static pj_str_t sip_str = { "sip", 3 };
+static pj_str_t sips_str = { "sips", 4 };
+
+#ifdef __GNUC__
+#  define HAPPY_FLAG	(void*)
+#else
+#  define HAPPY_FLAG
+#endif
+
+static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, 
+					       const pjsip_name_addr *rhs);
+static int pjsip_name_addr_print( pjsip_uri_context_e context,
+				  const pjsip_name_addr *name, 
+				  char *buf, pj_size_t size);
+static int pjsip_name_addr_compare(  pjsip_uri_context_e context,
+				     const pjsip_name_addr *naddr1,
+				     const pjsip_name_addr *naddr2);
+static int pjsip_url_print(  pjsip_uri_context_e context,
+			     const pjsip_url *url, 
+			     char *buf, pj_size_t size);
+static int pjsip_url_compare( pjsip_uri_context_e context,
+			      const pjsip_url *url1, const pjsip_url *url2);
+static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs);
+
+static pjsip_uri_vptr sip_url_vptr = 
+{
+    HAPPY_FLAG &pjsip_url_get_scheme,
+    HAPPY_FLAG &pjsip_get_uri,
+    HAPPY_FLAG &pjsip_url_print,
+    HAPPY_FLAG &pjsip_url_compare,
+    HAPPY_FLAG &pjsip_url_clone
+};
+
+static pjsip_uri_vptr sips_url_vptr = 
+{
+    HAPPY_FLAG &pjsips_url_get_scheme,
+    HAPPY_FLAG &pjsip_get_uri,
+    HAPPY_FLAG &pjsip_url_print,
+    HAPPY_FLAG &pjsip_url_compare,
+    HAPPY_FLAG &pjsip_url_clone
+};
+
+static pjsip_uri_vptr name_addr_vptr = 
+{
+    HAPPY_FLAG &pjsip_name_addr_get_scheme,
+    HAPPY_FLAG &pjsip_name_addr_get_uri,
+    HAPPY_FLAG &pjsip_name_addr_print,
+    HAPPY_FLAG &pjsip_name_addr_compare,
+    HAPPY_FLAG &pjsip_name_addr_clone
+};
+
+static const pj_str_t *pjsip_url_get_scheme(const pjsip_url *url)
+{
+    PJ_UNUSED_ARG(url)
+    return &sip_str;
+}
+
+static const pj_str_t *pjsips_url_get_scheme(const pjsip_url *url)
+{
+    PJ_UNUSED_ARG(url)
+    return &sips_str;
+}
+
+static void *pjsip_get_uri( pjsip_uri *uri )
+{
+    return uri;
+}
+
+static void *pjsip_name_addr_get_uri( pjsip_name_addr *name )
+{
+    return name->uri;
+}
+
+PJ_DEF(void) pjsip_url_init(pjsip_url *url, int secure)
+{
+    pj_memset(url, 0, sizeof(*url));
+    url->ttl_param = -1;
+    url->vptr = secure ? &sips_url_vptr : &sip_url_vptr;
+}
+
+PJ_DEF(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure )
+{
+    pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
+    pjsip_url_init(url, secure);
+    return url;
+}
+
+static int pjsip_url_print(  pjsip_uri_context_e context,
+			     const pjsip_url *url, 
+			     char *buf, pj_size_t size)
+{
+    int printed;
+    pj_size_t size_required;
+    char *startbuf = buf;
+    const pj_str_t *scheme;
+    *buf = '\0';
+
+    /* Check the buffer length. */
+    size_required = 6 + url->host.slen + 10 +
+		    url->user.slen + url->passwd.slen + 2 +
+		    url->user_param.slen + 6 +
+		    url->method_param.slen + 8 +
+		    url->transport_param.slen + 11 +
+		    9 + 5 +
+		    url->maddr_param.slen + 7 +
+		    3 +
+		    url->other_param.slen +
+		    url->header_param.slen;
+    if (size < size_required) {
+	return -1;
+    }
+
+    /* Print scheme ("sip:" or "sips:") */
+    scheme = pjsip_uri_get_scheme(url);
+    copy_advance_no_check(buf, *scheme);
+    *buf++ = ':';
+
+    /* Print "user:password@", if any. */
+    if (url->user.slen) {
+	copy_advance_no_check(buf, url->user);
+	if (url->passwd.slen) {
+	    *buf++ = ':';
+	    copy_advance_no_check(buf, url->passwd);
+	}
+
+	*buf++ = '@';
+    }
+
+    /* Print host. */
+    pj_assert(url->host.slen != 0);
+    copy_advance_no_check(buf, url->host);
+
+    /* Only print port if it is explicitly specified. 
+     * Port is not allowed in To and From header.
+     */
+    /* Unfortunately some UA requires us to send back the port
+     * number exactly as it was sent. We don't remember whether an
+     * UA has sent us port, so we'll just send the port indiscrimately
+     */
+    PJ_TODO(SHOULD_DISALLOW_URI_PORT_IN_FROM_TO_HEADER)
+    if (url->port /*&& context != PJSIP_URI_IN_FROMTO_HDR*/) {
+	*buf++ = ':';
+	printed = pj_utoa(url->port, buf);
+	buf += printed;
+    }
+
+    /* User param is allowed in all contexes */
+    copy_advance_pair_no_check(buf, ";user=", 6, url->user_param);
+
+    /* Method param is only allowed in external/other context. */
+    if (context == PJSIP_URI_IN_OTHER) {
+	copy_advance_pair_no_check(buf, ";method=", 8, url->method_param);
+    }
+
+    /* Transport is not allowed in From/To header. */
+    if (context != PJSIP_URI_IN_FROMTO_HDR) {
+	copy_advance_pair_no_check(buf, ";transport=", 11, url->transport_param);
+    }
+
+    /* TTL param is not allowed in From, To, Route, and Record-Route header. */
+    if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR &&
+	context != PJSIP_URI_IN_ROUTING_HDR) 
+    {
+	pj_memcpy(buf, ";ttl=", 5);
+	printed = pj_utoa(url->ttl_param, buf+5);
+	buf += printed + 5;
+    }
+
+    /* maddr param is not allowed in From and To header. */
+    if (context != PJSIP_URI_IN_FROMTO_HDR) {
+	copy_advance_pair_no_check(buf, ";maddr=", 7, url->maddr_param);
+    }
+
+    /* lr param is not allowed in From, To, and Contact header. */
+    if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR &&
+	context != PJSIP_URI_IN_CONTACT_HDR) 
+    {
+	pj_str_t lr = { ";lr", 3 };
+	copy_advance_no_check(buf, lr);
+    }
+
+    /* Other param. */
+    if (url->other_param.slen) {
+	copy_advance_no_check(buf, url->other_param);
+    }
+
+    /* Header param. */
+    if (url->header_param.slen) {
+	copy_advance_no_check(buf, url->header_param);
+    }
+
+    *buf = '\0';
+    return buf-startbuf;
+}
+
+static int pjsip_url_compare( pjsip_uri_context_e context,
+			      const pjsip_url *url1, const pjsip_url *url2)
+{
+    /* The easiest (and probably the most efficient) way to compare two URLs
+       are to print them, and compare them bytes per bytes. This technique
+       works quite well with RFC3261, as the RFC (unlike RFC2543) defines that
+       components specified in one URL does NOT match its default value if
+       it is not specified in the second URL. For example, parameter "user=ip"
+       does NOT match if it is omited in second URL.
+
+       HOWEVER, THE SAME CAN NOT BE APPLIED FOR other-param NOR header-param.
+       For these, each of the parameters must be compared one by one. Parameter
+       that exists in one URL will match the comparison. But parameter that
+       exists in both URLs and doesn't match wont match the URL comparison.
+
+       The solution for this is to compare 'standard' URL components with
+       bytes-to-bytes comparison, and compare other-param and header-param with
+       more intelligent comparison.
+     */
+    char str_url1[PJSIP_MAX_URL_SIZE];
+    char str_url2[PJSIP_MAX_URL_SIZE];
+    int len1, len2;
+
+    /* Must compare scheme first, as the second URI may not be SIP URL. */
+    if (pj_stricmp(pjsip_uri_get_scheme(url1), pjsip_uri_get_scheme(url2)))
+	return -1;
+
+    len1 = pjsip_url_print(context, url1, str_url1, sizeof(str_url1));
+    if (len1 < 1) {
+	pj_assert(0);
+	return -1;
+    }
+    len2 = pjsip_url_print(context, url2, str_url2, sizeof(str_url2));
+    if (len2 < 1) {
+	pj_assert(0);
+	return -1;
+    }
+
+    if (len1 != len2) {
+	/* Not equal. */
+	return -1;
+    }
+
+    if (strcmp(str_url1, str_url2)) {
+	/* Not equal */
+	return -1;
+    }
+
+    /* TODO: compare other-param and header-param in more intelligent manner. */
+    PJ_TODO(HPARAM_AND_OTHER_PARAM_COMPARISON_IN_URL_COMPARISON)
+
+    if (pj_strcmp(&url1->other_param, &url2->other_param)) {
+	/* Not equal. */
+	return -1;
+    }
+    if (pj_strcmp(&url1->header_param, &url2->header_param)) {
+	/* Not equal. */
+	return -1;
+    }
+
+    /* Seems to be equal, isn't it. */
+    return 0;
+    
+}
+
+
+PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, 
+			      const pjsip_url *rhs)
+{
+    pj_strdup( pool, &url->user, &rhs->user);
+    pj_strdup( pool, &url->passwd, &rhs->passwd);
+    pj_strdup( pool, &url->host, &rhs->host);
+    url->port = rhs->port;
+    pj_strdup( pool, &url->user_param, &rhs->user_param);
+    pj_strdup( pool, &url->method_param, &rhs->method_param);
+    pj_strdup( pool, &url->transport_param, &rhs->transport_param);
+    url->ttl_param = rhs->ttl_param;
+    pj_strdup( pool, &url->maddr_param, &rhs->maddr_param);
+    pj_strdup( pool, &url->other_param, &rhs->other_param);
+    pj_strdup( pool, &url->header_param, &rhs->header_param);
+    url->lr_param = rhs->lr_param;
+}
+
+static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs)
+{
+    pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
+    if (!url)
+	return NULL;
+
+    pjsip_url_init(url, IS_SIPS(rhs));
+    pjsip_url_assign(pool, url, rhs);
+    return url;
+}
+
+static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name)
+{
+    pj_assert(name->uri != NULL);
+    return pjsip_uri_get_scheme(name->uri);
+}
+
+PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name)
+{
+    name->vptr = &name_addr_vptr;
+    name->uri = NULL;
+    name->display.slen = 0;
+}
+
+PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool)
+{
+    pjsip_name_addr *name_addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
+    pjsip_name_addr_init(name_addr);
+    return name_addr;
+}
+
+static int pjsip_name_addr_print( pjsip_uri_context_e context,
+				  const pjsip_name_addr *name, 
+				  char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+
+    pj_assert(name->uri != NULL);
+
+    if (context != PJSIP_URI_IN_REQ_URI) {
+	copy_advance(buf, name->display);
+	if (name->display.slen) {
+	    *buf++ = ' ';
+	}
+	*buf++ = '<';
+    }
+
+    printed = pjsip_uri_print(context,name->uri, buf, size-(buf-startbuf));
+    if (printed < 1)
+	return -1;
+    buf += printed;
+
+    if (context != PJSIP_URI_IN_REQ_URI) {
+	*buf++ = '>';
+    }
+
+    *buf = '\0';
+    return buf-startbuf;
+}
+
+PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst,
+				    const pjsip_name_addr *src)
+{
+    pj_strdup( pool, &dst->display, &src->display);
+    dst->uri = pjsip_uri_clone(pool, src->uri);
+}
+
+static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, 
+					       const pjsip_name_addr *rhs)
+{
+    pjsip_name_addr *addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
+    if (!addr)
+	return NULL;
+
+    pjsip_name_addr_init(addr);
+    pjsip_name_addr_assign(pool, addr, rhs);
+    return addr;
+}
+
+static int pjsip_name_addr_compare(  pjsip_uri_context_e context,
+				     const pjsip_name_addr *naddr1,
+				     const pjsip_name_addr *naddr2)
+{
+    int d;
+
+    /* I'm not sure whether display name is included in the comparison. */
+    if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) {
+	return -1;
+    }
+
+    pj_assert( naddr1->uri != NULL );
+    pj_assert( naddr2->uri != NULL );
+
+    /* Compare name-addr as URL */
+    d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri);
+    if (d)
+	return d;
+
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_uri.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_uri.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip/sip_uri.h	(revision 1753)
@@ -0,0 +1,315 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_uri.h 7     6/19/05 6:12p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_URI_H__
+#define __PJSIP_SIP_URI_H__
+
+/**
+ * @file sip_uri.h
+ * @brief SIP URL Structures and Manipulations
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_config.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJSIP_URL URL Structures
+ * @brief SIP Url, tel: Url, and generic URI.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * URI context.
+ */
+typedef enum pjsip_uri_context_e
+{
+    PJSIP_URI_IN_REQ_URI,	/**< The URI is in Request URI. */
+    PJSIP_URI_IN_FROMTO_HDR,	/**< The URI is in From/To header. */
+    PJSIP_URI_IN_CONTACT_HDR,	/**< The URI is in Contact header. */
+    PJSIP_URI_IN_ROUTING_HDR,	/**< The URI is in Route/Record-Route header. */
+    PJSIP_URI_IN_OTHER,		/**< Other context (web page, business card, etc.) */
+} pjsip_uri_context_e;
+
+/**
+ * URI 'virtual' function table.
+ * All types of URI in this library (such as sip:, sips:, tel:, and name-addr) 
+ * will have pointer to this table as their first struct member. This table
+ * provides polimorphic behaviour to the URI.
+ */
+typedef struct pjsip_uri_vptr
+{
+    /** 
+     * Get URI scheme. 
+     * @param uri the URI (self).
+     * @return the URI scheme.
+     */
+    const pj_str_t* (*p_get_scheme)(const void *uri);
+
+    /**
+     * Get the URI object contained by this URI, or the URI itself if
+     * it doesn't contain another URI.
+     * @param uri the URI (self).
+     */
+    void* (*p_get_uri)(void *uri);
+
+    /**
+     * Print URI components to the buffer, following the rule of which 
+     * components are allowed for the context.
+     * @param context the context where the URI will be placed.
+     * @param uri the URI (self).
+     * @param buf the buffer.
+     * @param size the size of the buffer.
+     * @return the length printed.
+     */
+    int	(*p_print)(pjsip_uri_context_e context,
+		   const void *uri, 
+		   char *buf, pj_size_t size);
+
+    /** 
+     * Compare two URIs according to the context.
+     * @param context the context.
+     * @param uri1 the first URI (self).
+     * @param uri2 the second URI.
+     * @return zero if equal.
+     */
+    int	(*p_compare)(pjsip_uri_context_e context, 
+		     const void *uri1, const void *uri2);
+
+    /** 
+     * Clone URI. 
+     * @param pool the pool.
+     * @param the URI to clone (self).
+     * @return new URI.
+     */
+    void *(*p_clone)(pj_pool_t *pool, const void *uri);
+
+} pjsip_uri_vptr;
+
+
+/**
+ * The declaration of 'base class' for all URI scheme.
+ */
+struct pjsip_uri
+{
+    /** All URIs must have URI virtual function table as their first member. */
+    pjsip_uri_vptr *vptr;
+};
+
+/**
+ * This macro checks that the URL is a "sip:" or "sips:" URL.
+ * @param url The URL (pointer to)
+ * @return non-zero if TRUE.
+ */
+#define PJSIP_URI_SCHEME_IS_SIP(url)	\
+    (pj_strnicmp2(pjsip_uri_get_scheme(url), "sip", 3)==0)
+
+/**
+ * This macro checks that the URL is a "sips:" URL (not SIP).
+ * @param url The URL (pointer to)
+ * @return non-zero if TRUE.
+ */
+#define PJSIP_URI_SCHEME_IS_SIPS(url)	\
+    (pj_strnicmp2(pjsip_uri_get_scheme(url), "sips", 4)==0)
+
+/**
+ * This macro checks that the URL is a "tel:" URL.
+ * @param url The URL (pointer to)
+ * @return non-zero if TRUE.
+ */
+#define PJSIP_URI_SCHEME_IS_TEL(url)	\
+    (pj_strnicmp2(pjsip_uri_get_scheme(url), "tel", 3)==0)
+
+
+
+/**
+ * SIP and SIPS URL scheme.
+ */
+typedef struct pjsip_url
+{
+    pjsip_uri_vptr *vptr;		/**< Pointer to virtual function table.*/
+    pj_str_t	    user;		/**< Optional user part. */
+    pj_str_t	    passwd;		/**< Optional password part. */
+    pj_str_t	    host;		/**< Host part, always exists. */
+    int		    port;		/**< Optional port number, or zero. */
+    pj_str_t	    user_param;		/**< Optional user parameter */
+    pj_str_t	    method_param;	/**< Optional method parameter. */
+    pj_str_t	    transport_param;	/**< Optional transport parameter. */
+    int		    ttl_param;		/**< Optional TTL param, or -1. */
+    int		    lr_param;		/**< Optional loose routing param, or zero */
+    pj_str_t	    maddr_param;	/**< Optional maddr param */
+    pj_str_t	    other_param;	/**< Other parameters grouped together. */
+    pj_str_t	    header_param;	/**< Optional header parameter. */
+} pjsip_url;
+
+
+/**
+ * SIP name-addr, which typically appear in From, To, and Contact header.
+ * The SIP name-addr contains a generic URI and a display name.
+ */
+typedef struct pjsip_name_addr
+{
+    /** Pointer to virtual function table. */
+    pjsip_uri_vptr  *vptr;
+
+    /** Optional display name. */
+    pj_str_t	     display;
+
+    /** URI part. */
+    pjsip_uri	    *uri;
+
+} pjsip_name_addr;
+
+
+/**
+ * Generic function to get the URI scheme.
+ * @param uri	    the URI object.
+ * @return	    the URI scheme.
+ */
+PJ_INLINE(const pj_str_t*) pjsip_uri_get_scheme(const void *uri)
+{
+    return (*((pjsip_uri*)uri)->vptr->p_get_scheme)(uri);
+}
+
+/**
+ * Generic function to get the URI object contained by this URI, or the URI 
+ * itself if it doesn't contain another URI.
+ *
+ * @param uri	    the URI.
+ * @return	    the URI.
+ */
+PJ_INLINE(void*) pjsip_uri_get_uri(void *uri)
+{
+    return (*((pjsip_uri*)uri)->vptr->p_get_uri)(uri);
+}
+
+/**
+ * Generic function to compare two URIs.
+ *
+ * @param context   Comparison context.
+ * @param uri1	    The first URI.
+ * @param uri2	    The second URI.
+ * @return	    Zero if equal.
+ */
+PJ_INLINE(int) pjsip_uri_cmp(pjsip_uri_context_e context, 
+			     const void *uri1, const void *uri2)
+{
+    return (*((const pjsip_uri*)uri1)->vptr->p_compare)(context, uri1, uri2);
+}
+
+/**
+ * Generic function to print an URI object.
+ *
+ * @param context   Print context.
+ * @param uri	    The URI to print.
+ * @param buf	    The buffer.
+ * @param size	    Size of the buffer.
+ * @return	    Length printed.
+ */
+PJ_INLINE(int) pjsip_uri_print(pjsip_uri_context_e context,
+			       const void *uri,
+			       char *buf, pj_size_t size)
+{
+    return (*((const pjsip_uri*)uri)->vptr->p_print)(context, uri, buf, size);
+}
+
+/**
+ * Generic function to clone an URI object.
+ *
+ * @param pool	    Pool.
+ * @param uri	    URI to clone.
+ * @return	    New URI.
+ */
+PJ_INLINE(void*) pjsip_uri_clone( pj_pool_t *pool, const void *uri )
+{
+    return (*((const pjsip_uri*)uri)->vptr->p_clone)(pool, uri);
+}
+
+
+/**
+ * Create new SIP URL and initialize all fields with zero or NULL.
+ * @param pool	    The pool.
+ * @param secure    Tlag to indicate whether secure transport should be used.
+ * @return SIP URL.
+ */
+PJ_DECL(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure );
+
+/**
+ * Create new SIPS URL and initialize all fields with zero or NULL.
+ * @param pool	    The pool.
+ * @return	    SIPS URL.
+ */
+PJ_DECL(pjsip_url*) pjsips_url_create( pj_pool_t *pool );
+
+/**
+ * Initialize SIP URL (all fields are set to NULL or zero).
+ * @param url	    The URL.
+ */
+PJ_DECL(void)  pjsip_url_init(pjsip_url *url, int secure);
+
+/**
+ * Perform full assignment to the SIP URL.
+ * @param pool	    The pool.
+ * @param url	    Destination URL.
+ * @param rhs	    The source URL.
+ */
+PJ_DECL(void)  pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, const pjsip_url *rhs);
+
+/**
+ * Create new instance of name address and initialize all fields with zero or
+ * NULL.
+ * @param pool	    The pool.
+ * @return	    New SIP name address.
+ */
+PJ_DECL(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool);
+
+/**
+ * Initialize with default value.
+ * @param name_addr The name address.
+ */
+PJ_DECL(void) pjsip_name_addr_init(pjsip_name_addr *name_addr);
+
+/**
+ * Perform full assignment to the name address.
+ * @param pool	    The pool.
+ * @param addr	    The destination name address.
+ * @param rhs	    The source name address.
+ */
+PJ_DECL(void)  pjsip_name_addr_assign(pj_pool_t *pool, 
+				      pjsip_name_addr *addr, 
+				      const pjsip_name_addr *rhs);
+
+
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_URL_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.h	(revision 1753)
@@ -0,0 +1,42 @@
+/* $Header: /pjproject/pjsip/src/pjsip_auth.h 2     6/05/05 12:13p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_AUTH_H__
+#define __PJSIP_AUTH_H__
+
+/**
+ * @defgroup PJSIP_AUTH SIP Authorization module
+ */
+
+/**
+ * @file pjsip_auth.h
+ * @brief SIP Authorization Module.
+ */
+
+
+#include <pjsip_auth/sip_auth.h>
+#include <pjsip_auth/sip_auth_msg.h>
+#include <pjsip_auth/sip_auth_parser.h>
+
+#endif	/* __PJSIP_AUTH_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth.c	(revision 1753)
@@ -0,0 +1,809 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth.c 14    12/02/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_auth.h>
+#include <pjsip/sip_auth_parser.h>	/* just to get pjsip_DIGEST_STR */
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pj/md5.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/guid.h>
+
+/* Length of digest string. */
+#define MD5STRLEN 32
+
+/* Maximum stack size we use for storing username+realm+password etc. */
+#define MAX_TEMP  128
+
+/* A macro just to get rid of type mismatch between char and unsigned char */
+#define MD5_APPEND(pms,buf,len)	md5_append(pms, (const unsigned char*)buf, len)
+
+/* Logging. */
+#define THIS_FILE   "sip_auth.c"
+#if 0
+#  define AUTH_TRACE_(expr)  PJ_LOG(3, expr)
+#else
+#  define AUTH_TRACE_(expr)
+#endif
+
+static const char hex[] = "0123456789abcdef";
+
+/* Transform digest to string.
+ * output must be at least MD5STRLEN+1 bytes.
+ *
+ * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED!
+ */
+static void digest2str(const unsigned char digest[], char *output)
+{
+    char *p = output;
+    int i;
+
+    for (i = 0; i<16; ++i) {
+	int val = digest[i];
+	*p++ = hex[val >> 4];
+	*p++ = hex[val & 0x0F];
+    }
+}
+
+/*
+ * Create response digest based on the parameters and store the
+ * digest ASCII in 'result'. 
+ */
+static void create_digest( pj_str_t *result,
+			   const pj_str_t *nonce,
+			   const pj_str_t *nc,
+			   const pj_str_t *cnonce,
+			   const pj_str_t *qop,
+			   const pj_str_t *uri,
+			   const pjsip_cred_info *cred_info,
+			   const pj_str_t *method)
+{
+    char ha1[MD5STRLEN];
+    char ha2[MD5STRLEN];
+    unsigned char digest[16];
+    md5_state_t pms;
+
+    pj_assert(result->slen >= MD5STRLEN);
+
+    AUTH_TRACE_((THIS_FILE, "Begin creating digest"));
+
+    if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) {
+	/*** 
+	 *** ha1 = MD5(username ":" realm ":" password) 
+	 ***/
+	md5_init(&pms);
+	MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen);
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen);
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen);
+	md5_finish(&pms, digest);
+
+	digest2str(digest, ha1);
+
+    } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {
+	pj_assert(cred_info->data.slen == 32);
+	pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen );
+    }
+
+    AUTH_TRACE_((THIS_FILE, "  ha1=%.32s", ha1));
+
+    /***
+     *** ha2 = MD5(method ":" req_uri) 
+     ***/
+    md5_init(&pms);
+    MD5_APPEND( &pms, method->ptr, method->slen);
+    MD5_APPEND( &pms, ":", 1);
+    MD5_APPEND( &pms, uri->ptr, uri->slen);
+    md5_finish(&pms, digest);
+    digest2str(digest, ha2);
+
+    AUTH_TRACE_((THIS_FILE, "  ha2=%.32s", ha2));
+
+    /***
+     *** When qop is not used:
+     ***    response = MD5(ha1 ":" nonce ":" ha2) 
+     ***
+     *** When qop=auth is used:
+     ***    response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
+     ***/
+    md5_init(&pms);
+    MD5_APPEND( &pms, ha1, MD5STRLEN);
+    MD5_APPEND( &pms, ":", 1);
+    MD5_APPEND( &pms, nonce->ptr, nonce->slen);
+    if (qop && qop->slen != 0) {
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, nc->ptr, nc->slen);
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, cnonce->ptr, cnonce->slen);
+	MD5_APPEND( &pms, ":", 1);
+	MD5_APPEND( &pms, qop->ptr, qop->slen);
+    }
+    MD5_APPEND( &pms, ":", 1);
+    MD5_APPEND( &pms, ha2, MD5STRLEN);
+
+    /* This is the final response digest. */
+    md5_finish(&pms, digest);
+    
+    /* Convert digest to string and store in chal->response. */
+    result->slen = MD5STRLEN;
+    digest2str(digest, result->ptr);
+
+    AUTH_TRACE_((THIS_FILE, "  digest=%.32s", result->ptr));
+    AUTH_TRACE_((THIS_FILE, "Digest created"));
+}
+
+/*
+ * Finds out if qop offer contains "auth" token.
+ */
+static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer)
+{
+    pj_str_t qop;
+    char *p;
+
+    pj_strdup_with_null( pool, &qop, qop_offer);
+    p = qop.ptr;
+    while (*p) {
+	*p = (char)tolower(*p);
+	++p;
+    }
+
+    p = qop.ptr;
+    while (*p) {
+	if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') {
+	    int e = *(p+4);
+	    if (e=='"' || e==',' || e==0)
+		return PJ_TRUE;
+	    else
+		p += 4;
+	} else {
+	    ++p;
+	}
+    }
+
+    return PJ_FALSE;
+}
+
+/*
+ * Generate response digest. 
+ * Most of the parameters to generate the digest (i.e. username, realm, uri,
+ * and nonce) are expected to be in the credential. Additional parameters (i.e.
+ * password and method param) should be supplied in the argument.
+ *
+ * The resulting digest will be stored in cred->response.
+ * The pool is used to allocate 32 bytes to store the digest in cred->response.
+ */
+static pj_status_t respond_digest( pj_pool_t *pool,
+				   pjsip_digest_credential *cred,
+				   const pjsip_digest_challenge *chal,
+				   const pj_str_t *uri,
+				   const pjsip_cred_info *cred_info,
+				   const pj_str_t *cnonce,
+				   pj_uint32_t nc,
+				   const pj_str_t *method)
+{
+    /* Check algorithm is supported. We only support MD5. */
+    if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR))
+    {
+	PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"",
+		  chal->algorithm.slen, chal->algorithm.ptr));
+	return -1;
+    }
+
+    /* Build digest credential from arguments. */
+    pj_strdup(pool, &cred->username, &cred_info->username);
+    pj_strdup(pool, &cred->realm, &chal->realm);
+    pj_strdup(pool, &cred->nonce, &chal->nonce);
+    pj_strdup(pool, &cred->uri, uri);
+    cred->algorithm = pjsip_MD5_STR;
+    pj_strdup(pool, &cred->opaque, &chal->opaque);
+    
+    /* Allocate memory. */
+    cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN);
+    cred->response.slen = MD5STRLEN;
+
+    if (chal->qop.slen == 0) {
+	/* Server doesn't require quality of protection. */
+
+	/* Convert digest to string and store in chal->response. */
+	create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL,
+		       uri, cred_info, method);
+
+    } else if (has_auth_qop(pool, &chal->qop)) {
+	/* Server requires quality of protection. 
+	 * We respond with selecting "qop=auth" protection.
+	 */
+	cred->qop = pjsip_AUTH_STR;
+	cred->nc.ptr = pj_pool_alloc(pool, 16);
+	sprintf(cred->nc.ptr, "%06u", nc);
+
+	if (cnonce && cnonce->slen) {
+	    pj_strdup(pool, &cred->cnonce, cnonce);
+	} else {
+	    pj_str_t dummy_cnonce = { "b39971", 6};
+	    pj_strdup(pool, &cred->cnonce, &dummy_cnonce);
+	}
+
+	create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce, 
+		       &pjsip_AUTH_STR, uri, cred_info, method );
+
+    } else {
+	/* Server requires quality protection that we don't support. */
+	PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s", 
+		  chal->qop.slen, chal->qop.ptr));
+	return -1;
+    }
+
+    return 0;
+}
+
+#if PJSIP_AUTH_QOP_SUPPORT
+/*
+ * Update authentication session with a challenge.
+ */
+static void update_digest_session( pj_pool_t *ses_pool, 
+				   pjsip_auth_session *auth_sess,
+				   const pjsip_www_authenticate_hdr *hdr )
+{
+    if (hdr->challenge.digest.qop.slen == 0)
+	return;
+
+    /* Initialize cnonce and qop if not present. */
+    if (auth_sess->cnonce.slen == 0) {
+	/* Save the whole challenge */
+	auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr);
+
+	/* Create cnonce */
+	pj_create_unique_string( ses_pool, &auth_sess->cnonce );
+
+	/* Initialize nonce-count */
+	auth_sess->nc = 1;
+
+	/* Save realm. */
+	pj_assert(auth_sess->realm.slen != 0);
+	if (auth_sess->realm.slen == 0) {
+	    pj_strdup(ses_pool, &auth_sess->realm, 
+		      &hdr->challenge.digest.realm);
+	}
+
+    } else {
+	/* Update last_nonce and nonce-count */
+	if (!pj_strcmp(&hdr->challenge.digest.nonce, 
+		       &auth_sess->last_chal->challenge.digest.nonce)) 
+	{
+	    /* Same nonce, increment nonce-count */
+	    ++auth_sess->nc;
+	} else {
+	    /* Server gives new nonce. */
+	    pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce,
+		      &hdr->challenge.digest.nonce);
+	    /* Has the opaque changed? */
+	    if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque,
+			  &hdr->challenge.digest.opaque)) 
+	    {
+		pj_strdup(ses_pool, 
+			  &auth_sess->last_chal->challenge.digest.opaque,
+			  &hdr->challenge.digest.opaque);
+	    }
+	    auth_sess->nc = 1;
+	}
+    }
+}
+#endif	/* PJSIP_AUTH_QOP_SUPPORT */
+
+
+/* Find authentication session in the list. */
+static pjsip_auth_session *find_session( pjsip_auth_session *sess_list,
+					 const pj_str_t *realm )
+{
+    pjsip_auth_session *sess = sess_list->next;
+    while (sess != sess_list) {
+	if (pj_stricmp(&sess->realm, realm) == 0)
+	    return sess;
+	sess = sess->next;
+    }
+
+    return NULL;
+}
+
+/* 
+ * Create Authorization/Proxy-Authorization response header based on the challege
+ * in WWW-Authenticate/Proxy-Authenticate header.
+ */
+PJ_DEF(pjsip_authorization_hdr*)
+pjsip_auth_respond( pj_pool_t *req_pool,
+		    const pjsip_www_authenticate_hdr *hdr,
+		    const pjsip_uri *uri,
+		    const pjsip_cred_info *cred_info,
+		    const pjsip_method *method,
+		    pj_pool_t *sess_pool,
+		    pjsip_auth_session *auth_sess)
+{
+    pjsip_authorization_hdr *auth;
+    char tmp[PJSIP_MAX_URL_SIZE];
+    pj_str_t uri_str;
+    pj_pool_t *pool;
+
+    pj_assert(hdr != NULL);
+    pj_assert(uri != NULL);
+    pj_assert(cred_info != NULL);
+    pj_assert(method != NULL);
+
+    /* Print URL in the original request. */
+    uri_str.ptr = tmp;
+    uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp));
+    if (uri_str.slen < 1) {
+	pj_assert(!"URL is too long!");
+	PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!"));
+	return NULL;
+    }
+
+#   if (PJSIP_AUTH_HEADER_CACHING)
+    {
+	pool = sess_pool;
+	PJ_UNUSED_ARG(req_pool);
+    }
+#   else
+    {
+	pool = req_pool;
+	PJ_UNUSED_ARG(sess_pool);
+    }
+#   endif
+
+    if (hdr->type == PJSIP_H_WWW_AUTHENTICATE)
+	auth = pjsip_authorization_hdr_create(pool);
+    else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE)
+	auth = pjsip_proxy_authorization_hdr_create(pool);
+    else {
+	pj_assert(0);
+	return NULL;
+    }
+
+    /* Only support digest scheme at the moment. */
+    if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+	pj_status_t rc;
+	pj_str_t *cnonce = NULL;
+	pj_uint32_t nc = 1;
+
+	/* Update the session (nonce-count etc) if required. */
+#	if PJSIP_AUTH_QOP_SUPPORT
+	{
+	    if (auth_sess) {
+		update_digest_session( sess_pool, auth_sess, hdr );
+
+		cnonce = &auth_sess->cnonce;
+		nc = auth_sess->nc;
+	    }
+	}
+#	endif	/* PJSIP_AUTH_QOP_SUPPORT */
+
+	auth->scheme = pjsip_DIGEST_STR;
+	rc = respond_digest( pool, &auth->credential.digest,
+			     &hdr->challenge.digest, &uri_str, cred_info,
+			     cnonce, nc, &method->name);
+	if (rc != 0)
+	    return NULL;
+
+	/* Set qop type in auth session the first time only. */
+	if (hdr->challenge.digest.qop.slen != 0 && auth_sess) {
+	    if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+		pj_str_t *qop_val = &auth->credential.digest.qop;
+		if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) {
+		    auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH;
+		} else {
+		    auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN;
+		}
+	    }
+	}
+    } else {
+	auth = NULL;
+    }
+
+    /* Keep the new authorization header in the cache, only
+     * if no qop is not present.
+     */
+#   if PJSIP_AUTH_HEADER_CACHING
+    {
+	if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+	    pjsip_cached_auth_hdr *cached_hdr;
+
+	    /* Delete old header with the same method. */
+	    cached_hdr = auth_sess->cached_hdr.next;
+	    while (cached_hdr != &auth_sess->cached_hdr) {
+		if (pjsip_method_cmp(method, &cached_hdr->method)==0)
+		    break;
+		cached_hdr = cached_hdr->next;
+	    }
+
+	    /* Save the header to the list. */
+	    if (cached_hdr != &auth_sess->cached_hdr) {
+		cached_hdr->hdr = auth;
+	    } else {
+		cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr));
+		pjsip_method_copy( pool, &cached_hdr->method, method);
+		cached_hdr->hdr = auth;
+		pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr );
+	    }
+	}
+    }
+#   endif
+
+    return auth;
+
+}
+
+/* Verify incoming Authorization/Proxy-Authorization header against existing
+ * credentials. Will return TRUE if the authorization request matches any of
+ * the credential.
+ */
+PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr,
+				    const pj_str_t *method,
+				    const pjsip_cred_info *cred_info )
+{
+    if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
+	char digest_buf[MD5STRLEN];
+	pj_str_t digest;
+	const pjsip_digest_credential *dig = &hdr->credential.digest;
+
+	/* Check that username match. */
+	if (pj_strcmp(&dig->username, &cred_info->username) != 0)
+	    return PJ_FALSE;
+
+	/* Check that realm match. */
+	if (pj_strcmp(&dig->realm, &cred_info->realm) != 0)
+	    return PJ_FALSE;
+
+	/* Prepare for our digest calculation. */
+	digest.ptr = digest_buf;
+	digest.slen = MD5STRLEN;
+
+	/* Create digest for comparison. */
+	create_digest(  &digest, 
+			&hdr->credential.digest.nonce,
+			&hdr->credential.digest.nc, 
+			&hdr->credential.digest.cnonce,
+			&hdr->credential.digest.qop,
+			&hdr->credential.digest.uri,
+			cred_info, 
+			method );
+
+	return pj_stricmp(&digest, &hdr->credential.digest.response) == 0;
+
+    } else {
+	pj_assert(0);
+	return PJ_FALSE;
+    }
+}
+
+/* Find credential to use for the specified realm and scheme. */
+PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
+						     const pjsip_cred_info cred[],
+						     const pj_str_t *realm,
+						     const pj_str_t *scheme)
+{
+    unsigned i;
+    PJ_UNUSED_ARG(scheme)
+    for (i=0; i<count; ++i) {
+	if (pj_stricmp(&cred[i].realm, realm) == 0)
+	    return &cred[i];
+    }
+    return NULL;
+}
+
+#if PJSIP_AUTH_AUTO_SEND_NEXT
+static void new_auth_for_req( pjsip_tx_data *tdata,
+			      pj_pool_t *sess_pool,
+			      pjsip_auth_session *sess,
+			      int cred_count,
+			      const pjsip_cred_info cred_info[])
+{
+    const pjsip_cred_info *cred;
+    pjsip_authorization_hdr *hauth;
+
+    if (sess->last_chal == NULL)
+	return;
+
+    cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm,
+				 &sess->last_chal->scheme );
+    if (!cred)
+	return;
+
+    
+    hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
+				tdata->msg->line.req.uri,
+				cred, &tdata->msg->line.req.method,
+				sess_pool, sess);
+    if (hauth) {
+	pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth);
+    }
+}
+#endif
+
+/* 
+ * Initialize new request message with authorization headers.
+ * This function will put Authorization/Proxy-Authorization headers to the
+ * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
+ * and the session has previously sent Authorization/Proxy-Authorization header
+ * with the same method, then the same Authorization/Proxy-Authorization header
+ * will be resent from the cache only if qop is not present. If the stack is 
+ * configured to automatically generate next Authorization/Proxy-Authorization
+ * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
+ * Authorization headers are calculated and generated when they are not present
+ * in the case or if authorization session has qop.
+ *
+ * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
+ * are not set, this function will do nothing. The stack then will only send
+ * Authorization/Proxy-Authorization to respond 401/407 response.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
+					 pjsip_tx_data *tdata,
+					 pjsip_auth_session *sess_list,
+					 int cred_count, 
+					 const pjsip_cred_info cred_info[])
+{
+    pjsip_auth_session *sess;
+    pjsip_method *method = &tdata->msg->line.req.method;
+
+    pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
+
+    if (!sess_list)
+	return 0;
+
+    sess = sess_list->next;
+    while (sess != sess_list) {
+	if (sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+#	    if (PJSIP_AUTH_HEADER_CACHING)
+	    {
+		pjsip_cached_auth_hdr *entry = sess->cached_hdr.next;
+		while (entry != &sess->cached_hdr) {
+		    if (pjsip_method_cmp(&entry->method, method)==0) {
+			pjsip_authorization_hdr *hauth;
+			hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr);
+			pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+		    } else {
+#			if (PJSIP_AUTH_AUTO_SEND_NEXT)
+			{
+			    new_auth_for_req( tdata, sess_pool, sess, 
+					      cred_count, cred_info);
+			}
+#			else
+			{
+			    PJ_UNUSED_ARG(sess_pool);
+			    PJ_UNUSED_ARG(cred_count);
+			    PJ_UNUSED_ARG(cred_info);
+			}
+#			endif	/* PJSIP_AUTH_AUTO_SEND_NEXT */
+		    }
+		    entry = entry->next;
+		}
+	    }
+#	    elif (PJSIP_AUTH_AUTO_SEND_NEXT)
+	    {
+		new_auth_for_req( tdata, sess_pool, sess, 
+				  cred_count, cred_info);
+	    }
+#	    else
+	    {
+		PJ_UNUSED_ARG(sess_pool);
+		PJ_UNUSED_ARG(cred_count);
+		PJ_UNUSED_ARG(cred_info);
+	    }
+#	    endif   /* PJSIP_AUTH_HEADER_CACHING */
+
+	} 
+#	if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
+	else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) {
+	    /* For qop="auth", we have to re-create the authorization header. 
+	     */
+	    const pjsip_cred_info *cred;
+	    pjsip_authorization_hdr *hauth;
+
+	    cred = pjsip_auth_find_cred( cred_count, cred_info, 
+					 &sess->realm, 
+					 &sess->last_chal->scheme);
+	    if (!cred) {
+		sess = sess->next;
+		continue;
+	    }
+
+	    hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, 
+					tdata->msg->line.req.uri, 
+					cred,
+					&tdata->msg->line.req.method,
+					sess_pool, sess );
+	    if (hauth) {
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+	    }
+	}
+#	endif	/* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */
+
+	sess = sess->next;
+    }
+    return 0;
+}
+
+/* Process authorization challenge */
+static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
+					      const pjsip_www_authenticate_hdr *hchal,
+					      const pjsip_uri *uri,
+					      pjsip_tx_data *tdata,
+					      int cred_count,
+					      const pjsip_cred_info cred_info[],
+					      pj_pool_t *ses_pool,
+					      pjsip_auth_session *auth_sess)
+{
+    const pjsip_cred_info *cred;
+    pjsip_authorization_hdr *sent_auth = NULL, *hauth;
+    pjsip_hdr *hdr;
+
+    /* See if we have sent authorization header for this realm */
+    hdr = tdata->msg->hdr.next;
+    while (hdr != &tdata->msg->hdr) {
+	if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE &&
+	     hdr->type == PJSIP_H_AUTHORIZATION) ||
+	    (hchal->type == PJSIP_H_PROXY_AUTHENTICATE &&
+	     hdr->type == PJSIP_H_PROXY_AUTHORIZATION))
+	{
+	    sent_auth = (pjsip_authorization_hdr*) hdr;
+	    if (pj_stricmp(&hchal->challenge.common.realm, 
+			   &sent_auth->credential.common.realm )==0)
+	    {
+		break;
+	    }
+	}
+	hdr = hdr->next;
+    }
+
+    /* If we have sent, see if server rejected because of stale nonce or
+     * other causes.
+     */
+    if (hdr != &tdata->msg->hdr) {
+	if (hchal->challenge.digest.stale == 0) {
+	    /* Our credential is rejected. No point in trying to re-supply
+	     * the same credential.
+	     */
+	    PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s",
+		       sent_auth->credential.digest.username.slen,
+		       sent_auth->credential.digest.username.ptr,
+		       sent_auth->credential.digest.realm.slen,
+		       sent_auth->credential.digest.realm.ptr));
+	    return NULL;
+	}
+
+	/* Otherwise remove old, stale authorization header from the mesasge.
+	 * We will supply a new one.
+	 */
+	pj_list_erase(sent_auth);
+    }
+
+    /* Find credential to be used for the challenge. */
+    cred = pjsip_auth_find_cred( cred_count, cred_info, 
+				 &hchal->challenge.common.realm, &hchal->scheme);
+    if (!cred) {
+	const pj_str_t *realm = &hchal->challenge.common.realm;
+	PJ_LOG(4,(THIS_FILE, 
+		  "Unable to set auth for %s: can not find credential for %.*s/%.*s",
+		  tdata->obj_name, 
+		  realm->slen, realm->ptr,
+		  hchal->scheme.slen, hchal->scheme.ptr));
+	return NULL;
+    }
+
+    /* Respond to authorization challenge. */
+    hauth = pjsip_auth_respond( req_pool, hchal, uri, cred, 
+				&tdata->msg->line.req.method, 
+				ses_pool, auth_sess);
+    return hauth;
+}
+
+
+/* Reinitialize outgoing request after 401/407 response is received.
+ * The purpose of this function is:
+ *  - to add a Authorization/Proxy-Authorization header.
+ *  - to put the newly created Authorization/Proxy-Authorization header
+ *    in cached_list.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt, 
+					      pj_pool_t *ses_pool, 
+					      pjsip_auth_session *sess_list,
+					      int cred_count, 
+					      const pjsip_cred_info cred_info[],
+					      pjsip_tx_data *tdata, 
+					      const pjsip_rx_data *rdata)
+{
+    const pjsip_hdr *hdr;
+    pjsip_via_hdr *via;
+
+    PJ_UNUSED_ARG(endpt)
+
+    pj_assert(rdata->msg->type == PJSIP_RESPONSE_MSG);
+    pj_assert(rdata->msg->line.status.code == 401 ||
+	      rdata->msg->line.status.code == 407 );
+
+    /*
+     * Respond to each authentication challenge.
+     */
+    hdr = rdata->msg->hdr.next;
+    while (hdr != &rdata->msg->hdr) {
+	pjsip_auth_session *sess;
+	const pjsip_www_authenticate_hdr *hchal;
+	pjsip_authorization_hdr *hauth;
+
+	/* Find WWW-Authenticate or Proxy-Authenticate header. */
+	while (hdr->type != PJSIP_H_WWW_AUTHENTICATE &&
+	       hdr->type != PJSIP_H_PROXY_AUTHENTICATE &&
+	       hdr != &rdata->msg->hdr)
+	{
+	    hdr = hdr->next;
+	}
+	if (hdr == &rdata->msg->hdr)
+	    break;
+
+	hchal = (const pjsip_www_authenticate_hdr*) hdr;
+
+	/* Find authentication session for this realm, create a new one
+	 * if not present.
+	 */
+	sess = find_session(sess_list, &hchal->challenge.common.realm );
+	if (!sess) {
+	    sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess));
+	    pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm);
+	    sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE);
+#	    if (PJSIP_AUTH_HEADER_CACHING)
+	    {
+		pj_list_init(&sess->cached_hdr);
+	    }
+#	    endif
+	    pj_list_insert_before( sess_list, sess );
+	}
+
+	/* Create authorization header for this challenge, and update
+	 * authorization session.
+	 */
+	hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, 
+			      tdata, cred_count, cred_info, ses_pool, sess );
+	if (!hauth)
+	    return NULL;
+
+	/* Add to the message. */
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+
+	/* Process next header. */
+	hdr = hdr->next;
+    }
+
+
+    /* Remove branch param in Via header. */
+    via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+    if (via)
+	via->branch_param.slen = 0;
+
+    /* Increment reference counter. */
+    pjsip_tx_data_add_ref(tdata);
+
+    /* Done. */
+    return tdata;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth.h	(revision 1753)
@@ -0,0 +1,252 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth.h 11    8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_H__
+#define __PJSIP_AUTH_SIP_AUTH_H__
+
+/**
+ * @file pjsip_auth.h
+ * @brief SIP Authorization Module.
+ */
+
+#include <pjsip/sip_config.h>
+#include <pjsip/sip_auth_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_AUTH_API Authorization API's
+ * @ingroup PJSIP_AUTH
+ * @{
+ */
+
+ /** Type of data in the credential information. */
+typedef enum pjsip_cred_data_type
+{
+    PJSIP_CRED_DATA_PLAIN_PASSWD,   /**< Plain text password.	*/
+    PJSIP_CRED_DATA_DIGEST,	    /**< Hashed digest.		*/
+} pjsip_cred_data_type;
+
+/** Authentication's quality of protection (qop) type. */
+typedef enum pjsip_auth_qop_type
+{
+    PJSIP_AUTH_QOP_NONE,	    /**< No quality of protection. */
+    PJSIP_AUTH_QOP_AUTH,	    /**< Authentication. */
+    PJSIP_AUTH_QOP_AUTH_INT,	    /**< Authentication with integrity protection. */
+    PJSIP_AUTH_QOP_UNKNOWN,	    /**< Unknown protection. */
+} pjsip_auth_qop_type;
+
+
+/** 
+ * This structure describes credential information. 
+ * A credential information is a static, persistent information that identifies
+ * username and password required to authorize to a specific realm.
+ */
+struct pjsip_cred_info
+{
+    pj_str_t    realm;		/**< Realm.	    */
+    pj_str_t	scheme;		/**< Scheme.	    */
+    pj_str_t	username;	/**< User name.	    */
+    int		data_type;	/**< Type of data.  */
+    pj_str_t	data;		/**< The data, which can be a plaintext 
+				     password or a hashed digest. */
+};
+
+/**
+ * This structure describes cached value of previously sent Authorization
+ * or Proxy-Authorization header. The authentication framework keeps a list
+ * of this structure and will resend the same header to the same server
+ * as long as the method, uri, and nonce stays the same.
+ */
+typedef struct pjsip_cached_auth_hdr
+{
+    PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr)
+
+    pjsip_method	     method;
+    pjsip_authorization_hdr *hdr;
+
+} pjsip_cached_auth_hdr;
+
+
+/**
+ * This structure describes authentication information for the specified
+ * realm. Each instance of this structure describes authentication "session"
+ * between this endpoint and remote server. This "session" information is
+ * usefull to keep information that persists for more than one challenge,
+ * such as nonce-count and cnonce value.
+ *
+ * Other than that, this structure also keeps the last authorization headers
+ * that have been sent in the cache list.
+ */
+typedef struct pjsip_auth_session
+{
+    PJ_DECL_LIST_MEMBER(struct pjsip_auth_session)
+
+    pj_str_t			 realm;
+    pj_bool_t			 is_proxy;
+    pjsip_auth_qop_type		 qop_value;
+#if PJSIP_AUTH_QOP_SUPPORT
+    pj_uint32_t			 nc;
+    pj_str_t			 cnonce;
+#endif
+#if PJSIP_AUTH_AUTO_SEND_NEXT
+    pjsip_www_authenticate_hdr	*last_chal;
+#endif
+#if PJSIP_AUTH_HEADER_CACHING
+    pjsip_cached_auth_hdr	 cached_hdr;
+#endif
+
+} pjsip_auth_session;
+
+
+/**
+ * Create authorization header for the specified credential.
+ * Application calls this function to create Authorization or Proxy-Authorization
+ * header after receiving WWW-Authenticate or Proxy-Authenticate challenge
+ * (normally in 401/407 response).
+ * If authorization session argument is specified, this function will update
+ * the session with the updated information if required (e.g. to update
+ * nonce-count when qop is "auth" or "auth-int"). This function will also
+ * save the authorization header in the session's cached header list.
+ *
+ * @param req_pool	Pool to allocate new header for the request.
+ * @param hdr		The WWW-Authenticate or Proxy-Authenticate found in 
+ *			the response.
+ * @param uri		The URI for which authorization is targeted to.
+ * @param cred_info	The credential to be used for authentication.
+ * @param method	The method.
+ * @param sess_pool	Session pool to update session or to allocate message
+ *			in the cache. May be NULL if auth_sess is NULL.
+ * @param auth_sess	If not NULL, this specifies the specific authentication
+ *			session to be used or updated.
+ *
+ * @return		The Authorization header, which can be typecasted to 
+ *			Proxy-Authorization.
+ */
+PJ_DECL(pjsip_authorization_hdr*) pjsip_auth_respond( 
+					 pj_pool_t *req_pool,
+					 const pjsip_www_authenticate_hdr *hdr,
+					 const pjsip_uri *uri,
+					 const pjsip_cred_info *cred_info,
+					 const pjsip_method *method,
+					 pj_pool_t *sess_pool,
+					 pjsip_auth_session *auth_sess);
+
+/**
+ * Verify digest in the authorization request.
+ *
+ * @param hdr		The incoming Authorization/Proxy-Authorization header.
+ * @param method	The method.
+ * @param password	The plaintext password to verify.
+ *
+ * @return		Non-zero if authorization succeed.
+ */
+PJ_DECL(pj_bool_t) pjsip_auth_verify(	const pjsip_authorization_hdr *hdr,
+					const pj_str_t *method,
+					const pjsip_cred_info *cred_info );
+
+
+/**
+ * This function can be used to find credential information which matches
+ * the specified realm.
+ *
+ * @param count		Number of credentials in the parameter.
+ * @param cred		The array of credentials.
+ * @param realm		Realm to search.
+ * @param scheme	Authentication scheme.
+ *
+ * @return		The credential which matches the specified realm.
+ */
+PJ_DECL(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
+						      const pjsip_cred_info cred[],
+						      const pj_str_t *realm,
+						      const pj_str_t *scheme );
+
+
+/**
+ * Initialize new request message with authorization headers.
+ * This function will put Authorization/Proxy-Authorization headers to the
+ * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
+ * and the session has previously sent Authorization/Proxy-Authorization header
+ * with the same method, then the same Authorization/Proxy-Authorization header
+ * will be resent from the cache only if qop is not present. If the stack is 
+ * configured to automatically generate next Authorization/Proxy-Authorization
+ * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
+ * Authorization headers are calculated and generated when they are not present
+ * in the case or if authorization session has qop.
+ *
+ * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
+ * are not set, this function will do nothing. The stack then will only send
+ * Authorization/Proxy-Authorization to respond 401/407 response.
+ *
+ * @param sess_pool	Session level pool, where memory will be allocated from
+ *			for data that persists across requests (e.g. caching).
+ * @param tdata		The request message to be initialized.
+ * @param sess_list	List of authorization sessions that have been recorded.
+ * @param cred_count	Number of credentials.
+ * @param cred_info	Array of credentials.
+ *
+ * @return		Zero if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
+					  pjsip_tx_data *tdata,
+					  pjsip_auth_session *sess_list,
+					  int cred_count, 
+					  const pjsip_cred_info cred_info[]);
+
+/**
+ * Call this function when a transaction failed with 401 or 407 response.
+ * This function will reinitialize the original request message with the
+ * authentication challenge found in the response message, and add the
+ * new authorization header in the authorization cache.
+ *
+ * Note that upon return the reference counter of the transmit data
+ * will be incremented.
+ *
+ * @param endpt		Endpoint.
+ * @param pool		The pool to allocate memory for new cred_info.
+ * @param cached_list	Cached authorization headers.
+ * @param cred_count	Number of credentials.
+ * @param cred_info	Array of credentials to use.
+ * @param tdata		The original request message, which normally can be
+ *			retrieved from tsx->last_tx.
+ * @param rdata		The response message containing 401/407 status.
+ *
+ * @return		New transmit data buffer, or NULL if the dialog
+ *			can not respond to the authorization challenge.
+ */
+PJ_DECL(pjsip_tx_data*) 
+pjsip_auth_reinit_req( pjsip_endpoint *endpt,
+		       pj_pool_t *ses_pool,
+		       pjsip_auth_session *sess_list,
+		       int cred_count, const pjsip_cred_info cred_info[],
+		       pjsip_tx_data *tdata, const pjsip_rx_data *rdata);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_AUTH_SIP_AUTH_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_msg.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_msg.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_msg.c	(revision 1753)
@@ -0,0 +1,314 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_msg.c 9     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_auth_parser.h>
+#include <pj/pool.h>
+#include <pj/list.h>
+#include <pj/string.h>
+#include <pjsip/print.h>
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Authorization and Proxy-Authorization header.
+ */
+static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
+							       const pjsip_authorization_hdr *hdr);
+static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
+								       const pjsip_authorization_hdr *hdr);
+static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
+					  char *buf, pj_size_t size);
+
+static pjsip_hdr_vptr authorization_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print,
+};
+
+
+PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool)
+{
+    pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr);
+    return hdr;
+}
+
+PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool)
+{
+    pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr);
+    return hdr;
+}
+
+static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+    
+    copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"');
+    copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm);
+    copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"');
+    copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"');
+    //Note: there's no dbl-quote in qop in Authorization header 
+    // (unlike WWW-Authenticate)
+    //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"');
+    copy_advance_pair(buf, ", qop=", 6, cred->qop);
+    copy_advance_pair(buf, ", nc=", 5, cred->nc);
+    copy_advance(buf, cred->other_param);
+
+    return (int) (buf-startbuf);
+}
+
+static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size)
+{
+    PJ_UNUSED_ARG(cred)
+    PJ_UNUSED_ARG(buf)
+    PJ_UNUSED_ARG(size)
+    return -1;
+}
+
+static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
+					  char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+
+    copy_advance(buf, hdr->name);
+    *buf++ = ':';
+    *buf++ = ' ';
+
+    copy_advance(buf, hdr->scheme);
+    *buf++ = ' ';
+
+    if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0)
+    {
+	printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf);
+    } 
+    else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0)
+    {
+	printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf);
+    } 
+    else {
+	pj_assert(0);
+	return -1;
+    }
+
+    if (printed == -1)
+	return -1;
+
+    buf += printed;
+    *buf = '\0';
+    return (int)(buf-startbuf);
+}
+
+static pjsip_authorization_hdr* pjsip_authorization_hdr_clone(  pj_pool_t *pool,
+								const pjsip_authorization_hdr *rhs)
+{
+    /* This function also serves Proxy-Authorization header. */
+    pjsip_authorization_hdr *hdr;
+    if (rhs->type == PJSIP_H_AUTHORIZATION)
+	hdr = pjsip_authorization_hdr_create(pool);
+    else
+	hdr = pjsip_proxy_authorization_hdr_create(pool);
+
+    pj_strdup(pool, &hdr->scheme, &rhs->scheme);
+
+    if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
+	pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username);
+	pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm);
+	pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce);
+	pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri);
+	pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response);
+	pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm);
+	pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce);
+	pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque);
+	pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop);
+	pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc);
+	pj_strdup(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param);
+    } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
+	pj_assert(0);
+	return NULL;
+    } else {
+	pj_assert(0);
+	return NULL;
+    }
+
+    return hdr;
+}
+
+static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
+								       const pjsip_authorization_hdr *rhs)
+{
+    /* This function also serves Proxy-Authorization header. */
+    pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Proxy-Authenticate and WWW-Authenticate header.
+ */
+static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
+					     char *buf, pj_size_t size);
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
+								     const pjsip_www_authenticate_hdr *hdr);
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
+									     const pjsip_www_authenticate_hdr *hdr);
+
+static pjsip_hdr_vptr www_authenticate_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print,
+};
+
+
+PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool)
+{
+    pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr);
+    return hdr;
+}
+
+
+PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool)
+{
+    pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr);
+    return hdr;
+}
+
+static int print_digest_challenge( pjsip_digest_challenge *chal,
+				   char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+
+    copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"');
+    copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"');
+    copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"');
+    copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"');
+    if (chal->stale) {
+	pj_str_t true_str = { "true", 4 };
+	copy_advance_pair(buf, ",stale=", 7, true_str);
+    }
+    copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm);
+    copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"');
+    copy_advance(buf, chal->other_param);
+
+    return (int)(buf-startbuf);
+}
+
+static int print_pgp_challenge( pjsip_pgp_challenge *chal,
+			        char *buf, pj_size_t size)
+{
+    PJ_UNUSED_ARG(chal)
+    PJ_UNUSED_ARG(buf)
+    PJ_UNUSED_ARG(size)
+    return -1;
+}
+
+static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
+					     char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf + size;
+
+    copy_advance(buf, hdr->name);
+    *buf++ = ':';
+    *buf++ = ' ';
+
+    copy_advance(buf, hdr->scheme);
+    *buf++ = ' ';
+
+    if (pj_stricmp2(&hdr->scheme, "digest") == 0)
+	printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf);
+    else if (pj_stricmp2(&hdr->scheme, "pgp") == 0)
+	printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf);
+    else {
+	pj_assert(0);
+	return -1;
+    }
+
+    if (printed == -1)
+	return -1;
+
+    buf += printed;
+    *buf = '\0';
+    return (int)(buf-startbuf);
+}
+
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
+								     const pjsip_www_authenticate_hdr *rhs)
+{
+    /* This function also serves Proxy-Authenticate header. */
+    pjsip_www_authenticate_hdr *hdr;
+    if (rhs->type == PJSIP_H_WWW_AUTHENTICATE)
+	hdr = pjsip_www_authenticate_hdr_create(pool);
+    else
+	hdr = pjsip_proxy_authenticate_hdr_create(pool);
+
+    pj_strdup(pool, &hdr->scheme, &rhs->scheme);
+
+    if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
+	pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm);
+	pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain);
+	pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce);
+	pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque);
+	hdr->challenge.digest.stale = rhs->challenge.digest.stale;
+	pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm);
+	pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop);
+	pj_strdup(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param);
+    } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
+	pj_assert(0);
+	return NULL;
+    } else {
+	pj_assert(0);
+	return NULL;
+    }
+
+    return hdr;
+
+}
+
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
+									     const pjsip_www_authenticate_hdr *rhs)
+{
+    /* This function also serves Proxy-Authenticate header. */
+    pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_msg.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_msg.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_msg.h	(revision 1753)
@@ -0,0 +1,214 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_msg.h 7     6/19/05 6:12p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_MSG_H__
+#define __PJSIP_AUTH_SIP_AUTH_MSG_H__
+
+#include <pjsip/sip_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_MSG_AUTHORIZATION Header Field: Authorization and Proxy-Authorization
+ * @brief Authorization and Proxy-Authorization header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Common credential.
+ */
+struct pjsip_common_credential
+{
+    pj_str_t	realm;
+};
+
+typedef struct pjsip_common_credential pjsip_common_credential;
+
+
+/**
+ * This structure describe credential used in Authorization and
+ * Proxy-Authorization header for digest authentication scheme.
+ */
+struct pjsip_digest_credential
+{
+    pj_str_t	realm;
+    pj_str_t	username;
+    pj_str_t	nonce;
+    pj_str_t	uri;
+    pj_str_t	response;
+    pj_str_t	algorithm;
+    pj_str_t	cnonce;
+    pj_str_t	opaque;
+    pj_str_t	qop;
+    pj_str_t	nc;
+    pj_str_t	other_param;
+};
+
+typedef struct pjsip_digest_credential pjsip_digest_credential;
+
+/**
+ * This structure describe credential used in Authorization and
+ * Proxy-Authorization header for PGP authentication scheme.
+ */
+struct pjsip_pgp_credential
+{
+    pj_str_t	realm;
+    pj_str_t	version;
+    pj_str_t	signature;
+    pj_str_t	signed_by;
+    pj_str_t	nonce;
+};
+
+typedef struct pjsip_pgp_credential pjsip_pgp_credential;
+
+/**
+ * This structure describes SIP Authorization header (and also SIP
+ * Proxy-Authorization header).
+ */
+struct pjsip_authorization_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_authorization_hdr)
+    pj_str_t scheme;
+    union
+    {
+	pjsip_common_credential common;
+	pjsip_digest_credential digest;
+	pjsip_pgp_credential	pgp;
+    } credential;
+};
+
+typedef struct pjsip_authorization_hdr pjsip_authorization_hdr;
+
+/** SIP Proxy-Authorization header shares the same structure as SIP
+    Authorization header.
+ */
+typedef struct pjsip_authorization_hdr pjsip_proxy_authorization_hdr;
+
+/**
+ * Create SIP Authorization header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP Authorization header.
+ */
+PJ_DECL(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool);
+
+/**
+ * Create SIP Proxy-Authorization header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP Proxy-Authorization header.
+ */
+PJ_DECL(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup PJSIP_WWW_AUTH Header Field: Proxy-Authenticate and WWW-Authenticate
+ * @brief Proxy-Authenticate and WWW-Authenticate.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+struct pjsip_common_challenge
+{
+    pj_str_t	realm;
+};
+
+typedef struct pjsip_common_challenge pjsip_common_challenge;
+
+/**
+ * This structure describes authentication challenge used in Proxy-Authenticate
+ * or WWW-Authenticate for digest authentication scheme.
+ */
+struct pjsip_digest_challenge
+{
+    pj_str_t	realm;
+    pj_str_t	domain;
+    pj_str_t	nonce;
+    pj_str_t	opaque;
+    int		stale;
+    pj_str_t	algorithm;
+    pj_str_t	qop;
+    pj_str_t	other_param;
+};
+
+typedef struct pjsip_digest_challenge pjsip_digest_challenge;
+
+/**
+ * This structure describes authentication challenge used in Proxy-Authenticate
+ * or WWW-Authenticate for PGP authentication scheme.
+ */
+struct pjsip_pgp_challenge
+{
+    pj_str_t	realm;
+    pj_str_t	version;
+    pj_str_t	micalgorithm;
+    pj_str_t	pubalgorithm;
+    pj_str_t	nonce;
+};
+
+typedef struct pjsip_pgp_challenge pjsip_pgp_challenge;
+
+/**
+ * This structure describe SIP WWW-Authenticate header (Proxy-Authenticate
+ * header also uses the same structure).
+ */
+struct pjsip_www_authenticate_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_www_authenticate_hdr)
+    pj_str_t	scheme;
+    union
+    {
+	pjsip_common_challenge	common;
+	pjsip_digest_challenge	digest;
+	pjsip_pgp_challenge	pgp;
+    } challenge;
+};
+
+typedef struct pjsip_www_authenticate_hdr pjsip_www_authenticate_hdr;
+typedef struct pjsip_www_authenticate_hdr pjsip_proxy_authenticate_hdr;
+
+
+/**
+ * Create SIP WWW-Authenticate header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP WWW-Authenticate header.
+ */
+PJ_DECL(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool);
+
+/**
+ * Create SIP Proxy-Authenticate header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP Proxy-Authenticate header.
+ */
+PJ_DECL(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_AUTH_SIP_AUTH_MSG_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_parser.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_parser.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_parser.c	(revision 1753)
@@ -0,0 +1,268 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_parser.c 7     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_auth_parser.h>
+#include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pj/string.h>
+#include <pj/except.h>
+
+static pjsip_authorization_hdr*	      parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool);
+static pjsip_proxy_authorization_hdr* parse_hdr_proxy_authorization( pj_scanner *scanner, pj_pool_t *pool);
+static pjsip_www_authenticate_hdr*    parse_hdr_www_authenticate( pj_scanner *scanner, pj_pool_t *pool);
+static pjsip_proxy_authenticate_hdr*  parse_hdr_proxy_authenticate( pj_scanner *scanner, pj_pool_t *pool);
+
+static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred);
+static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred);
+static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal);
+static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal);
+
+const pj_str_t	pjsip_USERNAME_STR =	    { "username", 8 },
+		pjsip_REALM_STR =	    { "realm", 5},
+		pjsip_NONCE_STR =	    { "nonce", 5},
+		pjsip_URI_STR =		    { "uri", 3 },
+		pjsip_RESPONSE_STR =	    { "response", 8 },
+		pjsip_ALGORITHM_STR =	    { "algorithm", 9 },
+		pjsip_DOMAIN_STR =	    { "domain", 6 },
+		pjsip_STALE_STR =	    { "stale", 5},
+		pjsip_QOP_STR =		    { "qop", 3},
+		pjsip_CNONCE_STR =	    { "cnonce", 6},
+		pjsip_OPAQUE_STR =	    { "opaque", 6},
+		pjsip_NC_STR =		    { "nc", 2},
+		pjsip_TRUE_STR =	    { "true", 4},
+		pjsip_QUOTED_TRUE_STR =	    { "\"true\"", 6},
+		pjsip_FALSE_STR =	    { "false", 5},
+		pjsip_QUOTED_FALSE_STR =    { "\"false\"", 7},
+		pjsip_DIGEST_STR =	    { "Digest", 6},
+		pjsip_QUOTED_DIGEST_STR =   { "\"Digest\"", 8},
+		pjsip_PGP_STR =		    { "PGP", 3 },
+		pjsip_QUOTED_PGP_STR =	    { "\"PGP\"", 5 },
+		pjsip_MD5_STR =		    { "md5", 3 },
+		pjsip_QUOTED_MD5_STR =	    { "\"md5\"", 5},
+		pjsip_AUTH_STR =	    { "auth", 4},
+		pjsip_QUOTED_AUTH_STR =	    { "\"auth\"", 6 };
+
+
+static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred)
+{
+    for (;;) {
+	pj_str_t name, value;
+
+	pjsip_parse_param_imp(scanner, &name, &value, PJSIP_PARSE_REMOVE_QUOTE);
+
+	if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) {
+	    cred->username = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
+	    cred->realm = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
+	    cred->nonce = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_URI_STR)) {
+	    cred->uri = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) {
+	    cred->response = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
+	    cred->algorithm = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) {
+	    cred->cnonce = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
+	    cred->opaque = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
+	    cred->qop = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_NC_STR)) {
+	    cred->nc = value;
+
+	} else {
+	    pjsip_concat_param_imp(&cred->other_param, pool, &name, &value, ',');
+	}
+
+	/* Eat comma */
+	if (!pj_scan_is_eof(scanner) && *scanner->current == ',')
+	    pj_scan_get_char(scanner);
+	else
+	    break;
+    }
+}
+
+static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred)
+{
+    PJ_UNUSED_ARG(scanner)
+    PJ_UNUSED_ARG(pool)
+    PJ_UNUSED_ARG(cred)
+
+    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal)
+{
+    for (;;) {
+	pj_str_t name, value;
+
+	pjsip_parse_param_imp(scanner, &name, &value, PJSIP_PARSE_REMOVE_QUOTE);
+
+	if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
+	    chal->realm = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) {
+	    chal->domain = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
+	    chal->nonce = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
+	    chal->opaque = value;
+
+	} else if (!pj_stricmp(&name, &pjsip_STALE_STR)) {
+	    if (!pj_stricmp(&value, &pjsip_TRUE_STR) || !pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR))
+		chal->stale = 1;
+
+	} else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
+	    chal->algorithm = value;
+
+
+	} else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
+	    chal->qop = value;
+
+	} else {
+	    pjsip_concat_param_imp(&chal->other_param, pool, &name, &value, ',');
+	}
+
+	/* Eat comma */
+	if (!pj_scan_is_eof(scanner) && *scanner->current == ',')
+	    pj_scan_get_char(scanner);
+	else
+	    break;
+    }
+}
+
+static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal)
+{
+    PJ_UNUSED_ARG(scanner)
+    PJ_UNUSED_ARG(pool)
+    PJ_UNUSED_ARG(chal)
+
+    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool, 
+					 pjsip_authorization_hdr *hdr)
+{
+    if (*scanner->current == '"') {
+	pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
+	hdr->scheme.ptr++;
+	hdr->scheme.slen -= 2;
+    } else {
+	pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->scheme);
+    }
+
+    if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+
+	parse_digest_credential(scanner, pool, &hdr->credential.digest);
+
+    } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
+
+	parse_pgp_credential( scanner, pool, &hdr->credential.pgp);
+
+    } else {
+	PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+    }
+
+    pjsip_parse_end_hdr_imp( scanner );
+}
+
+static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool, 
+					pjsip_www_authenticate_hdr *hdr)
+{
+    if (*scanner->current == '"') {
+	pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
+	hdr->scheme.ptr++;
+	hdr->scheme.slen -= 2;
+    } else {
+	pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->scheme);
+    }
+
+    if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+
+	parse_digest_challenge(scanner, pool, &hdr->challenge.digest);
+
+    } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
+
+	parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp);
+
+    } else {
+	PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+    }
+
+    pjsip_parse_end_hdr_imp( scanner );
+}
+
+
+static pjsip_authorization_hdr *parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(pool);
+    int_parse_hdr_authorization(scanner, pool, hdr);
+    return hdr;
+}
+
+static pjsip_proxy_authorization_hdr *parse_hdr_proxy_authorization( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_proxy_authorization_hdr *hdr = pjsip_proxy_authorization_hdr_create(pool);
+    int_parse_hdr_authorization(scanner, pool, hdr);
+    return hdr;
+}
+
+static pjsip_www_authenticate_hdr *parse_hdr_www_authenticate( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_www_authenticate_hdr *hdr = pjsip_www_authenticate_hdr_create(pool);
+    int_parse_hdr_authenticate(scanner, pool, hdr);
+    return hdr;
+}
+
+static pjsip_proxy_authenticate_hdr *parse_hdr_proxy_authenticate( pj_scanner *scanner, pj_pool_t *pool)
+{
+    pjsip_proxy_authenticate_hdr *hdr = pjsip_proxy_authenticate_hdr_create(pool);
+    int_parse_hdr_authenticate(scanner, pool, hdr);
+    return hdr;
+}
+
+
+PJ_DEF(void) pjsip_auth_init_parser()
+{
+    pjsip_register_hdr_parser( "Authorization", NULL, (pjsip_parse_hdr_func*) &parse_hdr_authorization);
+    pjsip_register_hdr_parser( "Proxy-Authorization", NULL, (pjsip_parse_hdr_func*) &parse_hdr_proxy_authorization);
+    pjsip_register_hdr_parser( "WWW-Authenticate", NULL, (pjsip_parse_hdr_func*) &parse_hdr_www_authenticate);
+    pjsip_register_hdr_parser( "Proxy-Authenticate", NULL, (pjsip_parse_hdr_func*) &parse_hdr_proxy_authenticate);
+}
+
+PJ_DEF(void) pjsip_auth_deinit_parser()
+{
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_parser.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_parser.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_auth.old/sip_auth_parser.h	(revision 1753)
@@ -0,0 +1,90 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_parser.h 7     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_PARSER_H__
+#define __PJSIP_AUTH_SIP_AUTH_PARSER_H__
+
+/**
+ * @file pjsip_auth_parser.h
+ * @brief SIP Authorization Parser Module.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_AUTH_PARSER_MODULE Authorization Parser Module
+ * @ingroup PJSIP_AUTH
+ * @{
+ */
+
+/**
+ * Initialize and register authorization parser module.
+ * This will register parser handler for various Authorization related headers
+ * such as Authorization, WWW-Authenticate, Proxy-Authorizization, and 
+ * Proxy-Authenticate headers.
+ */
+PJ_DECL(void) pjsip_auth_init_parser();
+
+/**
+ * DeInitialize authorization parser module.
+ */
+PJ_DECL(void) pjsip_auth_deinit_parser();
+
+
+extern const pj_str_t	pjsip_USERNAME_STR,
+			pjsip_REALM_STR,
+			pjsip_NONCE_STR,
+			pjsip_URI_STR,
+			pjsip_RESPONSE_STR,
+			pjsip_ALGORITHM_STR,
+			pjsip_DOMAIN_STR,
+			pjsip_STALE_STR,
+			pjsip_QOP_STR,
+			pjsip_CNONCE_STR,
+			pjsip_OPAQUE_STR,
+			pjsip_NC_STR,
+			pjsip_TRUE_STR,
+			pjsip_FALSE_STR,
+			pjsip_DIGEST_STR,
+			pjsip_PGP_STR,
+			pjsip_MD5_STR,
+			pjsip_AUTH_STR;
+/*
+extern const pj_str_t	pjsip_QUOTED_TRUE_STR,
+			pjsip_QUOTED_FALSE_STR,
+			pjsip_QUOTED_DIGEST_STR,
+			pjsip_QUOTED_PGP_STR,
+			pjsip_QUOTED_MD5_STR,
+			pjsip_QUOTED_AUTH_STR;
+*/
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_AUTH_SIP_AUTH_PARSER_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_core.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_core.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_core.h	(revision 1753)
@@ -0,0 +1,41 @@
+/* $Header: /pjproject/pjsip/src/pjsip_core.h 2     6/19/05 6:12p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_CORE_H__
+#define __PJSIP_CORE_H__
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_auth.h>
+
+#endif	/* __PJSIP_CORE_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_dialog.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_dialog.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_dialog.c	(revision 1753)
@@ -0,0 +1,1820 @@
+/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_dialog.c 28    12/02/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_ua.h>
+#include <pjsip_mod_ua/sip_ua_private.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_parser.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/guid.h>
+#include <pj/except.h>
+#include <pj/pool.h>
+
+/* TLS to keep dialog lock record (initialized by UA) */
+int pjsip_dlg_lock_tls_id;
+
+struct dialog_lock_data
+{
+    struct dialog_lock_data *prev;
+    pjsip_dlg	    *dlg;
+    int			     is_alive;
+};
+
+/*
+ * Static function prototypes.
+ */
+static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
+				      pjsip_dlg *dlg,
+				      const pjsip_method *method,
+				      int cseq );
+static int  dlg_on_all_state_pre(  pjsip_dlg *dlg, 
+				   pjsip_transaction *tsx,
+				   pjsip_event *event);
+static int  dlg_on_all_state_post( pjsip_dlg *dlg, 
+				   pjsip_transaction *tsx,
+				   pjsip_event *event);
+static int  dlg_on_state_null( pjsip_dlg *dlg, 
+			       pjsip_transaction *tsx,
+			       pjsip_event *event);
+static int  dlg_on_state_incoming( pjsip_dlg *dlg, 
+				   pjsip_transaction *tsx,
+				   pjsip_event *event);
+static int  dlg_on_state_calling( pjsip_dlg *dlg, 
+				  pjsip_transaction *tsx,
+				  pjsip_event *event);
+static int  dlg_on_state_proceeding( pjsip_dlg *dlg, 
+				     pjsip_transaction *tsx,
+				     pjsip_event *event);
+static int  dlg_on_state_proceeding_caller( pjsip_dlg *dlg, 
+					    pjsip_transaction *tsx,
+					    pjsip_event *event);
+static int  dlg_on_state_proceeding_callee( pjsip_dlg *dlg, 
+					    pjsip_transaction *tsx,
+					    pjsip_event *event);
+static int  dlg_on_state_connecting( pjsip_dlg *dlg, 
+				     pjsip_transaction *tsx,
+				     pjsip_event *event);
+static int  dlg_on_state_established( pjsip_dlg *dlg, 
+				      pjsip_transaction *tsx,
+				      pjsip_event *event);
+static int  dlg_on_state_disconnected( pjsip_dlg *dlg, 
+				       pjsip_transaction *tsx,
+				       pjsip_event *event);
+static int  dlg_on_state_terminated( pjsip_dlg *dlg, 
+				     pjsip_transaction *tsx,
+				     pjsip_event *event);
+
+/*
+ * Dialog state handlers.
+ */
+static int  (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *,
+				    pjsip_event *) = 
+{
+    &dlg_on_state_null,
+    &dlg_on_state_incoming,
+    &dlg_on_state_calling,
+    &dlg_on_state_proceeding,
+    &dlg_on_state_connecting,
+    &dlg_on_state_established,
+    &dlg_on_state_disconnected,
+    &dlg_on_state_terminated,
+};
+
+/*
+ * Dialog state names.
+ */
+static const char* dlg_state_names[] = 
+{
+    "STATE_NULL",
+    "STATE_INCOMING",
+    "STATE_CALLING",
+    "STATE_PROCEEDING",
+    "STATE_CONNECTING",
+    "STATE_ESTABLISHED",
+    "STATE_DISCONNECTED",
+    "STATE_TERMINATED",
+};
+
+
+/*
+ * Get the dialog string state, normally for logging purpose.
+ */
+const char *pjsip_dlg_state_str(pjsip_dlg_state_e state)
+{
+    return dlg_state_names[state];
+}
+
+/* Lock dialog mutex. */
+static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
+{
+    struct dialog_lock_data *prev;
+
+    pj_mutex_lock(dlg->mutex);
+    prev = pj_thread_local_get(pjsip_dlg_lock_tls_id);
+    lck->prev = prev;
+    lck->dlg = dlg;
+    lck->is_alive = 1;
+    pj_thread_local_set(pjsip_dlg_lock_tls_id, lck);
+}
+
+/* Carefully unlock dialog mutex, protect against situation when the dialog
+ * has already been destroyed.
+ */
+static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
+{
+    pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck);
+    pj_assert(dlg == lck->dlg);
+
+    pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev);
+    if (lck->is_alive)
+	pj_mutex_unlock(dlg->mutex);
+
+    return lck->is_alive ? 0 : -1;
+}
+
+/*
+ * This is called by dialog's FSM to change dialog's state.
+ */
+static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state,
+			   pjsip_event *event)
+{
+    PJ_UNUSED_ARG(event);
+
+    PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)", 
+	       pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state),
+	       event ? pjsip_event_str(event->type) : "", 
+	       event ? pjsip_event_str(event->src_type) : "",
+	       event ? event->src.data : NULL));
+
+    dlg->state = state;
+    dlg->handle_tsx_event = dlg_state_handlers[state];
+}
+
+/*
+ * Invoke dialog's callback.
+ * This function is called by dialog's FSM, and interpret the event and call
+ * the corresponding callback registered by application.
+ */
+static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
+				   pjsip_event *event )
+{
+    pjsip_dlg_callback *cb = dlg->ua->dlg_cb;
+    if (!cb) {
+	PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered)."));
+	return;
+    }
+
+    /* Low level event: call the all-events callback. */
+    if (cb->on_all_events) {
+	(*cb->on_all_events)(dlg, dlg_event, event);
+    }
+
+    /* Low level event: call the tx/rx callback if this is a tx/rx event. */
+    if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx)
+    {
+	(*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data);
+    }
+    else if (event->type == PJSIP_EVENT_TX_MSG && 
+	event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg) 
+    {
+	(*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata);
+    } 
+    else if (event->type == PJSIP_EVENT_RX_MSG &&
+	     event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) {
+	(*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata);
+    }
+
+    /* Now trigger high level events. 
+     * High level event should only occurs when dialog's state has changed,
+     * except for on_provisional, which may be called multiple times whenever
+     * response message is sent
+     */
+    if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING &&
+	(event->type== PJSIP_EVENT_TSX_STATE_CHANGED) && 
+	event->obj.tsx == dlg->invite_tsx) 
+    {
+	/* Sent/received provisional responses. */
+	if (cb->on_provisional)
+	    (*cb->on_provisional)(dlg, event->obj.tsx, event);
+    }
+
+    if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) {
+	if (cb->on_mid_call_events)
+	    (*cb->on_mid_call_events)(dlg, event);
+	return;
+    }
+
+    if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED)
+	return;
+
+    if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) {
+
+	/* New incoming dialog. */
+	pj_assert (event->src_type == PJSIP_EVENT_RX_MSG);
+	(*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata);
+
+    } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) {
+
+	/* Dialog has just sent the first INVITE. */
+	if (cb->on_calling) {
+	    (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata);
+	}
+
+    } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) {
+
+	if (cb->on_disconnected)
+	    (*cb->on_disconnected)(dlg, event);
+
+    } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) {
+
+	if (cb->on_terminated)
+	    (*cb->on_terminated)(dlg);
+
+	pjsip_ua_destroy_dialog(dlg);
+
+    } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) {
+
+	if (cb->on_connecting)
+	    (*cb->on_connecting)(dlg, event);
+
+    } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
+
+	if (cb->on_established)
+	    (*cb->on_established)(dlg, event);
+    }
+}
+
+/*
+ * This callback receives event from the transaction layer (via User Agent),
+ * or from dialog timer (for 200/INVITE or ACK retransmission).
+ */
+void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, 
+			     pjsip_transaction *tsx, 
+			     pjsip_event *event)
+{
+    int status = 0;
+    struct dialog_lock_data lck;
+
+    PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)", 
+			 pjsip_dlg_state_str(dlg->state),
+			 pjsip_event_str(event->type),
+			 pjsip_event_str(event->src_type)));
+
+
+    lock_dialog(dlg, &lck);
+
+    status = dlg_on_all_state_pre( dlg, tsx, event);
+
+    if (status==0) {
+	status = (*dlg->handle_tsx_event)(dlg, tsx, event);
+    }
+    if (status==0) {
+	status = dlg_on_all_state_post( dlg, tsx, event);
+    }
+    
+    unlock_dialog(dlg, &lck);
+}
+
+/*
+ * This function contains common processing in all states, and it is called
+ * before the FSM is invoked.
+ */
+static int  dlg_on_all_state_pre( pjsip_dlg *dlg, 
+				  pjsip_transaction *tsx,
+				  pjsip_event *event)
+{
+    PJ_UNUSED_ARG(event)
+
+    if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED)
+	return 0;
+
+    if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING || 
+	tsx->state==PJSIP_TSX_STATE_TRYING)) 
+    {
+	++dlg->pending_tsx_count;
+
+    } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) 
+    {
+	--dlg->pending_tsx_count;
+	if (tsx == dlg->invite_tsx)
+	    dlg->invite_tsx = NULL;
+    }
+
+    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	tsx->handle_ack = 1;
+    }
+    return 0;
+}
+
+
+/*
+ * This function contains common processing in all states, and it is called
+ * after the FSM is invoked.
+ */
+static int  dlg_on_all_state_post( pjsip_dlg *dlg, 
+				   pjsip_transaction *tsx,
+				   pjsip_event *event)
+{
+    PJ_UNUSED_ARG(event)
+
+    if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) {
+	if (dlg->pending_tsx_count == 0 &&
+	    dlg->state != PJSIP_DIALOG_STATE_CONNECTING &&
+	    dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED &&
+	    dlg->state != PJSIP_DIALOG_STATE_TERMINATED) 
+	{
+	    dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+	    dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+	    return -1;
+	}
+    }
+
+    return 0;
+}
+
+
+/*
+ * Internal function to initialize dialog, contains common initialization
+ * for both UAS and UAC dialog.
+ */
+static pj_status_t dlg_init( pjsip_dlg *dlg )
+{
+    /* Init mutex. */
+    dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
+    if (!dlg->mutex) {
+	PJ_PERROR((dlg->obj_name, "pj_mutex_create()"));
+	return -1;
+    }
+
+    /* Init route-set (Initially empty) */
+    pj_list_init(&dlg->route_set);
+
+    /* Init auth credential list. */
+    pj_list_init(&dlg->auth_sess);
+
+    return PJ_OK;
+}
+
+/*
+ * This one is called just before dialog is destroyed.
+ * It is called while mutex is held.
+ */
+PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg )
+{
+    struct dialog_lock_data *lck;
+
+    //PJ_TODO(CHECK_THIS);
+    pj_assert(dlg->pending_tsx_count == 0);
+
+    /* Mark dialog as dead. */
+    lck = pj_thread_local_get(pjsip_dlg_lock_tls_id);
+    while (lck) {
+	if (lck->dlg == dlg)
+	    lck->is_alive = 0;
+	lck = lck->prev;
+    }
+}
+
+/*
+ * Initialize dialog from the received request.
+ * This is an internal function which is called by the User Agent (sip_ua.c), 
+ * and it will initialize most of dialog's properties with values from the
+ * received message.
+ */
+pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata )
+{
+    pjsip_msg *msg = rdata->msg;
+    pjsip_to_hdr *to;
+    pjsip_contact_hdr *contact;
+    pjsip_name_addr *name_addr;
+    pjsip_url *url;
+    unsigned flag;
+    pjsip_event event;
+
+    pj_assert(dlg && rdata);
+
+    PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata));
+
+    /* Must be an INVITE request. */
+    pj_assert(msg->type == PJSIP_REQUEST_MSG && 
+	      msg->line.req.method.id == PJSIP_INVITE_METHOD);
+
+    /* Init general dialog data. */
+    if (dlg_init(dlg) != PJ_OK) {
+	return -1;
+    }
+
+    /* Get the To header. */
+    to = rdata->to;
+
+    /* Copy URI in the To header as our local URI. */
+    dlg->local.info = pjsip_hdr_clone( dlg->pool, to);
+
+    /* Set tag in the local info. */
+    dlg->local.info->tag = dlg->local.tag;
+
+    /* Create local Contact to be advertised in the response.
+     * At the moment, just copy URI from the local URI as our contact.
+     */
+    dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
+    dlg->local.contact->star = 0;
+    name_addr = (pjsip_name_addr *)dlg->local.info->uri;
+    dlg->local.contact->uri = (pjsip_uri*) name_addr;
+    url = (pjsip_url*) name_addr->uri;
+    //url->port = rdata->via->sent_by.port;
+    //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) );
+
+    /* Save remote URI. */
+    dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from );
+    pjsip_fromto_set_to( dlg->remote.info );
+
+    /* Save remote Contact. */
+    contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+    if (contact) {
+    	dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
+    } else {
+	PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s", 
+		  pj_sockaddr_get_str_addr(&rdata->addr)));
+   	dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool );
+	dlg->remote.contact->uri = dlg->remote.info->uri;
+    }
+
+    /* Set initial remote target. */
+    dlg->remote.target = dlg->remote.contact->uri;
+
+    /* Save Call-ID. */
+    dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
+    pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id );
+
+    /* Initialize local CSeq and save remote CSeq.*/
+    dlg->local.cseq = rdata->timestamp.sec & 0xFFFF;
+    dlg->remote.cseq = rdata->cseq->cseq;
+
+    /* Secure? */
+    flag = pjsip_transport_get_flag(rdata->transport);
+    dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
+
+    /* Initial state is NULL. */
+    event.type = event.src_type = PJSIP_EVENT_RX_MSG;
+    event.src.rdata = rdata;
+    dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
+
+    PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete",  rdata));
+    return PJ_OK;
+}
+
+/*
+ * Set the contact details.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
+					   const pj_str_t *contact )
+{
+    pjsip_uri *local_uri;
+    pj_str_t tmp;
+
+    pj_strdup_with_null(dlg->pool, &tmp, contact);
+    local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen, 
+				 PJSIP_PARSE_URI_AS_NAMEADDR);
+    if (local_uri == NULL) {
+	PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI"));
+	return -1;
+    }
+
+    dlg->local.contact->star = 0;
+    dlg->local.contact->uri = local_uri;
+    return 0;
+}
+
+/*
+ * Set route set.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
+					     const pjsip_route_hdr *route_set )
+{
+    pjsip_route_hdr *hdr;
+
+    pj_list_init(&dlg->route_set);
+    hdr = route_set->next;
+    while (hdr != route_set) {
+	pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr);
+	pj_list_insert_before( &dlg->route_set, cloned);
+	hdr = hdr->next;
+    }
+    return 0;
+}
+
+/*
+ * Set route set without cloning the header.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
+						pjsip_route_hdr *route_set)
+{
+    pjsip_route_hdr *hdr;
+
+    pj_list_init(&dlg->route_set);
+    hdr = route_set->next;
+    while (hdr != route_set) {
+	pj_list_insert_before( &dlg->route_set, hdr);
+	hdr = hdr->next;
+    }
+    return 0;
+}
+
+/*
+ * Application calls this function when it wants to initiate an outgoing
+ * dialog (incoming dialogs are created automatically by UA when it receives
+ * INVITE, by calling pjsip_dlg_init_from_rdata()).
+ * This function should initialize most of the dialog's properties.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
+				    const pj_str_t *c_local_info,
+				    const pj_str_t *c_remote_info,
+				    const pj_str_t *c_target)
+{
+    pj_time_val tv;
+    pjsip_event event;
+    pj_str_t buf;
+
+    if (!dlg || !c_local_info || !c_remote_info) {
+	pj_assert(dlg && c_local_info && c_remote_info);
+	return -1;
+    }
+
+    PJ_LOG(5, (dlg->obj_name, "initializing"));
+
+    /* Init general dialog */
+    if (dlg_init(dlg) != PJ_OK) {
+	return -1;
+    }
+
+    /* Duplicate local info. */
+    pj_strdup_with_null( dlg->pool, &buf, c_local_info);
+
+    /* Build local URI. */
+    dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen, 
+				        PJSIP_PARSE_URI_AS_NAMEADDR);
+    if (dlg->local.target == NULL) {
+	PJ_LOG(2, (dlg->obj_name, 
+		   "pjsip_dlg_init: invalid local URI %s", buf.ptr));
+	return -1;
+    }
+
+    /* Set local URI. */
+    dlg->local.info = pjsip_from_hdr_create(dlg->pool);
+    dlg->local.info->uri = dlg->local.target;
+    dlg->local.info->tag = dlg->local.tag;
+
+    /* Create local Contact to be advertised in the response. */
+    dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
+    dlg->local.contact->star = 0;
+    dlg->local.contact->uri = dlg->local.target;
+
+    /* Set remote URI. */
+    dlg->remote.info = pjsip_to_hdr_create(dlg->pool);
+
+    /* Duplicate to buffer. */
+    pj_strdup_with_null( dlg->pool, &buf, c_remote_info);
+
+    /* Build remote info. */
+    dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen,
+					     PJSIP_PARSE_URI_AS_NAMEADDR);
+    if (dlg->remote.info->uri == NULL) {
+	PJ_LOG(2, (dlg->obj_name, 
+		   "pjsip_dlg_init: invalid remote URI %s", buf.ptr));
+	return -1;
+    }
+
+    /* Set remote Contact initially equal to the remote URI. */
+    dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
+    dlg->remote.contact->star = 0;
+    dlg->remote.contact->uri = dlg->remote.info->uri;
+
+    /* Set initial remote target. */
+    if (c_target != NULL) {
+	pj_strdup_with_null( dlg->pool, &buf, c_target);
+	dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0);
+	if (dlg->remote.target == NULL) {
+	    PJ_LOG(2, (dlg->obj_name, 
+		       "pjsip_dlg_init: invalid remote target %s", buf.ptr));
+	    return -1;
+	}
+    } else {
+	dlg->remote.target = dlg->remote.info->uri;
+    }
+
+    /* Create globally unique Call-ID */
+    dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
+    pj_create_unique_string( dlg->pool, &dlg->call_id->id );
+
+    /* Local and remote CSeq */
+    pj_gettimeofday(&tv);
+    dlg->local.cseq = tv.sec & 0xFFFF;
+    dlg->remote.cseq = 0;
+
+    /* Initial state is NULL. */
+    event.type = event.src_type = PJSIP_EVENT_TX_MSG;
+    event.src.data = NULL;
+    dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
+
+    /* Done. */
+    PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s",
+	       pjsip_role_name(dlg->role), 
+	       c_local_info->slen, c_local_info->ptr,
+	       c_remote_info->slen, c_remote_info->ptr));
+
+    return PJ_OK;
+}
+
+/*
+ * Set credentials.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_credentials(  pjsip_dlg *dlg,
+					        int count,
+						const pjsip_cred_info *cred)
+{
+    if (count > 0) {
+	dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info));
+	pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info));
+    }
+    dlg->cred_count = count;
+    return 0;
+}
+
+/*
+ * Create a new request within dialog (i.e. after the dialog session has been
+ * established). The construction of such requests follows the rule in 
+ * RFC3261 section 12.2.1.
+ */
+static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
+				      pjsip_dlg *dlg,
+				      const pjsip_method *method,
+				      int cseq )
+{
+    pjsip_tx_data *tdata;
+    pjsip_contact_hdr *contact;
+    pjsip_route_hdr *route, *end_list;
+
+    /* Contact Header field.
+     * Contact can only be present in requests that establish dialog (in the 
+     * core SIP spec, only INVITE).
+     */
+    if (method->id == PJSIP_INVITE_METHOD)
+	contact = dlg->local.contact;
+    else
+	contact = NULL;
+
+    tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt,
+						 method,
+						 dlg->remote.target,
+						 dlg->local.info,
+						 dlg->remote.info,
+						 contact,
+						 dlg->call_id,
+						 cseq,
+						 NULL);
+    if (!tdata) {
+	PJ_THROW(1);
+	return;
+    }
+
+    /* Just copy dialog route-set to Route header. 
+     * The transaction will do the processing as specified in Section 12.2.1
+     * of RFC 3261 in function tsx_process_route() in sip_transaction.c.
+     */
+    route = dlg->route_set.next;
+    end_list = &dlg->route_set;
+    for (; route != end_list; route = route->next ) {
+	pjsip_route_hdr *r;
+	r = pjsip_hdr_shallow_clone( tdata->pool, route );
+	pjsip_routing_hdr_set_route(r);
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r);
+    }
+
+    /* Copy authorization headers. */
+    pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess,
+			 dlg->cred_count, dlg->cred_info);
+
+    *p_tdata = tdata;
+}
+
+
+/*
+ * This function is called by application to create new outgoing request
+ * message for this dialog. After the request is created, application can
+ * modify the message (such adding headers), and eventually send the request.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
+						 const pjsip_method *method,
+						 int cseq)
+{
+    PJ_USE_EXCEPTION;
+    struct dialog_lock_data lck;
+    pjsip_tx_data *tdata = NULL;
+
+    pj_assert(dlg != NULL && method != NULL);
+    if (!dlg || !method) {
+	return NULL;
+    }
+
+    PJ_LOG(5, (dlg->obj_name, "Creating request"));
+
+    /* Lock dialog. */
+    lock_dialog(dlg, &lck);
+
+    /* Use outgoing CSeq and increment it by one. */
+    if (cseq < 0)
+	cseq = dlg->local.cseq + 1;
+
+    PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d", 
+			      method->name.slen, method->name.ptr, cseq));
+
+    /* Create the request. */
+    PJ_TRY {
+	dlg_create_request_throw(&tdata, dlg, method, cseq);
+	PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name));
+    }
+    PJ_DEFAULT {
+	/* Failed! Delete transmit data. */
+	if (tdata) {
+	    pjsip_tx_data_dec_ref( tdata );
+	    tdata = NULL;
+	}
+    }
+    PJ_END;
+
+    /* Unlock dialog. */
+    unlock_dialog(dlg, &lck);
+
+    return tdata;
+}
+
+/*
+ * Sends request.
+ * Select the transport for the request message
+ */
+static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata )
+{
+    pjsip_transaction *tsx;
+    pj_status_t status = PJ_OK;
+    struct dialog_lock_data lck;
+
+    pj_assert(dlg != NULL && tdata != NULL);
+    if (!dlg || !tdata) {
+	return -1;
+    }
+
+    PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name));
+
+    /* Lock dialog. */
+    lock_dialog(dlg, &lck);
+
+    /* Create a new transaction. */
+    tsx = pjsip_endpt_create_tsx( dlg->ua->endpt );
+    if (!tsx) {
+	unlock_dialog(dlg, &lck);
+	return -1;
+    }
+
+    PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name));
+
+    /* Initialize transaction */
+    tsx->module_data[dlg->ua->mod_id] = dlg;
+    status = pjsip_tsx_init_uac( tsx, tdata );
+    if (status != PJ_OK) {
+	unlock_dialog(dlg, &lck);
+	pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx );
+	return -1;
+    }
+    pjsip_endpt_register_tsx( dlg->ua->endpt, tsx );
+
+    /* Start the transaction. */
+    pjsip_tsx_on_tx_msg(tsx, tdata);
+
+    /* Unlock dialog. */
+    unlock_dialog(dlg, &lck);
+
+    return status;
+}
+
+/*
+ * This function can be called by application to send ANY outgoing message
+ * to remote party. 
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata )
+{
+    pj_status_t status;
+    int tsx_status;
+    struct dialog_lock_data lck;
+
+    pj_assert(dlg != NULL && tdata != NULL);
+    if (!dlg || !tdata) {
+	return -1;
+    }
+
+    lock_dialog(dlg, &lck);
+
+    if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+	int request_cseq;
+	pjsip_msg *msg = tdata->msg;
+	pjsip_cseq_hdr *cseq_hdr;
+
+	switch (msg->line.req.method.id) {
+	case PJSIP_CANCEL_METHOD:
+
+	    /* Check the INVITE transaction state. */
+	    tsx_status = dlg->invite_tsx->status_code;
+	    if (tsx_status >= 200) {
+		/* Already terminated. Can't cancel. */
+		status = -1;
+		goto on_return;
+	    }
+
+	    /* If we've got provisional response, then send CANCEL and wait for
+	     * the response to INVITE to arrive. Otherwise just send CANCEL and
+	     * terminate the INVITE.
+	     */
+	    if (tsx_status < 100) {
+		pjsip_tsx_terminate( dlg->invite_tsx, 
+				     PJSIP_SC_REQUEST_TERMINATED);
+		status = 0;
+		goto on_return;
+	    }
+
+	    status = 0;
+	    request_cseq = dlg->invite_tsx->cseq;
+	    break;
+
+	case PJSIP_ACK_METHOD:
+	    /* Sending ACK outside of transaction is not supported at present! */
+	    //pj_assert(0);
+	    status = 0;
+	    request_cseq = dlg->local.cseq;
+	    break;
+
+	case PJSIP_INVITE_METHOD:
+	    /* For an initial INVITE, reset dialog state to NULL so we get
+	     * 'normal' UAC notifications such as on_provisional(), etc.
+	     * Initial INVITE is the request that is sent when the dialog has
+	     * not been established yet. It's not necessarily the first INVITE
+	     * sent, as when the Authorization fails, subsequent INVITE are also
+	     * considered as an initial INVITE.
+	     */
+	    if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
+		/* Set state to NULL. */
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL);
+
+	    } else {
+		/* This is a re-INVITE */
+	    }
+	    status = 0;
+	    request_cseq = dlg->local.cseq + 1;
+	    break;
+
+	default:
+	    status = 0;
+	    request_cseq = dlg->local.cseq + 1;
+	    break;
+	}
+
+	if (status != 0)
+	    goto on_return;
+
+	/* Update dialog's local CSeq, if necessary. */
+	if (request_cseq != dlg->local.cseq)
+	    dlg->local.cseq = request_cseq;
+
+	/* Update CSeq header in the request. */
+	cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
+							 PJSIP_H_CSEQ, NULL);
+	pj_assert(cseq_hdr != NULL);
+
+	/* Update the CSeq */
+	cseq_hdr->cseq = request_cseq;
+
+	/* Force the whole message to be re-printed. */
+	pjsip_tx_data_invalidate_msg( tdata );
+
+	/* Now send the request. */
+	status = dlg_send_request(dlg, tdata);
+
+    } else {
+	/*
+	 * This is only valid for sending response to INVITE!
+	 */
+	pjsip_cseq_hdr *cseq_hdr;
+
+	if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) {
+	    status = -1;
+	    goto on_return;
+	}
+
+	cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
+							 PJSIP_H_CSEQ, NULL);
+	pj_assert(cseq_hdr);
+
+	if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) {
+	    status = -1;
+	    goto on_return;
+	}
+
+	pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq);
+	
+	pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata);
+	status = 0;
+    }
+
+on_return:
+    /* Unlock dialog. */
+    unlock_dialog(dlg, &lck);
+   
+    /* Whatever happen delete the message. */
+    pjsip_tx_data_dec_ref( tdata );
+
+    return status;
+}
+
+/*
+ * Sends outgoing invitation.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg )
+{
+    pjsip_method method;
+    struct dialog_lock_data lck;
+    const pjsip_allow_hdr *allow_hdr;
+    pjsip_tx_data *tdata;
+
+    pj_assert(dlg != NULL);
+    if (!dlg) {
+	return NULL;
+    }
+
+    PJ_LOG(4, (dlg->obj_name, "request to send invitation"));
+
+    /* Lock dialog. */
+    lock_dialog(dlg, &lck);
+
+    /* Create request. */
+    pjsip_method_set( &method, PJSIP_INVITE_METHOD);
+    tdata = pjsip_dlg_create_request( dlg, &method, -1 );
+    if (tdata == NULL) {
+	unlock_dialog(dlg, &lck);
+	return NULL;
+    }
+
+    /* Invite SHOULD contain "Allow" header. */
+    allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
+    if (allow_hdr) {
+	pjsip_msg_add_hdr( tdata->msg, 
+			   pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
+    }
+
+    /* Unlock dialog. */
+    unlock_dialog(dlg, &lck);
+
+    return tdata;
+}
+
+/*
+ * Cancel pending outgoing dialog invitation. 
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg )
+{
+    pjsip_tx_data *tdata = NULL;
+    struct dialog_lock_data lck;
+
+    pj_assert(dlg != NULL);
+    if (!dlg) {
+	return NULL;
+    }
+
+    PJ_LOG(4, (dlg->obj_name, "request to cancel invitation"));
+
+    lock_dialog(dlg, &lck);
+
+    /* Check the INVITE transaction. */
+    if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) {
+	PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
+				  "no INVITE transaction found"));
+	goto on_return;
+    }
+
+    /* Construct the CANCEL request. */
+    tdata = pjsip_endpt_create_cancel( dlg->ua->endpt,
+				       dlg->invite_tsx->last_tx );
+    if (tdata == NULL) {
+	PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
+				  "unable to construct request"));
+	goto on_return;
+    }
+
+    /* Add reference counter to tdata. */
+    pjsip_tx_data_add_ref(tdata);
+
+on_return:
+    unlock_dialog(dlg, &lck);
+    return tdata;
+}
+
+
+/*
+ * Answer incoming dialog invitation, with either provisional responses
+ * or a final response.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code )
+{
+    pjsip_tx_data *tdata = NULL;
+    pjsip_msg *msg;
+    struct dialog_lock_data lck;
+
+    pj_assert(dlg != NULL);
+    if (!dlg) {
+	return NULL;
+    }
+
+    PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code));
+
+    /* Lock dialog. */
+    lock_dialog(dlg, &lck);
+
+    /* Must have pending INVITE. */
+    if (dlg->invite_tsx == NULL) {
+	PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found"));
+	goto on_return;
+    }
+    /* Must be UAS. */
+    if (dlg->role != PJSIP_ROLE_UAS) {
+	PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS"));
+	goto on_return;
+    }
+    /* Must have not answered with final response before. */
+    if (dlg->invite_tsx->status_code >= 200) {
+	PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated "
+				  "with status %d", dlg->invite_tsx->status_code));
+	goto on_return;
+    }
+
+    /* Get transmit data and the message. 
+     * We will rewrite the message with a new status code.
+     */
+    tdata = dlg->invite_tsx->last_tx;
+    msg = tdata->msg;
+
+    /* Set status code and reason phrase. */
+    if (code < 100 || code >= 700) code = 500;
+    msg->line.status.code = code;
+    msg->line.status.reason = *pjsip_get_status_text(code);
+
+    /* For 2xx response, Contact and Record-Route must be added. */
+    if (PJSIP_IS_STATUS_IN_CLASS(code,200)) {
+	const pjsip_allow_hdr *allow_hdr;
+
+	if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) {
+	    pjsip_contact_hdr *contact;
+	    contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact);
+	    pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact );
+	}
+
+	/* 2xx response MUST contain "Allow" header. */
+	allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
+	if (allow_hdr) {
+	    pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
+	}
+    }
+
+    /* for all but 100 responses, To-tag must be set. */
+    if (code != 100) {
+	pjsip_to_hdr *to;
+	to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL);
+	to->tag = dlg->local.tag;
+    }
+
+    /* Reset packet buffer. */
+    pjsip_tx_data_invalidate_msg(tdata);
+
+    /* Add reference counter */
+    pjsip_tx_data_add_ref(tdata);
+
+on_return:
+
+    /* Unlock dialog. */
+    unlock_dialog(dlg, &lck);
+
+    return tdata;
+}
+
+
+/*
+ * Send BYE request to terminate the dialog's session.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg )
+{
+    pjsip_method method;
+    struct dialog_lock_data lck;
+    pjsip_tx_data *tdata;
+
+    if (!dlg) {
+	pj_assert(dlg != NULL);
+	return NULL;
+    }
+
+    PJ_LOG(4, (dlg->obj_name, "request to terminate session"));
+
+    lock_dialog(dlg, &lck);
+
+    pjsip_method_set( &method, PJSIP_BYE_METHOD);
+    tdata = pjsip_dlg_create_request( dlg, &method, -1 );
+
+    unlock_dialog(dlg, &lck);
+
+    return tdata;
+}
+
+/*
+ * High level function to disconnect dialog's session. Depending on dialog's 
+ * state, this function will either send CANCEL, final response, or BYE to
+ * trigger the disconnection. A status code must be supplied, which will be
+ * sent if dialog will be transmitting a final response to INVITE.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, 
+					     int status_code )
+{
+    pjsip_tx_data *tdata = NULL;
+
+    pj_assert(dlg != NULL);
+    if (!dlg) {
+	return NULL;
+    }
+
+    switch (dlg->state) {
+    case PJSIP_DIALOG_STATE_INCOMING:
+	tdata = pjsip_dlg_answer(dlg, status_code);
+	break;
+
+    case PJSIP_DIALOG_STATE_CALLING:
+	tdata = pjsip_dlg_cancel(dlg);
+	break;
+
+    case PJSIP_DIALOG_STATE_PROCEEDING:
+	if (dlg->role == PJSIP_ROLE_UAC) {
+	    tdata = pjsip_dlg_cancel(dlg);
+	} else {
+	    tdata = pjsip_dlg_answer(dlg, status_code);
+	}
+	break;
+
+    case PJSIP_DIALOG_STATE_ESTABLISHED:
+	tdata = pjsip_dlg_bye(dlg);
+	break;
+
+    default:
+	PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()", 
+		  dlg_state_names[dlg->state]));
+	break;
+    }
+
+    return tdata;
+}
+
+/*
+ * Handling of the receipt of 2xx/INVITE response.
+ */
+static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg,
+				    pjsip_event *event )
+{
+    pjsip_msg *msg;
+    pjsip_contact_hdr *contact;
+    pjsip_hdr *hdr, *end_hdr;
+    pjsip_method method;
+    pjsip_tx_data *ack_tdata;
+    
+    /* Get the message */
+    msg = event->src.rdata->msg;
+    
+    /* Update remote's tag information. */
+    pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag);
+
+    /* Copy Contact information in the 2xx/INVITE response to dialog's.
+     * remote contact
+     */
+    contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+    if (contact) {
+	dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
+    } else {
+	/* duplicate contact from "From" header (?) */
+	PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!"));
+	dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
+	dlg->remote.contact->uri = dlg->remote.info->uri;
+    }
+    
+    /* Copy Record-Route header (in reverse order) as dialog's route-set,
+     * overwriting previous route-set, if any, even if the received route-set
+     * is empty.
+     */
+    pj_list_init(&dlg->route_set);
+    end_hdr = &msg->hdr;
+    for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) {
+	if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+	    pjsip_route_hdr *r;
+	    r = pjsip_hdr_clone(dlg->pool, hdr);
+	    pjsip_routing_hdr_set_route(r);
+	    pj_list_insert_before(&dlg->route_set, r);
+	}
+    }
+    
+    /* On receipt of 200/INVITE response, send ACK. 
+     * This ack must be saved and retransmitted whenever we receive
+     * 200/INVITE retransmission, until 64*T1 seconds elapsed.
+     */
+    pjsip_method_set( &method, PJSIP_ACK_METHOD);
+    ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq);
+    if (ack_tdata == NULL) {
+	//PJ_TODO(HANDLE_CREATE_ACK_FAILURE)
+	PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request"));
+	return;
+    }	
+    
+    /* Send with the transaction. */
+    pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata);
+	
+    /* Decrement reference counter because pjsip_dlg_create_request 
+     * automatically increments the request.
+     */
+    pjsip_tx_data_dec_ref( ack_tdata );
+}
+
+/*
+ * State NULL, before any events have been received.
+ */
+static int  dlg_on_state_null( pjsip_dlg *dlg, 
+			       pjsip_transaction *tsx,
+			       pjsip_event *event)
+{
+    if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+	event->src_type == PJSIP_EVENT_RX_MSG) 
+    {
+	pjsip_hdr *hdr, *hdr_list;
+
+	pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+	/* Save the INVITE transaction. */
+	dlg->invite_tsx = tsx;
+
+	/* Change state to INCOMING */
+	dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event);
+
+	/* Create response buffer. */
+	tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100);
+	pjsip_tx_data_add_ref(tsx->last_tx);
+
+	/* Copy the Record-Route headers into dialog's route_set, maintaining
+	 * the order.
+	 */
+	pj_list_init(&dlg->route_set);
+	hdr_list = &event->src.rdata->msg->hdr;
+	hdr = hdr_list->next;
+	while (hdr != hdr_list) {
+	    if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+		pjsip_route_hdr *route;
+		route = pjsip_hdr_clone(dlg->pool, hdr);
+		pjsip_routing_hdr_set_route(route);
+		pj_list_insert_before(&dlg->route_set, route);
+	    }
+	    hdr = hdr->next;
+	}
+
+	/* Notify application. */
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+    } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+	       event->src_type == PJSIP_EVENT_TX_MSG) 
+    {
+	pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+	/* Save the INVITE transaction. */
+	dlg->invite_tsx = tsx;
+
+	/* Change state to CALLING. */
+	dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event);
+
+	/* Notify application. */
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+    } else {
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+    }
+
+    return 0;
+}
+
+/*
+ * State INCOMING is after the (callee) dialog has been initialized with
+ * the incoming request, but before any responses is sent by the dialog.
+ */
+static int  dlg_on_state_incoming( pjsip_dlg *dlg, 
+				   pjsip_transaction *tsx,
+				   pjsip_event *event)
+{
+    return dlg_on_state_proceeding_callee( dlg, tsx, event );
+}
+
+/*
+ * State CALLING is after the (caller) dialog has sent outgoing invitation
+ * but before any responses are received.
+ */
+static int  dlg_on_state_calling( pjsip_dlg *dlg, 
+				  pjsip_transaction *tsx,
+				  pjsip_event *event)
+{
+    if (tsx == dlg->invite_tsx) {
+	return dlg_on_state_proceeding_caller( dlg, tsx, event );
+    }
+    return 0;
+}
+
+/*
+ * State PROCEEDING is after provisional response is received.
+ * Since the processing is similar to state CALLING, this function is also
+ * called for CALLING state.
+ */
+static int  dlg_on_state_proceeding_caller( pjsip_dlg *dlg, 
+					    pjsip_transaction *tsx,
+					    pjsip_event *event)
+{
+    int dlg_is_terminated = 0;
+
+    /* We only care about our INVITE transaction. 
+     * Ignore other transaction progression (such as CANCEL).
+     */
+    if (tsx != dlg->invite_tsx) {
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+	return 0;
+    }
+
+    if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) {
+	switch (tsx->state) {
+	case PJSIP_TSX_STATE_PROCEEDING:
+	    if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) {
+		/* Change state to PROCEEDING */
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
+
+		/* Notify application. */
+		dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+	    } else {
+		/* Also notify application. */
+		dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+	    }
+	    break;
+
+	case PJSIP_TSX_STATE_COMPLETED:
+	    /* Change dialog state. */
+	    if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+		/* Update remote target, take it from the contact hdr. */
+		pjsip_contact_hdr *contact;
+		contact = pjsip_msg_find_hdr(event->src.rdata->msg, 
+					     PJSIP_H_CONTACT, NULL);
+		if (contact) {
+		    dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri);
+		} else {
+		    PJ_LOG(4,(dlg->obj_name, 
+			      "Warning: found no Contact hdr in 200/OK"));
+		}
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
+	    } else if (tsx->status_code==401 || tsx->status_code==407) {
+		/* Handle Authentication challenge. */
+		pjsip_method invite_method;
+		pjsip_tx_data *tdata;
+
+		tdata = pjsip_auth_reinit_req( dlg->ua->endpt,
+					       dlg->pool, &dlg->auth_sess,
+					       dlg->cred_count, dlg->cred_info,
+					       tsx->last_tx, event->src.rdata);
+		/*
+		pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD);
+		tdata = pjsip_dlg_create_request(dlg, &invite_method, -1);
+		if (tdata) {
+		    tdata = pjsip_auth_reinit_req( dlg->ua->endpt,
+						   dlg->pool, &dlg->auth_sess,
+						   dlg->cred_count, dlg->cred_info,
+						   tdata, event->src.rdata);
+		}
+		 */
+		if (tdata) {
+		    /* Re-use original request, with a new transaction. 
+		     * Need not to worry about CSeq, dialog will take care.
+		     */
+		    pjsip_dlg_send_msg(dlg, tdata);
+		    return 0;
+		} else {
+		    dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+		}
+	    } else {
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+	    }
+
+	    /* Notify application. */
+	    dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+	    /* Send ACK when dialog is connected. */
+	    if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+		pj_assert(event->src_type == PJSIP_EVENT_RX_MSG);
+		dlg_on_recv_2xx_invite(dlg, event);
+	    }
+	    break;
+
+	case PJSIP_TSX_STATE_TERMINATED:
+	    /*
+	     * Transaction is terminated because of timeout or transport error.
+	     * To let the application go to normal state progression, call the
+	     * callback twice. First is to emulate disconnection, and then call
+	     * again (with state TERMINATED) to destroy the dialog.
+	     */
+	    dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+	    dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+	    /* The INVITE transaction will be destroyed, so release reference 
+	     * to it. 
+	     */
+	    dlg->invite_tsx = NULL;
+
+	    /* We should terminate the dialog now.
+	     * But it's possible that we have other pending transactions (for 
+	     * example, outgoing CANCEL is in progress).
+	     * So destroy the dialog only if there's no other transaction.
+	     */
+	    if (dlg->pending_tsx_count == 0) {
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+		dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+		dlg_is_terminated = 1;
+	    }
+	    break;
+
+	default:
+	    pj_assert(0);
+	    break;
+	}
+    } else {
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+    }
+    return dlg_is_terminated ? -1 : 0;
+}
+
+/*
+ * State PROCEEDING for UAS is after the callee send provisional response.
+ * This function is also called for INCOMING state.
+ */
+static int  dlg_on_state_proceeding_callee( pjsip_dlg *dlg, 
+					    pjsip_transaction *tsx,
+					    pjsip_event *event)
+{
+    int dlg_is_terminated = 0;
+
+    pj_assert( dlg->invite_tsx != NULL );
+
+    if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+	event->src_type == PJSIP_EVENT_TX_MSG && 
+	tsx == dlg->invite_tsx) 
+    {
+	switch (tsx->state) {
+	case PJSIP_TSX_STATE_PROCEEDING:
+	    /* Change state to PROCEEDING */
+	    dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
+
+	    /* Notify application. */
+	    dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+	    break;
+
+	case PJSIP_TSX_STATE_COMPLETED:
+	case PJSIP_TSX_STATE_TERMINATED:
+	    /* Change dialog state. */
+	    if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
+	    } else {
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+	    }
+
+	    /* Notify application. */
+	    dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+	    /* If transaction is terminated in non-2xx situation, 
+	     * terminate dialog as well. This happens when something unexpected
+	     * occurs, such as transport error.
+	     */
+	    if (tsx->state == PJSIP_TSX_STATE_TERMINATED && 
+		!PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) 
+	    {
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+		dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+		dlg_is_terminated = 1;
+	    }
+	    break;
+
+	default:
+	    pj_assert(0);
+	    break;
+	}
+
+    } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+	       event->src_type == PJSIP_EVENT_RX_MSG && 
+	       tsx->method.id == PJSIP_CANCEL_METHOD) 
+    {
+	pjsip_tx_data *tdata;
+
+	/* Check if sequence number matches the pending INVITE. */
+	if (dlg->invite_tsx==NULL ||
+	    pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0) 
+	{
+	    PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE"));
+
+	    /* No matching INVITE transaction found. */
+	    tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+						event->src.rdata,
+						PJSIP_SC_CALL_TSX_DOES_NOT_EXIST );
+	    pjsip_tsx_on_tx_msg(tsx, tdata);
+	    return 0;
+	}
+
+	/* Always respond the CANCEL with 200/CANCEL no matter what. */
+	tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+					    event->src.rdata,
+					    200 );
+	pjsip_tsx_on_tx_msg( tsx, tdata );
+
+	/* Respond the INVITE transaction with 487, only if transaction has
+	 * not completed. 
+	 */
+	if (dlg->invite_tsx->last_tx) {
+	    if (dlg->invite_tsx->status_code < 200) {
+		tdata = dlg->invite_tsx->last_tx;
+		tdata->msg->line.status.code = 487;
+		tdata->msg->line.status.reason = *pjsip_get_status_text(487);
+		/* Reset packet buffer. */
+		pjsip_tx_data_invalidate_msg(tdata);
+		pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
+	    } else {
+		PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, "
+					  "Transaction already terminated "
+					  "with status %d",
+					  dlg->invite_tsx->status_code));
+	    }
+	} else {
+	    tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+						event->src.rdata,
+						487);
+	    pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
+	}
+    } else {
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+    }
+
+    return dlg_is_terminated ? -1 : 0;
+}
+
+static int  dlg_on_state_proceeding( pjsip_dlg *dlg, 
+				     pjsip_transaction *tsx,
+				     pjsip_event *event)
+{
+    if (dlg->role == PJSIP_ROLE_UAC) {
+	return dlg_on_state_proceeding_caller( dlg, tsx, event );
+    } else {
+	return dlg_on_state_proceeding_callee( dlg, tsx, event );
+    }
+}
+
+static int  dlg_on_state_connecting( pjsip_dlg *dlg, 
+				     pjsip_transaction *tsx,
+				     pjsip_event *event)
+{
+    if (tsx == dlg->invite_tsx) {
+	if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+	    (tsx->state == PJSIP_TSX_STATE_TERMINATED ||
+	    tsx->state == PJSIP_TSX_STATE_COMPLETED ||
+	    tsx->state == PJSIP_TSX_STATE_CONFIRMED)) 
+	{
+	    if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event);
+	    } else {
+		/* Probably because we never get the ACK, or transport error
+		* when sending ACK.
+		*/
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+	    }
+	    dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+	} else {
+	    dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+	}
+    } else {
+	/* Handle case when transaction is started when dialog is connecting
+	* (e.g. BYE requests cross wire.
+	*/
+	if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+	    event->src_type == PJSIP_EVENT_RX_MSG &&
+	    tsx->role == PJSIP_ROLE_UAS)
+	{
+	    pjsip_tx_data *response;
+
+	    if (tsx->status_code >= 200)
+		return 0;
+
+	    if (tsx->method.id == PJSIP_BYE_METHOD) {
+		/* Set state to DISCONNECTED. */
+		dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+
+		/* Notify application. */
+		dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+		response = pjsip_endpt_create_response(	dlg->ua->endpt, 
+							event->src.rdata, 200);
+	    } else {
+		response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
+							PJSIP_SC_INTERNAL_SERVER_ERROR);
+	    }
+
+	    if (response)
+		pjsip_tsx_on_tx_msg(tsx, response);
+
+	    return 0;
+	}
+    }
+    return 0;
+}
+
+static int  dlg_on_state_established( pjsip_dlg *dlg, 
+				      pjsip_transaction *tsx,
+				      pjsip_event *event)
+{
+    PJ_UNUSED_ARG(tsx)
+
+    if (tsx && tsx->method.id == PJSIP_BYE_METHOD) {
+	/* Set state to DISCONNECTED. */
+	dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+
+	/* Notify application. */
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+	/* Answer with 200/BYE. */
+	if (event->src_type == PJSIP_EVENT_RX_MSG) {
+	    pjsip_tx_data *tdata;
+	    tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+						event->src.rdata,
+						200 );
+	    if (tdata)
+		pjsip_tsx_on_tx_msg( tsx, tdata );
+	}
+    } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) {
+	pjsip_method_e method = event->src.rdata->cseq->method.id;
+
+	PJ_TODO(PROPERLY_HANDLE_REINVITATION)
+
+	/* Reinvitation. The message may be INVITE or an ACK. */
+	if (method == PJSIP_INVITE_METHOD) {
+	    if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) {
+		/* Section 14.2: A UAS that receives a second INVITE before it 
+		* sends the final response to a first INVITE with a lower
+		* CSeq sequence number on the same dialog MUST return a 500 
+		* (Server Internal Error) response to the second INVITE and 
+		* MUST include a Retry-After header field with a randomly 
+		* chosen value of between 0 and 10 seconds.
+		*/
+		pjsip_retry_after_hdr *hdr;
+		pjsip_tx_data *tdata = 
+		    pjsip_endpt_create_response(dlg->ua->endpt, 
+						event->src.rdata, 500);
+
+		if (!tdata)
+		    return 0;
+
+		/* Add Retry-After. */
+		hdr = pjsip_retry_after_hdr_create(tdata->pool);
+		hdr->ivalue = 9;
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
+
+		/* Send. */
+		pjsip_tsx_on_tx_msg(tsx, tdata);
+
+		return 0;
+	    }
+
+	    /* Keep this as our current INVITE transaction. */
+	    dlg->invite_tsx = tsx;
+
+	    /* Create response buffer. */
+	    tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, 
+							event->src.rdata, 100);
+	    pjsip_tx_data_add_ref(tsx->last_tx);
+
+	}
+
+	/* Notify application. */
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event);
+
+    }  else {
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+    }
+
+    return 0;
+}
+
+static int  dlg_on_state_disconnected( pjsip_dlg *dlg, 
+				       pjsip_transaction *tsx,
+				       pjsip_event *event)
+{
+    PJ_UNUSED_ARG(tsx)
+
+    /* Handle case when transaction is started when dialog is disconnected
+     * (e.g. BYE requests cross wire.
+     */
+    if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+	event->src_type == PJSIP_EVENT_RX_MSG &&
+	tsx->role == PJSIP_ROLE_UAS)
+    {
+	pjsip_tx_data *response = NULL;
+
+	if (tsx->status_code >= 200)
+	    return 0;
+
+	if (tsx->method.id == PJSIP_BYE_METHOD) {
+	    response = pjsip_endpt_create_response( dlg->ua->endpt, 
+						    event->src.rdata, 200);
+	} else {
+	    response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 
+						    PJSIP_SC_INTERNAL_SERVER_ERROR);
+	}
+	if (response)
+	    pjsip_tsx_on_tx_msg(tsx, response);
+
+	return 0;
+    } 
+    /* Handle case when outgoing BYE was rejected with 401/407 */
+    else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+	     event->src_type == PJSIP_EVENT_RX_MSG &&
+	     tsx->role == PJSIP_ROLE_UAC)
+    {
+	if (tsx->status_code==401 || tsx->status_code==407) {
+	    pjsip_tx_data *tdata;
+	    tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool,
+					   &dlg->auth_sess,
+					   dlg->cred_count, dlg->cred_info,
+					   tsx->last_tx, event->src.rdata);
+	    if (tdata) {
+		pjsip_dlg_send_msg(dlg, tdata);
+	    }
+	}
+    }
+	    
+
+    if (dlg->pending_tsx_count == 0) {
+	/* Set state to TERMINATED. */
+	dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+
+	/* Notify application. */
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+	return -1;
+    } else {
+	dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+    }
+
+    return 0;
+}
+
+static int  dlg_on_state_terminated( pjsip_dlg *dlg, 
+				     pjsip_transaction *tsx,
+				     pjsip_event *event)
+{
+    PJ_UNUSED_ARG(dlg)
+    PJ_UNUSED_ARG(tsx)
+    PJ_UNUSED_ARG(event)
+
+    return -1;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_dialog.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_dialog.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_dialog.h	(revision 1753)
@@ -0,0 +1,640 @@
+/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_dialog.h 13    8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_DIALOG_H__
+#define __PJSIP_DIALOG_H__
+
+/**
+ * @file dialog.h
+ * @brief SIP Dialog abstraction
+ */
+
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_auth.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSUA_DIALOG SIP Dialog
+ * @ingroup PJSUA
+ * @{
+ * \brief
+ *   This file contains SIP dialog, a higher level abstraction of SIP session.
+ *
+ * \par Overview
+ * A SIP dialog is an abstraction of communication session between two user 
+ * agents that persist for some time. The dialog facilitates sequencing of 
+ * messages between the user agents and proper routing of requests between both
+ * of them. The dialog represents a context in which to interpret SIP messages.
+ * However method independent User Agent processing for requests and responses 
+ * outside of a dialog exists, hence a dialog is not necessary for message 
+ * processing.
+ *
+ * A dialog is identified at each User Agent with a dialog Id, which consists 
+ * of a Call-Id value, a local tag and a remote tag. 
+ *
+ * A dialog contains certain pieces of data needed for further message 
+ * transmissions within the dialog. This data consists of:
+ *  - Dialog Id - used to identify the dialog.
+ *  - Local sequence number - used to order requests from the UA to its peer.
+ *  - Remote sequence number - used to order requests from its peer to the UA.
+ *  - Local URI - the address of the local party.
+ *  - Remote URI - the address of the remote party.
+ *  - Remote target - the address from the Contact header field of the request 
+ *    or response or refresh request or response.
+ *  - "secure" boolean - determines if the dialog is secure.
+ *  - Route set - an ordered list of URIs. The route set is the list of servers
+ *    that need to be traversed to send a request to the peer. 
+ *  - Authentication info - array of authentication credentials to be used
+ *    by the dialog to authenticate to proxies and servers.
+ *
+ * \par Manipulating Dialog
+ * Application should use functions declared in this file to do something with 
+ * the dialog. Among other things, application can:
+ *   - create outgoing dialog (#pjsip_dlg_init()).
+ *   - sends outgoing invitation (#pjsip_dlg_invite()).
+ *   - sends response (provisional and final) to incoming invitation
+ *     (#pjsip_dlg_answer())
+ *   - disconnect dialog (#pjsip_dlg_disconnect()).
+ *   - send other request (#pjsip_dlg_create_request() and #pjsip_dlg_send_msg())
+ *
+ * \par Getting Dialog's Notification
+ * Dialog emits notification about various things that's happening to it (e.g.
+ * a message is received, dialog state has changed, etc.). Normally it is in
+ * the interest of the application to capture these notifications, by
+ * supplying the function to be called when the event occurs in #pjsip_dlg_callback
+ * structure, and register this structure to user agent by calling 
+ * #pjsip_ua_set_dialog_callback().
+ *
+ * \par Incoming Invitation
+ * Upon receiving a new incoming invitation, user agent will automatically create
+ * a new dialog, and inform application via \b pjsip_dlg_callback.
+ */
+
+/** Forward declaration for user agent structure. */
+typedef struct pjsip_user_agent pjsip_user_agent;
+
+/** Forward declaration for dialog structure. */
+typedef struct pjsip_dlg pjsip_dlg;
+
+/**
+ * \brief Type of events that are reported by the dialog to the application callback
+ * function.
+ */
+typedef enum pjsip_dlg_event_e
+{
+    /** Dialog state has changed. */
+    PJSIP_DIALOG_EVENT_STATE_CHANGED,
+
+    /** Any mid-call messages (reinvitation, message, etc.). */
+    PJSIP_DIALOG_EVENT_MID_CALL_REQUEST,
+
+    /** Other events (low level events). */
+    PJSIP_DIALOG_EVENT_OTHER,
+
+} pjsip_dlg_event_e;
+
+
+/** 
+ * \brief Structure registered by applications to receive dialog notifications 
+ * from the User Agent. 
+ *
+ * Applications registers this structure to get notifications from the User Agent
+ * about dialog state changes and other events. Application can set any of
+ * the callback function to NULL if it doesn't want to handle the notification,
+ * however, setting some callbacks to NULL probably will cause some undesired
+ * result (such as setting \b on_incoming to NULL will cause the creation of
+ * a lot of dialogs with no owner).
+ */
+struct pjsip_dlg_callback
+{
+    /**
+     * This is a low level, uninterpreted callback that is called by framework 
+     * for all kinds of events, such as transaction events, dialog events, etc.
+     * @param dlg	The dialog.
+     * @param dlg_event The type of dialog event.
+     * @param event	The event descriptor.
+     */
+    void (*on_all_events)(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event, 
+			  pjsip_event *event );
+
+    /**
+     * This is a low level callback that is called by the framework when the
+     * underlying transaction is about to send outgoing message. This callback
+     * is provided to allow application to modify the message before it is 
+     * transmitted. 
+     * @param dlg	The dialog.
+     * @param tsx	The transaction that transmits the message.
+     * @param tdata	The transmission data, which contains the message.
+     * @param retransmission The number of times this message has been sent.
+     *			Zero indicates the message is about to be sent the first time,
+     *			one indicates this is the first retransmission, etc.
+     */
+    void (*on_before_tx)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+			 pjsip_tx_data *tdata, pj_bool_t retransmission);
+
+    /**
+     * This is a low level callback that is called by the framework when the dialog
+     * has sent a message. Note that a receive of retransmission will not trigger
+     * this callback since retransmission is handled internally by transaction.
+     * @param dlg	The dialog.
+     * @param tsx	The transaction that transmits the message.
+     * @param tdata	The transmission data, which contains the message.
+     */
+    void (*on_tx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx, 
+		      pjsip_tx_data *tdata);
+
+    /**
+     * This is a low level callback that is called by the framework when the
+     * dialog has received a message. Note that a receipt of retransmission 
+     * will not trigger this callback since retransmission is handled internally
+     * by transaction.
+     * @param dlg	The dialog.
+     * @param tsx	The transaction that receives the message.
+     * @param rdata	The receive data, which contains the message.
+     */
+    void (*on_rx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx, 
+		      pjsip_rx_data *rdata);
+
+    /**
+     * This callback is called by the framework when the user agent
+     * instance receives an incoming INVITE message.
+     * @param dlg	The new dialog that's just created to handle the incoming call.
+     * @param tsx	The INVITE transaction that's just created.
+     * @param rdata	The receive data, which contains the INVITE message.
+     */
+    void (*on_incoming)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+		        pjsip_rx_data *rdata);
+
+    /**
+     * This callback is called by the framework when the dialog is sending
+     * the first outgoing INVITE message.
+     * @param dlg	The dialog.
+     * @param tsx	The INVITE transaction.
+     * @param tdata	The transmit data, which contains the INVITE message.
+     */
+    void (*on_calling)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+		       pjsip_tx_data *tdata);
+
+    /**
+     * This callback is called by the framework when the initial INVITE
+     * transaction has sent/received provisional response.
+     * @param dlg	The dialog.
+     * @param tsx	The transaction.
+     * @param event	The event, which src_type will always be either
+     *			PJSIP_EVENT_RX_MSG or PJSIP_EVENT_TX_MSG. The provisional
+     *			response message itself will be in either \b rdata or \b tdata.
+     * @see pjsip_event.
+     */
+    void (*on_provisional)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+			   pjsip_event *event);
+
+    /**
+     * This callback is called for both UAS and UAC dialog when 200 response
+     * to INVITE is sent or received.
+     * @param dlg	The dialog.
+     * @param event	The event, which src_type can only be either 
+     *			PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG.
+     * @see pjsip_event
+     */
+    void (*on_connecting)(pjsip_dlg *dlg, pjsip_event *event);
+
+    /**
+     * This callback is called for both UAS and UAC when an ACK request is
+     * sent or received by the dialog.
+     * @param dlg	The dialog.
+     * @param event	The event, which src_type can only be either 
+     *			PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG.
+     * @see pjsip_event
+     */
+    void (*on_established)(pjsip_dlg *dlg, pjsip_event *event);
+
+    /**
+     * This callback is called when the dialog is disconnected, i.e. upon
+     * sending/receiving non-200 response to INVITE, sending/receiving
+     * CANCEL to initial INVITE, and sending/receiving BYE.
+     *
+     * @param dlg	The dialog.
+     * @param event	The event.
+     * @see pjsip_event
+     */
+    void (*on_disconnected)(pjsip_dlg *dlg, pjsip_event *event);
+
+    /**
+     * This callback is called when the dialog is about to be destroyed.
+     * @param dlg The dialog.
+     */
+    void (*on_terminated)(pjsip_dlg *dlg);
+
+    /**
+     * This callback will be called when the dialog receives mid call events
+     * such as re-invitation or incoming pager.
+     *
+     * @param dlg	The dialog.
+     * @param event	The event.
+     */
+    void (*on_mid_call_events)(pjsip_dlg *dlg, pjsip_event *event);
+
+};  /* struct pjsip_dlg_callback */
+
+
+
+/**
+ * Dialog state.
+ */
+typedef enum pjsip_dlg_state_e
+{
+    /** 
+     * State NULL is after the dialog is instantiated but before any
+     * initialization is done. 
+     */
+    PJSIP_DIALOG_STATE_NULL,
+
+    /**
+     * State INCOMING is after the (callee) dialog has been initialized with
+     * the incoming request, but before any responses is sent by the dialog.
+     */
+    PJSIP_DIALOG_STATE_INCOMING,
+
+    /**
+     * State CALLING is after the (caller) dialog has sent outgoing invitation
+     * but before any responses are received.
+     */
+    PJSIP_DIALOG_STATE_CALLING,
+
+    /**
+     * State PROCEEDING is after the dialog sent/received provisional 
+     * responses, but before final response is sent/received.
+     */
+    PJSIP_DIALOG_STATE_PROCEEDING,
+
+    /**
+     * State CONNECTING is after the dialog has sent/received final response
+     * to the invitation, but before acknowledgement is sent.
+     */
+    PJSIP_DIALOG_STATE_CONNECTING,
+
+    /**
+     * State ESTABLISHED occurs after the invitation has been accepted and
+     * acknowledged.
+     */
+    PJSIP_DIALOG_STATE_ESTABLISHED,
+
+    /**
+     * State DISCONNECTED occurs after either party successfully disconnect
+     * the session.
+     */
+    PJSIP_DIALOG_STATE_DISCONNECTED,
+
+    /**
+     * State TERMINATE occurs when the dialog is ready to be destroyed.
+     */
+    PJSIP_DIALOG_STATE_TERMINATED,
+
+} pjsip_dlg_state_e;
+
+
+/**
+ * Get the dialog string state.
+ *
+ * @param state	    Dialog state.
+ * @return	    The string describing the state.
+ */
+const char *pjsip_dlg_state_str(pjsip_dlg_state_e state);
+
+/**
+ * This structure is used to describe dialog's participants, which in this
+ * case is local party (i.e. us) and remote party.
+ */
+typedef struct pjsip_dlg_party
+{
+    pjsip_uri		*target;    /**< Target URL.			*/
+    pjsip_fromto_hdr	*info;	    /**< URL in From/To header.		*/
+    pj_str_t		 tag;	    /**< Tag.				*/
+    pjsip_contact_hdr	*contact;   /**< URL in Contact.		*/
+    pj_sockaddr_in	 addr;	    /**< The current transport address. */
+    int			 cseq;	    /**< Sequence number counter.	*/
+} pjsip_dlg_party;
+
+
+/**
+ * This structure describes the dialog structure.
+ */
+struct pjsip_dlg
+{
+    PJ_DECL_LIST_MEMBER(struct pjsip_dlg)
+
+    char	        obj_name[PJ_MAX_OBJ_NAME];  /**< Log identification.	*/
+
+    pjsip_user_agent   *ua;			    /**< User agent instance.	*/
+    pj_pool_t	       *pool;			    /**< Dialog's pool.		*/
+    pjsip_dlg_state_e   state;			    /**< Dialog's call state.	*/
+    pjsip_role_e	role;			    /**< Dialog's role.		*/
+    pj_mutex_t	       *mutex;			    /**< Dialog's mutex.	*/
+
+    pjsip_dlg_party     local;			    /**< Local party info.	*/
+    pjsip_dlg_party     remote;			    /**< Remote party info.	*/
+
+    pjsip_cid_hdr      *call_id;		    /**< Call-ID		*/
+    pj_bool_t	        secure;			    /**< Use secure transport?	*/
+
+    pjsip_route_hdr     route_set;		    /**< Dialog's route set.	*/
+    pjsip_transaction  *invite_tsx;		    /**< Current INVITE transaction. */
+    int			pending_tsx_count;	    /**< Total pending tsx count. */
+
+    int			cred_count;		    /**< Number of credentials. */
+    pjsip_cred_info    *cred_info;		    /**< Array of credentials.	*/
+
+    pjsip_auth_session	auth_sess;		    /**< List of auth session. */
+
+    pjsip_msg_body     *body;
+
+    void	       *user_data;		    /**< Application's data.	*/
+
+    int  (*handle_tsx_event)(struct pjsip_dlg *,    /**< Internal state handler.*/
+			     pjsip_transaction *,
+			     pjsip_event *);
+};
+
+
+/**
+ * Initialize dialog with local and remote info. This function is normally
+ * called after application creates the dialog with #pjsip_ua_create_dialog
+ * for UAC dialogs.
+ *
+ * This function will initialize local and remote info from the URL, generate
+ * a globally unique Call-ID, initialize CSeq, and initialize other dialog's
+ * internal attributes.
+ *
+ * @param dlg		The dialog to initialize.
+ * @param local_info	URI/name address to be used as local info 
+ *			(From and Contact headers).
+ * @param remote_info	URI/name address to be used as remote info (To header).
+ * @param target	URI for initial remote's target, or NULL to set the
+ *			initial target the same as remote_info.
+ *
+ * @return		zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
+				     const pj_str_t *local_info,
+				     const pj_str_t *remote_info,
+				     const pj_str_t *target);
+
+
+/**
+ * Set authentication credentials to be used by this dialog.
+ *
+ * If authentication credentials are set for the dialog, the dialog will try to
+ * perform authentication automatically using the credentials supplied, and 
+ * also cache the last Authorization or Proxy-Authorization headers for next 
+ * requests.
+ * 
+ * If none of the credentials are suitable or accepted by remote, then
+ * the dialog will just pass the authorization failure response back to
+ * application.
+ *
+ * @param dlg		The dialog.
+ * @param count		Number of credentials in the array.
+ * @param cred		Array of credentials.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
+					        int count,
+						const pjsip_cred_info cred[]);
+
+/**
+ * Override local contact details.
+ *
+ * Call this function to change the contact details to be advertised in Contact
+ * header. Application normally need to call this function for incoming calls
+ * before answering the call with 200/OK, because for an incoming dialogs, the
+ * initial local contact info are generated from the To header, which is 
+ * normally not the appropriate one.
+ *
+ * @param dlg		The dialog.
+ * @param contact	The contact to use.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
+					    const pj_str_t *contact );
+
+
+/**
+ * Set initial route set to be used by the dialog. This initial route set
+ * governs where and how the initial INVITE request will be routed. This
+ * initial route set will be overwritten with the route set found in the
+ * 2xx response of INVITE.
+ *
+ * Application only needs to call this function if it wants to have custom
+ * route for individual dialogs. If only a single route for all dialogs is
+ * needed, then application can set the global route by calling function
+ * #pjsip_endpt_set_proxies().
+ *
+ * @param dlg		The dialog.
+ * @param route_set	The route set list.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
+					      const pjsip_route_hdr *route_set );
+
+
+/**
+ * Variation of #pjsip_dlg_set_route_set where the headers will be used
+ * as it is (i.e. without cloned).
+ *
+ * @param dlg		The dialog.
+ * @param route_set	The route set list.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
+						 pjsip_route_hdr *route_set);
+
+/**
+ * Create initial outgoing INVITE message.
+ *
+ * This function is just a simple wrapper to #pjsip_dlg_create_request(),
+ * so it follows the same rule there. In addition, this function also adds
+ * \b Allow header to the outgoing request.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
+ * state. Note that upon return the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg		The dialog.
+ *
+ * @return		The dialog transmit data, or NULL.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg );
+
+
+/**
+ * Answer incoming dialog invitation, with either provisional responses
+ * or a final response. Application can only call this function when there's
+ * a pending invitation to be answered.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
+ * state. Note that upon return the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg	The dialog.
+ * @param code	The response code, which can be:
+ *		- 100-199 Provisional response (application can issue multiple 
+ *                        provisional responses).
+ *		- 200-299 To answer the invitation (normally status code 200
+ *                        is sent).
+ *              - 300-699 To reject the invitation.
+ * @return	Transmit data if successfull.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code );
+
+
+/**
+ * High level function to create message to disconnect dialog. Depending 
+ * on dialog's  state, this function will either create CANCEL, final response,
+ * or BYE message. A status code must be supplied, which will be set if dialog 
+ * will be transmitting a final response to INVITE.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg to actually send the message and update the dialog's
+ * state. Note that upon return the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg		The dialog.
+ * @param status_code	The status code for disconnection.
+ * @return		Transmit data if successfull.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, int status_code);
+
+/**
+ * Create CANCEL message to cancel pending outgoing dialog invitation. 
+ * Normally application should call #pjsip_dlg_disconnect() instead, because
+ * that function will create the correct message regardless of the state of 
+ * the dialog. 
+ *
+ * Application can call this function at anytime after it issues outgoing 
+ * invitation and before receiving final response. However, there's no 
+ * guarantee that the invitation will be successfully cancelled, since the 
+ * CANCEL request and the final response can pass over in the wire. So the 
+ * application must prepare to have the dialog connected even after the 
+ * dialog is cancelled.
+ *
+ * The final state of the dialog will be reported in the dialog callback.
+ * If the CANCEL request succeeded, then the dialog will be disconnected with
+ * status code \a PJSIP_SC_REQUEST_TERMINATED.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
+ * state. 
+ *
+ * Upon return of this function, the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg		The dialog.
+ * @return		The dialog transmit data containing the CANCEL message,
+ *			or NULL.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg );
+
+
+/**
+ * Create BYE message. Application shouldn't normally need to use this function,
+ * but rather it's preferable to use #pjsip_dlg_disconnect() instead because
+ * that function will work to disconnect the session no matter what the state
+ * is.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
+ * state. Note that upon return the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg		The dialog.
+ * @return		The BYE message or NULL.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg );
+
+/**
+ * This function is called by application to create new outgoing request
+ * message for this dialog. After the request is created, application can
+ * modify the message (such adding headers), and eventually send the request
+ * by calling #pjsip_dlg_send_msg().
+ *
+ * This function will initialize the request message with dialog's properties
+ * as follows:
+ *  - the request line is initialized with the method and the target is
+ *    initialized from current remote target.
+ *  - \b From, \b To, \b Contact, and \b Call-Id headers will be added.
+ *  - An initial \b CSeq header will be provided (although the value will be
+ *    verified again when the message is actually sent with #pjsip_dlg_send_msg().
+ *  - \b Route headers will be added from dialog's route set.
+ *  - Authentication headers (\b Authorization or \b Proxy-Authorization) will
+ *    be added from dialog's authorization cache.
+ *
+ * Note that upon return the reference counter of the transmit data
+ * will be set to one. When the message is sent, #pjsip_dlg_send_msg() will
+ * decrement the reference counter, and when the reference counter reach zero,
+ * the message will be deleted.
+ *
+ * @param dlg		The dialog.
+ * @param method	The request method.
+ * @param cseq		Specify CSeq, or -1 to let the dialog specify CSeq.
+ *
+ * @return		Transmit data for the new request.
+ *
+ * @see pjsip_dlg_send_msg()
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
+						  const pjsip_method *method,
+						  int cseq);
+
+
+/**
+ * This function can be called by application to send outgoing message (request
+ * or response) to remote party. Note that after calling this function, the
+ * transmit data will be deleted regardless of the return status. To prevent
+ * deletion, application must increase the reference count, but then it will
+ * be responsible to delete this transmit data itself (by decreasing the
+ * reference count).
+ *
+ * @param dlg		The dialog.
+ * @param tdata		The transmit data, which contains the request message.
+ * @return		zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg,
+					 pjsip_tx_data *tdata );
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_DIALOG_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_reg.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_reg.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_reg.c	(revision 1753)
@@ -0,0 +1,516 @@
+/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_reg.c 14    8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip_mod_ua/sip_reg.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_auth_msg.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/log.h>
+
+#define REFRESH_TIMER		1
+#define DELAY_BEFORE_REFRESH	5
+#define THIS_FILE		"sip_regc.c"
+
+/**
+ * SIP client registration structure.
+ */
+struct pjsip_regc
+{
+    pj_pool_t	        *pool;
+    pjsip_endpoint	*endpt;
+    pj_bool_t		 _delete_flag;
+    int			pending_tsx;
+
+    void		*token;
+    pjsip_regc_cb	*cb;
+
+    pj_str_t		str_srv_url;
+    pjsip_uri		*srv_url;
+    pjsip_cid_hdr	*cid_hdr;
+    pjsip_cseq_hdr	*cseq_hdr;
+    pjsip_from_hdr	*from_hdr;
+    pjsip_to_hdr	*to_hdr;
+    char		*contact_buf;
+    pjsip_generic_string_hdr	*contact_hdr;
+    pjsip_expires_hdr	*expires_hdr;
+    pjsip_contact_hdr	*unreg_contact_hdr;
+    pjsip_expires_hdr	*unreg_expires_hdr;
+    pj_uint32_t		 expires;
+
+    /* Credentials. */
+    int			 cred_count;
+    pjsip_cred_info     *cred_info;
+    
+    /* Authorization sessions. */
+    pjsip_auth_session	 auth_sess_list;
+
+    /* Auto refresh registration. */
+    pj_bool_t		 auto_reg;
+    pj_timer_entry	 timer;
+};
+
+
+
+PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
+				       pjsip_regc_cb *cb)
+{
+    pj_pool_t *pool;
+    pjsip_regc *regc;
+
+    if (cb == NULL)
+	return NULL;
+
+    pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
+    regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc));
+
+    regc->pool = pool;
+    regc->endpt = endpt;
+    regc->token = token;
+    regc->cb = cb;
+    regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
+    regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
+
+    pj_list_init(&regc->auth_sess_list);
+
+    return regc;
+}
+
+
+PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc)
+{
+    if (regc->pending_tsx) {
+	regc->_delete_flag = 1;
+	regc->cb = NULL;
+    } else {
+	pjsip_endpt_destroy_pool(regc->endpt, regc->pool);
+    }
+}
+
+
+PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
+{
+    return regc->pool;
+}
+
+static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
+{
+    if (expires != regc->expires) {
+	regc->expires_hdr = pjsip_expires_hdr_create(regc->pool);
+	regc->expires_hdr->ivalue = expires;
+    } else {
+	regc->expires_hdr = NULL;
+    }
+}
+
+
+static pj_status_t set_contact( pjsip_regc *regc,
+			        int contact_cnt,
+				const pj_str_t contact[] )
+{
+    int i;
+    char *s;
+    const pj_str_t contact_STR = { "Contact", 7};
+
+    /* Concatenate contacts. */
+    for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
+	if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
+	    return -1;
+	}
+	pj_memcpy(s, contact[i].ptr, contact[i].slen);
+	s += contact[i].slen;
+
+	if (i != contact_cnt - 1) {
+	    *s++ = ',';
+	    *s++ = ' ';
+	}
+    }
+
+    /* Set "Contact" header. */
+    regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR);
+    regc->contact_hdr->hvalue.ptr = regc->contact_buf;
+    regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
+
+    return 0;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
+				     const pj_str_t *srv_url,
+				     const pj_str_t *from_url,
+				     const pj_str_t *to_url,
+				     int contact_cnt,
+				     const pj_str_t contact[],
+				     pj_uint32_t expires)
+{
+    pj_str_t tmp;
+
+    /* Copy server URL. */
+    pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
+
+    /* Set server URL. */
+    tmp = regc->str_srv_url;
+    regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
+    if (regc->srv_url == NULL) {
+	return -1;
+    }
+
+    /* Set "From" header. */
+    pj_strdup_with_null(regc->pool, &tmp, from_url);
+    regc->from_hdr = pjsip_from_hdr_create(regc->pool);
+    regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, 
+					  PJSIP_PARSE_URI_AS_NAMEADDR);
+    if (!regc->from_hdr->uri) {
+	PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr));
+	return -1;
+    }
+
+    /* Set "To" header. */
+    pj_strdup_with_null(regc->pool, &tmp, to_url);
+    regc->to_hdr = pjsip_to_hdr_create(regc->pool);
+    regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, 
+					PJSIP_PARSE_URI_AS_NAMEADDR);
+    if (!regc->to_hdr->uri) {
+	PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
+	return -1;
+    }
+
+
+    /* Set "Contact" header. */
+    if (set_contact( regc, contact_cnt, contact) != 0)
+	return -1;
+
+    /* Set "Expires" header, if required. */
+    set_expires( regc, expires);
+
+    /* Set "Call-ID" header. */
+    regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
+    pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
+
+    /* Set "CSeq" header. */
+    regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
+    regc->cseq_hdr->cseq = 0;
+    pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
+
+    /* Create "Contact" header used in unregistration. */
+    regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
+    regc->unreg_contact_hdr->star = 1;
+
+    /* Create "Expires" header used in unregistration. */
+    regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool);
+    regc->unreg_expires_hdr->ivalue = 0;
+
+    /* Done. */
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
+						int count,
+						const pjsip_cred_info cred[] )
+{
+    if (count > 0) {
+	regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info));
+	pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info));
+    }
+    regc->cred_count = count;
+    return 0;
+}
+
+static pjsip_tx_data *create_request(pjsip_regc *regc)
+{
+    pjsip_tx_data *tdata;
+    pjsip_msg *msg;
+
+    /* Create transmit data. */
+    tdata = pjsip_endpt_create_tdata(regc->endpt);
+    if (!tdata) {
+	return NULL;
+    }
+
+    /* Create request message. */
+    msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+    tdata->msg = msg;
+
+    /* Initialize request line. */
+    pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD);
+    msg->line.req.uri = regc->srv_url;
+
+    /* Add headers. */
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr);
+    
+    /* Add cached authorization headers. */
+    pjsip_auth_init_req( regc->pool, tdata, &regc->auth_sess_list,
+			 regc->cred_count, regc->cred_info );
+
+    /* Add reference counter to transmit data. */
+    pjsip_tx_data_add_ref(tdata);
+
+    return tdata;
+}
+
+
+PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg)
+{
+    pjsip_msg *msg;
+    pjsip_tx_data *tdata;
+
+    tdata = create_request(regc);
+    if (!tdata)
+	return NULL;
+
+    msg = tdata->msg;
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr);
+    if (regc->expires_hdr)
+	pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr);
+
+    if (regc->timer.id != 0) {
+	pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
+	regc->timer.id = 0;
+    }
+
+    regc->auto_reg = autoreg;
+
+    return tdata;
+}
+
+
+PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc)
+{
+    pjsip_tx_data *tdata;
+    pjsip_msg *msg;
+
+    if (regc->timer.id != 0) {
+	pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
+	regc->timer.id = 0;
+    }
+
+    tdata = create_request(regc);
+    if (!tdata)
+	return NULL;
+
+    msg = tdata->msg;
+    pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
+    pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
+
+    return tdata;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_contact(  pjsip_regc *regc,
+					        int contact_cnt,
+						const pj_str_t contact[] )
+{
+    return set_contact( regc, contact_cnt, contact );
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_expires(  pjsip_regc *regc,
+					        pj_uint32_t expires )
+{
+    set_expires( regc, expires );
+    return 0;
+}
+
+
+static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason,
+			  pjsip_rx_data *rdata, pj_int32_t expiration,
+			  int contact_cnt, pjsip_contact_hdr *contact[])
+{
+    struct pjsip_regc_cbparam cbparam;
+
+
+    cbparam.regc = regc;
+    cbparam.token = regc->token;
+    cbparam.code = status;
+    cbparam.reason = *reason;
+    cbparam.rdata = rdata;
+    cbparam.contact_cnt = contact_cnt;
+    cbparam.expiration = expiration;
+    if (contact_cnt) {
+	pj_memcpy( cbparam.contact, contact, 
+		   contact_cnt*sizeof(pjsip_contact_hdr*));
+    }
+
+    (*regc->cb)(&cbparam);
+}
+
+static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
+				   struct pj_timer_entry *entry)
+{
+    pjsip_regc *regc = entry->user_data;
+    pjsip_tx_data *tdata;
+    
+    PJ_UNUSED_ARG(timer_heap)
+
+    entry->id = 0;
+    tdata = pjsip_regc_register(regc, 1);
+    if (tdata) {
+	pjsip_regc_send(regc, tdata);
+    } else {
+	pj_str_t reason = pj_str("Unable to create txdata");
+	call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
+    }
+}
+
+static void tsx_callback(void *token, pjsip_event *event)
+{
+    pjsip_regc *regc = token;
+    pjsip_transaction *tsx = event->obj.tsx;
+    
+    /* If registration data has been deleted by user then remove registration 
+     * data from transaction's callback, and don't call callback.
+     */
+    if (regc->_delete_flag) {
+	--regc->pending_tsx;
+
+    } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
+	       tsx->status_code == PJSIP_SC_UNAUTHORIZED)
+    {
+	pjsip_rx_data *rdata = event->src.rdata;
+	pjsip_tx_data *tdata;
+
+	tdata = pjsip_auth_reinit_req( regc->endpt,
+				       regc->pool, &regc->auth_sess_list,
+				       regc->cred_count, regc->cred_info,
+				       tsx->last_tx, event->src.rdata );
+
+	if (tdata) {
+	    --regc->pending_tsx;
+	    pjsip_regc_send(regc, tdata);
+	    return;
+	} else {
+	    call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason,
+			  rdata, -1, 0, NULL);
+	    --regc->pending_tsx;
+	}
+    } else {
+	int contact_cnt = 0;
+	pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
+	pjsip_rx_data *rdata;
+	pj_int32_t expiration = 0xFFFF;
+
+	if (tsx->status_code/100 == 2) {
+	    int i;
+	    pjsip_contact_hdr *hdr;
+	    pjsip_msg *msg;
+	    pjsip_expires_hdr *expires;
+
+	    rdata = event->src.rdata;
+	    msg = rdata->msg;
+	    hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+	    while (hdr) {
+		contact[contact_cnt++] = hdr;
+		hdr = hdr->next;
+		if (hdr == (void*)&msg->hdr)
+		    break;
+		hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
+	    }
+
+	    expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
+
+	    if (expires)
+		expiration = expires->ivalue;
+	    
+	    for (i=0; i<contact_cnt; ++i) {
+		hdr = contact[i];
+		if (hdr->expires >= 0 && hdr->expires < expiration)
+		    expiration = contact[i]->expires;
+	    }
+
+	    if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
+		pj_time_val delay = { 0, 0};
+
+		delay.sec = expiration - DELAY_BEFORE_REFRESH;
+		if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && 
+		    delay.sec > (pj_int32_t)regc->expires) 
+		{
+		    delay.sec = regc->expires;
+		}
+		if (delay.sec < DELAY_BEFORE_REFRESH) 
+		    delay.sec = DELAY_BEFORE_REFRESH;
+		regc->timer.cb = &regc_refresh_timer_cb;
+		regc->timer.id = REFRESH_TIMER;
+		regc->timer.user_data = regc;
+		pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
+	    }
+
+	} else {
+	    rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL;
+	}
+
+
+	/* Call callback. */
+	if (expiration == 0xFFFF) expiration = -1;
+	call_callback(regc, tsx->status_code, 
+		      (rdata ? &rdata->msg->line.status.reason 
+			: pjsip_get_status_text(tsx->status_code)),
+		      rdata, expiration, 
+		      contact_cnt, contact);
+
+	--regc->pending_tsx;
+    }
+
+    /* Delete the record if user destroy regc during the callback. */
+    if (regc->_delete_flag && regc->pending_tsx==0) {
+	pjsip_regc_destroy(regc);
+    }
+}
+
+PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
+{
+    int status;
+
+    /* Make sure we don't have pending transaction. */
+    if (regc->pending_tsx) {
+	pj_str_t reason = pj_str("Transaction in progress");
+	call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
+	pjsip_tx_data_dec_ref( tdata );
+	return;
+    }
+
+    /* Invalidate message buffer. */
+    pjsip_tx_data_invalidate_msg(tdata);
+
+    /* Increment CSeq */
+    regc->cseq_hdr->cseq++;
+
+    /* Send. */
+    status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
+    if (status==0)
+	++regc->pending_tsx;
+    else {
+	pj_str_t reason = pj_str("Unable to send request.");
+	call_callback(regc, status, &reason, NULL, -1, 0, NULL);
+    }
+}
+
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_reg.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_reg.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_reg.h	(revision 1753)
@@ -0,0 +1,215 @@
+/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_reg.h 11    8/24/05 10:35a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_REG_H__
+#define __PJSIP_SIP_REG_H__
+
+/**
+ * @file sip_reg.h
+ * @brief SIP Registration Client
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_auth.h>
+#include <pjsip_mod_ua/sip_ua.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSUA_REGC SIP Registration Client
+ * @ingroup PJSUA
+ * @{
+ * \brief
+ *   API for performing registration for user agent.
+ */
+
+/** Typedef for client registration data. */
+typedef struct pjsip_regc pjsip_regc;
+
+/** Maximum contacts in registration. */
+#define PJSIP_REGC_MAX_CONTACT	10
+
+/** Expiration not specified. */
+#define PJSIP_REGC_EXPIRATION_NOT_SPECIFIED	((pj_uint32_t)0xFFFFFFFFUL)
+
+/** Buffer to hold all contacts. */
+#define PJSIP_REGC_CONTACT_BUF_SIZE	512
+
+/** Structure to hold parameters when calling application's callback.
+ *  The application's callback is called when the client registration process
+ *  has finished.
+ */
+struct pjsip_regc_cbparam
+{
+    pjsip_regc		*regc;
+    void		*token;
+    int			 code;
+    pj_str_t		 reason;
+    pjsip_rx_data	*rdata;
+    int			 contact_cnt;
+    int			 expiration;
+    pjsip_contact_hdr	*contact[PJSIP_REGC_MAX_CONTACT];
+};
+
+
+/** Type declaration for callback to receive registration result. */
+typedef void pjsip_regc_cb(struct pjsip_regc_cbparam *param);
+
+
+/**
+ * Get the module instance for client registration module.
+ *
+ * @return	    client registration module.
+ */
+PJ_DECL(pjsip_module*) pjsip_regc_get_module(void);
+
+
+/**
+ * Create client registration structure.
+ *
+ * @param endpt	    Endpoint, used to allocate pool from.
+ * @param token	    A data to be associated with the client registration struct.
+ * @param cb	    Pointer to callback function to receive registration status.
+ *
+ * @return	    client registration structure.
+ */
+PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
+				        pjsip_regc_cb *cb);
+
+
+/**
+ * Destroy client registration structure. If a registration transaction is
+ * in progress, then the structure will be deleted only after a final response
+ * has been received, and in this case, the callback won't be called.
+ *
+ * @param regc	    The client registration structure.
+ */
+PJ_DECL(void) pjsip_regc_destroy(pjsip_regc *regc);
+
+/**
+ * Get the memory pool associated with a registration client handle.
+ *
+ * @param regc	    The client registration structure.
+ * @return pool	    handle.
+ */
+PJ_DECL(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc);
+
+/**
+ * Initialize client registration structure with various information needed to
+ * perform the registration.
+ *
+ * @param regc	    The client registration structure.
+ * @param from_url  The person performing the registration, must be a SIP URL type.
+ * @param to_url    The address of record for which the registration is targetd, must
+ *		    be a SIP/SIPS URL.
+ * @param ccnt	    Number of contacts in the array.
+ * @param contact   Array of contacts.
+ * @param expires   Default expiration interval (in seconds) to be applied for
+ *		    contact URL that doesn't have expiration settings. If the
+ *		    value PJSIP_REGC_EXPIRATION_NOT_SPECIFIED is given, then 
+ *		    no default expiration will be applied.
+ * @return	    zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc,
+				     const pj_str_t *srv_url,
+				     const pj_str_t *from_url,
+				     const pj_str_t *to_url,
+				     int ccnt,
+				     const pj_str_t contact[],
+				     pj_uint32_t expires);
+
+
+/**
+ * Set authentication credentials to use by this registration.
+ *
+ * @param dlg		The registration structure.
+ * @param count		Number of credentials in the array.
+ * @param cred		Array of credentials.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
+						 int count,
+						 const pjsip_cred_info cred[] );
+
+/**
+ * Create REGISTER request for the specified client registration structure.
+ *
+ * After successfull registration, application can inspect the contacts in
+ * the client registration structure to list what contacts are associaciated
+ * with the address of record being targeted in the registration.
+ *
+ * @param regc	    The client registration structure.
+ * @param autoreg   If non zero, the library will automatically refresh the
+ *		    next registration until application unregister.
+ *
+ * @return	    SIP REGISTER request.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg);
+
+
+/**
+ * Create REGISTER request to unregister all contacts from server records.
+ *
+ * @param regc	    The client registration structure.
+ *
+ * @return	    SIP REGISTER request.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc);
+
+/**
+ * Update Contact details in the client registration structure.
+ *
+ * @param regc	    The client registration structure.
+ * @param ccnt	    Number of contacts.
+ * @param contact   Array of contacts.
+ * @return	    zero if sucessfull.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
+					        int ccnt,
+						const pj_str_t contact[] );
+
+/**
+ * Update the expires value.
+ *
+ * @param regc	    The client registration structure.
+ * @param expires   The new expires value.
+ * @return	    zero on successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
+					        pj_uint32_t expires );
+
+/**
+ * Sends outgoing REGISTER request.
+ * The process will complete asynchronously, and application
+ * will be notified via the callback when the process completes.
+ *
+ * @param regc	    The client registration structure.
+ * @param tdata	    Transmit data.
+ */
+PJ_DECL(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata);
+
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_REG_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_ua.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_ua.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_ua.c	(revision 1753)
@@ -0,0 +1,511 @@
+/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_ua.c 14    6/22/05 2:19a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip_mod_ua/sip_ua.h>
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_ua_private.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_transaction.h>
+#include <pj/list.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/os.h>
+#include <pj/hash.h>
+#include <pj/pool.h>
+
+#define PJSIP_POOL_LEN_USER_AGENT   1024
+#define PJSIP_POOL_INC_USER_AGENT   0
+
+
+#define LOG_THIS    "useragent.."
+
+/*
+ * Static prototypes.
+ */
+static pj_status_t ua_init( pjsip_endpoint *endpt,
+			    struct pjsip_module *mod, pj_uint32_t id );
+static pj_status_t ua_start( struct pjsip_module *mod );
+static pj_status_t ua_deinit( struct pjsip_module *mod );
+static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt );
+static pjsip_dlg *find_dialog( pjsip_user_agent *ua,
+			       pjsip_rx_data *rdata );
+static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
+				       pj_str_t *key );
+PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg );
+
+/*
+ * Default UA instance.
+ */
+static pjsip_user_agent ua_instance;
+
+/*
+ * Module interface.
+ */
+static struct pjsip_module mod_ua = 
+{
+    { "User-Agent", 10 },   /* Name.		*/
+    0,			    /* Flag		*/
+    128,		    /* Priority		*/
+    NULL,		    /* User agent instance, initialized by APP.	*/
+    0,			    /* Number of methods supported (will be initialized later). */
+    { 0 },		    /* Array of methods (will be initialized later) */
+    &ua_init,		    /* init_module()	*/
+    &ua_start,		    /* start_module()	*/
+    &ua_deinit,		    /* deinit_module()	*/
+    &ua_tsx_handler,	    /* tsx_handler()	*/
+};
+
+/*
+ * Initialize user agent instance.
+ */
+static pj_status_t ua_init( pjsip_endpoint *endpt,
+			    struct pjsip_module *mod, pj_uint32_t id )
+{
+    static pjsip_method m_invite, m_ack, m_cancel, m_bye;
+    pjsip_user_agent *ua = mod->mod_data;
+    extern int pjsip_dlg_lock_tls_id;	/* defined in sip_dialog.c */
+
+    pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD );
+    pjsip_method_set( &m_ack, PJSIP_ACK_METHOD );
+    pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD );
+    pjsip_method_set( &m_bye, PJSIP_BYE_METHOD );
+
+    mod->method_cnt = 4;
+    mod->methods[0] = &m_invite;
+    mod->methods[1] = &m_ack;
+    mod->methods[2] = &m_cancel;
+    mod->methods[3] = &m_bye;
+
+    /* Initialize the user agent. */
+    ua->endpt = endpt;
+    ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA, 
+				       PJSIP_POOL_INC_UA);
+    if (!ua->pool) {
+	return -1;
+    }
+    ua->mod_id = id;
+    ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0);
+    if (!ua->mutex) {
+	return -1;
+    }
+    ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT);
+    if (ua->dlg_table == NULL) {
+	return -1;
+    }
+    pj_list_init(&ua->dlg_list);
+
+    /* Initialize dialog lock. */
+    pjsip_dlg_lock_tls_id = pj_thread_local_alloc();
+    if (pjsip_dlg_lock_tls_id == -1) {
+	return -1;
+    }
+    pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL);
+
+    return 0;
+}
+
+/*
+ * Start user agent instance.
+ */
+static pj_status_t ua_start( struct pjsip_module *mod )
+{
+    PJ_UNUSED_ARG(mod)
+    return 0;
+}
+
+/*
+ * Destroy user agent.
+ */
+static pj_status_t ua_deinit( struct pjsip_module *mod )
+{
+    pjsip_user_agent *ua = mod->mod_data;
+
+    pj_mutex_unlock(ua->mutex);
+
+    /* Release pool */
+    if (ua->pool) {
+	pjsip_endpt_destroy_pool( ua->endpt, ua->pool );
+    }
+    return 0;
+}
+
+/*
+ * Get the module interface for the UA module.
+ */
+PJ_DEF(pjsip_module*) pjsip_ua_get_module(void)
+{
+    mod_ua.mod_data = &ua_instance;
+    return &mod_ua;
+}
+
+/*
+ * Register callback to receive dialog notifications.
+ */
+PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua, 
+					   pjsip_dlg_callback *cb )
+{
+    ua->dlg_cb = cb;
+}
+
+/* 
+ * Find dialog.
+ * This function is called for a new transactions, which a dialog hasn't been
+ * 'attached' to the transaction.
+ */
+static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata )
+{
+    pjsip_dlg *dlg;
+    pj_str_t *tag;
+
+    /* Non-CANCEL requests/response can be found by looking at the tag in the
+     * hash table. CANCEL requests don't have tags, so instead we'll try to
+     * find the UAS INVITE transaction in endpoint's hash table
+     */
+    if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) {
+
+	/* Create key for the rdata, but this time, use INVITE as the
+	 * method.
+	 */
+	pj_str_t key;
+	pjsip_role_e role;
+	pjsip_method invite_method;
+	pjsip_transaction *invite_tsx;
+
+	if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+	    role = PJSIP_ROLE_UAS;
+	} else {
+	    role = PJSIP_ROLE_UAC;
+	}
+	pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD);
+	pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata);
+
+	/* Lookup the INVITE transaction */
+	invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key);
+
+	/* We should find the dialog attached to the INVITE transaction */
+	return invite_tsx ? 
+	    (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL;
+
+    } else {
+	if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+	    tag = &rdata->to_tag;
+	} else {
+	    tag = &rdata->from_tag;
+	}
+	/* Find the dialog in UA hash table */
+	pj_mutex_lock(ua->mutex);
+	dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen );
+	pj_mutex_unlock(ua->mutex);
+    }
+    
+    return dlg;
+}
+
+/*
+ * This function receives event notification from transactions. It is called by
+ * endpoint.
+ */
+static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+    pjsip_user_agent *ua = mod->mod_data;
+    pjsip_dlg *dlg = NULL;
+    pjsip_transaction *tsx = event->obj.tsx;
+
+    PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)", 
+	       (tsx ? tsx->obj_name : "NULL"),  pjsip_event_str(event->type), 
+	       pjsip_event_str(event->src_type), event->src.data));
+
+    /* Special case to handle ACK which doesn't match any INVITE transactions. */
+    if (event->type == PJSIP_EVENT_RX_ACK_MSG) {
+	/* Find the dialog based on the "tag". */
+	dlg = find_dialog( ua, event->src.rdata );
+
+	/* We should be able to find it. */
+	if (!dlg) {
+	    PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK"));
+	    return;
+	}
+
+	/* Match CSeq with pending INVITE in dialog. */
+	if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) {
+	    /* A match found. */
+	    tsx = dlg->invite_tsx;
+
+	    /* Pass the event to transaction if transaction handles ACK. */
+	    if (tsx->handle_ack) {
+		PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction"));
+		pjsip_tsx_on_rx_msg(tsx, event->src.rdata);
+		return;
+	    }
+	} else {
+	    tsx = NULL;
+	    PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK"));
+	    return;
+	}
+    }
+
+    /* For discard event, transaction is NULL. */
+    if (tsx == NULL) {
+	return;
+    }
+
+    /* Try to pickup the dlg from the transaction. */
+    dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id];
+
+    if (dlg != NULL) {
+
+	/* Nothing to do now. */
+
+    } else if (event->src_type == PJSIP_EVENT_RX_MSG)  {
+
+	/* This must be a new UAS transaction. */
+
+	/* Finds dlg that can handle this transaction. */
+	dlg = find_dialog( ua, event->src.rdata);
+
+	/* Create a new dlg if there's no existing dlg that can handle 
+	   the request, ONLY if the incoming message is an INVITE request.
+	 */
+	if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) {
+
+	    if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
+		/* Create new dialog. */
+		dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS );
+
+		if (dlg == NULL ||
+		    pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0) 
+		{
+		    pjsip_tx_data *tdata;
+
+		    /* Dialog initialization has failed. Respond request with 500 */
+		    if (dlg) {
+			pjsip_ua_destroy_dialog(dlg);
+		    }
+		    tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, 
+							PJSIP_SC_INTERNAL_SERVER_ERROR);
+		    if (tdata) {
+			pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
+		    }
+		    return;
+		}
+
+	    } else {
+		pjsip_tx_data *tdata;
+
+		/* Check the method */
+		switch (tsx->method.id) {
+		case PJSIP_INVITE_METHOD:
+		case PJSIP_ACK_METHOD:
+		case PJSIP_BYE_METHOD:
+		case PJSIP_CANCEL_METHOD:
+		    /* Stale non-INVITE request.
+		     * For now, respond all stale requests with 481 (?).
+		     */
+		    tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, 
+							PJSIP_SC_CALL_TSX_DOES_NOT_EXIST);
+		    if (tdata) {
+			pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
+		    }
+		    break;
+		}
+
+		return;
+	    }
+	} else {
+	    /* Check the method */
+	    switch (tsx->method.id) {
+	    case PJSIP_INVITE_METHOD:
+	    case PJSIP_ACK_METHOD:
+	    case PJSIP_BYE_METHOD:
+	    case PJSIP_CANCEL_METHOD:
+		/* These methods belongs to dialog.
+		 * If we receive these methods while no dialog is found, 
+		 * then it must be a stale responses.
+		 */
+		break;
+	    default:
+		return;
+	    }
+
+	}
+	
+	if (dlg == NULL) {
+	    PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d",
+		       event->src.rdata, 
+		       pj_sockaddr_get_str_addr(&event->src.rdata->addr),
+		       pj_sockaddr_get_port(&event->src.rdata->addr)));
+	}
+
+	/* Set the dlg in the transaction (dlg can be NULL). */
+	tsx->module_data[ua->mod_id] = dlg;
+
+    } else {
+	/* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG
+	 * if UAS is responding to a transaction which does not exist. 
+	 * Just ignore.
+	 */
+	return;
+    }
+
+    /* Pass the event to the dlg. */
+    if (dlg) {
+	pjsip_dlg_on_tsx_event(dlg, tsx, event);
+    }
+}
+
+/*
+ * Register dialog to UA.
+ */
+static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
+				       pj_str_t *key )
+{
+    /* Assure that no entry with similar key exists in the hash table. */
+    pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0);
+
+    /* Insert entry to hash table. */
+    pj_hash_set( dlg->pool, ua->dlg_table, 
+		 key->ptr, key->slen, dlg);
+
+    /* Insert to the list. */
+    pj_list_insert_before(&ua->dlg_list, dlg);
+    return PJ_OK;
+}
+
+/*
+ * Create a new dialog.
+ */
+PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
+					      pjsip_role_e role )
+{
+    pj_pool_t *pool;
+    pjsip_dlg *dlg;
+
+    PJ_UNUSED_ARG(ua)
+
+    /* Create pool for the dialog. */
+    pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p",
+				    PJSIP_POOL_LEN_DIALOG,
+				    PJSIP_POOL_INC_DIALOG);
+
+    /* Create the dialog. */
+    dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg));
+    dlg->pool = pool;
+    dlg->ua = ua;
+    dlg->role = role;
+    sprintf(dlg->obj_name, "dlg%p", dlg);
+
+    /* Create mutex for the dialog. */
+    dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
+    if (!dlg->mutex) {
+	pjsip_endpt_destroy_pool(ua->endpt, pool);
+	return NULL;
+    }
+
+    /* Create unique tag for the dialog. */
+    pj_create_unique_string( pool, &dlg->local.tag );
+
+    /* Register dialog. */
+    pj_mutex_lock(ua->mutex);
+    if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_OK) {
+	pj_mutex_unlock(ua->mutex);
+	pj_mutex_destroy(dlg->mutex);
+	pjsip_endpt_destroy_pool( ua->endpt, pool );
+	return NULL;
+    }
+    pj_mutex_unlock(ua->mutex);
+
+    PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role)));
+    return dlg;
+}
+
+/*
+ * Destroy dialog.
+ */
+PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg )
+{
+    PJ_LOG(5, (dlg->obj_name, "destroying.."));
+
+    /* Lock dialog's mutex. 
+     * Check the mutex validity first since this function can be called
+     * on dialog initialization failure (which might be because mutex could not
+     * be allocated in the first place).
+     */
+    if (dlg->mutex) {
+	pj_mutex_lock(dlg->mutex);
+    }
+
+    /* This must be called while holding dialog's mutex, if any. */
+    pjsip_on_dialog_destroyed(dlg);
+
+    /* Lock UA. */
+    pj_mutex_lock(dlg->ua->mutex);
+
+    /* Erase from hash table. */
+    pj_hash_set( dlg->pool, dlg->ua->dlg_table,
+		 dlg->local.tag.ptr, dlg->local.tag.slen, NULL);
+
+    /* Erase from the list. */
+    pj_list_erase(dlg);
+
+    /* Unlock UA. */
+    pj_mutex_unlock(dlg->ua->mutex);
+
+    /* Unlock mutex. */
+    if (dlg->mutex) {
+	pj_mutex_unlock(dlg->mutex);
+    }
+
+    /* Destroy the pool. */
+    pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool);
+}
+
+/*
+ * Dump user agent state to log file.
+ */
+PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+    PJ_LOG(3,(LOG_THIS, "Dumping user agent"));
+    PJ_LOG(3,(LOG_THIS, "  Pool capacity=%u, used=%u", 
+			pj_pool_get_capacity(ua->pool),
+			pj_pool_get_used_size(ua->pool)));
+    PJ_LOG(3,(LOG_THIS, "  Number of dialogs=%u", pj_hash_count(ua->dlg_table)));
+
+    if (pj_hash_count(ua->dlg_table)) {
+	pjsip_dlg *dlg;
+
+	PJ_LOG(3,(LOG_THIS, "  Dumping dialog list:"));
+	dlg = ua->dlg_list.next;
+	while (dlg != (pjsip_dlg*) &ua->dlg_list) {
+	    PJ_LOG(3, (LOG_THIS, "    %s %s", dlg->obj_name, 
+		       pjsip_dlg_state_str(dlg->state)));
+	    dlg = dlg->next;
+	}
+    }
+#endif
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_ua.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_ua.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_ua.h	(revision 1753)
@@ -0,0 +1,102 @@
+/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_ua.h 6     6/17/05 11:16p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIP_UA_H__
+#define __PJSIP_SIP_UA_H__
+
+/**
+ * @file ua.h
+ * @brief SIP User Agent Library
+ */
+
+#include <pjsip_mod_ua/sip_dialog.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSUA SIP User Agent Stack
+ */
+
+/**
+ * @defgroup PJSUA_UA SIP User Agent
+ * @ingroup PJSUA
+ * @{
+ * \brief
+ *   User Agent manages the interactions between application and SIP dialogs.
+ */
+
+typedef struct pjsip_dlg_callback pjsip_dlg_callback;
+
+/**
+ * \brief This structure describes a User Agent instance.
+ */
+struct pjsip_user_agent
+{
+    pjsip_endpoint     *endpt;
+    pj_pool_t	       *pool;
+    pj_mutex_t	       *mutex;
+    pj_uint32_t		mod_id;
+    pj_hash_table_t    *dlg_table;
+    pjsip_dlg_callback *dlg_cb;
+    pj_list		dlg_list;
+};
+
+/**
+ * Create a new dialog.
+ */
+PJ_DECL(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
+					       pjsip_role_e role );
+
+
+/**
+ * Destroy dialog.
+ */
+PJ_DECL(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg );
+
+
+/** 
+ * Register callback to receive dialog notifications.
+ */
+PJ_DECL(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua, 
+					    pjsip_dlg_callback *cb );
+
+
+/**
+ * Get the module interface for the UA module.
+ */
+PJ_DECL(pjsip_module*) pjsip_ua_get_module(void);
+
+
+/**
+ * Dump user agent state to log file.
+ */
+PJ_DECL(void) pjsip_ua_dump( pjsip_user_agent *ua );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_UA_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_ua_private.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_ua_private.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_mod_ua/sip_ua_private.h	(revision 1753)
@@ -0,0 +1,42 @@
+/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_ua_private.h 3     3/25/05 12:51p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+
+#ifndef __PJSIP_UA_PRIVATE_H__
+#define __PJSIP_UA_PRIVATE_H__
+
+
+/*
+ * Internal dialog functions.
+ */
+pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg,
+				       pjsip_rx_data *rdata );
+
+
+void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, 
+			     pjsip_transaction *tsx, 
+			     pjsip_event *event);
+
+
+#endif	/* __PJSIP_UA_PRIVATE_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple.h	(revision 1753)
@@ -0,0 +1,46 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple.h 5     6/17/05 12:26a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+
+/**
+ * @defgroup PJSIP_SIMPLE SIP Event, Instant Messaging and Presence Extension (SIMPLE)
+ */
+
+/**
+ * @file pjsip_simple.h
+ * @brief SIP SIMPLE Extension
+ */
+
+/*
+ * Include this header file to get all functionalities for SIMPLE extension
+ * (SIP for Instant Messaging and Presence Leveraging Extension).
+ */
+#ifndef __PJSIP_SIMPLE_H__
+#define __PJSIP_SIMPLE_H__
+
+#include <pjsip_simple/messaging.h>
+#include <pjsip_simple/event_notify.h>
+#include <pjsip_simple/pidf.h>
+#include <pjsip_simple/presence.h>
+
+#endif	/* __PJSIP_SIMPLE_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify.c	(revision 1753)
@@ -0,0 +1,1649 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify.c 11    8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip_simple/event_notify.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pj/pool.h>
+#include <pj/timer.h>
+#include <pj/string.h>
+#include <pj/hash.h>
+#include <pj/os.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/guid.h>
+
+#define THIS_FILE		"event_sub"
+
+/* String names for state. 
+ * The names here should be compliant with sub_state names in RFC3265.
+ */
+static const pj_str_t state[] = {
+    { "null", 4 }, 
+    { "active", 6 },
+    { "pending", 7 },
+    { "terminated", 10 },
+    { "unknown", 7 }
+};
+
+/* Timer IDs */
+#define TIMER_ID_REFRESH	1
+#define TIMER_ID_UAS_EXPIRY	2
+
+/* Static configuration. */
+#define SECONDS_BEFORE_EXPIRY	10
+#define MGR_POOL_SIZE		512
+#define MGR_POOL_INC		0
+#define SUB_POOL_SIZE		2048
+#define SUB_POOL_INC		0
+#define HASH_TABLE_SIZE		32
+
+/* Static vars. */
+static int mod_id;
+static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}};
+static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}};
+
+typedef struct package
+{
+    PJ_DECL_LIST_MEMBER(struct package)
+    pj_str_t		    event;
+    int			    accept_cnt;
+    pj_str_t		   *accept;
+    pjsip_event_sub_pkg_cb  cb;
+} package;
+
+/* Event subscription manager singleton instance. */
+static struct pjsip_event_sub_mgr
+{
+    pj_pool_t		    *pool;
+    pj_hash_table_t	    *ht;
+    pjsip_endpoint	    *endpt;
+    pj_mutex_t		    *mutex;
+    pjsip_allow_events_hdr  *allow_events;
+    package		     pkg_list;
+} mgr;
+
+/* Fordward declarations for static functions. */
+static pj_status_t	mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t);
+static pj_status_t	mod_deinit(pjsip_module*);
+static void		tsx_handler(pjsip_module*, pjsip_event*);
+static pjsip_event_sub *find_sub(pjsip_rx_data *);
+static void		on_subscribe_request(pjsip_transaction*, pjsip_rx_data*);
+static void		on_subscribe_response(void *, pjsip_event*);
+static void		on_notify_request(pjsip_transaction *, pjsip_rx_data*);
+static void		on_notify_response(void *, pjsip_event *);
+static void		refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
+static void		uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
+static pj_status_t	send_sub_refresh( pjsip_event_sub *sub );
+
+/* Module descriptor. */
+static pjsip_module event_sub_module = 
+{
+    {"EventSub", 8},			/* Name.		*/
+    0,					/* Flag			*/
+    128,				/* Priority		*/
+    &mgr,				/* User data.		*/
+    2,					/* Number of methods supported . */
+    { &SUBSCRIBE, &NOTIFY },		/* Array of methods */
+    &mod_init,				/* init_module()	*/
+    NULL,				/* start_module()	*/
+    &mod_deinit,			/* deinit_module()	*/
+    &tsx_handler,			/* tsx_handler()	*/
+};
+
+/*
+ * Module initialization.
+ * This will be called by endpoint when it initializes all modules.
+ */
+static pj_status_t mod_init( pjsip_endpoint *endpt,
+			     struct pjsip_module *mod, pj_uint32_t id )
+{
+    pj_pool_t *pool;
+
+    pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC);
+    if (!pool)
+	return -1;
+
+    /* Manager initialization: create hash table and mutex. */
+    mgr.pool = pool;
+    mgr.endpt = endpt;
+    mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE);
+    if (!mgr.ht)
+	return -1;
+
+    mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE);
+    if (!mgr.mutex)
+	return -1;
+
+    /* Attach manager to module. */
+    mod->mod_data = &mgr;
+
+    /* Init package list. */
+    pj_list_init(&mgr.pkg_list);
+
+    /* Init Allow-Events header. */
+    mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool);
+
+    /* Save the module ID. */
+    mod_id = id;
+
+    pjsip_event_notify_init_parser();
+    return 0;
+}
+
+/*
+ * Module deinitialization.
+ * Called by endpoint.
+ */
+static pj_status_t mod_deinit( struct pjsip_module *mod )
+{
+    pj_mutex_lock(mgr.mutex);
+    pj_mutex_destroy(mgr.mutex);
+    pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool);
+    return 0;
+}
+
+/*
+ * This public function is called by application to register callback.
+ * In exchange, the instance of the module is returned.
+ */
+PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void)
+{
+    return &event_sub_module;
+}
+
+/*
+ * Register event package.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event,
+						  int accept_cnt,
+						  const pj_str_t accept[],
+						  const pjsip_event_sub_pkg_cb *cb )
+{
+    package *pkg;
+    int i;
+
+    pj_mutex_lock(mgr.mutex);
+
+    /* Create and register new package. */
+    pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg));
+    pj_strdup(mgr.pool, &pkg->event, event);
+    pj_list_insert_before(&mgr.pkg_list, pkg);
+
+    /* Save Accept specification. */
+    pkg->accept_cnt = accept_cnt;
+    pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t));
+    for (i=0; i<accept_cnt; ++i) {
+	pj_strdup(mgr.pool, &pkg->accept[i], &accept[i]);
+    }
+
+    /* Copy callback. */
+    pj_memcpy(&pkg->cb, cb, sizeof(*cb));
+
+    /* Update Allow-Events header. */
+    pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS);
+    mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event;
+
+    pj_mutex_unlock(mgr.mutex);
+    return 0;
+}
+
+/*
+ * Create subscription key (for hash table).
+ */
+static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool,
+				   pjsip_role_e role, 
+				   const pj_str_t *call_id, const pj_str_t *from_tag)
+{
+    char *p;
+
+    p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3);
+    *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C');
+    *p++ = '$';
+    pj_memcpy(p, call_id->ptr, call_id->slen);
+    p += call_id->slen;
+    *p++ = '$';
+    pj_memcpy(p, from_tag->ptr, from_tag->slen);
+    p += from_tag->slen;
+
+    key->slen = p - key->ptr;
+}
+
+
+/*
+ * Create UAC subscription.
+ */
+PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
+						 const pj_str_t *from,
+						 const pj_str_t *to,
+						 const pj_str_t *event,
+						 int expires,
+						 int accept_cnt,
+						 const pj_str_t accept[],
+						 void *user_data,
+						 const pjsip_event_sub_cb *cb)
+{
+    pjsip_tx_data *tdata;
+    pj_pool_t *pool;
+    const pjsip_hdr *hdr;
+    pjsip_event_sub *sub;
+    PJ_USE_EXCEPTION;
+
+    PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s",
+			 event->slen, event->ptr, to->slen, to->ptr));
+
+    /* Create pool for the event subscription. */
+    pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC);
+    if (!pool) {
+	return NULL;
+    }
+
+    /* Init subscription. */
+    sub = pj_pool_calloc(pool, 1, sizeof(*sub));
+    sub->pool = pool;
+    sub->endpt = endpt;
+    sub->role = PJSIP_ROLE_UAC;
+    sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
+    sub->state_str = state[sub->state];
+    sub->user_data = user_data;
+    sub->timer.id = 0;
+    sub->default_interval = expires;
+    pj_memcpy(&sub->cb, cb, sizeof(*cb));
+    pj_list_init(&sub->auth_sess);
+    pj_list_init(&sub->route_set);
+    sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
+    if (!sub->mutex) {
+	pjsip_endpt_destroy_pool(endpt, pool);
+	return NULL;
+    }
+
+    /* The easiest way to parse the parameters is to create a dummy request! */
+    tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from,
+					NULL, -1, NULL);
+    if (!tdata) {
+	pj_mutex_destroy(sub->mutex);
+	pjsip_endpt_destroy_pool(endpt, pool);
+	return NULL;
+    }
+
+    /* 
+     * Duplicate headers in the request to our structure. 
+     */
+    PJ_TRY {
+	int i;
+
+	/* From */
+	hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
+	pj_assert(hdr != NULL);
+	sub->from = pjsip_hdr_clone(pool, hdr);
+        
+	/* To */
+	hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL);
+	pj_assert(hdr != NULL);
+	sub->to = pjsip_hdr_clone(pool, hdr);
+
+	/* Contact. */
+	sub->contact = pjsip_contact_hdr_create(pool);
+	sub->contact->uri = sub->from->uri;
+
+	/* Call-ID */
+	hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL);
+	pj_assert(hdr != NULL);
+	sub->call_id = pjsip_hdr_clone(pool, hdr);
+
+	/* CSeq */
+	sub->cseq = pj_rand() % 0xFFFF;
+
+	/* Event. */
+	sub->event = pjsip_event_hdr_create(sub->pool);
+	pj_strdup(pool, &sub->event->event_type, event);
+
+	/* Expires. */
+	sub->uac_expires = pjsip_expires_hdr_create(pool);
+	sub->uac_expires->ivalue = expires;
+
+	/* Accept. */
+	sub->local_accept = pjsip_accept_hdr_create(pool);
+	for (i=0; i<accept_cnt && i < PJSIP_MAX_ACCEPT_COUNT; ++i) {
+	    sub->local_accept->count++;
+	    pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]);
+	}
+
+	/* Register to hash table. */
+	create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC, 
+			       &sub->call_id->id, &sub->from->tag);
+	pj_mutex_lock( mgr.mutex );
+	pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
+	pj_mutex_unlock( mgr.mutex );
+
+    }
+    PJ_DEFAULT {
+	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init", 
+			     sub, state[sub->state].ptr, PJ_GET_EXCEPTION()));
+
+	pjsip_tx_data_dec_ref(tdata);
+	pj_mutex_destroy(sub->mutex);
+	pjsip_endpt_destroy_pool(endpt, sub->pool);
+	return NULL;
+    }
+    PJ_END;
+
+    /* All set, delete temporary transmit data as we don't need it. */
+    pjsip_tx_data_dec_ref(tdata);
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s",
+			 sub, state[sub->state].ptr,
+			 to->slen, to->ptr, event->slen, event->ptr));
+
+    return sub;
+}
+
+/*
+ * Set credentials.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
+						     int count,
+						     const pjsip_cred_info cred[])
+{
+    pj_mutex_lock(sub->mutex);
+    if (count > 0) {
+	sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info));
+	pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info));
+    }
+    sub->cred_cnt = count;
+    pj_mutex_unlock(sub->mutex);
+    return 0;
+}
+
+/*
+ * Set route-set.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
+						   const pjsip_route_hdr *route_set )
+{
+    const pjsip_route_hdr *hdr;
+
+    pj_mutex_lock(sub->mutex);
+
+    /* Clear existing route set. */
+    pj_list_init(&sub->route_set);
+
+    /* Duplicate route headers. */
+    hdr = route_set->next;
+    while (hdr != route_set) {
+	pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr);
+	pj_list_insert_before(&sub->route_set, new_hdr);
+	hdr = hdr->next;
+    }
+
+    pj_mutex_unlock(sub->mutex);
+
+    return 0;
+}
+
+/*
+ * Send subscribe request.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub )
+{
+    pj_status_t status;
+
+    pj_mutex_lock(sub->mutex);
+    status = send_sub_refresh(sub);
+    pj_mutex_unlock(sub->mutex);
+
+    return status;
+}
+
+/*
+ * Destroy subscription.
+ * If there are pending transactions, then this will just set the flag.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub)
+{
+    pj_assert(sub != NULL);
+    if (sub == NULL)
+	return -1;
+
+    /* Application must terminate the subscription first. */
+    pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL ||
+	      sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed", 
+			 sub, state[sub->state].ptr));
+
+    pj_mutex_lock(mgr.mutex);
+    pj_mutex_lock(sub->mutex);
+
+    /* Set delete flag. */
+    sub->delete_flag = 1;
+
+    /* Unregister timer, if any. */
+    if (sub->timer.id != 0) {
+	pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+	sub->timer.id = 0;
+    }
+
+    if (sub->pending_tsx > 0) {
+	pj_mutex_unlock(sub->mutex);
+	pj_mutex_unlock(mgr.mutex);
+	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later",
+			     sub, state[sub->state].ptr,
+			     sub->pending_tsx));
+	return 1;
+    }
+
+    /* Unregister from hash table. */
+    pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL);
+
+    /* Destroy. */
+    pj_mutex_destroy(sub->mutex);
+    pjsip_endpt_destroy_pool(sub->endpt, sub->pool);
+
+    pj_mutex_unlock(mgr.mutex);
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub));
+    return 0;
+}
+
+/* Change state. */
+static void sub_set_state( pjsip_event_sub *sub, int new_state)
+{
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s",
+	      sub, state[sub->state].ptr, state[new_state].ptr));
+    sub->state = new_state;
+    sub->state_str = state[new_state];
+}
+
+/*
+ * Refresh subscription.
+ */
+static pj_status_t send_sub_refresh( pjsip_event_sub *sub )
+{
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+    const pjsip_route_hdr *route;
+
+    pj_assert(sub->role == PJSIP_ROLE_UAC);
+    pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED);
+    if (sub->role != PJSIP_ROLE_UAC || 
+	sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED)
+    {
+	return -1;
+    }
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription", 
+			 sub, state[sub->state].ptr));
+
+    /* Create request. */
+    tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, 
+						 &SUBSCRIBE,
+						 sub->to->uri,
+						 sub->from, sub->to, 
+						 sub->contact, sub->call_id,
+						 sub->cseq++,
+						 NULL);
+
+    if (!tdata) {
+	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!",
+			     sub, state[sub->state].ptr));
+	return -1;
+    }
+
+    pjsip_msg_add_hdr( tdata->msg, 
+		       pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+    pjsip_msg_add_hdr( tdata->msg, 
+		       pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
+    pjsip_msg_add_hdr( tdata->msg, 
+		       pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept));
+    pjsip_msg_add_hdr( tdata->msg, 
+		       pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
+
+    /* Authentication */
+    pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
+			 sub->cred_cnt, sub->cred_info);
+
+    /* Route set. */
+    route = sub->route_set.next;
+    while (route != &sub->route_set) {
+	pj_list_insert_before( &tdata->msg->hdr,
+			       pjsip_hdr_shallow_clone(tdata->pool, route));
+	route = route->next;
+    }
+
+    /* Send */
+    status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, 
+				       &on_subscribe_response);
+    if (status == 0) {
+	sub->pending_tsx++;
+    } else {
+	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!", 
+			     sub, state[sub->state].ptr));
+    }
+
+    return status;
+}
+
+/*
+ * Stop subscription.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub )
+{
+    pjsip_tx_data *tdata;
+    const pjsip_route_hdr *route;
+    pj_status_t status;
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...", 
+			 sub, state[sub->state].ptr));
+
+    /* Lock subscription. */
+    pj_mutex_lock(sub->mutex);
+
+    pj_assert(sub->role == PJSIP_ROLE_UAC);
+
+    /* Kill refresh timer, if any. */
+    if (sub->timer.id != 0) {
+	sub->timer.id = 0;
+	pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+    }
+
+    /* Create request. */
+    tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, 
+						 &SUBSCRIBE,
+						 sub->to->uri,
+						 sub->from, sub->to, 
+						 sub->contact, sub->call_id,
+						 sub->cseq++,
+						 NULL);
+
+    if (!tdata) {
+	pj_mutex_unlock(sub->mutex);
+	return -1;
+    }
+
+    /* Add headers to request. */
+    pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+    sub->uac_expires->ivalue = 0;
+    pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
+
+    /* Add authentication. */
+    pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
+			 sub->cred_cnt, sub->cred_info);
+
+
+    /* Route set. */
+    route = sub->route_set.next;
+    while (route != &sub->route_set) {
+	pj_list_insert_before( &tdata->msg->hdr,
+			       pjsip_hdr_shallow_clone(tdata->pool, route));
+	route = route->next;
+    }
+
+    /* Prevent timer from refreshing itself. */
+    sub->default_interval = 0;
+
+    /* Set state. */
+    sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED );
+
+    /* Send the request. */
+    status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, 
+				       &on_subscribe_response);
+    if (status == 0) {
+	sub->pending_tsx++;
+    }
+
+    pj_mutex_unlock(sub->mutex);
+
+    if (status != 0) {
+	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!", 
+			     sub, state[sub->state].ptr));
+    }
+
+    return status;
+}
+
+/*
+ * Send notify.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub,
+					   pjsip_event_sub_state new_state,
+					   const pj_str_t *reason,
+					   pjsip_msg_body *body)
+{
+    pjsip_tx_data *tdata;
+    pjsip_sub_state_hdr *ss_hdr;
+    const pjsip_route_hdr *route;
+    pj_time_val now;
+    pj_status_t status;
+    pjsip_event_sub_state old_state = sub->state;
+
+    pj_gettimeofday(&now);
+
+    pj_assert(sub->role == PJSIP_ROLE_UAS);
+    if (sub->role != PJSIP_ROLE_UAS)
+	return -1;
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY", 
+			 sub, state[new_state].ptr));
+
+    /* Lock subscription. */
+    pj_mutex_lock(sub->mutex);
+
+    /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */
+    if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) {
+	pj_assert(0);
+	pj_mutex_unlock(sub->mutex);
+	return -1;
+    }
+
+    /* Update state no matter what. */
+    sub_set_state(sub, new_state);
+
+    /* Create transmit data. */
+    tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
+						 &NOTIFY,
+						 sub->to->uri,
+						 sub->from, sub->to,
+						 sub->contact, sub->call_id,
+						 sub->cseq++,
+						 NULL);
+    if (!tdata) {
+	pj_mutex_unlock(sub->mutex);
+	return -1;
+    }
+
+    /* Add Event header. */
+    pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+
+    /* Add Subscription-State header. */
+    ss_hdr = pjsip_sub_state_hdr_create(tdata->pool);
+    ss_hdr->sub_state = state[new_state];
+    ss_hdr->expires_param = sub->expiry_time.sec - now.sec;
+    if (ss_hdr->expires_param < 0)
+	ss_hdr->expires_param = 0;
+    if (reason)
+	pj_strdup(tdata->pool, &ss_hdr->reason_param, reason);
+    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr);
+
+    /* Add Allow-Events header. */
+    pjsip_msg_add_hdr( tdata->msg, 
+		       pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
+
+    /* Add authentication */
+    pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
+			 sub->cred_cnt, sub->cred_info);
+
+    /* Route set. */
+    route = sub->route_set.next;
+    while (route != &sub->route_set) {
+	pj_list_insert_before( &tdata->msg->hdr,
+			       pjsip_hdr_shallow_clone(tdata->pool, route));
+	route = route->next;
+    }
+
+    /* Attach body. */
+    tdata->msg->body = body;
+
+    /* That's it, send! */
+    status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response);
+    if (status == 0)
+	sub->pending_tsx++;
+
+    /* If terminated notify application. */
+    if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
+	if (sub->cb.on_sub_terminated) {
+	    sub->pending_tsx++;
+	    (*sub->cb.on_sub_terminated)(sub, reason);
+	    sub->pending_tsx--;
+	}
+    }
+
+    /* Unlock subscription. */
+    pj_mutex_unlock(sub->mutex);
+
+    if (status != 0) {
+	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY", 
+			     sub, state[sub->state].ptr));
+    }
+
+    if (sub->delete_flag && sub->pending_tsx <= 0) {
+	pjsip_event_sub_destroy(sub);
+    }
+    return status;
+}
+
+
+/* If this timer callback is called, it means subscriber hasn't refreshed its
+ * subscription on-time. Set the state to terminated. This will also send
+ * NOTIFY with Subscription-State set to terminated.
+ */
+static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
+{
+    pjsip_event_sub *sub = entry->user_data;
+    pj_str_t reason = { "timeout", 7 };
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!", 
+			 sub, state[sub->state].ptr));
+
+    pj_mutex_lock(sub->mutex);
+    sub->timer.id = 0;
+
+    if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) {
+	/* Notify application, but prevent app from destroying the sub. */
+	++sub->pending_tsx;
+	(*sub->cb.on_sub_terminated)(sub, &reason);
+	--sub->pending_tsx;
+    }
+    //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED, 
+    //			    &reason, NULL);
+    pj_mutex_unlock(sub->mutex);
+
+}
+
+/* Schedule notifier expiration. */
+static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay)
+{
+    pj_time_val delay = { 0, 0 };
+    pj_parsed_time pt;
+
+    if (sub->timer.id != 0)
+	pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+
+    pj_gettimeofday(&sub->expiry_time);
+    sub->expiry_time.sec += sec_delay;
+
+    sub->timer.id = TIMER_ID_UAS_EXPIRY;
+    sub->timer.user_data = sub;
+    sub->timer.cb = &uas_expire_timer_cb;
+    delay.sec = sec_delay;
+    pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay);
+
+    pj_time_decode(&sub->expiry_time, &pt);
+    PJ_LOG(4,(THIS_FILE, 
+	      "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)",
+	      sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay));
+}
+
+/* This timer is called for UAC to refresh the subscription. */
+static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
+{
+    pjsip_event_sub *sub = entry->user_data;
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer", 
+			 sub, state[sub->state].ptr));
+
+    pj_mutex_lock(sub->mutex);
+    sub->timer.id = 0;
+    send_sub_refresh(sub);
+    pj_mutex_unlock(sub->mutex);
+}
+
+
+/* This will update the UAC's refresh schedule. */
+static void update_next_refresh(pjsip_event_sub *sub, int interval)
+{
+    pj_time_val delay = {0, 0};
+    pj_parsed_time pt;
+
+    if (interval < SECONDS_BEFORE_EXPIRY) {
+	PJ_LOG(4,(THIS_FILE, 
+		  "event_sub%p (%s): expiration delay too short (%d sec)! updated.",
+		  sub, state[sub->state].ptr, interval));
+	interval = SECONDS_BEFORE_EXPIRY;
+    }
+
+    if (sub->timer.id != 0)
+	pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+
+    sub->timer.id = TIMER_ID_REFRESH;
+    sub->timer.user_data = sub;
+    sub->timer.cb = &refresh_timer_cb;
+    pj_gettimeofday(&sub->expiry_time);
+    delay.sec = interval - SECONDS_BEFORE_EXPIRY;
+    sub->expiry_time.sec += delay.sec;
+
+    pj_time_decode(&sub->expiry_time, &pt);
+    PJ_LOG(4,(THIS_FILE, 
+	      "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)",
+	      sub, state[sub->state].ptr, 
+	      pt.hour, pt.min, pt.sec,
+	      delay.sec));
+
+    pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay );
+}
+
+
+/* Find subscription in the hash table. 
+ * If found, lock the subscription before returning to caller.
+ */
+static pjsip_event_sub *find_sub(pjsip_rx_data *rdata)
+{
+    pj_str_t key;
+    pjsip_role_e role;
+    pjsip_event_sub *sub;
+    pjsip_method *method = &rdata->msg->line.req.method;
+    pj_str_t *tag;
+
+    if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+	if (pjsip_method_cmp(method, &SUBSCRIBE)==0) {
+	    role = PJSIP_ROLE_UAS;
+	    tag = &rdata->to_tag;
+	} else {
+	    pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
+	    role = PJSIP_ROLE_UAC;
+	    tag = &rdata->to_tag;
+	}
+    } else {
+	if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) {
+	    role = PJSIP_ROLE_UAC;
+	    tag = &rdata->from_tag;
+	} else {
+	    pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
+	    role = PJSIP_ROLE_UAS;
+	    tag = &rdata->from_tag;
+	}
+    }
+    create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag);
+
+    pj_mutex_lock(mgr.mutex);
+    sub = pj_hash_get(mgr.ht, key.ptr, key.slen);
+    if (sub)
+	pj_mutex_lock(sub->mutex);
+    pj_mutex_unlock(mgr.mutex);
+
+    return sub;
+}
+
+
+/* This function is called when we receive SUBSCRIBE request message 
+ * to refresh existing subscription.
+ */
+static void on_received_sub_refresh( pjsip_event_sub *sub, 
+				     pjsip_transaction *tsx, pjsip_rx_data *rdata)
+{
+    pjsip_event_hdr *e;
+    pjsip_expires_hdr *expires;
+    pj_str_t hname;
+    int status = 200;
+    pj_str_t reason_phrase = { NULL, 0 };
+    int new_state = sub->state;
+    int old_state = sub->state;
+    int new_interval = 0;
+    pjsip_tx_data *tdata;
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh", 
+			 sub, state[sub->state].ptr));
+
+    /* Check that the event matches. */
+    hname = pj_str("Event");
+    e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
+    if (!e) {
+	status = 400;
+	reason_phrase = pj_str("Missing Event header");
+	goto send_response;
+    }
+    if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 ||
+	pj_stricmp(&e->id_param, &sub->event->id_param) != 0)
+    {
+	status = 481;
+	reason_phrase = pj_str("Subscription does not exist");
+	goto send_response;
+    }
+
+    /* Check server state. */
+    if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) {
+	status = 481;
+	reason_phrase = pj_str("Subscription does not exist");
+	goto send_response;
+    }
+
+    /* Check expires header. */
+    expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL);
+    if (!expires) {
+	/*
+	status = 400;
+	reason_phrase = pj_str("Missing Expires header");
+	goto send_response;
+	*/
+	new_interval = sub->default_interval;
+    } else {
+	/* Check that interval is not too short. 
+	 * Note that expires time may be zero (for unsubscription).
+	 */
+	new_interval = expires->ivalue;
+	if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) {
+	    status = PJSIP_SC_INTERVAL_TOO_BRIEF;
+	    goto send_response;
+	}
+    }
+
+    /* Update interval. */
+    sub->default_interval = new_interval;
+    pj_gettimeofday(&sub->expiry_time);
+    sub->expiry_time.sec += new_interval;
+
+    /* Update timer only if this is not unsubscription. */
+    if (new_interval > 0) {
+	sub->default_interval = new_interval;
+	sub_schedule_uas_expire( sub, new_interval );
+
+	/* Call callback. */
+	if (sub->cb.on_received_refresh) {
+	    sub->pending_tsx++;
+	    (*sub->cb.on_received_refresh)(sub, rdata);
+	    sub->pending_tsx--;
+	}
+    }
+
+send_response:
+    tdata = pjsip_endpt_create_response( sub->endpt, rdata, status);
+    if (tdata) {
+	if (reason_phrase.slen)
+	    tdata->msg->line.status.reason = reason_phrase;
+
+	/* Add Expires header. */
+	expires = pjsip_expires_hdr_create(tdata->pool);
+	expires->ivalue = sub->default_interval;
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires);
+
+	if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+	    pjsip_msg_add_hdr(tdata->msg, 
+			      pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
+	}
+	/* Send down to transaction. */
+	pjsip_tsx_on_tx_msg(tsx, tdata);
+    }
+
+    if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+	/* Notify application if sub is terminated. */
+	new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+	sub_set_state(sub, new_state);
+	if (new_state!=old_state && sub->cb.on_sub_terminated) {
+	    pj_str_t reason = {"", 0};
+	    if (reason_phrase.slen) reason = reason_phrase;
+	    else reason = *pjsip_get_status_text(status);
+
+	    sub->pending_tsx++;
+	    (*sub->cb.on_sub_terminated)(sub, &reason);
+	    sub->pending_tsx--;
+	}
+    }
+
+    pj_mutex_unlock(sub->mutex);
+
+    /* Prefer to call log when we're not holding the mutex. */
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d", 
+			 sub, state[sub->state].ptr,
+			 (tdata ? tdata->obj_name : "null"), status));
+
+    /* Check if application has requested deletion. */
+    if (sub->delete_flag && sub->pending_tsx <= 0) {
+	pjsip_event_sub_destroy(sub);
+    }
+
+}
+
+
+/* This function is called when we receive SUBSCRIBE request message for 
+ * a new subscription.
+ */
+static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata )
+{
+    package *pkg;
+    pj_pool_t *pool;
+    pjsip_event_sub *sub = NULL;
+    pj_str_t hname;
+    int status = 200;
+    pj_str_t reason = { NULL, 0 };
+    pjsip_tx_data *tdata;
+    pjsip_expires_hdr *expires;
+    pjsip_accept_hdr *accept;
+    pjsip_event_hdr *evhdr;
+
+    /* Get the Event header. */
+    hname = pj_str("Event");
+    evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
+    if (!evhdr) {
+	status = 400;
+	reason = pj_str("No Event header in request");
+	goto send_response;
+    }
+
+    /* Find corresponding package. 
+     * We don't lock the manager's mutex since we assume the package list
+     * won't change once the application is running!
+     */
+    pkg = mgr.pkg_list.next;
+    while (pkg != &mgr.pkg_list) {
+	if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0)
+	    break;
+	pkg = pkg->next;
+    }
+
+    if (pkg == &mgr.pkg_list) {
+	/* Event type is not supported by any packages! */
+	status = 489;
+	reason = pj_str("Bad Event");
+	goto send_response;
+    }
+
+    /* First check that the Accept specification matches the 
+     * package's Accept types.
+     */
+    accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
+    if (accept) {
+	unsigned i;
+	pj_str_t *content_type = NULL;
+
+	for (i=0; i<accept->count && !content_type; ++i) {
+	    int j;
+	    for (j=0; j<pkg->accept_cnt; ++j) {
+		if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) {
+		    content_type = &pkg->accept[j];
+		    break;
+		}
+	    }
+	}
+
+	if (!content_type) {
+	    status = PJSIP_SC_NOT_ACCEPTABLE_HERE;
+	    goto send_response;
+	}
+    }
+
+    /* Check whether the package wants to accept the subscription. */
+    pj_assert(pkg->cb.on_query_subscribe != NULL);
+    (*pkg->cb.on_query_subscribe)(rdata, &status);
+    if (!PJSIP_IS_STATUS_IN_CLASS(status,200))
+	goto send_response;
+
+    /* Create new subscription record. */
+    pool = pjsip_endpt_create_pool(tsx->endpt, "esub", 
+				   SUB_POOL_SIZE, SUB_POOL_INC);
+    if (!pool) {
+	status = 500;
+	goto send_response;
+    }
+    sub = pj_pool_calloc(pool, 1, sizeof(*sub));
+    sub->pool = pool;
+    sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
+    if (!sub->mutex) {
+	status = 500;
+	goto send_response;
+    }
+
+    PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub));
+
+    /* Start locking mutex. */
+    pj_mutex_lock(sub->mutex);
+
+    /* Init UAS subscription */
+    sub->endpt = tsx->endpt;
+    sub->role = PJSIP_ROLE_UAS;
+    sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
+    sub->state_str = state[sub->state];
+    pj_list_init(&sub->auth_sess);
+    pj_list_init(&sub->route_set);
+    sub->from = pjsip_hdr_clone(pool, rdata->to);
+    pjsip_fromto_set_from(sub->from);
+    if (sub->from->tag.slen == 0) {
+	pj_create_unique_string(pool, &sub->from->tag);
+	rdata->to->tag = sub->from->tag;
+    }
+    sub->to = pjsip_hdr_clone(pool, rdata->from);
+    pjsip_fromto_set_to(sub->to);
+    sub->contact = pjsip_contact_hdr_create(pool);
+    sub->contact->uri = sub->from->uri;
+    sub->call_id = pjsip_cid_hdr_create(pool);
+    pj_strdup(pool, &sub->call_id->id, &rdata->call_id);
+    sub->cseq = pj_rand() % 0xFFFF;
+    
+    expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL);
+    if (expires) {
+	sub->default_interval = expires->ivalue;
+	if (sub->default_interval > 0 && 
+	    sub->default_interval < SECONDS_BEFORE_EXPIRY) 
+	{
+	    status = 423; /* Interval too short. */
+	    goto send_response;
+	}
+    } else {
+	sub->default_interval = 600;
+    }
+
+    /* Clone Event header. */
+    sub->event = pjsip_hdr_clone(pool, evhdr);
+
+    /* Register to hash table. */
+    create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id,
+			  &sub->from->tag);
+    pj_mutex_lock(mgr.mutex);
+    pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
+    pj_mutex_unlock(mgr.mutex);
+
+    /* Set timer where subscription will expire only when expires<>0. 
+     * Subscriber may send new subscription with expires==0.
+     */
+    if (sub->default_interval != 0) {
+	sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY);
+    }
+
+    /* Notify application. */
+    if (pkg->cb.on_subscribe) {
+	pjsip_event_sub_cb *cb = NULL;
+	sub->pending_tsx++;
+	(*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval);
+	sub->pending_tsx--;
+	if (cb == NULL)
+	    pj_memset(&sub->cb, 0, sizeof(*cb));
+	else
+	    pj_memcpy(&sub->cb, cb, sizeof(*cb));
+    }
+
+
+send_response:
+    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d", 
+			  sub, state[sub->state].ptr, status));
+
+    tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status);
+    if (tdata) {
+	if (reason.slen) {
+	    /* Customize reason text. */
+	    tdata->msg->line.status.reason = reason;
+	}
+	if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+	    /* Add Expires header. */
+	    pjsip_expires_hdr *hdr;
+
+	    hdr = pjsip_expires_hdr_create(tdata->pool);
+	    hdr->ivalue = sub->default_interval;
+	    pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr );
+	}
+	if (status == 423) {
+	    /* Add Min-Expires header. */
+	    pjsip_min_expires_hdr *hdr;
+
+	    hdr = pjsip_min_expires_hdr_create(tdata->pool);
+	    hdr->ivalue = SECONDS_BEFORE_EXPIRY;
+	    pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr);
+	}
+	if (status == 489 || 
+	    status==PJSIP_SC_NOT_ACCEPTABLE_HERE ||
+	    PJSIP_IS_STATUS_IN_CLASS(status,200)) 
+	{
+	    /* Add Allow-Events header. */
+	    pjsip_hdr *hdr;
+	    hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
+	    pjsip_msg_add_hdr(tdata->msg, hdr);
+
+	    /* Should add Accept header?. */
+	}
+
+	pjsip_tsx_on_tx_msg(tsx, tdata);
+    }
+
+    /* If received new subscription with expires=0, terminate. */
+    if (sub && sub->default_interval == 0) {
+	pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
+	if (sub->cb.on_sub_terminated) {
+	    pj_str_t reason = { "timeout", 7 };
+	    (*sub->cb.on_sub_terminated)(sub, &reason);
+	}
+    }
+
+    if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) {
+	if (sub && sub->mutex) {
+	    pjsip_event_sub_destroy(sub);
+	} else if (sub) {
+	    pjsip_endpt_destroy_pool(tsx->endpt, sub->pool);
+	}
+    } else {
+	pj_assert(status >= 200);
+	pj_mutex_unlock(sub->mutex);
+    }
+}
+
+/* This is the main callback when SUBSCRIBE request is received. */
+static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
+{
+    pjsip_event_sub *sub = find_sub(rdata);
+
+    if (sub)
+	on_received_sub_refresh(sub, tsx, rdata);
+    else
+	on_new_subscription(tsx, rdata);
+}
+
+
+/* This callback is called when response to SUBSCRIBE is received. */
+static void on_subscribe_response(void *token, pjsip_event *event)
+{
+    pjsip_event_sub *sub = token;
+    pjsip_transaction *tsx = event->obj.tsx;
+    int new_state, old_state = sub->state;
+
+    pj_assert(tsx->status_code >= 200);
+    if (tsx->status_code < 200)
+	return;
+
+    pj_assert(sub->role == PJSIP_ROLE_UAC);
+
+    /* Lock mutex. */
+    pj_mutex_lock(sub->mutex);
+
+    /* If request failed with 401/407 error, silently retry the request. */
+    if (tsx->status_code==401 || tsx->status_code==407) {
+	pjsip_tx_data *tdata;
+	tdata = pjsip_auth_reinit_req(sub->endpt,
+				      sub->pool, &sub->auth_sess,
+				      sub->cred_cnt, sub->cred_info,
+				      tsx->last_tx, event->src.rdata );
+	if (tdata) {
+	    int status;
+	    pjsip_cseq_hdr *cseq;
+	    cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+	    cseq->cseq = sub->cseq++;
+	    status = pjsip_endpt_send_request( sub->endpt, tdata, 
+					       -1, sub, 
+					       &on_subscribe_response);
+	    if (status == 0) {
+		pj_mutex_unlock(sub->mutex);
+		return;
+	    }
+	}
+    }
+
+    if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
+	/* Update To tag. */
+	if (sub->to->tag.slen == 0)
+	    pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag);
+
+	new_state = sub->state;
+
+    } else if (tsx->status_code == 481) {
+	new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+
+    } else if (tsx->status_code >= 300) {
+	/* RFC 3265 Section 3.1.4.2:
+         * If a SUBSCRIBE request to refresh a subscription fails 
+	 * with a non-481 response, the original subscription is still 
+	 * considered valid for the duration of original exires.
+	 *
+	 * Note:
+	 * Since we normally send SUBSCRIBE for refreshing the subscription,
+	 * it means the subscription already expired anyway. So we terminate
+	 * the subscription now.
+	 */
+	if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) {
+	    new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+	} else {
+	    /* Use this to be compliant with Section 3.1.4.2
+	      new_state = sub->state;
+	     */
+	    new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+	}
+    } else {
+	pj_assert(0);
+	new_state = sub->state;
+    }
+
+    if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) {
+	sub_set_state(sub, new_state);
+    }
+
+    if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE ||
+	sub->state == PJSIP_EVENT_SUB_STATE_PENDING)
+    {
+	/*
+	 * Register timer for next subscription refresh, but only when
+	 * we're not unsubscribing. Also update default_interval and Expires
+	 * header.
+	 */
+	if (sub->default_interval > 0 && !sub->delete_flag) {
+	    pjsip_expires_hdr *exp = NULL;
+	    
+	    /* Could be transaction timeout. */
+	    if (event->src_type == PJSIP_EVENT_RX_MSG) {
+		exp = pjsip_msg_find_hdr(event->src.rdata->msg,
+					 PJSIP_H_EXPIRES, NULL);
+	    }
+
+	    if (exp) {
+		int delay = exp->ivalue;
+		if (delay > 0) {
+		    pj_time_val new_expiry;
+		    pj_gettimeofday(&new_expiry);
+		    new_expiry.sec += delay;
+		    if (sub->timer.id==0 || 
+			new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) 
+		    {
+		    //if (delay > 0 && delay < sub->default_interval) {
+			sub->default_interval = delay;
+			sub->uac_expires->ivalue = delay;
+			update_next_refresh(sub, delay);
+		    }
+		}
+	    }
+	}
+    }
+
+    /* Call callback. */
+    if (!sub->delete_flag) {
+	if (sub->cb.on_received_sub_response) {
+	    (*sub->cb.on_received_sub_response)(sub, event);
+	}
+    }
+
+    /* Notify application if we're terminated. */
+    if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
+	if (sub->cb.on_sub_terminated) {
+	    pj_str_t reason;
+	    if (event->src_type == PJSIP_EVENT_RX_MSG)
+		reason = event->src.rdata->msg->line.status.reason;
+	    else
+		reason = *pjsip_get_status_text(tsx->status_code);
+
+	    (*sub->cb.on_sub_terminated)(sub, &reason);
+	}
+    }
+
+    /* Decrement pending tsx count. */
+    --sub->pending_tsx;
+    pj_assert(sub->pending_tsx >= 0);
+
+    if (sub->delete_flag && sub->pending_tsx <= 0) {
+	pjsip_event_sub_destroy(sub);
+    } else {
+	pj_mutex_unlock(sub->mutex);
+    }
+
+    /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */
+}
+
+/*
+ * This callback called when we receive incoming NOTIFY request.
+ */
+static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
+{
+    pjsip_event_sub *sub;
+    pjsip_tx_data *tdata;
+    int status = 200;
+    int old_state;
+    pj_str_t reason = { NULL, 0 };
+    pj_str_t reason_phrase = { NULL, 0 };
+    int new_state = PJSIP_EVENT_SUB_STATE_NULL;
+
+    /* Find subscription based on Call-ID and From tag. 
+     * This will also automatically lock the subscription, if it's found.
+     */
+    sub = find_sub(rdata);
+    if (!sub) {
+	/* RFC 3265: Section 3.2 Description of NOTIFY Behavior:
+	 * Answer with 481 Subscription does not exist.
+	 */
+	PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!"));
+	status = 481;
+	reason_phrase = pj_str("Subscription does not exist");
+
+    } else {
+	pj_assert(sub->role == PJSIP_ROLE_UAC);
+	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY", 
+			     sub, state[sub->state].ptr));
+
+    }
+
+    new_state = old_state = sub->state;
+
+    /* RFC 3265: Section 3.2.1
+     * Check that the Event header match the subscription. 
+     */
+    if (status == 200) {
+	pjsip_event_hdr *hdr;
+	pj_str_t hname = { "Event", 5 };
+
+	hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
+	if (!hdr) {
+	    status = PJSIP_SC_BAD_REQUEST;
+	    reason_phrase = pj_str("No Event header found");
+	} else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 ||
+		   pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0) 
+	{
+	    status = 481;
+	    reason_phrase = pj_str("Subscription does not exist");
+	}
+    }
+
+    /* Update subscription state and timer. */
+    if (status == 200) {
+	pjsip_sub_state_hdr *hdr;
+	const pj_str_t hname = { "Subscription-State", 18 };
+	const pj_str_t state_active = { "active", 6 },
+		       state_pending = { "pending", 7},
+		       state_terminated = { "terminated", 10 };
+
+	hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
+	if (!hdr) {
+	    status = PJSIP_SC_BAD_REQUEST;
+	    reason_phrase = pj_str("No Subscription-State header found");
+	    goto process;
+	} 
+
+	/*
+	 * Update subscription state.
+	 */
+	if (pj_stricmp(&hdr->sub_state, &state_active) == 0) {
+	    if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
+		new_state = PJSIP_EVENT_SUB_STATE_ACTIVE;
+	} else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) {
+	    if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
+		new_state = PJSIP_EVENT_SUB_STATE_PENDING;
+	} else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) {
+	    new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+	} else {
+	    new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN;
+	}
+
+	reason = hdr->reason_param;
+
+	if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL &&
+	    sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) 
+	{
+	    sub_set_state(sub, new_state);
+	    if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) {
+		pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state);
+	    } else {
+		sub->state_str = state[new_state];
+	    }
+	}
+
+	/*
+	 * Update timeout timer in required, just in case notifier changed the 
+         * expiration to shorter time.
+	 * Section 3.2.2: the expires param can only shorten the interval.
+	 */
+	if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE || 
+	     sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0) 
+	{
+	    pj_time_val now, new_expiry;
+
+	    pj_gettimeofday(&now);
+	    new_expiry.sec = now.sec + hdr->expires_param;
+	    if (sub->timer.id==0 || 
+		new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) 
+	    {
+		update_next_refresh(sub, hdr->expires_param);
+	    }
+	}
+    }
+
+process:
+    /* Note: here we sub MAY BE NULL! */
+
+    /* Send response to NOTIFY */
+    tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status );
+    if (tdata) {
+	if (reason_phrase.slen)
+	    tdata->msg->line.status.reason = reason_phrase;
+
+	if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+	    pjsip_hdr *hdr;
+	    hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
+	    pjsip_msg_add_hdr( tdata->msg, hdr);
+	}
+
+	pjsip_tsx_on_tx_msg(tsx, tdata);
+    }
+
+    /* Call NOTIFY callback, if any. */
+    if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) {
+	sub->pending_tsx++;
+	(*sub->cb.on_received_notify)(sub, rdata);
+	sub->pending_tsx--;
+    }
+
+    /* Check if subscription is terminated and call callback. */
+    if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
+	if (sub->cb.on_sub_terminated) {
+	    sub->pending_tsx++;
+	    (*sub->cb.on_sub_terminated)(sub, &reason);
+	    sub->pending_tsx--;
+	}
+    }
+
+    /* Check if application has requested deletion. */
+    if (sub && sub->delete_flag && sub->pending_tsx <= 0) {
+	pjsip_event_sub_destroy(sub);
+    } else if (sub) {
+	pj_mutex_unlock(sub->mutex);
+    }
+}
+
+/* This callback is called when we received NOTIFY response. */
+static void on_notify_response(void *token, pjsip_event *event)
+{
+    pjsip_event_sub *sub = token;
+    pjsip_event_sub_state old_state = sub->state;
+    pjsip_transaction *tsx = event->obj.tsx;
+
+    /* Lock the subscription. */
+    pj_mutex_lock(sub->mutex);
+
+    pj_assert(sub->role == PJSIP_ROLE_UAS);
+
+    /* If request failed with authorization failure, silently retry. */
+    if (tsx->status_code==401 || tsx->status_code==407) {
+	pjsip_tx_data *tdata;
+	tdata = pjsip_auth_reinit_req(sub->endpt,
+				      sub->pool, &sub->auth_sess,
+				      sub->cred_cnt, sub->cred_info,
+				      tsx->last_tx, event->src.rdata );
+	if (tdata) {
+	    int status;
+	    pjsip_cseq_hdr *cseq;
+	    cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+	    cseq->cseq = sub->cseq++;
+	    status = pjsip_endpt_send_request( sub->endpt, tdata, 
+					       -1, sub, 
+					       &on_notify_response);
+	    if (status == 0) {
+		pj_mutex_unlock(sub->mutex);
+		return;
+	    }
+	}
+    }
+
+    /* Notify application. */
+    if (sub->cb.on_received_notify_response)
+	(*sub->cb.on_received_notify_response)(sub, event);
+
+    /* Check for response 481. */
+    if (event->obj.tsx->status_code == 481) {
+	/* Remote says that the subscription does not exist! 
+	 * Terminate subscription!
+	 */
+	sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED);
+	if (sub->timer.id) {
+	    pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+	    sub->timer.id = 0;
+	}
+
+	PJ_LOG(4, (THIS_FILE, 
+		   "event_sub%p (%s): got 481 response to NOTIFY. Terminating...",
+		   sub, state[sub->state].ptr));
+
+	/* Notify app. */
+	if (sub->state!=old_state && sub->cb.on_sub_terminated) 
+	    (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason);
+    }
+
+    /* Decrement pending transaction count. */
+    --sub->pending_tsx;
+    pj_assert(sub->pending_tsx >= 0);
+
+    /* Check that the subscription is marked for deletion. */
+    if (sub->delete_flag && sub->pending_tsx <= 0) {
+	pjsip_event_sub_destroy(sub);
+    } else {
+	pj_mutex_unlock(sub->mutex);
+    }
+
+    /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */
+}
+
+
+/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY 
+ * requests. 
+ */
+static void tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+    pjsip_msg *msg;
+    pjsip_rx_data *rdata;
+
+    /* Only want incoming message events. */
+    if (event->src_type != PJSIP_EVENT_RX_MSG)
+	return;
+
+    rdata = event->src.rdata;
+    msg = rdata->msg;
+
+    /* Only want to process request messages. */
+    if (msg->type != PJSIP_REQUEST_MSG)
+	return;
+
+    /* Only want the first notification. */
+    if (event->obj.tsx && event->obj.tsx->status_code >= 100)
+	return;
+
+    if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) {
+	/* Process incoming SUBSCRIBE request. */
+	on_subscribe_request( event->obj.tsx, rdata );
+    } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) {
+	/* Process incoming NOTIFY request. */
+	on_notify_request( event->obj.tsx, rdata );
+    }
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify.h	(revision 1753)
@@ -0,0 +1,335 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify.h 7     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_H__
+#define __PJSIP_SIMPLE_EVENT_NOTIFY_H__
+
+/**
+ * @file event_notify.h
+ * @brief SIP Specific Event Notification Extension (RFC 3265)
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_auth.h>
+#include <pjsip_simple/event_notify_msg.h>
+#include <pj/timer.h>
+
+/**
+ * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This module provides the implementation of SIP Extension for SIP Specific
+ * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and
+ * NOTIFY methods.
+ *
+ * This module itself is extensible; new event packages can be registered to
+ * this module to handle specific extensions (such as presence).
+ */
+
+PJ_BEGIN_DECL
+
+typedef struct pjsip_event_sub_cb pjsip_event_sub_cb;
+typedef struct pjsip_event_sub pjsip_event_sub;
+
+/**
+ * This enumeration describes subscription state as described in the RFC 3265.
+ * The standard specifies that extensions may define additional states. In the
+ * case where the state is not known, the subscription state will be set to
+ * PJSIP_EVENT_SUB_STATE_UNKNOWN, and the token will be kept in state_str
+ * member of the susbcription structure.
+ */
+typedef enum pjsip_event_sub_state
+{
+    /** State is NULL. */
+    PJSIP_EVENT_SUB_STATE_NULL,
+
+    /** Subscription is active. */
+    PJSIP_EVENT_SUB_STATE_ACTIVE,
+
+    /** Subscription is pending. */
+    PJSIP_EVENT_SUB_STATE_PENDING,
+
+    /** Subscription is terminated. */
+    PJSIP_EVENT_SUB_STATE_TERMINATED,
+
+    /** Subscription state can not be determined. Application can query
+     *  the state information in state_str member.
+     */
+    PJSIP_EVENT_SUB_STATE_UNKNOWN,
+
+} pjsip_event_sub_state;
+
+/**
+ * This structure describes notification to be called when incoming SUBSCRIBE
+ * request is received. The module will call the callback registered by package
+ * that matches the event description in the incoming SUBSCRIBE.
+ */
+typedef struct pjsip_event_sub_pkg_cb
+{
+    /**
+     * This callback is called to first enquery the package whether it wants
+     * to accept incoming SUBSCRIBE request. If it does, then on_subscribe
+     * will be called.
+     *
+     * @param rdata	The incoming request.
+     * @param status	The status code to be returned back to subscriber.
+     */
+    void (*on_query_subscribe)(pjsip_rx_data *rdata, int *status);
+
+    /**
+     * This callback is called when the module receives incoming SUBSCRIBE
+     * request.
+     *
+     * @param sub	The subscription instance.
+     * @param rdata	The received buffer.
+     * @param cb	Callback to be registered to the subscription instance.
+     * @param expires	The expiration to be set.
+     */
+    void (*on_subscribe)(pjsip_event_sub *sub, pjsip_rx_data *rdata,
+			 pjsip_event_sub_cb **cb, int *expires);
+
+} pjsip_event_sub_pkg_cb;
+
+/**
+ * This structure describes callback that is registered by application or
+ * package to receive notifications about a subscription.
+ */
+struct pjsip_event_sub_cb
+{
+    /**
+     * This callback is used by both subscriber and notifier. It is called 
+     * when the subscription has been terminated.
+     *
+     * @param sub	The subscription instance.
+     * @param reason	The termination reason.
+     */
+    void (*on_sub_terminated)(pjsip_event_sub *sub, const pj_str_t *reason);
+
+    /**
+     * This callback is called when we received SUBSCRIBE request to refresh
+     * the subscription.
+     *
+     * @param sub	The subscription instance.
+     * @param rdata	The received SUBSCRIBE request.
+     */
+    void (*on_received_refresh)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+
+    /**
+     * This callback is called when the module receives final response on
+     * previously sent SUBSCRIBE request.
+     *
+     * @param sub	The subscription instance.
+     * @param event	The event.
+     */
+    void (*on_received_sub_response)(pjsip_event_sub *sub, pjsip_event *event);
+
+    /**
+     * This callback is called when the module receives incoming NOTIFY
+     * request.
+     *
+     * @param sub	The subscription instance.
+     * @param rdata	The received data.
+     */
+    void (*on_received_notify)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+
+    /**
+     * This callback is called when the module receives final response to
+     * previously sent NOTIFY request.
+     *
+     * @param sub	The subscription instance.
+     * @param event	The event.
+     */
+    void (*on_received_notify_response)(pjsip_event_sub *sub, pjsip_event *event);
+
+};
+
+/**
+ * This structure describes an event subscription record. The structure is used
+ * to represent both subscriber and notifier.
+ */
+struct pjsip_event_sub
+{
+    pj_pool_t		*pool;		    /**< Pool. */
+    pjsip_endpoint	*endpt;		    /**< Endpoint. */
+    pjsip_event_sub_cb	 cb;		    /**< Callback. */
+    pj_mutex_t		*mutex;		    /**< Mutex. */
+    pjsip_role_e	 role;		    /**< Role (UAC=subscriber, UAS=notifier) */
+    pjsip_event_sub_state state;	    /**< Subscription state. */
+    pj_str_t		 state_str;	    /**< String describing the state. */
+    pjsip_from_hdr	*from;		    /**< Cached local info (From) */
+    pjsip_to_hdr	*to;		    /**< Cached remote info (To) */
+    pjsip_contact_hdr	*contact;	    /**< Cached local contact. */
+    pjsip_cid_hdr	*call_id;	    /**< Cached Call-ID */
+    int			 cseq;		    /**< Outgoing CSeq */
+    pjsip_event_hdr	*event;		    /**< Event description. */
+    pjsip_expires_hdr	*uac_expires;	    /**< Cached Expires header (UAC only). */
+    pjsip_accept_hdr	*local_accept;	    /**< Local Accept header. */
+    pjsip_route_hdr	 route_set;	    /**< Route-set. */
+
+    pj_str_t		 key;		    /**< Key in the hash table. */
+    void		*user_data;	    /**< Application data. */
+    int			 default_interval;  /**< Refresh interval. */
+    pj_timer_entry	 timer;		    /**< Internal timer. */
+    pj_time_val		 expiry_time;	    /**< Time when subscription expires. */
+    int			 pending_tsx;	    /**< Number of pending transactions. */
+    pj_bool_t		 delete_flag;	    /**< Pending deletion flag. */
+
+    pjsip_auth_session	 auth_sess;	    /**< Authorization sessions.	*/
+    unsigned		 cred_cnt;	    /**< Number of credentials.		*/
+    pjsip_cred_info	*cred_info;	    /**< Array of credentials.		*/
+};
+
+
+
+
+/**
+ * Initialize the module and get the instance of the module to be registered to
+ * endpoint.
+ *
+ * @return		The module instance.
+ */
+PJ_DECL(pjsip_module*) pjsip_event_sub_get_module(void);
+
+
+/**
+ * Register event package.
+ *
+ * @param event		The event identification for the package.
+ * @param accept_cnt	Number of strings in Accept array.
+ * @param accept	Array of Accept value.
+ * @param cb		Callback to receive incoming SUBSCRIBE for the package.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event_name,
+						   int accept_cnt,
+						   const pj_str_t accept[],
+						   const pjsip_event_sub_pkg_cb *cb );
+
+
+/**
+ * Create initial subscription instance (client).
+ *
+ * @param endpt		The endpoint.
+ * @param from		URL to put in From header.
+ * @param to		The target resource.
+ * @param event		Event package.
+ * @param expires	Expiration time.
+ * @param accept	Accept specification.
+ * @param user_data	Application data to attach to this subscription.
+ *
+ * @return		New client subscription instance.
+ */
+PJ_DECL(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
+						  const pj_str_t *from,
+						  const pj_str_t *to,
+						  const pj_str_t *event,
+						  int expires,
+						  int accept_cnt,
+						  const pj_str_t accept[],
+						  void *user_data,
+						  const pjsip_event_sub_cb *cb);
+
+/**
+ * Set credentials to be used for outgoing request messages.
+ *
+ * @param sub		Subscription instance.
+ * @param count		Number of credentials.
+ * @param cred		Array of credential info.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
+						      int count,
+						      const pjsip_cred_info cred[]);
+
+/**
+ * Set route set for outgoing requests.
+ *
+ * @param sub		Subscription instance.
+ * @param route_set	List of route headers.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
+						    const pjsip_route_hdr *route_set );
+
+
+/**
+ * Send SUBSCRIBE request.
+ *
+ * @param sub		Subscription instance.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub );
+
+/**
+ * Terminate subscription (client). This will send unsubscription request to
+ * notifier.
+ *
+ * @param sub		Client subscription instance.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub );
+
+
+/**
+ * For notifier, send NOTIFY request to subscriber, and set the state of
+ * the subscription.
+ *
+ * @param sub		The server subscription (notifier) instance.
+ * @param state		New state to set.
+ * @param reason	Specify reason if new state is terminated, otherwise
+ *			put NULL.
+ * @param type		Description of content type.
+ * @param body		Text body to send with the NOTIFY, or NULL if the
+ *			NOTIFY request should not contain any message body.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_notify( pjsip_event_sub *sub,
+					     pjsip_event_sub_state state,
+					     const pj_str_t *reason,
+					     pjsip_msg_body *body);
+
+/**
+ * Destroy subscription instance.
+ *
+ * @param sub		The client or server subscription instance.
+ *
+ * @return		Zero on success, one if the subscription will be
+ *			deleted automatically later, or -1 on error.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif	/* __PJSIP_SIMPLE_EVENT_NOTIFY_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify_msg.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify_msg.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify_msg.c	(revision 1753)
@@ -0,0 +1,327 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify_msg.c 2     6/21/05 12:37a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip_simple/event_notify_msg.h>
+#include <pjsip/print.h>
+#include <pjsip/sip_parser.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/except.h>
+
+static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, 
+				  char *buf, pj_size_t size);
+static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, 
+					       const pjsip_event_hdr *hdr);
+static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
+						       const pjsip_event_hdr*);
+
+static pjsip_hdr_vptr event_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_event_hdr_print,
+};
+
+
+PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool)
+{
+    pj_str_t event = { "Event", 5 };
+    pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    hdr->type = PJSIP_H_OTHER;
+    hdr->name = hdr->sname = event;
+    hdr->vptr = &event_hdr_vptr;
+    pj_list_init(hdr);
+    return hdr;
+}
+
+static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, 
+				  char *buf, pj_size_t size)
+{
+    char *p = buf;
+    char *endbuf = buf+size;
+    int printed;
+
+    copy_advance(p, hdr->name);
+    *p++ = ':';
+    *p++ = ' ';
+
+    copy_advance(p, hdr->event_type);
+    copy_advance_pair(p, ";id=", 4, hdr->id_param);
+    if (hdr->other_param.slen)
+	copy_advance(p, hdr->other_param);
+    return p - buf;
+}
+
+static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, 
+					       const pjsip_event_hdr *rhs)
+{
+    pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
+    pj_strdup(pool, &hdr->event_type, &rhs->event_type);
+    pj_strdup(pool, &hdr->id_param, &rhs->id_param);
+    pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+    return hdr;
+}
+
+static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
+						       const pjsip_event_hdr *rhs )
+{
+    pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+
+static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, 
+					char *buf, pj_size_t size);
+static pjsip_allow_events_hdr* 
+pjsip_allow_events_hdr_clone(pj_pool_t *pool, 
+			     const pjsip_allow_events_hdr *hdr);
+static pjsip_allow_events_hdr* 
+pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
+				     const pjsip_allow_events_hdr*);
+
+static pjsip_hdr_vptr allow_event_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print,
+};
+
+
+PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool)
+{
+    pj_str_t allow_events = { "Allow-Events", 12 };
+    pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    hdr->type = PJSIP_H_OTHER;
+    hdr->name = hdr->sname = allow_events;
+    hdr->vptr = &allow_event_hdr_vptr;
+    pj_list_init(hdr);
+    return hdr;
+}
+
+static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, 
+					char *buf, pj_size_t size)
+{
+    char *p = buf;
+    char *endbuf = buf+size;
+    int printed;
+
+    copy_advance(p, hdr->name);
+    *p++ = ':';
+    *p++ = ' ';
+
+    if (hdr->event_cnt > 0) {
+	int i;
+	copy_advance(p, hdr->events[0]);
+	for (i=1; i<hdr->event_cnt; ++i) {
+	    copy_advance_pair(p, ",", 1, hdr->events[i]);
+	}
+    }
+
+    return p - buf;
+}
+
+static pjsip_allow_events_hdr* 
+pjsip_allow_events_hdr_clone(pj_pool_t *pool, 
+			     const pjsip_allow_events_hdr *rhs)
+{
+    int i;
+
+    pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
+    hdr->event_cnt = rhs->event_cnt;
+    for (i=0; i<rhs->event_cnt; ++i) {
+	pj_strdup(pool, &hdr->events[i], &rhs->events[i]);
+    }
+    return hdr;
+}
+
+static pjsip_allow_events_hdr* 
+pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
+				     const pjsip_allow_events_hdr *rhs)
+{
+    pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+
+static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, 
+				     char *buf, pj_size_t size);
+static pjsip_sub_state_hdr* 
+pjsip_sub_state_hdr_clone(pj_pool_t *pool, 
+			  const pjsip_sub_state_hdr *hdr);
+static pjsip_sub_state_hdr* 
+pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
+				  const pjsip_sub_state_hdr*);
+
+static pjsip_hdr_vptr sub_state_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone,
+    (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print,
+};
+
+
+PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool)
+{
+    pj_str_t sub_state = { "Subscription-State", 18 };
+    pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+    hdr->type = PJSIP_H_OTHER;
+    hdr->name = hdr->sname = sub_state;
+    hdr->vptr = &sub_state_hdr_vptr;
+    hdr->expires_param = -1;
+    hdr->retry_after = -1;
+    pj_list_init(hdr);
+    return hdr;
+}
+
+static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, 
+				     char *buf, pj_size_t size)
+{
+    char *p = buf;
+    char *endbuf = buf+size;
+    int printed;
+
+    copy_advance(p, hdr->name);
+    *p++ = ':';
+    *p++ = ' ';
+
+    copy_advance(p, hdr->sub_state);
+    copy_advance_pair(p, ";reason=", 8, hdr->reason_param);
+    if (hdr->expires_param >= 0) {
+	pj_memcpy(p, ";expires=", 9);
+	p += 9;
+	printed = pj_utoa(hdr->expires_param, p);
+	p += printed;
+    }
+    if (hdr->retry_after >= 0) {
+	pj_memcpy(p, ";retry-after=", 13);
+	p += 9;
+	printed = pj_utoa(hdr->retry_after, p);
+	p += printed;
+    }
+    if (hdr->other_param.slen)
+	copy_advance(p, hdr->other_param);
+
+    return p - buf;
+}
+
+static pjsip_sub_state_hdr* 
+pjsip_sub_state_hdr_clone(pj_pool_t *pool, 
+			  const pjsip_sub_state_hdr *rhs)
+{
+    pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
+    pj_strdup(pool, &hdr->sub_state, &rhs->sub_state);
+    pj_strdup(pool, &hdr->reason_param, &rhs->reason_param);
+    hdr->retry_after = rhs->retry_after;
+    hdr->expires_param = rhs->expires_param;
+    pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+    return hdr;
+}
+
+static pjsip_sub_state_hdr* 
+pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
+				  const pjsip_sub_state_hdr *rhs)
+{
+    pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+    pj_memcpy(hdr, rhs, sizeof(*hdr));
+    return hdr;
+}
+
+static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner, 
+					pj_pool_t *pool)
+{
+    pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
+    const pj_str_t id_param = { "id", 2 };
+
+    pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type);
+
+    while (*scanner->current == ';') {
+	pj_str_t pname, pvalue;
+	pj_scan_get_char(scanner);
+	pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
+	if (pj_stricmp(&pname, &id_param)==0) {
+	    hdr->id_param = pvalue;
+	} else {
+	    pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
+	}
+    }
+    pjsip_parse_end_hdr_imp( scanner );
+    return hdr;
+}
+
+static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner, 
+						      pj_pool_t *pool)
+{
+    pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
+
+    pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]);
+    hdr->event_cnt = 1;
+
+    while (*scanner->current == ',') {
+	pj_scan_get_char(scanner);
+	pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]);
+	if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) {
+	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+	}
+    }
+
+    pjsip_parse_end_hdr_imp( scanner );
+    return hdr;
+}
+
+static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner, 
+						pj_pool_t *pool)
+{
+    pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
+    const pj_str_t reason = { "reason", 6 },
+		   expires = { "expires", 7 },
+		   retry_after = { "retry-after", 11 };
+    pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state);
+
+    while (*scanner->current == ';') {
+	pj_str_t pname, pvalue;
+
+	pj_scan_get_char(scanner);
+	pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
+	if (pj_stricmp(&pname, &reason) == 0) {
+	    hdr->reason_param = pvalue;
+	} else if (pj_stricmp(&pname, &expires) == 0) {
+	    hdr->expires_param = pj_strtoul(&pvalue);
+	} else if (pj_stricmp(&pname, &retry_after) == 0) {
+	    hdr->retry_after = pj_strtoul(&pvalue);
+	} else {
+	    pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
+	}
+    }
+
+    pjsip_parse_end_hdr_imp( scanner );
+    return hdr;
+}
+
+PJ_DEF(void) pjsip_event_notify_init_parser(void)
+{
+    pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event);
+    pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events);
+    pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state);
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify_msg.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify_msg.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/event_notify_msg.h	(revision 1753)
@@ -0,0 +1,122 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify_msg.h 4     8/24/05 10:33a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__
+#define __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__
+
+/**
+ * @file event_notify_msg.h
+ * @brief SIP Event Notification Headers (RFC 3265)
+ */
+#include <pjsip/sip_msg.h>
+
+/**
+ * @ingroup PJSIP_EVENT_NOT
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+
+/** Max events in Allow-Events header. */
+#define PJSIP_MAX_ALLOW_EVENTS	16
+
+/**
+ * This structure describes Event header.
+ */
+typedef struct pjsip_event_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr)
+    pj_str_t	    event_type;	    /**< Event name. */
+    pj_str_t	    id_param;	    /**< Optional event ID parameter. */
+    pj_str_t	    other_param;    /**< Other parameter, concatenated together. */
+} pjsip_event_hdr;
+
+/**
+ * Create an Event header.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New Event header instance.
+ */
+PJ_DECL(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * This structure describes Allow-Events header.
+ */
+typedef struct pjsip_allow_events_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_allow_events_hdr)
+    int		    event_cnt;			    /**< Number of event names. */
+    pj_str_t	    events[PJSIP_MAX_ALLOW_EVENTS]; /**< Event names. */
+} pjsip_allow_events_hdr;
+
+
+/**
+ * Create a new Allow-Events header.
+ *
+ * @param pool.	    The pool.
+ *
+ * @return	    Allow-Events header.
+ */
+PJ_DECL(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * This structure describes Subscription-State header.
+ */
+typedef struct pjsip_sub_state_hdr
+{
+    PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr)
+    pj_str_t	    sub_state;		/**< Subscription state. */
+    pj_str_t	    reason_param;	/**< Optional termination reason. */
+    int		    expires_param;	/**< Expires param, or -1. */
+    int		    retry_after;	/**< Retry after param, or -1. */
+    pj_str_t	    other_param;	/**< Other parameter, concatenated together. */
+} pjsip_sub_state_hdr;
+
+/**
+ * Create new Subscription-State header.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    Subscription-State header.
+ */
+PJ_DECL(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool);
+
+/**
+ * Initialize parser for event notify module.
+ */
+PJ_DEF(void) pjsip_event_notify_init_parser(void);
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+#endif	/* __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/messaging.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/messaging.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/messaging.c	(revision 1753)
@@ -0,0 +1,357 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/messaging.c 7     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip_simple/messaging.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_misc.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/guid.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define THIS_FILE   "messaging"
+
+struct messaging_data
+{
+    void		     *token;
+    pjsip_messaging_cb	     cb;
+};
+
+struct pjsip_messaging_session
+{
+    pj_pool_t		*pool;
+    pjsip_endpoint	*endpt;
+    pjsip_from_hdr	*from;
+    pjsip_to_hdr	*to;
+    pjsip_cid_hdr	*call_id;
+    pjsip_cseq_hdr	*cseq;
+};
+
+static int module_id;
+static pjsip_on_new_msg_cb incoming_cb;
+static pjsip_method message_method;
+
+
+/*
+ * Set global callback to receive incoming message.
+ */
+PJ_DEF(pjsip_on_new_msg_cb) 
+pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb)
+{
+    pjsip_on_new_msg_cb prev_cb = incoming_cb;
+    incoming_cb = cb;
+    return prev_cb;
+}
+
+
+/*
+ * Create an independent message (ie. not associated with a session).
+ */
+PJ_DEF(pjsip_tx_data*) 
+pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, 
+				    const pjsip_uri *target,
+				    const pjsip_from_hdr *param_from,
+				    const pjsip_to_hdr *param_to, 
+				    const pjsip_cid_hdr *param_call_id,
+				    int param_cseq, 
+				    const pj_str_t *param_text)
+{
+    return pjsip_endpt_create_request_from_hdr( endpt, &message_method, 
+						target,
+						param_from, param_to,
+						NULL, param_call_id,
+						param_cseq, param_text );
+}
+
+/*
+ * Create independent message from string (instead of from header).
+ */
+PJ_DEF(pjsip_tx_data*) 
+pjsip_messaging_create_msg( pjsip_endpoint *endpt, 
+			    const pj_str_t *target,
+			    const pj_str_t *param_from,
+			    const pj_str_t *param_to, 
+			    const pj_str_t *param_call_id,
+			    int param_cseq, 
+			    const pj_str_t *param_text)
+{
+    return pjsip_endpt_create_request( endpt, &message_method, target, 
+				       param_from, param_to, NULL, param_call_id,
+				       param_cseq, param_text);
+}
+
+/*
+ * Initiate transaction to send outgoing message.
+ */
+PJ_DEF(pj_status_t) 
+pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata, 
+			  void *token, pjsip_messaging_cb cb )
+{
+    pjsip_transaction *tsx;
+    struct messaging_data *msg_data;
+
+    /* Create transaction. */
+    tsx = pjsip_endpt_create_tsx(endpt);
+    if (!tsx) {
+	pjsip_tx_data_dec_ref(tdata);
+	return -1;
+    }
+
+    /* Save parameters to messaging data and attach to tsx. */
+    msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data));
+    msg_data->cb = cb;
+    msg_data->token = token;
+
+    /* Init transaction. */
+    tsx->module_data[module_id] = msg_data;
+    if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
+	pjsip_tx_data_dec_ref(tdata);
+	pjsip_endpt_destroy_tsx(endpt, tsx);
+	return -1;
+    }
+
+    pjsip_endpt_register_tsx(endpt, tsx);
+
+    /* 
+     * Instruct transaction to send message.
+     * Further events will be received via transaction's event.
+     */
+    pjsip_tsx_on_tx_msg(tsx, tdata);
+
+    /* Decrement reference counter. */
+    pjsip_tx_data_dec_ref(tdata);
+    return 0;
+}
+
+
+/*
+ * Create 'IM session'.
+ */
+PJ_DEF(pjsip_messaging_session*) 
+pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from,
+			        const pj_str_t *param_to )
+{
+    pj_pool_t *pool;
+    pjsip_messaging_session *ses;
+    pj_str_t tmp, to;
+
+    pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024);
+    if (!pool)
+	return NULL;
+
+    ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session));
+    ses->pool = pool;
+    ses->endpt = endpt;
+
+    ses->call_id = pjsip_cid_hdr_create(pool);
+    pj_create_unique_string(pool, &ses->call_id->id);
+
+    ses->cseq = pjsip_cseq_hdr_create(pool);
+    ses->cseq->cseq = pj_rand();
+    ses->cseq->method = message_method;
+
+    ses->from = pjsip_from_hdr_create(pool);
+    pj_strdup_with_null(pool, &tmp, param_from);
+    ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
+    if (ses->from->uri == NULL) {
+	pjsip_endpt_destroy_pool(endpt, pool);
+	return NULL;
+    }
+    pj_create_unique_string(pool, &ses->from->tag);
+
+    ses->to = pjsip_to_hdr_create(pool);
+    pj_strdup_with_null(pool, &to, param_from);
+    ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
+    if (ses->to->uri == NULL) {
+	pjsip_endpt_destroy_pool(endpt, pool);
+	return NULL;
+    }
+
+    PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr));
+    return ses;
+}
+
+
+/*
+ * Send IM message using identification from 'IM session'.
+ */
+PJ_DEF(pjsip_tx_data*)
+pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text )
+{
+    return pjsip_endpt_create_request_from_hdr( ses->endpt,
+						&message_method,
+						ses->to->uri,
+						ses->from,
+						ses->to,
+						NULL,
+						ses->call_id,
+						ses->cseq->cseq++,
+						text);
+}
+
+
+/*
+ * Destroy 'IM session'.
+ */
+PJ_DEF(pj_status_t)
+pjsip_messaging_destroy_session( pjsip_messaging_session *ses )
+{
+    /*
+     * NOTE ABOUT POSSIBLE BUG HERE...
+     *
+     * We don't check number of pending transaction before destroying IM
+     * session. As the result, the headers in the txdata of pending transaction
+     * wil be INVALID once the IM session is deleted (because we only
+     * shallo_clone()-ed them).
+     *
+     * This normally should be okay, because once the message is
+     * submitted to transaction, the transaction (or rather the transport)
+     * will 'print' the message to a buffer, and once it is printed, it
+     * won't try to access the original message again. So even when the 
+     * original message has a dangling pointer, we should be safe.
+     *
+     * However, it will cause a problem if:
+     *	- resolving completes asynchronously and with a substantial delay,
+     *	  and before the resolver/transport finished its job the user
+     *	  destroy the IM session.
+     *	- if the transmit data is invalidated after the IM session is
+     *	  destroyed.
+     */
+
+    pjsip_endpt_destroy_pool(ses->endpt, ses->pool);
+    return 0;
+}
+
+
+static pj_status_t messaging_init( pjsip_endpoint *endpt,
+				   struct pjsip_module *mod, pj_uint32_t id )
+{
+    PJ_UNUSED_ARG(endpt)
+    PJ_UNUSED_ARG(mod)
+
+    module_id = id;
+    return 0;
+}
+
+static pj_status_t messaging_start( struct pjsip_module *mod )
+{
+    PJ_UNUSED_ARG(mod)
+    return 0;
+}
+
+static pj_status_t messaging_deinit( struct pjsip_module *mod )
+{
+    PJ_UNUSED_ARG(mod)
+    return 0;
+}
+
+static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+    pjsip_transaction *tsx = event->obj.tsx;
+    struct messaging_data *mod_data;
+
+    PJ_UNUSED_ARG(mod)
+
+    /* Ignore non transaction event */
+    if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL)
+	return;
+
+    /* If this is an incoming message, inform application. */
+    if (tsx->role == PJSIP_ROLE_UAS) {
+	int status = 100;
+	pjsip_tx_data *tdata;
+
+	/* Check if we already answered this request. */
+	if (tsx->status_code >= 200)
+	    return;
+
+	/* Only handle MESSAGE requests!. */
+	if (pjsip_method_cmp(&tsx->method, &message_method) != 0)
+	    return;
+
+	/* Call application callback. */
+	if (incoming_cb)
+	    status = (*incoming_cb)(event->src.rdata);
+
+	if (status < 200 || status >= 700)
+	    status = PJSIP_SC_INTERNAL_SERVER_ERROR;
+
+	/* Respond request. */
+	tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status );
+	if (tdata)
+	    pjsip_tsx_on_tx_msg(tsx, tdata);
+
+	return;
+    }
+
+    /* Ignore if it's not something that came from messaging module. */
+    mod_data = tsx->module_data[ module_id ];
+    if (mod_data == NULL)
+	return;
+
+    /* Ignore non final response. */
+    if (tsx->status_code < 200)
+	return;
+
+    /* Don't want to call the callback more than once. */
+    tsx->module_data[ module_id ] = NULL;
+
+    /* Now call the callback. */
+    if (mod_data->cb) {
+	(*mod_data->cb)(mod_data->token, tsx->status_code);
+    }
+}
+
+static pjsip_module messaging_module = 
+{
+    { "Messaging", 9},	    /* Name.		*/
+    0,			    /* Flag		*/
+    128,		    /* Priority		*/
+    NULL,		    /* User agent instance, initialized by APP.	*/
+    0,			    /* Number of methods supported (will be initialized later). */
+    { 0 },		    /* Array of methods (will be initialized later) */
+    &messaging_init,	    /* init_module()	*/
+    &messaging_start,	    /* start_module()	*/
+    &messaging_deinit,	    /* deinit_module()	*/
+    &messaging_tsx_handler, /* tsx_handler()	*/
+};
+
+PJ_DEF(pjsip_module*) pjsip_messaging_get_module()
+{
+    static pj_str_t method_str = { "MESSAGE", 7 };
+
+    pjsip_method_init_np( &message_method, &method_str);
+
+    messaging_module.method_cnt = 1;
+    messaging_module.methods[0] = &message_method;
+
+    return &messaging_module;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/messaging.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/messaging.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/messaging.h	(revision 1753)
@@ -0,0 +1,273 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/messaging.h 6     8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIMPLE_MESSAGING_H__
+#define __PJSIP_SIMPLE_MESSAGING_H__
+
+/**
+ * @file messaging.h
+ * @brief Instant Messaging Extension (RFC 3428)
+ */
+
+#include <pjsip/sip_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_MESSAGING SIP Instant Messaging (RFC 3428) Module
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This module provides the implementation of SIP Extension for Instant
+ * Messaging (RFC 3428). It extends PJSIP by supporting MESSAGE method.
+ *
+ * The RFC 3428 doesn't provide any means of dialog for the purpose of sending/
+ * receiving instant messaging. IM with SIP is basicly sessionless, which means
+ * that there is absolutely no correlation between IM messages sent or received
+ * by a host. Any correlation between IM messages is only perceivable by
+ * user, phsychologically.
+ *
+ * However, the RFC doesn't prohibit sending IM within a dialog (presumably
+ * using the same Call-ID and CSeq namespace), although it prohibits creating
+ * a dialog specificly for creating IM session.
+ *
+ * The implementation here is modeled to support both ways of sending IM msgs,
+ * i.e. sending IM message individually and sending IM message within a dialog.
+ * Although IM message can be associated with a dialog, this implementation of
+ * IM module is completely independent of the User Agent library in PJSIP. Yes,
+ * that's what is called modularity, and it demonstrates the clearness 
+ * of PJSIP design (the last sentence is of course marketing crap :)).
+ *
+ * To send an IM message as part of dialog, application would first create the
+ * message using #pjsip_messaging_create_msg, using dialog's Call-ID, CSeq,
+ * From, and To header, then send the message using #pjsip_dlg_send_msg instead
+ * of #pjsip_messaging_send_msg. 
+ *
+ * To send IM messages individually, application has two options. The first is
+ * to create the request with #pjsip_messaging_create_msg then send it with
+ * #pjsip_messaging_send_msg. But this way, application has to pre-construct
+ * From and To header first, which is not too convenient.
+ *
+ * The second option (to send IM messages not associated with a dialog) is to
+ * first create an 'IM session'. An 'IM session' here is not a SIP dialog, as
+ * it doesn't have Contact header etc. An 'IM session' here is just a local
+ * state to cache most of IM headers, for convenience and optimization. Appl 
+ * creates an IM session with #pjsip_messaging_create_session, and destroy
+ * the session with #pjsip_messaging_destroy_session. To create an outgoing
+ * IM message, application would call #pjsip_messaging_session_create_msg,
+ * and to send the message it would use #pjsip_messaging_send_msg.
+ *
+ * Message authorization is handled by application, as usual, by inserting a 
+ * proper WWW-Authenticate or Proxy-Authenticate header before sending the 
+ * message.
+ *
+ * And the last bit, to handle incoing IM messages.
+ *
+ * To handle incoming IM messages, application would register a global callback
+ * to be called when incoming messages arrive, by registering with function
+ * #pjsip_messaging_set_incoming_callback. This will be the global callback
+ * for all incoming IM messages. Although the message was sent as part of
+ * a dialog, it would still come here. And as long as the request has proper
+ * identification (Call-ID, From/To tag), the dialog will be aware of the
+ * request and update it's state (remote CSeq) accordingly.
+ */
+
+
+
+/**
+ * Typedef for callback to be called when outgoing message has been sent
+ * and a final response has been received.
+ */
+typedef void (*pjsip_messaging_cb)(void *token, int status_code);
+
+/**
+ * Typedef for callback to receive incoming message.
+ *
+ * @param rdata	    Incoming message data.
+ *
+ * @return	    The status code to be returned back to original sender.
+ *		    Application must return a final status code upon returning
+ *		    from this function, or otherwise the stack will respond
+ *		    with error.
+ */
+typedef int (*pjsip_on_new_msg_cb)(pjsip_rx_data *rdata);
+
+/**
+ * Opaque data type for instant messaging session.
+ */
+typedef struct pjsip_messaging_session pjsip_messaging_session;
+
+/**
+ * Get the messaging module.
+ *
+ * @return SIP module.
+ */
+PJ_DECL(pjsip_module*) pjsip_messaging_get_module();
+
+/**
+ * Set the global callback to be called when incoming message is received.
+ *
+ * @param cb	    The callback to be called when incoming message is received.
+ *
+ * @return	    The previous callback.
+ */
+PJ_DECL(pjsip_on_new_msg_cb) 
+pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb);
+
+
+/**
+ * Create an instant message transmit data buffer using the specified arguments.
+ * The returned transmit data buffers will have it's reference counter set
+ * to 1, and when application send the buffer, the send function will decrement
+ * the reference counter. When the reference counter reach zero, the buffer
+ * will be deleted. As long as the function does not increment the buffer's 
+ * reference counter between creating and sending the request,  the buffer 
+ * will always be deleted and no memory leak will occur.
+ *
+ * @param endpt	    Endpoint instance.
+ * @param target    Target URL.
+ * @param from	    The "From" header, which content will be copied to request.
+ *		    If the "From" header doesn't have a tag parameter, the
+ *		    function will generate one.
+ * @param to	    The "To" header, which content will be copied to request.
+ * @param call_id   Optionally specify Call-ID, or put NULL to make this
+ *		    function generate a unique Call-ID automatically.
+ * @param cseq	    Optionally specify CSeq, or put -1 to make this function
+ *		    generate a random CSeq.
+ * @param text	    Optionally specify "text/plain" message body, or put NULL 
+ *		    if application wants to put body other than "text/plain"
+ *		    manually.
+ *
+ * @return	    SIP transmit data buffer, which reference count has been
+ *		    set to 1.
+ */
+PJ_DECL(pjsip_tx_data*) 
+pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, 
+				    const pjsip_uri *target,
+				    const pjsip_from_hdr *from,
+				    const pjsip_to_hdr *to, 
+				    const pjsip_cid_hdr *call_id,
+				    int cseq, 
+				    const pj_str_t *text);
+
+/**
+ * Create instant message, by specifying URL string for both From and To header.
+ *
+ * @param endpt	    Endpoint instance.
+ * @param target    Target URL.
+ * @param from	    URL of the sender.
+ * @param to	    URL of the recipient.
+ * @param call_id   Optionally specify Call-ID, or put NULL to make this
+ *		    function generate a unique Call-ID automatically.
+ * @param cseq	    Optionally specify CSeq, or put -1 to make this function
+ *		    generate a random CSeq.
+ * @param text	    Optionally specify "text/plain" message body, or put NULL 
+ *		    if application wants to put body other than "text/plain"
+ *		    manually.
+ *
+ * @return	    SIP transmit data buffer, which reference count has been
+ *		    set to 1.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_messaging_create_msg( pjsip_endpoint *endpt, 
+						    const pj_str_t *target,
+						    const pj_str_t *from,
+						    const pj_str_t *to, 
+						    const pj_str_t *call_id,
+						    int cseq, 
+						    const pj_str_t *text);
+
+/**
+ * Send the instant message transmit buffer and attach a callback to be called
+ * when the request has received a final response. This function will decrement
+ * the transmit buffer's reference counter, and when the reference counter
+ * reach zero, the buffer will be deleted. As long as the function does not
+ * increment the buffer's reference counter between creating the request and
+ * calling this function, the buffer will always be deleted regardless whether
+ * the sending was failed or succeeded.
+ *
+ * @param endpt	    Endpoint instance.
+ * @param tdata	    Transmit data buffer.
+ * @param token	    Token to be associated with the SIP transaction which sends
+ *		    this request.
+ * @param cb	    The callback to be called when the SIP request has received
+ *		    a final response from destination.
+ *
+ * @return	    Zero if the transaction was started successfully. Note that
+ *		    this doesn't mean the request has been received successfully
+ *		    by remote recipient.
+ */
+PJ_DECL(pj_status_t) pjsip_messaging_send_msg( pjsip_endpoint *endpt, 
+					       pjsip_tx_data *tdata, 
+					       void *token, 
+					       pjsip_messaging_cb cb );
+
+/**
+ * Create an instant messaging session, which can conveniently be used to send
+ * multiple instant messages to the same recipient.
+ *
+ * @param endpt	    Endpoint instance.
+ * @param from	    URI of sender. The function will add a unique tag parameter
+ *		    to this URI in the From header.
+ * @param to	    URI of recipient.
+ *
+ * @return	    Messaging session.
+ */
+PJ_DECL(pjsip_messaging_session*) 
+pjsip_messaging_create_session( pjsip_endpoint *endpt, 
+			        const pj_str_t *from,
+			        const pj_str_t *to );
+
+/**
+ * Create an instant message using instant messaging session, and optionally
+ * attach a text message.
+ *
+ * @param ses	    The instant messaging session.
+ * @param text	    Optional "text/plain" message to be attached as the
+ *		    message body. If this parameter is NULL, then no message
+ *		    body will be created, and application can attach any
+ *		    type of message body before the request is sent.
+ *
+ * @return	    SIP transmit data buffer, which reference counter has been
+ *		    set to 1.
+ */
+PJ_DECL(pjsip_tx_data*)
+pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, 
+				    const pj_str_t *text );
+
+/**
+ * Destroy an instant messaging session.
+ *
+ * @param ses	    The instant messaging session.
+ *
+ * @return	    Zero on successfull.
+ */
+PJ_DECL(pj_status_t)
+pjsip_messaging_destroy_session( pjsip_messaging_session *ses );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/pidf.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/pidf.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/pidf.c	(revision 1753)
@@ -0,0 +1,355 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/pidf.c 3     6/21/05 12:37a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip_simple/pidf.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+struct pjpidf_op_desc pjpidf_op = 
+{
+    {
+	&pjpidf_pres_construct,
+	&pjpidf_pres_add_tuple,
+	&pjpidf_pres_get_first_tuple,
+	&pjpidf_pres_get_next_tuple,
+	&pjpidf_pres_find_tuple,
+	&pjpidf_pres_remove_tuple,
+	&pjpidf_pres_add_note,
+	&pjpidf_pres_get_first_note,
+	&pjpidf_pres_get_next_note
+    },
+    {
+	&pjpidf_tuple_construct,
+	&pjpidf_tuple_get_id,
+	&pjpidf_tuple_set_id,
+	&pjpidf_tuple_get_status,
+	&pjpidf_tuple_get_contact,
+	&pjpidf_tuple_set_contact,
+	&pjpidf_tuple_set_contact_prio,
+	&pjpidf_tuple_get_contact_prio,
+	&pjpidf_tuple_add_note,
+	&pjpidf_tuple_get_first_note,
+	&pjpidf_tuple_get_next_note,
+	&pjpidf_tuple_get_timestamp,
+	&pjpidf_tuple_set_timestamp,
+	&pjpidf_tuple_set_timestamp_np
+    },
+    {
+	&pjpidf_status_construct,
+	&pjpidf_status_is_basic_open,
+	&pjpidf_status_set_basic_open
+    }
+};
+
+static pj_str_t PRESENCE = { "presence", 8 };
+static pj_str_t ENTITY = { "entity", 6};
+static pj_str_t	TUPLE = { "tuple", 5 };
+static pj_str_t ID = { "id", 2 };
+static pj_str_t NOTE = { "note", 4 };
+static pj_str_t STATUS = { "status", 6 };
+static pj_str_t CONTACT = { "contact", 7 };
+static pj_str_t PRIORITY = { "priority", 8 };
+static pj_str_t TIMESTAMP = { "timestamp", 9 };
+static pj_str_t BASIC = { "basic", 5 };
+static pj_str_t OPEN = { "open", 4 };
+static pj_str_t CLOSED = { "closed", 6 };
+static pj_str_t EMPTY_STRING = { NULL, 0 };
+
+static void xml_init_node(pj_pool_t *pool, pj_xml_node *node,
+			  pj_str_t *name, const pj_str_t *value)
+{
+    pj_list_init(&node->attr_head);
+    pj_list_init(&node->node_head);
+    node->name = *name;
+    if (value) pj_strdup(pool, &node->content, value);
+    else node->content.ptr=NULL, node->content.slen=0;
+}
+
+static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
+				    const pj_str_t *value)
+{
+    pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
+    attr->name = *name;
+    pj_strdup(pool, &attr->value, value);
+    return attr;
+}
+
+/* Presence */
+PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres,
+				   const pj_str_t *entity)
+{
+    pj_xml_attr *attr;
+
+    xml_init_node(pool, pres, &PRESENCE, NULL);
+    attr = xml_create_attr(pool, &ENTITY, entity);
+    pj_xml_add_attr(pres, attr);
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres,
+					    const pj_str_t *id)
+{
+    pjpidf_tuple *t = pj_pool_alloc(pool, sizeof(*t));
+    pjpidf_tuple_construct(pool, t, id);
+    pj_xml_add_node(pres, t);
+    return t;
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres)
+{
+    return pj_xml_find_node(pres, &TUPLE);
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, 
+						 pjpidf_tuple *tuple)
+{
+    return pj_xml_find_next_node(pres, tuple, &TUPLE);
+}
+
+static pj_bool_t find_tuple_by_id(pj_xml_node *node, const void *id)
+{
+    return pj_xml_find_attr(node, &ID, id) != NULL;
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id)
+{
+    return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id);
+}
+
+PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t)
+{
+    PJ_UNUSED_ARG(pres)
+    pj_list_erase(t);
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, 
+					  const pj_str_t *text)
+{
+    pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
+    xml_init_node(pool, note, &NOTE, text);
+    pj_xml_add_node(pres, note);
+    return note;
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres)
+{
+    return pj_xml_find_node( pres, &NOTE);
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note)
+{
+    return pj_xml_find_next_node(t, note, &NOTE);
+}
+
+
+/* Tuple */
+PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t,
+				    const pj_str_t *id)
+{
+    pj_xml_attr *attr;
+    pjpidf_status *st;
+
+    xml_init_node(pool, t, &TUPLE, NULL);
+    attr = xml_create_attr(pool, &ID, id);
+    pj_xml_add_attr(t, attr);
+    st = pj_pool_alloc(pool, sizeof(*st));
+    pjpidf_status_construct(pool, st);
+    pj_xml_add_node(t, st);
+}
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t)
+{
+    const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL);
+    pj_assert(attr);
+    return &attr->value;
+}
+
+PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id)
+{
+    pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL);
+    pj_assert(attr);
+    pj_strdup(pool, &attr->value, id);
+}
+
+
+PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t)
+{
+    pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS);
+    pj_assert(st);
+    return st;
+}
+
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t)
+{
+    pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
+    if (!node)
+	return &EMPTY_STRING;
+    return &node->content;
+}
+
+PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, 
+				      const pj_str_t *contact)
+{
+    pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
+    if (!node) {
+	node = pj_pool_alloc(pool, sizeof(*node));
+	xml_init_node(pool, node, &CONTACT, contact);
+	pj_xml_add_node(t, node);
+    } else {
+	pj_strdup(pool, &node->content, contact);
+    }
+}
+
+PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, 
+					   const pj_str_t *prio)
+{
+    pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
+    pj_xml_attr *attr;
+
+    if (!node) {
+	node = pj_pool_alloc(pool, sizeof(*node));
+	xml_init_node(pool, node, &CONTACT, NULL);
+	pj_xml_add_node(t, node);
+    }
+    attr = pj_xml_find_attr(node, &PRIORITY, NULL);
+    if (!attr) {
+	attr = xml_create_attr(pool, &PRIORITY, prio);
+	pj_xml_add_attr(node, attr);
+    } else {
+	pj_strdup(pool, &attr->value, prio);
+    }
+}
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t)
+{
+    pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
+    pj_xml_attr *attr;
+
+    if (!node)
+	return &EMPTY_STRING;
+    attr = pj_xml_find_attr(node, &PRIORITY, NULL);
+    if (!attr)
+	return &EMPTY_STRING;
+    return &attr->value;
+}
+
+
+PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t,
+					   const pj_str_t *text)
+{
+    pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
+    xml_init_node(pool, note, &NOTE, text);
+    pj_xml_add_node(t, note);
+    return note;
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t)
+{
+    return pj_xml_find_node(t, &NOTE);
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n)
+{
+    return pj_xml_find_next_node(t, n, &NOTE);
+}
+
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t)
+{
+    pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP);
+    return node ? &node->content : &EMPTY_STRING;
+}
+
+PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t,
+					const pj_str_t *ts)
+{
+    pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
+    if (!node) {
+	node = pj_pool_alloc(pool, sizeof(*node));
+	xml_init_node(pool, node, &TIMESTAMP, ts);
+    } else {
+	pj_strdup(pool, &node->content, ts);
+    }
+}
+
+
+PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t, 
+					   pj_str_t *ts)
+{
+    pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
+    if (!node) {
+	node = pj_pool_alloc(pool, sizeof(*node));
+	xml_init_node(pool, node, &TIMESTAMP, ts);
+    } else {
+	node->content = *ts;
+    }
+}
+
+
+/* Status */
+PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st)
+{
+    pj_xml_node *node;
+
+    xml_init_node(pool, st, &STATUS, NULL);
+    node = pj_pool_alloc(pool, sizeof(*node));
+    xml_init_node(pool, node, &BASIC, &CLOSED);
+    pj_xml_add_node(st, node);
+}
+
+PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st)
+{
+    pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC);
+    pj_assert(node != NULL);
+    return pj_stricmp(&node->content, &OPEN)==0;
+}
+
+PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open)
+{
+    pj_xml_node *node = pj_xml_find_node(st, &BASIC);
+    pj_assert(node != NULL);
+    node->content = open ? OPEN : CLOSED;
+}
+
+PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity)
+{
+    pjpidf_pres *pres = pj_pool_alloc(pool, sizeof(*pres));
+    pjpidf_pres_construct(pool, pres, entity);
+    return pres;
+}
+
+PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len)
+{
+    pjpidf_pres *pres = pj_xml_parse(pool, text, len);
+    if (pres) {
+	if (pj_stricmp(&pres->name, &PRESENCE) != 0)
+	    return NULL;
+    }
+    return pres;
+}
+
+PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len)
+{
+    return pj_xml_print(pres, buf, len, PJ_TRUE);
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/pidf.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/pidf.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/pidf.h	(revision 1753)
@@ -0,0 +1,181 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/pidf.h 3     8/24/05 10:33a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIMPLE_PIDF_H__
+#define __PJSIP_SIMPLE_PIDF_H__
+
+/**
+ * @file pidf.h
+ * @brief PIDF/Presence Information Data Format (RFC 3863)
+ */
+#include <pj/types.h>
+#include <pj/xml.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJSIP_SIMPLE_PIDF PIDF/Presence Information Data Format (RFC 3863)
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This file provides tools for manipulating Presence Information Data 
+ * Format (PIDF) as described in RFC 3863.
+ */
+typedef struct pj_xml_node pjpidf_pres;
+typedef struct pj_xml_node pjpidf_tuple;
+typedef struct pj_xml_node pjpidf_status;
+typedef struct pj_xml_node pjpidf_note;
+
+typedef struct pjpidf_status_op
+{
+    void	    (*construct)(pj_pool_t*, pjpidf_status*);
+    pj_bool_t	    (*is_basic_open)(const pjpidf_status*);
+    void	    (*set_basic_open)(pjpidf_status*, pj_bool_t);
+} pjpidf_status_op;
+
+typedef struct pjpidf_tuple_op
+{
+    void	    (*construct)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+
+    const pj_str_t* (*get_id)(const pjpidf_tuple* );
+    void	    (*set_id)(pj_pool_t*, pjpidf_tuple *, const pj_str_t*);
+
+    pjpidf_status*  (*get_status)(pjpidf_tuple* );
+
+    const pj_str_t* (*get_contact)(const pjpidf_tuple*);
+    void	    (*set_contact)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+    void	    (*set_contact_prio)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+    const pj_str_t* (*get_contact_prio)(const pjpidf_tuple*);
+
+    pjpidf_note*    (*add_note)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+    pjpidf_note*    (*get_first_note)(pjpidf_tuple*);
+    pjpidf_note*    (*get_next_note)(pjpidf_tuple*, pjpidf_note*);
+
+    const pj_str_t* (*get_timestamp)(const pjpidf_tuple*);
+    void	    (*set_timestamp)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+    void	    (*set_timestamp_np)(pj_pool_t*,pjpidf_tuple*, pj_str_t*);
+
+} pjpidf_tuple_op;
+
+typedef struct pjpidf_pres_op
+{
+    void	    (*construct)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
+
+    pjpidf_tuple*   (*add_tuple)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
+    pjpidf_tuple*   (*get_first_tuple)(pjpidf_pres*);
+    pjpidf_tuple*   (*get_next_tuple)(pjpidf_pres*, pjpidf_tuple*);
+    pjpidf_tuple*   (*find_tuple)(pjpidf_pres*, const pj_str_t*);
+    void	    (*remove_tuple)(pjpidf_pres*, pjpidf_tuple*);
+
+    pjpidf_note*    (*add_note)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
+    pjpidf_note*    (*get_first_note)(pjpidf_pres*);
+    pjpidf_note*    (*get_next_note)(pjpidf_pres*, pjpidf_note*);
+
+} pjpidf_pres_op;
+
+
+extern struct pjpidf_op_desc
+{
+    pjpidf_pres_op	pres;
+    pjpidf_tuple_op	tuple;
+    pjpidf_status_op	status;
+} pjpidf_op;
+
+
+/******************************************************************************
+ * Top level API for managing presence document. 
+ *****************************************************************************/
+PJ_DECL(pjpidf_pres*)    pjpidf_create(pj_pool_t *pool, const pj_str_t *entity);
+PJ_DECL(pjpidf_pres*)	 pjpidf_parse(pj_pool_t *pool, char *text, int len);
+PJ_DECL(int)		 pjpidf_print(const pjpidf_pres* pres, char *buf, int len);
+
+
+/******************************************************************************
+ * API for managing Presence node.
+ *****************************************************************************/
+PJ_DECL(void)		 pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres,
+					       const pj_str_t *entity);
+PJ_DECL(pjpidf_tuple*)	 pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, 
+					       const pj_str_t *id);
+PJ_DECL(pjpidf_tuple*)	 pjpidf_pres_get_first_tuple(pjpidf_pres *pres);
+PJ_DECL(pjpidf_tuple*)	 pjpidf_pres_get_next_tuple(pjpidf_pres *pres, 
+						    pjpidf_tuple *t);
+PJ_DECL(pjpidf_tuple*)	 pjpidf_pres_find_tuple(pjpidf_pres *pres, 
+						const pj_str_t *id);
+PJ_DECL(void)		 pjpidf_pres_remove_tuple(pjpidf_pres *pres, 
+						  pjpidf_tuple*);
+
+PJ_DECL(pjpidf_note*)	 pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, 
+					      const pj_str_t *text);
+PJ_DECL(pjpidf_note*)	 pjpidf_pres_get_first_note(pjpidf_pres *pres);
+PJ_DECL(pjpidf_note*)	 pjpidf_pres_get_next_note(pjpidf_pres*, pjpidf_note*);
+
+
+/******************************************************************************
+ * API for managing Tuple node.
+ *****************************************************************************/
+PJ_DECL(void)		 pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t,
+						const pj_str_t *id);
+PJ_DECL(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t );
+PJ_DECL(void)		 pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, 
+					     const pj_str_t *id);
+
+PJ_DECL(pjpidf_status*)  pjpidf_tuple_get_status(pjpidf_tuple *t);
+
+PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t);
+PJ_DECL(void)		 pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, 
+						  const pj_str_t *contact);
+PJ_DECL(void)		 pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, 
+						       const pj_str_t *prio);
+PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t);
+
+PJ_DECL(pjpidf_note*)	 pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t,
+					       const pj_str_t *text);
+PJ_DECL(pjpidf_note*)	 pjpidf_tuple_get_first_note(pjpidf_tuple *t);
+PJ_DECL(pjpidf_note*)	 pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n);
+
+PJ_DECL(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t);
+PJ_DECL(void)		 pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t,
+						    const pj_str_t *ts);
+PJ_DECL(void)		 pjpidf_tuple_set_timestamp_np(	pj_pool_t*, pjpidf_tuple *t,
+							pj_str_t *ts);
+
+
+/******************************************************************************
+ * API for managing Status node.
+ *****************************************************************************/
+PJ_DECL(void)		 pjpidf_status_construct(pj_pool_t*, pjpidf_status*);
+PJ_DECL(pj_bool_t)	 pjpidf_status_is_basic_open(const pjpidf_status*);
+PJ_DECL(void)		 pjpidf_status_set_basic_open(pjpidf_status*, pj_bool_t);
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJSIP_SIMPLE_PIDF_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/presence.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/presence.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/presence.c	(revision 1753)
@@ -0,0 +1,404 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/presence.c 7     8/24/05 10:33a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip_simple/presence.h>
+#include <pjsip/sip_transport.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/os.h>
+#include <stdio.h>
+
+/* Forward declarations. */
+static void on_query_subscribe(pjsip_rx_data *rdata, int *status);
+static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
+			 pjsip_event_sub_cb **cb, int *expires);
+static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason);
+static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+
+/* Some string constants. */
+static pj_str_t PRESENCE_EVENT = { "presence", 8 };
+
+/* Accept types. */
+static pj_str_t accept_names[] = {
+    { "application/pidf+xml", 20 },
+    { "application/xpidf+xml", 21 }
+};
+static pjsip_media_type accept_types[] = {
+    {
+	{ "application", 11 },
+	{ "pidf+xml", 8 }
+    },
+    {
+	{ "application", 11 },
+	{ "xpidf+xml", 9 }
+    }
+};
+
+/* Callback that is registered by application. */
+static pjsip_presence_cb cb;
+
+/* Package callback to be register to event_notify */
+static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe,
+					 &on_subscribe };
+
+/* Global/static callback to be registered to event_notify */
+static pjsip_event_sub_cb sub_cb = { &on_sub_terminated,
+				     &on_sub_received_refresh,
+				     NULL,
+				     &on_received_notify,
+				     NULL };
+
+/*
+ * Initialize presence module.
+ * This will register event package "presence" to event framework.
+ */
+PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb)
+{
+    pj_memcpy(&cb, pcb, sizeof(*pcb));
+    pjsip_event_sub_register_pkg( &PRESENCE_EVENT, 
+				  sizeof(accept_names)/sizeof(accept_names[0]),
+				  accept_names,
+				  &pkg_cb);
+}
+
+/*
+ * Create presence subscription.
+ */
+PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
+						 const pj_str_t *local_url,
+						 const pj_str_t *remote_url,
+						 int expires,
+						 void *user_data )
+{
+    pjsip_event_sub *sub;
+    pjsip_presentity *pres;
+
+    if (expires < 0)
+	expires = 300;
+
+    /* Create event subscription */
+    sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT, 
+				 expires, 
+				 sizeof(accept_names)/sizeof(accept_names[0]),
+				 accept_names,
+				 NULL, &sub_cb);
+    if (!sub)
+	return NULL;
+
+    /* Allocate presence descriptor. */
+    pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
+    pres->sub = sub;
+    pres->user_data = user_data;
+    sub->user_data = pres;
+
+    return pres;
+}
+
+/*
+ * Send SUBSCRIBE.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres )
+{
+    return pjsip_event_sub_subscribe( pres->sub );
+}
+
+/*
+ * Set credentials to be used for outgoing requests.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
+						    int count,
+						    const pjsip_cred_info cred[])
+{
+    return pjsip_event_sub_set_credentials(pres->sub, count, cred);
+}
+
+/*
+ * Set route-set.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
+						  const pjsip_route_hdr *hdr )
+{
+    return pjsip_event_sub_set_route_set( pres->sub, hdr );
+}
+
+/*
+ * Unsubscribe.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres )
+{
+    return pjsip_event_sub_unsubscribe(pres->sub);
+}
+
+/*
+ * This is the pjsip_msg_body callback to print XML body.
+ */
+static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size)
+{
+    return pj_xml_print( body->data, buf, size, PJ_TRUE );
+}
+
+/*
+ * Create and initialize PIDF document and msg body (notifier only).
+ */
+static pj_status_t init_presence_info( pjsip_presentity *pres )
+{
+    pj_str_t uri;
+    pj_pool_t *pool = pres->sub->pool;
+    char tmp[PJSIP_MAX_URL_SIZE];
+    pjpidf_tuple *tuple;
+    const pjsip_media_type *content_type = NULL;
+
+    pj_assert(pres->uas_body == NULL);
+
+    /* Make entity_id */
+    uri.ptr = tmp;
+    uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri, 
+			      tmp, sizeof(tmp));
+    if (uri.slen < 0)
+	return -1;
+
+    if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
+	pj_str_t s;
+
+	/* Create <presence>. */
+	pres->uas_data.pidf = pjpidf_create(pool, &s);
+
+	/* Create <tuple> */
+	pj_create_unique_string(pool, &s);
+	tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s);
+
+	/* Set <contact> */
+	s.ptr = tmp;
+	s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp));
+	if (s.slen < 0)
+	    return -1;
+	pjpidf_tuple_set_contact(pool, tuple, &s);
+
+	/* Content-Type */
+	content_type = &accept_types[PJSIP_PRES_TYPE_PIDF];
+
+    } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
+
+	/* Create XPIDF */
+	pres->uas_data.xpidf = pjxpidf_create(pool, &uri);
+
+	/* Content-Type. */
+	content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF];
+    }
+
+    /* Create message body */
+    pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
+    pres->uas_body->content_type = *content_type;
+    pres->uas_body->data = pres->uas_data.pidf;
+    pres->uas_body->len = 0;
+    pres->uas_body->print_body = &print_xml;
+
+    return 0;
+}
+
+/*
+ * Send NOTIFY and set subscription state.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
+					   pjsip_event_sub_state state,
+					   pj_bool_t is_online )
+{
+    pj_str_t reason = { "", 0 };
+
+    if (pres->uas_data.pidf == NULL) {
+	if (init_presence_info(pres) != 0)
+	    return -1;
+    }
+
+    /* Update basic status in PIDF/XPIDF document. */
+    if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
+	pjpidf_tuple *first;
+	pjpidf_status *status;
+	pj_time_val now;
+	pj_parsed_time pnow;
+
+	first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf);
+	pj_assert(first);
+	status = pjpidf_op.tuple.get_status(first);
+	pj_assert(status);
+	pjpidf_op.status.set_basic_open(status, is_online);
+
+	/* Update timestamp. */
+	if (pres->timestamp.ptr == 0) {
+	    pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24);
+	}
+	pj_gettimeofday(&now);
+	pj_time_decode(&now, &pnow);
+	pres->timestamp.slen = sprintf(pres->timestamp.ptr,
+				       "%04d-%02d-%02dT%02d:%02d:%02dZ",
+				       pnow.year, pnow.mon, pnow.day,
+				       pnow.hour, pnow.min, pnow.sec);
+	pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp);
+
+    } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
+	pjxpidf_set_status( pres->uas_data.xpidf, is_online );
+
+    } else {
+	pj_assert(0);
+    }
+
+    /* Send notify. */
+    return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body);
+}
+
+/*
+ * Destroy subscription (can be called for both subscriber and notifier).
+ */
+PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres )
+{
+    return pjsip_event_sub_destroy(pres->sub);
+}
+
+/*
+ * This callback is called by event framework to query whether we want to
+ * accept an incoming subscription.
+ */
+static void on_query_subscribe(pjsip_rx_data *rdata, int *status)
+{
+    if (cb.accept_presence) {
+	(*cb.accept_presence)(rdata, status);
+    }
+}
+
+/*
+ * This callback is called by event framework after we accept the incoming
+ * subscription, to notify about the new subscription instance.
+ */
+static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
+			 pjsip_event_sub_cb **set_sub_cb, int *expires)
+{
+    pjsip_presentity *pres;
+    pjsip_accept_hdr *accept;
+
+    pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
+    pres->sub = sub;
+    pres->pres_type = PJSIP_PRES_TYPE_PIDF;
+    sub->user_data = pres;
+    *set_sub_cb = &sub_cb;
+
+    accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
+    if (accept) {
+	unsigned i;
+	int found = 0;
+	for (i=0; i<accept->count && !found; ++i) {
+	    int j;
+	    for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) {
+		if (!pj_stricmp(&accept->values[i], &accept_names[j])) {
+		    pres->pres_type = j;
+		    found = 1;
+		    break;
+		}
+	    }
+	}
+	pj_assert(found );
+    }
+
+    (*cb.on_received_request)(pres, rdata, expires);
+}
+
+/*
+ * This callback is called by event framework when the subscription is
+ * terminated.
+ */
+static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason)
+{
+    pjsip_presentity *pres = sub->user_data;
+    if (cb.on_terminated)
+	(*cb.on_terminated)(pres, reason);
+}
+
+/*
+ * This callback is called by event framework when it receives incoming
+ * SUBSCRIBE request to refresh the subscription.
+ */
+static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata)
+{
+    pjsip_presentity *pres = sub->user_data;
+    if (cb.on_received_refresh)
+	(*cb.on_received_refresh)(pres, rdata);
+}
+
+/*
+ * This callback is called by event framework when it receives incoming
+ * NOTIFY request.
+ */
+static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata)
+{
+    pjsip_presentity *pres = sub->user_data;
+
+    if (cb.on_received_update) {
+	pj_status_t is_open;
+	pjsip_msg_body *body;
+	int i;
+
+	body = rdata->msg->body;
+	if (!body)
+	    return;
+
+	for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) {
+	    if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) &&
+		!pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype))
+	    {
+		break;
+	    }
+	}
+
+	if (i==PJSIP_PRES_TYPE_PIDF) {
+	    pjpidf_pres *pres;
+	    pjpidf_tuple *tuple;
+	    pjpidf_status *status;
+
+	    pres = pjpidf_parse(rdata->pool, body->data, body->len);
+	    if (!pres)
+		return;
+	    tuple = pjpidf_pres_get_first_tuple(pres);
+	    if (!tuple)
+		return;
+	    status = pjpidf_tuple_get_status(tuple);
+	    if (!status)
+		return;
+	    is_open = pjpidf_status_is_basic_open(status);
+
+	} else if (i==PJSIP_PRES_TYPE_XPIDF) {
+	    pjxpidf_pres *pres;
+
+	    pres = pjxpidf_parse(rdata->pool, body->data, body->len);
+	    if (!pres)
+		return;
+	    is_open = pjxpidf_get_status(pres);
+
+	} else {
+	    return;
+	}
+
+	(*cb.on_received_update)(pres, is_open);
+    }
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/presence.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/presence.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/presence.h	(revision 1753)
@@ -0,0 +1,234 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/presence.h 6     8/24/05 10:33a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIMPLE_PRESENCE_H__
+#define __PJSIP_SIMPLE_PRESENCE_H__
+
+/**
+ * @file presence.h
+ * @brief SIP Extension for Presence (RFC 3856)
+ */
+#include <pjsip_simple/event_notify.h>
+#include <pjsip_simple/pidf.h>
+#include <pjsip_simple/xpidf.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJSIP_SIMPLE_PRES SIP Extension for Presence (RFC 3856)
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This module contains the implementation of SIP Presence Extension as 
+ * described in RFC 3856. It uses the SIP Event Notification framework
+ * (event_notify.h) and extends the framework by implementing "presence"
+ * event package.
+ */
+
+/**
+ * Presence message body type.
+ */
+typedef enum pjsip_pres_type
+{
+    PJSIP_PRES_TYPE_PIDF,
+    PJSIP_PRES_TYPE_XPIDF,
+} pjsip_pres_type;
+
+/**
+ * This structure describe a presentity, for both subscriber and notifier.
+ */
+typedef struct pjsip_presentity
+{
+    pjsip_event_sub *sub;	    /**< Event subscribtion record.	*/
+    pjsip_pres_type  pres_type;	    /**< Presentity type.		*/
+    pjsip_msg_body  *uas_body;	    /**< Message body (UAS only).	*/
+    union {
+	pjpidf_pres *pidf;
+	pjxpidf_pres *xpidf;
+    }		     uas_data;	    /**< UAS data.			*/
+    pj_str_t	     timestamp;	    /**< Time of last update.		*/
+    void	    *user_data;	    /**< Application data.		*/
+} pjsip_presentity;
+
+
+/**
+ * This structure describe callback that is registered to receive notification
+ * from the presence module.
+ */
+typedef struct pjsip_presence_cb
+{
+    /**
+     * This callback is first called when the module receives incoming 
+     * SUBSCRIBE request to determine whether application wants to accept
+     * the request. If it does, then on_presence_request will be called.
+     *
+     * @param rdata	The received message.
+     * @return		Application should return 2xx to accept the request,
+     *			or failure status (>=300) to reject the request.
+     */
+    void (*accept_presence)(pjsip_rx_data *rdata, int *status);
+
+    /**
+     * This callback is called when the module receive the first presence
+     * subscription request.
+     *
+     * @param pres	The presence descriptor.
+     * @param rdata	The incoming request.
+     * @param timeout	Timeout to be set for incoming request. Otherwise
+     *			app can just leave this and accept the default.
+     */
+    void (*on_received_request)(pjsip_presentity *pres, pjsip_rx_data *rdata,
+				int *timeout);
+
+    /**
+     * This callback is called when the module received subscription refresh
+     * request.
+     *
+     * @param pres	The presence descriptor.
+     * @param rdata	The incoming request.
+     */
+    void (*on_received_refresh)(pjsip_presentity *pres, pjsip_rx_data *rdata);
+
+    /**
+     * This callback is called when the module receives incoming NOTIFY
+     * request.
+     *
+     * @param pres	The presence descriptor.
+     * @param open	The latest status of the presentity.
+     */
+    void (*on_received_update)(pjsip_presentity *pres, pj_bool_t open);
+
+    /**
+     * This callback is called when the subscription has terminated.
+     *
+     * @param sub	The subscription instance.
+     * @param reason	The termination reason.
+     */
+    void (*on_terminated)(pjsip_presentity *pres, const pj_str_t *reason);
+
+} pjsip_presence_cb;
+
+
+/**
+ * Initialize the presence module and register callback.
+ *
+ * @param cb		Callback structure.
+ */
+PJ_DECL(void) pjsip_presence_init(const pjsip_presence_cb *cb);
+
+
+/**
+ * Create to presence subscription of a presentity URL.
+ *
+ * @param endpt		Endpoint instance.
+ * @param local_url	Local URL.
+ * @param remote_url	Remote URL which the presence is being subscribed.
+ * @param expires	The expiration.
+ * @param user_data	User data to attach to presence subscription.
+ *
+ * @return		The presence structure if successfull, or NULL if
+ *			failed.
+ */
+PJ_DECL(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
+						  const pj_str_t *local_url,
+						  const pj_str_t *remote_url,
+						  int expires,
+						  void *user_data );
+
+/**
+ * Set credentials to be used by this presentity for outgoing requests.
+ *
+ * @param pres		Presentity instance.
+ * @param count		Number of credentials in the array.
+ * @param cred		Array of credentials.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
+						     int count,
+						     const pjsip_cred_info cred[]);
+
+/**
+ * Set route set for outgoing requests.
+ *
+ * @param pres		Presentity instance.
+ * @param route_set	List of route headers.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
+						   const pjsip_route_hdr *hdr );
+
+/**
+ * Send SUBSCRIBE request for the specified presentity.
+ *
+ * @param pres		The presentity instance.
+ *
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres );
+
+/**
+ * Ceased the presence subscription.
+ *
+ * @param pres		The presence structure.
+ * 
+ * @return		Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres );
+
+/**
+ * Notify subscriber about change in local status.
+ *
+ * @param pres		The presence structure.
+ * @param state		Set the state of the subscription.
+ * @param open		Set the presence status (open or closed).
+ *
+ * @return		Zero if a NOTIFY request can be sent.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
+					    pjsip_event_sub_state state,
+					    pj_bool_t open );
+
+/**
+ * Destroy presence structure and the underlying subscription.
+ *
+ * @param pres		The presence structure.
+ *
+ * @return		Zero if the subscription was destroyed, or one if
+ *			the subscription can not be destroyed immediately
+ *			and will be destroyed later, or -1 if failed.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres );
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJSIP_SIMPLE_PRESENCE_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/xpidf.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/xpidf.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/xpidf.c	(revision 1753)
@@ -0,0 +1,299 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/xpidf.c 3     6/22/05 11:42p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip_simple/xpidf.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+
+static pj_str_t PRESENCE = { "presence", 8 };
+static pj_str_t STATUS = { "status", 6 };
+static pj_str_t OPEN = { "open", 4 };
+static pj_str_t CLOSED = { "closed", 6 };
+static pj_str_t URI = { "uri", 3 };
+static pj_str_t ATOM = { "atom", 4 };
+static pj_str_t ATOMID = { "atomid", 6 };
+static pj_str_t ADDRESS = { "address", 7 };
+static pj_str_t SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 };
+static pj_str_t PRESENTITY = { "presentity", 10 };
+static pj_str_t EMPTY_STRING = { NULL, 0 };
+
+static pj_xml_node* xml_create_node(pj_pool_t *pool, 
+				    pj_str_t *name, const pj_str_t *value)
+{
+    pj_xml_node *node;
+
+    node = pj_pool_alloc(pool, sizeof(pj_xml_node));
+    pj_list_init(&node->attr_head);
+    pj_list_init(&node->node_head);
+    node->name = *name;
+    if (value) pj_strdup(pool, &node->content, value);
+    else node->content.ptr=NULL, node->content.slen=0;
+
+    return node;
+}
+
+static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
+				    const pj_str_t *value)
+{
+    pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
+    attr->name = *name;
+    pj_strdup(pool, &attr->value, value);
+    return attr;
+}
+
+
+PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr)
+{
+    pjxpidf_pres *pres;
+    pj_xml_node *presentity;
+    pj_xml_node *atom;
+    pj_xml_node *addr;
+    pj_xml_node *status;
+    pj_xml_attr *attr;
+    pj_str_t uri;
+    pj_str_t tmp;
+
+    /* <presence> */
+    pres = xml_create_node(pool, &PRESENCE, NULL);
+
+    /* <presentity> */
+    presentity = xml_create_node(pool, &PRESENTITY, NULL);
+    pj_xml_add_node(pres, presentity);
+
+    /* uri attribute */
+    uri.ptr = pj_pool_alloc(pool, uri_cstr->slen + SUBSCRIBE_PARAM.slen);
+    pj_strcpy( &uri, uri_cstr);
+    pj_strcat( &uri, &SUBSCRIBE_PARAM);
+    attr = xml_create_attr(pool, &URI, &uri);
+    pj_xml_add_attr(presentity, attr);
+
+    /* <atom> */
+    atom = xml_create_node(pool, &ATOM, NULL);
+    pj_xml_add_node(pres, atom);
+
+    /* atom id */
+    pj_create_unique_string(pool, &tmp);
+    attr = xml_create_attr(pool, &ATOMID, &tmp);
+    pj_xml_add_attr(atom, attr);
+
+    /* address */
+    addr = xml_create_node(pool, &ADDRESS, NULL);
+    pj_xml_add_node(atom, addr);
+
+    /* address'es uri */
+    attr = xml_create_attr(pool, &URI, uri_cstr);
+    pj_xml_add_attr(addr, attr);
+
+    /* status */
+    status = xml_create_node(pool, &STATUS, NULL);
+    pj_xml_add_node(addr, status);
+
+    /* status attr */
+    attr = xml_create_attr(pool, &STATUS, &OPEN);
+    pj_xml_add_attr(status, attr);
+
+    return pres;
+}   
+
+
+
+PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len)
+{
+    pjxpidf_pres *pres;
+    pj_xml_node *node;
+
+    pres = pj_xml_parse(pool, text, len);
+    if (!pres)
+	return NULL;
+
+    /* Validate <presence> */
+    if (pj_stricmp(&pres->name, &PRESENCE) != 0)
+	return NULL;
+    if (pj_xml_find_attr(pres, &URI, NULL) == NULL)
+	return NULL;
+
+    /* Validate <presentity> */
+    node = pj_xml_find_node(pres, &PRESENTITY);
+    if (node == NULL)
+	return NULL;
+
+    /* Validate <atom> */
+    node = pj_xml_find_node(pres, &ATOM);
+    if (node == NULL)
+	return NULL;
+    if (pj_xml_find_attr(node, &ATOMID, NULL) == NULL)
+	return NULL;
+
+    /* Address */
+    node = pj_xml_find_node(node, &ADDRESS);
+    if (node == NULL)
+	return NULL;
+    if (pj_xml_find_attr(node, &URI, NULL) == NULL)
+	return NULL;
+
+
+    /* Status */
+    node = pj_xml_find_node(node, &STATUS);
+    if (node == NULL)
+	return NULL;
+    if (pj_xml_find_attr(node, &STATUS, NULL) == NULL)
+	return NULL;
+
+    return pres;
+}
+
+
+PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len)
+{
+    return pj_xml_print(pres, text, len, PJ_TRUE);
+}
+
+
+PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres)
+{
+    pj_xml_node *presentity;
+    pj_xml_attr *attr;
+
+    presentity = pj_xml_find_node(pres, &PRESENTITY);
+    if (!presentity)
+	return &EMPTY_STRING;
+
+    attr = pj_xml_find_attr(presentity, &URI, NULL);
+    if (!attr)
+	return &EMPTY_STRING;
+
+    return &attr->value;
+}
+
+
+PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, 
+				    const pj_str_t *uri)
+{
+    pj_xml_node *presentity;
+    pj_xml_node *atom;
+    pj_xml_node *addr;
+    pj_xml_attr *attr;
+    pj_str_t dup_uri;
+
+    presentity = pj_xml_find_node(pres, &PRESENTITY);
+    if (!presentity) {
+	pj_assert(0);
+	return -1;
+    }
+    atom = pj_xml_find_node(pres, &ATOM);
+    if (!atom) {
+	pj_assert(0);
+	return -1;
+    }
+    addr = pj_xml_find_node(atom, &ADDRESS);
+    if (!addr) {
+	pj_assert(0);
+	return -1;
+    }
+
+    /* Set uri in presentity */
+    attr = pj_xml_find_attr(presentity, &URI, NULL);
+    if (!attr) {
+	pj_assert(0);
+	return -1;
+    }
+    pj_strdup(pool, &dup_uri, uri);
+    attr->value = dup_uri;
+
+    /* Set uri in address. */
+    attr = pj_xml_find_attr(addr, &URI, NULL);
+    if (!attr) {
+	pj_assert(0);
+	return -1;
+    }
+    attr->value = dup_uri;
+
+    return 0;
+}
+
+
+PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres)
+{
+    pj_xml_node *atom;
+    pj_xml_node *addr;
+    pj_xml_node *status;
+    pj_xml_attr *attr;
+
+    atom = pj_xml_find_node(pres, &ATOM);
+    if (!atom) {
+	pj_assert(0);
+	return PJ_FALSE;
+    }
+    addr = pj_xml_find_node(atom, &ADDRESS);
+    if (!addr) {
+	pj_assert(0);
+	return PJ_FALSE;
+    }
+    status = pj_xml_find_node(atom, &STATUS);
+    if (!status) {
+	pj_assert(0);
+	return PJ_FALSE;
+    }
+    attr = pj_xml_find_attr(status, &STATUS, NULL);
+    if (!attr) {
+	pj_assert(0);
+	return PJ_FALSE;
+    }
+
+    return pj_stricmp(&attr->value, &OPEN) ? PJ_TRUE : PJ_FALSE;
+}
+
+
+PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status)
+{
+    pj_xml_node *atom;
+    pj_xml_node *addr;
+    pj_xml_node *status;
+    pj_xml_attr *attr;
+
+    atom = pj_xml_find_node(pres, &ATOM);
+    if (!atom) {
+	pj_assert(0);
+	return -1;
+    }
+    addr = pj_xml_find_node(atom, &ADDRESS);
+    if (!addr) {
+	pj_assert(0);
+	return -1;
+    }
+    status = pj_xml_find_node(addr, &STATUS);
+    if (!status) {
+	pj_assert(0);
+	return -1;
+    }
+    attr = pj_xml_find_attr(status, &STATUS, NULL);
+    if (!attr) {
+	pj_assert(0);
+	return -1;
+    }
+
+    attr->value = ( online_status ? OPEN : CLOSED );
+    return 0;
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/xpidf.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/xpidf.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_simple/xpidf.h	(revision 1753)
@@ -0,0 +1,138 @@
+/* $Header: /pjproject/pjsip/src/pjsip_simple/xpidf.h 3     8/24/05 10:33a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#ifndef __PJSIP_SIMPLE_XPIDF_H__
+#define __PJSIP_SIMPLE_XPIDF_H__
+
+/**
+ * @file xpidf.h
+ * @brief XPIDF/Presence Information Data Format
+ */
+#include <pj/types.h>
+#include <pj/xml.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_SIMPLE_XPIDF XPIDF/Presence Information Data Format
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This is an old presence data format as described in:
+ * draft-rosenberg-impp-pidf-00.txt.
+ *
+ * We won't support this format extensively here, as it seems there's not
+ * too many implementations support this anymore, as it shouldn't.
+ */
+
+/** Type definitions for XPIDF root document. */
+typedef pj_xml_node pjxpidf_pres;
+
+
+/**
+ * Create a new XPIDF document.
+ *
+ * @param pool	    Pool.
+ * @param uri	    URI to set in the XPIDF document.
+ *
+ * @return	    XPIDF document.
+ */
+PJ_DECL(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri);
+
+
+/**
+ * Parse XPIDF document.
+ *
+ * @param pool	    Pool.
+ * @param text	    Input text.
+ * @param len	    Length of input text.
+ *
+ * @return	    XPIDF document.
+ */
+PJ_DECL(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len);
+
+
+/**
+ * Print XPIDF document.
+ *
+ * @param pres	    The XPIDF document to print.
+ * @param text	    Buffer to place the output.
+ * @param len	    Length of the buffer.
+ *
+ * @return	    The length printed.
+ */
+PJ_DECL(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len);
+
+
+/**
+ * Get URI in the XPIDF document
+ *
+ * @param pres	    XPIDF document
+ *
+ * @return	    The URI, or an empty string.
+ */
+PJ_DECL(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres);
+
+
+/**
+ * Set the URI of the XPIDF document.
+ *
+ * @param pool	    Pool.
+ * @param pres	    The XPIDF document.
+ * @param uri	    URI to set in the XPIDF document.
+ *
+ * @return	    Zero on success.
+ */
+PJ_DECL(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, 
+				     const pj_str_t *uri);
+
+
+/**
+ * Get presence status in the XPIDF document.
+ *
+ * @param pres	    XPIDF document.
+ *
+ * @return	    True to indicate the contact is online.
+ */
+PJ_DECL(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres);
+
+
+/**
+ * Set presence status in the XPIDF document.
+ *
+ * @param pres	    XPIDF document.
+ * @param status    Status to set, True for online, False for offline.
+ *
+ * @return	    Zero on success.
+ */
+PJ_DECL(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t status);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJSIP_SIMPLE_XPIDF_H__ */
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_ua.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_ua.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsip_ua.h	(revision 1753)
@@ -0,0 +1,33 @@
+/* $Header: /pjproject/pjsip/src/pjsip_ua.h 1     4/17/05 11:59a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+
+#ifndef __PJSIP_UA_H__
+#define __PJSIP_UA_H__
+
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_reg.h>
+#include <pjsip_mod_ua/sip_ua.h>
+
+#endif	/* __PJSIP_UA_H__ */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/getopt.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/getopt.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/getopt.c	(revision 1753)
@@ -0,0 +1,1066 @@
+/* $Header: /pjproject/pjsip/src/pjsua/getopt.c 4     5/14/05 12:24a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+
+#ifdef _MSC_VER
+/* in VC this file will generate a lot of warning about old style function
+ * declarations.
+ */
+# pragma warning(push, 3)
+#endif
+
+/* 
+ * getopt entry points
+ *
+ * modified by Mike Borella <mike_borella@mw.3com.com>
+ *
+ * $Id: getopt.c,v 1.4 2000/10/30 22:06:03 mborella Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef HAVE_GETOPT_LONG
+
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include "getopt.h"
+
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef	NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (int argc, char *const *argv, const char *options, 
+	     const struct option *long_options, int *opt_index)
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+ 
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+int
+getopt (int argc, char * const * argv, const char * optstring)
+{
+  return _getopt_internal (argc, argv, optstring,
+			   (const struct option *) 0,
+			   (int *) 0,
+			   0);
+}
+
+#endif	/* Not ELIDE_CODE.  */
+
+
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+
+//#include <strings.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef	__GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#include <unistd.h>
+#endif	/* GNU C library.  */
+
+#ifdef VMS
+#include <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#if defined (WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really.  See?  Capital letters.  */
+#include <windows.h>
+#define getpid() GetCurrentProcessId()
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+   When compiling libc, the _ macro is predefined.  */
+#ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid)	gettext (msgid)
+#else
+# define _(msgid)	(msgid)
+#endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+   causes problems with re-calling getopt as programs generally don't
+   know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+
+#ifdef	__GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define	my_index	strchr
+#else
+
+static char *
+my_index (const char *str, int chr)
+{
+  while (*str)
+    {
+      if (*str == chr)
+	return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+/* Defined in getopt_init.c  */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+extern pid_t __libc_pid;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+   is valid for the getopt call we must make sure that the ARGV passed
+   to getopt is that one passed to the process.  */
+static void
+__attribute__ ((unused))
+store_args_and_env (int argc, char *const *argv)
+{
+  /* XXX This is no good solution.  We should rather copy the args so
+     that we can compare them later.  But we must not use malloc(3).  */
+  original_argc = argc;
+  original_argv = argv;
+}
+text_set_element (__libc_subinit, store_args_and_env);
+
+# define SWAP_FLAGS(ch1, ch2) \
+  if (nonoption_flags_len > 0)						      \
+    {									      \
+      char __tmp = __getopt_nonoption_flags[ch1];			      \
+      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];	      \
+      __getopt_nonoption_flags[ch2] = __tmp;				      \
+    }
+#else	/* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif	/* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+#if defined (__STDC__) && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+#ifdef _LIBC
+  /* First make sure the handling of the `__getopt_nonoption_flags'
+     string can work normally.  Our top argument must be in the range
+     of the string.  */
+  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+    {
+      /* We must extend the array.  The user plays games with us and
+	 presents new arguments.  */
+      char *new_str = malloc (top + 1);
+      if (new_str == NULL)
+	nonoption_flags_len = nonoption_flags_max_len = 0;
+      else
+	{
+	  memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
+	  memset (&new_str[nonoption_flags_max_len], '\0',
+		  top + 1 - nonoption_flags_max_len);
+	  nonoption_flags_max_len = top + 1;
+	  __getopt_nonoption_flags = new_str;
+	}
+    }
+#endif
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+	{
+	  /* Bottom segment is the short one.  */
+	  int len = middle - bottom;
+	  register int i;
+
+	  /* Swap it with the top part of the top segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[top - (middle - bottom) + i];
+	      argv[top - (middle - bottom) + i] = tem;
+	      SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+	    }
+	  /* Exclude the moved bottom segment from further swapping.  */
+	  top -= len;
+	}
+      else
+	{
+	  /* Top segment is the short one.  */
+	  int len = top - middle;
+	  register int i;
+
+	  /* Swap it with the bottom part of the bottom segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[middle + i];
+	      argv[middle + i] = tem;
+	      SWAP_FLAGS (bottom + i, middle + i);
+	    }
+	  /* Exclude the moved top segment from further swapping.  */
+	  bottom += len;
+	}
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+#if defined (__STDC__) && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+#ifdef _LIBC
+  if (posixly_correct == NULL
+      && argc == original_argc && argv == original_argv)
+    {
+      if (nonoption_flags_max_len == 0)
+	{
+	  if (__getopt_nonoption_flags == NULL
+	      || __getopt_nonoption_flags[0] == '\0')
+	    nonoption_flags_max_len = -1;
+	  else
+	    {
+	      const char *orig_str = __getopt_nonoption_flags;
+	      int len = nonoption_flags_max_len = strlen (orig_str);
+	      if (nonoption_flags_max_len < argc)
+		nonoption_flags_max_len = argc;
+	      __getopt_nonoption_flags =
+		(char *) malloc (nonoption_flags_max_len);
+	      if (__getopt_nonoption_flags == NULL)
+		nonoption_flags_max_len = -1;
+	      else
+		{
+		  memcpy (__getopt_nonoption_flags, orig_str, len);
+		  memset (&__getopt_nonoption_flags[len], '\0',
+			  nonoption_flags_max_len - len);
+		}
+	    }
+	}
+      nonoption_flags_len = nonoption_flags_max_len;
+    }
+  else
+    nonoption_flags_len = 0;
+#endif
+
+  return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  optarg = NULL;
+
+  if (optind == 0 || !__getopt_initialized)
+    {
+      if (optind == 0)
+	optind = 1;	/* Don't scan ARGV[0], the program name.  */
+      optstring = _getopt_initialize (argc, argv, optstring);
+      __getopt_initialized = 1;
+    }
+
+  /* Test whether ARGV[optind] points to a non-option argument.
+     Either it does not have option syntax, or there is an environment flag
+     from the shell indicating it is not an option.  The later information
+     is only used when the used in the GNU libc.  */
+#ifdef _LIBC
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'	      \
+		     || (optind < nonoption_flags_len			      \
+			 && __getopt_nonoption_flags[optind] == '1'))
+#else
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+	 moved back by the user (who may also have changed the arguments).  */
+      if (last_nonopt > optind)
+	last_nonopt = optind;
+      if (first_nonopt > optind)
+	first_nonopt = optind;
+
+      if (ordering == PERMUTE)
+	{
+	  /* If we have just processed some options following some non-options,
+	     exchange them so that the options come first.  */
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (last_nonopt != optind)
+	    first_nonopt = optind;
+
+	  /* Skip any additional non-options
+	     and extend the range of non-options previously skipped.  */
+
+	  while (optind < argc && NONOPTION_P)
+	    optind++;
+	  last_nonopt = optind;
+	}
+
+      /* The special ARGV-element `--' means premature end of options.
+	 Skip it like a null option,
+	 then exchange with previous non-options as if it were an option,
+	 then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+	{
+	  optind++;
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (first_nonopt == last_nonopt)
+	    first_nonopt = optind;
+	  last_nonopt = argc;
+
+	  optind = argc;
+	}
+
+      /* If we have done all the ARGV-elements, stop the scan
+	 and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+	{
+	  /* Set the next-arg-index to point at the non-options
+	     that we previously skipped, so the caller will digest them.  */
+	  if (first_nonopt != last_nonopt)
+	    optind = first_nonopt;
+	  return -1;
+	}
+
+      /* If we have come to a non-option and did not permute it,
+	 either stop the scan or describe it to the caller and pass it by.  */
+
+      if (NONOPTION_P)
+	{
+	  if (ordering == REQUIRE_ORDER)
+	    return -1;
+	  optarg = argv[optind++];
+	  return 1;
+	}
+
+      /* We have found another option-ARGV-element.
+	 Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+		  + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+	  || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound = -1;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+	/* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+	 or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	if (!strncmp (p->name, nextchar, nameend - nextchar))
+	  {
+	    if ((unsigned int) (nameend - nextchar)
+		== (unsigned int) strlen (p->name))
+	      {
+		/* Exact match found.  */
+		pfound = p;
+		indfound = option_index;
+		exact = 1;
+		break;
+	      }
+	    else if (pfound == NULL)
+	      {
+		/* First nonexact match found.  */
+		pfound = p;
+		indfound = option_index;
+	      }
+	    else
+	      /* Second or later nonexact match found.  */
+	      ambig = 1;
+	  }
+
+      if (ambig && !exact)
+	{
+	  if (opterr)
+	    fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+		     argv[0], argv[optind]);
+	  nextchar += strlen (nextchar);
+	  optind++;
+	  optopt = 0;
+	  return '?';
+	}
+
+      if (pfound != NULL)
+	{
+	  option_index = indfound;
+	  optind++;
+	  if (*nameend)
+	    {
+	      /* Don't test has_arg with >, because some C compilers don't
+		 allow it to be used on enums.  */
+	      if (pfound->has_arg)
+		optarg = nameend + 1;
+	      else
+		{
+		  if (opterr)
+		   {
+		   if (argv[optind - 1][1] == '-')
+		    /* --option */
+		    fprintf (stderr,
+		     _("%s: option `--%s' doesn't allow an argument\n"),
+		     argv[0], pfound->name);
+		   else
+		      {	
+		    /* +option or -option */
+		    fprintf (stderr,
+		     _("%s: option `%c%s' doesn't allow an argument\n"),
+		     argv[0], argv[optind - 1][0], pfound->name);
+                      }
+		    }
+		  nextchar += strlen (nextchar);
+
+		  optopt = pfound->val;
+		  return '?';
+		}
+	    }
+	  else if (pfound->has_arg == 1)
+	    {
+	      if (optind < argc)
+		optarg = argv[optind++];
+	      else
+		{
+		  if (opterr)
+		    fprintf (stderr,
+			   _("%s: option `%s' requires an argument\n"),
+			   argv[0], argv[optind - 1]);
+		  nextchar += strlen (nextchar);
+		  optopt = pfound->val;
+		  return optstring[0] == ':' ? ':' : '?';
+		}
+	    }
+	  nextchar += strlen (nextchar);
+	  if (longind != NULL)
+	    *longind = option_index;
+	  if (pfound->flag)
+	    {
+	      *(pfound->flag) = pfound->val;
+	      return 0;
+	    }
+	  return pfound->val;
+	}
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+	 or the option starts with '--' or is not a valid short
+	 option, then it's an error.
+	 Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+	  || my_index (optstring, *nextchar) == NULL)
+	{
+	  if (opterr)
+	    {
+	      if (argv[optind][1] == '-')
+		/* --option */
+		fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+			 argv[0], nextchar);
+	      else
+		/* +option or -option */
+		fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+			 argv[0], argv[optind][0], nextchar);
+	    }
+	  nextchar = (char *) "";
+	  optind++;
+	  optopt = 0;
+	  return '?';
+	}
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+	if (opterr)
+	  {
+	    if (posixly_correct)
+	      /* 1003.2 specifies the format of this message.  */
+	      fprintf (stderr, _("%s: illegal option -- %c\n"),
+		       argv[0], c);
+	    else
+	      fprintf (stderr, _("%s: invalid option -- %c\n"),
+		       argv[0], c);
+	  }
+	optopt = c;
+	return '?';
+      }
+    /* Convenience. Treat POSIX -W foo same as long option --foo */
+    if (temp[0] == 'W' && temp[1] == ';')
+      {
+	char *nameend;
+	const struct option *p;
+	const struct option *pfound = NULL;
+	int exact = 0;
+	int ambig = 0;
+	int indfound = 0;
+	int option_index;
+
+	/* This is an option that requires an argument.  */
+	if (*nextchar != '\0')
+	  {
+	    optarg = nextchar;
+	    /* If we end this ARGV-element by taking the rest as an arg,
+	       we must advance to the next element now.  */
+	    optind++;
+	  }
+	else if (optind == argc)
+	  {
+	    if (opterr)
+	      {
+		/* 1003.2 specifies the format of this message.  */
+		fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+			 argv[0], c);
+	      }
+	    optopt = c;
+	    if (optstring[0] == ':')
+	      c = ':';
+	    else
+	      c = '?';
+	    return c;
+	  }
+	else
+	  /* We already incremented `optind' once;
+	     increment it again when taking next ARGV-elt as argument.  */
+	  optarg = argv[optind++];
+
+	/* optarg is now the argument, see if it's in the
+	   table of longopts.  */
+
+	for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+	  /* Do nothing.  */ ;
+
+	/* Test all long options for either exact match
+	   or abbreviated matches.  */
+	for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	  if (!strncmp (p->name, nextchar, nameend - nextchar))
+	    {
+	      if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+		{
+		  /* Exact match found.  */
+		  pfound = p;
+		  indfound = option_index;
+		  exact = 1;
+		  break;
+		}
+	      else if (pfound == NULL)
+		{
+		  /* First nonexact match found.  */
+		  pfound = p;
+		  indfound = option_index;
+		}
+	      else
+		/* Second or later nonexact match found.  */
+		ambig = 1;
+	    }
+	if (ambig && !exact)
+	  {
+	    if (opterr)
+	      fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+		       argv[0], argv[optind]);
+	    nextchar += strlen (nextchar);
+	    optind++;
+	    return '?';
+	  }
+	if (pfound != NULL)
+	  {
+	    option_index = indfound;
+	    if (*nameend)
+	      {
+		/* Don't test has_arg with >, because some C compilers don't
+		   allow it to be used on enums.  */
+		if (pfound->has_arg)
+		  optarg = nameend + 1;
+		else
+		  {
+		    if (opterr)
+		      fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+			       argv[0], pfound->name);
+
+		    nextchar += strlen (nextchar);
+		    return '?';
+		  }
+	      }
+	    else if (pfound->has_arg == 1)
+	      {
+		if (optind < argc)
+		  optarg = argv[optind++];
+		else
+		  {
+		    if (opterr)
+		      fprintf (stderr,
+			       _("%s: option `%s' requires an argument\n"),
+			       argv[0], argv[optind - 1]);
+		    nextchar += strlen (nextchar);
+		    return optstring[0] == ':' ? ':' : '?';
+		  }
+	      }
+	    nextchar += strlen (nextchar);
+	    if (longind != NULL)
+	      *longind = option_index;
+	    if (pfound->flag)
+	      {
+		*(pfound->flag) = pfound->val;
+		return 0;
+	      }
+	    return pfound->val;
+	  }
+	  nextchar = NULL;
+	  return 'W';	/* Let the application handle it.   */
+      }
+    if (temp[1] == ':')
+      {
+	if (temp[2] == ':')
+	  {
+	    /* This is an option that accepts an argument optionally.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		optind++;
+	      }
+	    else
+	      optarg = NULL;
+	    nextchar = NULL;
+	  }
+	else
+	  {
+	    /* This is an option that requires an argument.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		/* If we end this ARGV-element by taking the rest as an arg,
+		   we must advance to the next element now.  */
+		optind++;
+	      }
+	    else if (optind == argc)
+	      {
+		if (opterr)
+		  {
+		    /* 1003.2 specifies the format of this message.  */
+		    fprintf (stderr,
+			   _("%s: option requires an argument -- %c\n"),
+			   argv[0], c);
+		  }
+		optopt = c;
+		if (optstring[0] == ':')
+		  c = ':';
+		else
+		  c = '?';
+	      }
+	    else
+	      /* We already incremented `optind' once;
+		 increment it again when taking next ARGV-elt as argument.  */
+	      optarg = argv[optind++];
+	    nextchar = NULL;
+	  }
+      }
+    return c;
+  }
+}
+
+#endif	/* Not ELIDE_CODE.  */
+
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/getopt.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/getopt.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/getopt.h	(revision 1753)
@@ -0,0 +1,162 @@
+/* $Header: /pjproject/pjsip/src/pjsua/getopt.h 3     5/05/05 11:43p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+/* Declarations for getopt.
+   Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument		(or 0) if the option does not take an argument,
+   required_argument	(or 1) if the option requires an argument,
+   optional_argument 	(or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+  const char *name;
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+# define no_argument		0
+# define required_argument	1
+# define optional_argument	2
+
+
+/* Get definitions and prototypes for functions to process the
+   arguments in ARGV (ARGC of them, minus the program name) for
+   options given in OPTS.
+
+   Return the option character from OPTS just read.  Return -1 when
+   there are no more options.  For unrecognized options, or options
+   missing arguments, `optopt' is set to the option letter, and '?' is
+   returned.
+
+   The OPTS string is a list of characters which are recognized option
+   letters, optionally followed by colons, specifying that that letter
+   takes an argument, to be placed in `optarg'.
+
+   If a letter in OPTS is followed by two colons, its argument is
+   optional.  This behavior is specific to the GNU `getopt'.
+
+   The argument `--' causes premature termination of argument
+   scanning, explicitly telling `getopt' that there are no more
+   options.
+
+   If OPTS begins with `--', then non-option arguments are treated as
+   arguments to the option '\0'.  This behavior is specific to the GNU
+   `getopt'.  */
+
+int getopt (int argc, char *const *argv, const char *shortopts);
+
+int getopt_long (int argc, char *const *argv, const char *options,
+		        const struct option *longopts, int *longind);
+int getopt_long_only (int argc, char *const *argv,
+			     const char *shortopts,
+		             const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+int _getopt_internal (int argc, char *const *argv,
+			     const char *shortopts,
+		             const struct option *longopts, int *longind,
+			     int long_only);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* getopt.h */
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/main.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/main.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/main.c	(revision 1753)
@@ -0,0 +1,1833 @@
+/* $Header: /pjproject/pjsip/src/pjsua/main.c 38    8/31/05 9:05p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+
+#include <pjlib.h>
+#include <pjsip_core.h>
+#include <pjsip_ua.h>
+#include <pjsip_simple.h>
+#include <pjmedia.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <pj/stun.h>
+
+#define START_PORT	    5060
+#define MAX_BUDDIES	    32
+#define THIS_FILE	    "main.c"
+#define MAX_PRESENTITY	    32
+#define PRESENCE_TIMEOUT    60
+
+/* By default we'll have one worker thread, except when threading 
+ * is disabled. 
+ */
+#if PJ_HAS_THREADS
+#  define WORKER_COUNT	1
+#else
+#  define WORKER_COUNT	0
+#endif
+
+/* Global variable. */
+static struct
+{
+    /* Control. */
+    pj_pool_factory *pf;
+    pjsip_endpoint  *endpt;
+    pj_pool_t	    *pool;
+    pjsip_user_agent *user_agent;
+    int		     worker_cnt;
+    int		     worker_quit_flag;
+
+    /* User info. */
+    char	     user_id[64];
+    pj_str_t	     local_uri;
+    pj_str_t	     contact;
+    pj_str_t	     real_contact;
+
+    /* Dialog. */
+    pjsip_dlg	    *cur_dlg;
+
+    /* Authentication. */
+    int		     cred_count;
+    pjsip_cred_info  cred_info[4];
+
+    /* Media stack. */
+    pj_bool_t	     null_audio;
+    pj_med_mgr_t    *mmgr;
+
+    /* Misc. */
+    int		     app_log_level;
+    char	    *log_filename;
+    FILE	    *log_file;
+
+    /* Proxy URLs */
+    pj_str_t	     proxy;
+    pj_str_t	     outbound_proxy;
+
+    /* UA auto options. */
+    int		     auto_answer;	/* -1 to disable. */
+    int		     auto_hangup;	/* -1 to disable */
+
+    /* Registration. */
+    pj_str_t	     registrar_uri;
+    pjsip_regc	    *regc;
+    pj_int32_t	     reg_timeout;
+    pj_timer_entry   regc_timer;
+
+    /* STUN */
+    pj_str_t	     stun_srv1;
+    int		     stun_port1;
+    pj_str_t	     stun_srv2;
+    int		     stun_port2;
+
+    /* UDP sockets and their public address. */
+    int		     sip_port;
+    pj_sock_t	     sip_sock;
+    pj_sockaddr_in   sip_sock_name;
+    pj_sock_t	     rtp_sock;
+    pj_sockaddr_in   rtp_sock_name;
+    pj_sock_t	     rtcp_sock;
+    pj_sockaddr_in   rtcp_sock_name;
+
+    /* SIMPLE */
+    pj_bool_t	     hide_status;
+    pj_bool_t	     offer_x_ms_msg;
+    int		     im_counter;
+    int		     buddy_cnt;
+    pj_str_t	     buddy[MAX_BUDDIES];
+    pj_bool_t	     buddy_status[MAX_BUDDIES];
+    pj_bool_t	     no_presence;
+    pjsip_presentity *buddy_pres[MAX_BUDDIES];
+
+    int		    pres_cnt;
+    pjsip_presentity *pres[MAX_PRESENTITY];
+
+} global;
+
+enum { AUTO_ANSWER, AUTO_HANGUP };
+
+/* This is the data that will be 'attached' on per dialog basis. */
+struct dialog_data
+{
+    /* Media session. */
+    pj_media_session_t *msession;
+
+    /* x-ms-chat session. */
+    pj_bool_t		x_ms_msg_session;
+
+    /* Cached SDP body, updated when media session changed. */
+    pjsip_msg_body *body;
+
+    /* Timer. */
+    pj_bool_t	     has_auto_timer;
+    pj_timer_entry   auto_timer;
+};
+
+/*
+ * These are the callbacks to be registered to dialog to receive notifications
+ * about various events in the dialog.
+ */
+static void dlg_on_all_events	(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
+				 pjsip_event *event );
+static void dlg_on_before_tx	(pjsip_dlg *dlg, pjsip_transaction *tsx, 
+				 pjsip_tx_data *tdata, int retransmission);
+static void dlg_on_tx_msg	(pjsip_dlg *dlg, pjsip_transaction *tsx, 
+				 pjsip_tx_data *tdata);
+static void dlg_on_rx_msg	(pjsip_dlg *dlg, pjsip_transaction *tsx, 
+				 pjsip_rx_data *rdata);
+static void dlg_on_incoming	(pjsip_dlg *dlg, pjsip_transaction *tsx,
+				 pjsip_rx_data *rdata);
+static void dlg_on_calling	(pjsip_dlg *dlg, pjsip_transaction *tsx,
+				 pjsip_tx_data *tdata);
+static void dlg_on_provisional	(pjsip_dlg *dlg, pjsip_transaction *tsx,
+				 pjsip_event *event);
+static void dlg_on_connecting	(pjsip_dlg *dlg, pjsip_event *event);
+static void dlg_on_established	(pjsip_dlg *dlg, pjsip_event *event);
+static void dlg_on_disconnected	(pjsip_dlg *dlg, pjsip_event *event);
+static void dlg_on_terminated	(pjsip_dlg *dlg);
+static void dlg_on_mid_call_evt	(pjsip_dlg *dlg, pjsip_event *event);
+
+/* The callback structure that will be registered to UA layer. */
+struct pjsip_dlg_callback dlg_callback = {
+    &dlg_on_all_events,
+    &dlg_on_before_tx,
+    &dlg_on_tx_msg,
+    &dlg_on_rx_msg,
+    &dlg_on_incoming,
+    &dlg_on_calling,
+    &dlg_on_provisional,
+    &dlg_on_connecting,
+    &dlg_on_established,
+    &dlg_on_disconnected,
+    &dlg_on_terminated,
+    &dlg_on_mid_call_evt
+};
+
+
+/* 
+ * Auxiliary things are put in misc.c, so that this main.c file is more 
+ * readable. 
+ */
+#include "misc.c"
+
+static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap,
+				     struct pj_timer_entry *entry)
+{
+    pjsip_dlg *dlg = entry->user_data;
+    struct dialog_data *dlg_data = dlg->user_data;
+    
+    PJ_UNUSED_ARG(timer_heap)
+
+    dlg_data->has_auto_timer = 0;
+
+    if (entry->id == AUTO_ANSWER) {
+	pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200);
+	if (tdata) {
+	    struct dialog_data *dlg_data = global.cur_dlg->user_data;
+	    tdata->msg->body = dlg_data->body;
+	    pjsip_dlg_send_msg(dlg, tdata);
+	}
+    } else {
+	pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500);
+	if (tdata) 
+	    pjsip_dlg_send_msg(dlg, tdata);
+    }
+}
+
+static void update_registration(pjsip_regc *regc, int renew)
+{
+    pjsip_tx_data *tdata;
+
+    PJ_LOG(3,(THIS_FILE, "Performing SIP registration..."));
+
+    if (renew) {
+	tdata = pjsip_regc_register(regc, 1);
+    } else {
+	tdata = pjsip_regc_unregister(regc);
+    }
+
+    pjsip_regc_send( regc, tdata );
+}
+
+static void regc_cb(struct pjsip_regc_cbparam *param)
+{
+    /*
+     * Print registration status.
+     */
+    if (param->code < 0 || param->code >= 300) {
+	PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)", 
+		   param->code, pjsip_get_status_text(param->code)->ptr));
+	global.regc = NULL;
+
+    } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
+	PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), "
+			      "will re-register in %d seconds", 
+			      param->code,
+			      pjsip_get_status_text(param->code)->ptr,
+			      param->expiration));
+
+    } else {
+	PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
+    }
+}
+
+static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata,
+				     int *timeout)
+{
+    int state;
+    int i;
+    char url[PJSIP_MAX_URL_SIZE];
+    int urllen;
+
+    PJ_UNUSED_ARG(rdata)
+
+    if (*timeout > 0) {
+	state = PJSIP_EVENT_SUB_STATE_ACTIVE;
+	if (*timeout > 300)
+	    *timeout = 300;
+    } else {
+	state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+    }
+
+    urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1);
+    if (urllen < 1) {
+	strcpy(url, "<unknown>");
+    } else {
+	url[urllen] = '\0';
+    }
+    PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s", 
+			 url, 
+			 (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated")));
+
+    for (i=0; i<global.pres_cnt; ++i)
+	if (global.pres[i] == pres)
+	    break;
+    if (i == global.pres_cnt)
+	global.pres[global.pres_cnt++] = pres;
+
+    pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
+    pjsip_presence_notify(pres, state, !global.hide_status);
+
+}
+
+static void pres_on_received_refresh(pjsip_presentity *pres, pjsip_rx_data *rdata)
+{
+    pres_on_received_request(pres, rdata, &pres->sub->default_interval);
+}
+
+/* This is called by presence framework when we receives presence update
+ * of a resource (buddy).
+ */
+static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open)
+{
+    int buddy_index = (int)pres->user_data;
+
+    global.buddy_status[buddy_index] = is_open;
+    PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s", 
+			 global.buddy[buddy_index].ptr,
+			 (is_open ? "Online" : "Offline")));
+}
+
+/* This is called when the subscription is terminated. */
+static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason)
+{
+    if (pres->sub->role == PJSIP_ROLE_UAC) {
+	int buddy_index = (int)pres->user_data;
+	PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)", 
+			    global.buddy[buddy_index].ptr,
+			    reason->slen, reason->ptr));
+	global.buddy_pres[buddy_index] = NULL;
+	global.buddy_status[buddy_index] = 0;
+    } else {
+	int i;
+	PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)", 
+			    reason->slen, reason->ptr));
+	pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1);
+	for (i=0; i<global.pres_cnt; ++i) {
+	    if (global.pres[i] == pres) {
+		int j;
+		global.pres[i] = NULL;
+		for (j=i+1; j<global.pres_cnt; ++j)
+		    global.pres[j-1] = global.pres[j];
+		global.pres_cnt--;
+		break;
+	    }
+	}
+    }
+    pjsip_presence_destroy(pres);
+}
+
+
+/* Callback attached to SIP body to print the body to message buffer. */
+static int print_msg_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
+{
+    pjsip_msg_body *body = msg_body;
+    return pjsdp_print ((pjsdp_session_desc*)body->data, buf, size);
+}
+
+/* When media session has changed, call this function to update the cached body
+ * information in the dialog. 
+ */
+static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg)
+{
+    struct dialog_data *dlg_data = dlg->user_data;
+    pjsdp_session_desc *sdp;
+
+    sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg);
+    if (!sdp) {
+	dlg_data->body = NULL;
+	return NULL;
+    }
+
+    /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new
+     * "m=" line in the SDP.
+     */
+    if (dlg_data->x_ms_msg_session >= 0 && 
+	dlg_data->x_ms_msg_session >= (int)sdp->media_count) 
+    {
+	pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m));
+	sdp->media[sdp->media_count] = m;
+	dlg_data->x_ms_msg_session = sdp->media_count++;
+    }
+
+    /*
+     * For "x-ms-message" line, remove all attributes and connection line etc.
+     */
+    if (dlg_data->x_ms_msg_session >= 0) {
+	pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session];
+	if (m) {
+	    m->desc.media = pj_str("x-ms-message");
+	    m->desc.port = 5060;
+	    m->desc.transport = pj_str("sip");
+	    m->desc.fmt_count = 1;
+	    m->desc.fmt[0] = pj_str("null");
+	    m->attr_count = 0;
+	    m->conn = NULL;
+	}
+    }
+
+    dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body));
+    dlg_data->body->content_type.type = pj_str("application");
+    dlg_data->body->content_type.subtype = pj_str("sdp");
+    dlg_data->body->len = 0;	/* ignored */
+    dlg_data->body->print_body = &print_msg_body;
+
+    dlg_data->body->data = sdp;
+    return dlg_data->body;
+}
+
+/* This callback will be called on every occurence of events in dialogs */
+static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
+			      pjsip_event *event )
+{
+    PJ_UNUSED_ARG(dlg_evt)
+    PJ_UNUSED_ARG(event)
+
+    PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg));
+}
+
+/* This callback is called before each outgoing msg is sent (including 
+ * retransmission). Application can override this notification if it wants
+ * to modify the message before transmission or if it wants to do something
+ * else for each transmission.
+ */
+static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx, 
+			     pjsip_tx_data *tdata, int ret_cnt)
+{
+    PJ_UNUSED_ARG(tsx)
+    PJ_UNUSED_ARG(tdata)
+
+    if (ret_cnt > 0) {
+	PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)", 
+			      dlg->obj_name, ret_cnt));
+    }
+}
+
+/* This callback is called after a message is sent. */
+static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, 
+			  pjsip_tx_data *tdata)
+{
+    PJ_UNUSED_ARG(tsx)
+    PJ_UNUSED_ARG(tdata)
+
+    PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
+}
+
+/* This callback is called on receipt of incoming message. */
+static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, 
+			  pjsip_rx_data *rdata)
+{
+    PJ_UNUSED_ARG(tsx)
+    PJ_UNUSED_ARG(rdata)
+    PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
+}
+
+/* This callback is called after dialog has sent INVITE */
+static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx,
+			   pjsip_tx_data *tdata)
+{
+    PJ_UNUSED_ARG(tsx)
+    PJ_UNUSED_ARG(tdata)
+
+    pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG &&
+	      tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
+	      tsx->method.id == PJSIP_INVITE_METHOD);
+
+    PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name));
+}
+
+static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp)
+{
+    struct dialog_data *dlg_data = dlg->user_data;
+    pj_bool_t sdp_x_ms_msg_index = -1;
+    int i;
+    int mcnt;
+    const pj_media_stream_info *mi[PJSDP_MAX_MEDIA];
+    int has_active;
+    pj_media_sock_info sock_info;
+
+    /* Find "m=x-ms-message" line in the SDP. */
+    for (i=0; i<(int)sdp->media_count; ++i) {
+	if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0)
+	    sdp_x_ms_msg_index = i;
+    }
+
+    /*
+     * Create media session.
+     */
+    pj_memset(&sock_info, 0, sizeof(sock_info));
+    sock_info.rtp_sock = global.rtp_sock;
+    sock_info.rtcp_sock = global.rtcp_sock;
+    pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
+
+    dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info);
+
+    /* A session will always be created, unless there is memory
+     * alloc problem.
+     */
+    pj_assert(dlg_data->msession);
+
+    /* See if we can take the offer by checking that we have at least
+     * one media stream active.
+     */
+    mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi);
+    for (i=0, has_active=0; i<mcnt; ++i) {
+	if (mi[i]->fmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) {
+	    has_active = 1;
+	    break;
+	}
+    }
+
+    if (!has_active && sdp_x_ms_msg_index==-1) {
+	pjsip_tx_data *tdata;
+
+	/* Unable to accept remote's SDP. 
+	 * Answer with 488 (Not Acceptable Here)
+	 */
+	/* Create 488 response. */
+	tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE);
+
+	/* Send response. */
+	if (tdata)
+	    pjsip_dlg_send_msg(dlg, tdata);
+	return;
+    }
+
+    dlg_data->x_ms_msg_session = sdp_x_ms_msg_index;
+
+    /* Create msg body to be used later in 2xx/response */
+    create_msg_body(dlg, 0);
+
+}
+
+/* This callback is called after an INVITE is received. */
+static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx,
+			    pjsip_rx_data *rdata)
+{
+    struct dialog_data *dlg_data;
+    pjsip_msg *msg;
+    pjsip_tx_data *tdata;
+    char buf[128];
+    int len;
+
+    PJ_UNUSED_ARG(tsx)
+
+    pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG &&
+	      rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
+	      tsx->method.id == PJSIP_INVITE_METHOD);
+
+    /*
+     * Notify user!
+     */
+    PJ_LOG(3, (THIS_FILE, ""));
+    PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name));
+    PJ_LOG(3, (THIS_FILE, ""));
+    len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, 
+			   (pjsip_name_addr*)dlg->remote.info->uri, 
+			   buf, sizeof(buf)-1);
+    if (len > 0) {
+	buf[len] = '\0';
+	PJ_LOG(3,(THIS_FILE, "From:\t%s", buf));
+    }
+    len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, 
+			   (pjsip_name_addr*)dlg->local.info->uri, 
+			   buf, sizeof(buf)-1);
+    if (len > 0) {
+	buf[len] = '\0';
+	PJ_LOG(3,(THIS_FILE, "To:\t%s", buf));
+    }
+    PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name));
+    PJ_LOG(3, (THIS_FILE, ""));
+
+    /*
+     * Process incoming dialog.
+     */
+
+    dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
+    dlg->user_data = dlg_data;
+
+    /* Update contact. */
+    pjsip_dlg_set_contact(dlg, &global.contact);
+
+    /* Initialize credentials. */
+    pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
+
+    /* Create media session if the request has "application/sdp" body. */
+    msg = rdata->msg;
+    if (msg->body && 
+	pj_stricmp2(&msg->body->content_type.type, "application")==0 &&
+	pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0)
+    {
+	pjsdp_session_desc *sdp;
+
+	/* Parse SDP body, and instantiate media session based on remote's SDP.
+	 * Then create our SDP body from the session.
+	 */
+	sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool);
+	if (!sdp)
+	    goto send_answer;
+
+	create_session_from_sdp(dlg, sdp);
+
+    } else if (msg->body) {
+	/* The request has a message body other than "application/sdp" */
+	pjsip_accept_hdr *accept;
+
+	/* Create response. */
+	tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
+
+	/* Add "Accept" header. */
+	accept = pjsip_accept_hdr_create(tdata->pool);
+	accept->values[0] = pj_str("application/sdp");
+	accept->count = 1;
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept);
+
+	/* Send response. */
+	pjsip_dlg_send_msg(dlg, tdata);
+	return;
+
+    } else {
+	/* The request has no message body. We can take this request, but
+	 * no media session will be activated.
+	 */
+	/* Nothing to do here. */
+    }
+
+send_answer:
+    /* Immediately answer with 100 (or 180? */
+    tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING );
+    pjsip_dlg_send_msg(dlg, tdata);
+
+    /* Set current dialog to this dialog if we don't currently have
+     * current dialog.
+     */
+    if (global.cur_dlg == NULL) {
+	global.cur_dlg = dlg;
+    }
+
+    /* Auto-answer if option is specified. */
+    if (global.auto_answer >= 0) {
+	pj_time_val delay = { 0, 0};
+	struct dialog_data *dlg_data = dlg->user_data;
+
+	PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds", 
+			      global.auto_answer));
+
+	delay.sec = global.auto_answer;
+	dlg_data->auto_timer.user_data = dlg;
+	dlg_data->auto_timer.id = AUTO_ANSWER;
+	dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
+	dlg_data->has_auto_timer = 1;
+	pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
+    }
+}
+
+/* This callback is called when dialog has sent/received a provisional response
+ * to INVITE.
+ */
+static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx,
+			       pjsip_event *event)
+{
+    const char *action;
+
+    pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
+	       event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
+	       event->src.tdata->msg->line.status.code/100 == 1 &&
+	       tsx->method.id == PJSIP_INVITE_METHOD) 
+	       ||
+	       (event->src_type == PJSIP_EVENT_RX_MSG &&
+	       event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
+	       event->src.rdata->msg->line.status.code/100 == 1 &&
+	       tsx->method.id == PJSIP_INVITE_METHOD));
+
+    if (event->src_type == PJSIP_EVENT_TX_MSG)
+	action = "Sending";
+    else
+	action = "Received";
+
+    PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)", 
+			  dlg->obj_name, action, tsx->status_code,
+			  pjsip_get_status_text(tsx->status_code)->ptr));
+}
+
+/* This callback is called when 200 response to INVITE is sent/received. */
+static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event)
+{
+    struct dialog_data *dlg_data = dlg->user_data;
+    const char *action;
+
+    pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
+	       event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
+	       event->src.tdata->msg->line.status.code/100 == 2)
+	       ||
+	       (event->src_type == PJSIP_EVENT_RX_MSG &&
+	       event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
+	       event->src.rdata->msg->line.status.code/100 == 2));
+
+    if (event->src_type == PJSIP_EVENT_RX_MSG)
+	action = "Received";
+    else
+	action = "Sending";
+
+    PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action));
+
+    if (event->src_type == PJSIP_EVENT_RX_MSG) {
+	/* On receipt of 2xx response, negotiate our media capability
+	 * and start media.
+	 */
+	pjsip_msg *msg = event->src.rdata->msg;
+	pjsip_msg_body *body;
+	pjsdp_session_desc *sdp;
+
+	/* Get SDP from message. */
+
+	/* Ignore if no SDP body is present. */
+	body = msg->body;
+	if (!body) {
+	    PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!",
+				  dlg->obj_name));
+	    return;
+	}
+
+	if (pj_stricmp2(&body->content_type.type, "application") != 0 &&
+	    pj_stricmp2(&body->content_type.subtype, "sdp") != 0) 
+	{
+	    PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!",
+				   dlg->obj_name));
+	    return;
+	}
+
+	/* Got what seems to be a SDP content. Parse it. */
+	sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool);
+	if (!sdp) {
+	    PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!",
+				  dlg->obj_name));
+	    return;
+	}
+
+	/* Negotiate media session with remote's media capability. */
+	if (pj_media_session_update (dlg_data->msession, sdp) != 0) {
+	    PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!",
+				  dlg->obj_name));
+	    return;
+	}
+
+	/* Update the saved SDP body because media session has changed. 
+	 * Also set ack flag to '1', because we only want to send one format/
+	 * codec for each media streams.
+	 */
+	create_msg_body(dlg, 1);
+
+	/* Activate media. */
+	pj_media_session_activate (dlg_data->msession);
+
+    } else {
+	pjsip_msg *msg = event->src.tdata->msg;
+
+	if (msg->body) {
+	    /* On transmission of 2xx response, start media session. */
+	    pj_media_session_activate (dlg_data->msession);
+	}
+    }
+
+}
+
+/* This callback is called when ACK to initial INVITE is sent/received. */
+static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event)
+{
+    const char *action;
+
+    pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
+	       event->src.tdata->msg->type == PJSIP_REQUEST_MSG &&
+	       event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
+	       ||
+	       (event->src_type == PJSIP_EVENT_RX_MSG &&
+	       event->src.rdata->msg->type == PJSIP_REQUEST_MSG &&
+	       event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD));
+
+    if (event->src_type == PJSIP_EVENT_RX_MSG)
+	action = "Received";
+    else
+	action = "Sending";
+
+    PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED", 
+			  dlg->obj_name, action));
+
+    /* Attach SDP body for outgoing ACK. */
+    if (event->src_type == PJSIP_EVENT_TX_MSG &&
+	event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
+    {
+	struct dialog_data *dlg_data = dlg->user_data;
+	event->src.tdata->msg->body = dlg_data->body;
+    }
+
+    /* Auto-hangup if option is specified. */
+    if (global.auto_hangup >= 0) {
+	pj_time_val delay = { 0, 0};
+	struct dialog_data *dlg_data = dlg->user_data;
+
+	PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds", 
+			      global.auto_hangup));
+
+	delay.sec = global.auto_hangup;
+	dlg_data->auto_timer.user_data = dlg;
+	dlg_data->auto_timer.id = AUTO_HANGUP;
+	dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
+	dlg_data->has_auto_timer = 1;
+	pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
+    }
+}
+
+
+/* This callback is called when dialog is disconnected (because of final
+ * response, BYE, or timer).
+ */
+static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event)
+{
+    struct dialog_data *dlg_data = dlg->user_data;
+    int status_code;
+    const pj_str_t *reason;
+    
+    PJ_UNUSED_ARG(event)
+
+    /* Cancel auto-answer/auto-hangup timer. */
+    if (dlg_data->has_auto_timer) {
+	pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer);
+	dlg_data->has_auto_timer = 0;
+    }
+
+    if (dlg->invite_tsx)
+	status_code = dlg->invite_tsx->status_code;
+    else
+	status_code = 200;
+
+    if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) {
+	if (event->src_type == PJSIP_EVENT_RX_MSG)
+	    reason = &event->src.rdata->msg->line.status.reason;
+	else if (event->src_type == PJSIP_EVENT_TX_MSG)
+	    reason = &event->src.tdata->msg->line.status.reason;
+	else
+	    reason = pjsip_get_status_text(event->obj.tsx->status_code);
+    } else {
+	reason = &event->obj.tsx->method.name;
+    }
+
+    PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)", 
+			  dlg->obj_name, status_code, 
+			  reason->slen, reason->ptr));
+
+    if (dlg_data->msession) {
+	pj_media_session_destroy (dlg_data->msession);
+	dlg_data->msession = NULL;
+    }
+}
+
+/* This callback is called when dialog is about to be destroyed. */
+static void dlg_on_terminated(pjsip_dlg *dlg)
+{
+    PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name));
+
+    /* If current dialog is equal to this dialog, update it. */
+    if (global.cur_dlg == dlg) {
+	global.cur_dlg = global.cur_dlg->next;
+	if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
+	    global.cur_dlg = NULL;
+	}
+    }
+}
+
+/* This callback is called for any requests when dialog is established. */
+static void dlg_on_mid_call_evt	(pjsip_dlg *dlg, pjsip_event *event)
+{
+    pjsip_transaction *tsx = event->obj.tsx;
+
+    if (event->src_type == PJSIP_EVENT_RX_MSG &&
+	event->src.rdata->msg->type == PJSIP_REQUEST_MSG) 
+    {
+	if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) {
+	    /* Re-invitation. */
+	    pjsip_tx_data *tdata;
+
+	    PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)",
+				 dlg->obj_name));
+	    tdata = pjsip_dlg_answer(dlg, 200);
+	    if (tdata) {
+		struct dialog_data *dlg_data = dlg->user_data;
+		tdata->msg->body = dlg_data->body;
+		pjsip_dlg_send_msg(dlg, tdata);
+	    }
+	} else {
+	    /* Don't worry, endpoint will answer with 500 or whetever. */
+	}
+
+    } else if (tsx->status_code/100 == 2) {
+	PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)",
+		  dlg->obj_name, 
+		  tsx->method.name.slen, tsx->method.name.ptr,
+		  tsx->status_code, 
+		  pjsip_get_status_text(tsx->status_code)->ptr));
+
+
+    } else if (tsx->status_code >= 300) {
+	pj_bool_t report_failure = PJ_TRUE;
+
+	/* Check for authentication failures. */
+	if (tsx->status_code==401 || tsx->status_code==407) {
+	    pjsip_tx_data *tdata;
+	    tdata = pjsip_auth_reinit_req( global.endpt,
+					   dlg->pool, &dlg->auth_sess,
+					   dlg->cred_count, dlg->cred_info,
+					   tsx->last_tx, event->src.rdata );
+	    if (tdata) {
+		int rc;
+		rc = pjsip_dlg_send_msg( dlg, tdata);
+		report_failure = (rc != 0);
+	    }
+	}
+	if (report_failure) {
+	    const pj_str_t *reason;
+	    if (event->src_type == PJSIP_EVENT_RX_MSG) {
+		reason = &event->src.rdata->msg->line.status.reason;
+	    } else {
+		reason = pjsip_get_status_text(tsx->status_code);
+	    }
+	    PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)",
+		      dlg->obj_name, tsx->status_code, 
+		      reason->slen, reason->ptr));
+	}
+    }
+}
+
+/* Initialize sockets and optionally get the public address via STUN. */
+static pj_status_t init_sockets()
+{
+    enum { 
+	RTP_START_PORT = 4000,
+	RTP_RANDOM_START = 2,
+	RTP_RETRY = 10 
+    };
+    enum {
+	SIP_SOCK,
+	RTP_SOCK,
+	RTCP_SOCK,
+    };
+    int i;
+    int rtp_port;
+    pj_sock_t sock[3];
+    pj_sockaddr_in mapped_addr[3];
+
+    for (i=0; i<3; ++i)
+	sock[i] = PJ_INVALID_SOCKET;
+
+    /* Create and bind SIP UDP socket. */
+    sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+    if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) {
+	PJ_LOG(2,(THIS_FILE, "Unable to create socket"));
+	goto on_error;
+    }
+    if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) {
+	PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port"));
+	goto on_error;
+    }
+
+    /* Initialize start of RTP port to try. */
+    rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2;
+
+    /* Loop retry to bind RTP and RTCP sockets. */
+    for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
+
+	/* Create and bind RTP socket. */
+	sock[RTP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+	if (sock[RTP_SOCK] == PJ_INVALID_SOCKET)
+	    goto on_error;
+	if (pj_sock_bind_in(sock[RTP_SOCK], 0, (pj_uint16_t)rtp_port) != 0) {
+	    pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+	    continue;
+	}
+
+	/* Create and bind RTCP socket. */
+	sock[RTCP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+	if (sock[RTCP_SOCK] == PJ_INVALID_SOCKET)
+	    goto on_error;
+	if (pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1)) != 0) {
+	    pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+	    pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
+	    continue;
+	}
+
+	/*
+	 * If we're configured to use STUN, then find out the mapped address,
+	 * and make sure that the mapped RTCP port is adjacent with the RTP.
+	 */
+	if (global.stun_port1 == 0) {
+	    pj_str_t hostname;
+	    pj_sockaddr_in addr;
+
+	    /* Get local IP address. */
+	    char hostname_buf[PJ_MAX_HOSTNAME];
+	    if (gethostname(hostname_buf, sizeof(hostname_buf)))
+		goto on_error;
+	    hostname = pj_str(hostname_buf);
+
+	    pj_memset( &addr, 0, sizeof(addr));
+	    addr.sin_family = PJ_AF_INET;
+	    if (pj_sockaddr_set_str_addr( &addr, &hostname) != PJ_OK)
+		goto on_error;
+
+	    for (i=0; i<3; ++i)
+		pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
+
+	    mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)global.sip_port);
+	    mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port);
+	    mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
+	    break;
+	} else {
+	    pj_status_t rc;
+	    rc = pj_stun_get_mapped_addr( global.pf, 3, sock,
+					  &global.stun_srv1, global.stun_port1,
+					  &global.stun_srv2, global.stun_port2,
+					  mapped_addr);
+	    if (rc != 0) {
+		PJ_LOG(3,(THIS_FILE, "Error: %s", pj_stun_get_err_msg(rc)));
+		goto on_error;
+	    }
+
+	    if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1)
+		break;
+
+	    pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+	    pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
+	}
+    }
+
+    if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) {
+	PJ_LOG(2,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination"));
+	goto on_error;
+    }
+
+    global.sip_sock = sock[SIP_SOCK];
+    pj_memcpy(&global.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in));
+    global.rtp_sock = sock[RTP_SOCK];
+    pj_memcpy(&global.rtp_sock_name, &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in));
+    global.rtcp_sock = sock[RTCP_SOCK];
+    pj_memcpy(&global.rtcp_sock_name, &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in));
+
+    PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
+	      pj_inet_ntoa(global.sip_sock_name.sin_addr), 
+	      pj_ntohs(global.sip_sock_name.sin_port)));
+    PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
+	      pj_inet_ntoa(global.rtp_sock_name.sin_addr), 
+	      pj_ntohs(global.rtp_sock_name.sin_port)));
+    PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d",
+	      pj_inet_ntoa(global.rtcp_sock_name.sin_addr), 
+	      pj_ntohs(global.rtcp_sock_name.sin_port)));
+    return 0;
+
+on_error:
+    for (i=0; i<3; ++i) {
+	if (sock[i] != PJ_INVALID_SOCKET)
+	    pj_sock_close(sock[i]);
+    }
+    return -1;
+}
+
+static void log_function(int level, const char *buffer, int len)
+{
+    /* Write to both stdout and file. */
+    if (level <= global.app_log_level)
+	pj_log_to_stdout(level, buffer, len);
+    if (global.log_file) {
+	fwrite(buffer, len, 1, global.log_file);
+	fflush(global.log_file);
+    }
+}
+
+/* Initialize stack. */
+static pj_status_t init_stack()
+{
+    pj_status_t status;
+    pj_sockaddr_in bind_addr;
+    pj_sockaddr_in bind_name;
+    const char *local_addr;
+    static char local_uri[128];
+
+    /* Optionally set logging file. */
+    if (global.log_filename) {
+	global.log_file = fopen(global.log_filename, "wt");
+    }
+
+    /* Initialize endpoint. This will also call initialization to all the
+     * modules.
+     */
+    global.endpt = pjsip_endpt_create(global.pf);
+    if (global.endpt == NULL) {
+	return -1;
+    }
+
+    /* Set dialog callback. */
+    pjsip_ua_set_dialog_callback(global.user_agent, &dlg_callback);
+
+    /* Init listener's bound address and port. */
+    pj_sockaddr_init2(&bind_addr, "0.0.0.0", global.sip_port);
+    pj_sockaddr_init(&bind_name, pj_gethostname(), global.sip_port);
+
+    /* Add UDP transport listener. */
+    status = pjsip_endpt_create_udp_listener( global.endpt, global.sip_sock,
+					      &global.sip_sock_name);
+    if (status != 0)
+	return -1;
+
+    local_addr = pj_inet_ntoa(global.sip_sock_name.sin_addr);
+
+#if PJ_HAS_TCP
+    /* Add TCP transport listener. */
+    status = pjsip_endpt_create_listener( global.endpt, PJSIP_TRANSPORT_TCP, 
+					  &bind_addr, &bind_name);
+    if (status != 0)
+	return -1;
+#endif
+
+    /* Determine user_id to be put in Contact */
+    if (global.local_uri.slen) {
+	pj_pool_t *pool = pj_pool_create(global.pf, "parser", 1024, 0, NULL);
+	pjsip_uri *uri;
+
+	uri = pjsip_parse_uri(pool, global.local_uri.ptr, global.local_uri.slen, 0);
+	if (uri) {
+	    if (pj_stricmp2(pjsip_uri_get_scheme(uri), "sip")==0) {
+		pjsip_url *url = (pjsip_url*)pjsip_uri_get_uri(uri);
+		if (url->user.slen)
+		    strncpy(global.user_id, url->user.ptr, url->user.slen);
+	    }
+	} 
+	pj_pool_release(pool);
+    } 
+    
+    if (global.user_id[0]=='\0') {
+	strcpy(global.user_id, "user");
+    }
+
+    /* build contact */
+    global.real_contact.ptr = local_uri;
+    global.real_contact.slen = 
+	sprintf(local_uri, "<sip:%s@%s:%d>", global.user_id, local_addr, global.sip_port);
+
+    if (global.contact.slen == 0)
+	global.contact = global.real_contact;
+
+    /* initialize local_uri with contact if it's not specified in cmdline */
+    if (global.local_uri.slen == 0)
+	global.local_uri = global.contact;
+
+    /* Init proxy. */
+    if (global.proxy.slen || global.outbound_proxy.slen) {
+	int count = 0;
+	pj_str_t proxy_url[2];
+
+	if (global.outbound_proxy.slen) {
+	    proxy_url[count++] = global.outbound_proxy;
+	}
+	if (global.proxy.slen) {
+	    proxy_url[count++] = global.proxy;
+	}
+
+	if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) {
+	    PJ_LOG(2,(THIS_FILE, "Error setting proxy address!"));
+	    return -1;
+	}
+    }
+
+    /* initialize SIP registration if registrar is configured */
+    if (global.registrar_uri.slen) {
+	global.regc = pjsip_regc_create( global.endpt, NULL, &regc_cb);
+	pjsip_regc_init( global.regc, &global.registrar_uri, 
+			 &global.local_uri, 
+			 &global.local_uri,
+			 1, &global.contact, 
+			 global.reg_timeout);
+	pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info );
+    }
+
+    return PJ_OK;
+}
+
+/* Worker thread function, only used when threading is enabled. */
+static void *PJ_THREAD_FUNC worker_thread(void *unused)
+{
+    PJ_UNUSED_ARG(unused)
+
+    while (!global.worker_quit_flag) {
+	pj_time_val timeout = { 0, 10 };
+	pjsip_endpt_handle_events (global.endpt, &timeout);
+    }
+    return NULL;
+}
+
+
+/* Make call to the specified URI. */
+static pjsip_dlg *make_call(pj_str_t *remote_uri)
+{
+    pjsip_dlg *dlg;
+    pj_str_t local = global.contact;
+    pj_str_t remote = *remote_uri;
+    struct dialog_data *dlg_data;
+    pjsip_tx_data *tdata;
+    pj_media_sock_info sock_info;
+
+    /* Create new dialog instance. */
+    dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC);
+
+    /* Attach our own user data. */
+    dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
+    dlg->user_data = dlg_data;
+
+    /* Create media session. */
+    pj_memset(&sock_info, 0, sizeof(sock_info));
+    sock_info.rtp_sock = global.rtp_sock;
+    sock_info.rtcp_sock = global.rtcp_sock;
+    pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
+
+    dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info);
+    dlg_data->x_ms_msg_session = -1;
+
+    if (global.offer_x_ms_msg) {
+	const pj_media_stream_info *minfo[32];
+	unsigned cnt;
+
+	cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo);
+	if (cnt > 0)
+	    dlg_data->x_ms_msg_session = cnt;
+    } 
+
+    /* Initialize dialog with local and remote URI. */
+    if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_OK) {
+	pjsip_ua_destroy_dialog(dlg);
+	return NULL;
+    }
+
+    /* Initialize credentials. */
+    pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
+
+    /* Send INVITE! */
+    tdata = pjsip_dlg_invite(dlg);
+    tdata->msg->body = create_msg_body (dlg, 0);
+
+    if (pjsip_dlg_send_msg(dlg, tdata) != PJ_OK) {
+	pjsip_ua_destroy_dialog(dlg);
+	return NULL;
+    }
+
+    return dlg;
+}
+
+/*
+ * Callback to receive incoming IM message.
+ */
+static int on_incoming_im_msg(pjsip_rx_data *rdata)
+{
+    pjsip_msg *msg = rdata->msg;
+    pjsip_msg_body *body = msg->body;
+    int len;
+    char to[128], from[128];
+
+
+    len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, 
+			   rdata->from->uri, from, sizeof(from));
+    if (len > 0) from[len] = '\0';
+    else strcpy(from, "<URL too long..>");
+
+    len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, 
+			   rdata->to->uri, to, sizeof(to));
+    if (len > 0) to[len] = '\0';
+    else strcpy(to, "<URL too long..>");
+
+    PJ_LOG(3,(THIS_FILE, "Incoming instant message:"));
+    
+    printf("----- BEGIN INSTANT MESSAGE ----->\n");
+    printf("From:\t%s\n", from);
+    printf("To:\t%s\n", to);
+    printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : ""));
+    printf("<------ END INSTANT MESSAGE ------\n");
+
+    fflush(stdout);
+
+    /* Must answer with final response. */
+    return 200;
+}
+
+/*
+ * Input URL.
+ */
+static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection)
+{
+    int i;
+
+    *selection = -1;
+
+    printf("\nBuddy list:\n");
+    printf("---------------------------------------\n");
+    for (i=0; i<global.buddy_cnt; ++i) {
+	printf(" %d\t%s  <%s>\n", i+1, global.buddy[i].ptr,
+		(global.buddy_status[i]?"Online":"Offline"));
+    }
+    printf("-------------------------------------\n");
+
+    printf("Choices\n"
+	   "\t0        For current dialog.\n"
+	   "\t[1-%02d]   Select from buddy list\n"
+	   "\tURL      An URL\n"
+	   , global.buddy_cnt);
+    printf("Input: ");
+
+    fflush(stdout);
+    fgets(buf, len, stdin);
+    buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */
+
+    while (isspace(*buf)) ++buf;
+
+    if (!*buf || *buf=='\n' || *buf=='\r')
+	return NULL;
+
+    i = atoi(buf);
+
+    if (i == 0) {
+	if (isdigit(*buf)) {
+	    *selection = 0;
+	    *out = pj_str("0");
+	    return out;
+	} else {
+	    if (verify_sip_url(buf) != 0) {
+		puts("Invalid URL specified!");
+		return NULL;
+	    }
+	    *out = pj_str(buf);
+	    return out;
+	}
+    } else if (i > global.buddy_cnt || i < 0) {
+	printf("Error: invalid selection!\n");
+	return NULL;
+    } else {
+	*out = global.buddy[i-1];
+	*selection = i;
+	return out;
+    }
+}
+
+
+static void generic_request_callback( void *token, pjsip_event *event )
+{
+    pjsip_transaction *tsx = event->obj.tsx;
+    
+    PJ_UNUSED_ARG(token)
+
+    if (tsx->status_code/100 == 2) {
+	PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)",
+		  event->obj.tsx->method.name.slen,
+		  event->obj.tsx->method.name.ptr,
+		  tsx->status_code,
+		  pjsip_get_status_text(tsx->status_code)->ptr));
+    } else if (tsx->status_code==401 || tsx->status_code==407)  {
+	pjsip_tx_data *tdata;
+	tdata = pjsip_auth_reinit_req( global.endpt,
+				       global.pool, NULL, global.cred_count, global.cred_info,
+				       tsx->last_tx, event->src.rdata);
+	if (tdata) {
+	    int rc;
+	    pjsip_cseq_hdr *cseq;
+	    cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+	    cseq->cseq++;
+	    rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, 
+					    &generic_request_callback);
+	    if (rc == 0)
+		return;
+	}
+	PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)",
+		  event->obj.tsx->method.name.slen,
+		  event->obj.tsx->method.name.ptr,
+		  event->obj.tsx->status_code,
+		  pjsip_get_status_text(event->obj.tsx->status_code)->ptr));
+    } else {
+	const pj_str_t *reason;
+	if (event->src_type == PJSIP_EVENT_RX_MSG)
+	    reason = &event->src.rdata->msg->line.status.reason;
+	else
+	    reason = pjsip_get_status_text(tsx->status_code);
+	PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)",
+		  event->obj.tsx->method.name.slen,
+		  event->obj.tsx->method.name.ptr,
+		  event->obj.tsx->status_code,
+		  reason->slen, reason->ptr));
+    }
+}
+
+
+static void ui_send_im_message()
+{
+    char line[100];
+    char text_buf[100];
+    pj_str_t str;
+    pj_str_t text_msg;
+    int selection, rc;
+    pjsip_tx_data *tdata;
+  
+    if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
+	return;
+
+	
+    printf("Enter text to send (empty to cancel): "); fflush(stdout);
+    fgets(text_buf, sizeof(text_buf), stdin);
+    text_buf[strlen(text_buf)-1] = '\0';
+    if (!*text_buf)
+	return;
+
+    text_msg = pj_str(text_buf);
+    
+    if (selection==0) {
+	pjsip_method message_method;
+	pj_str_t str_MESSAGE = { "MESSAGE", 7 };
+
+	/* Send IM to current dialog. */
+	if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
+	    printf("No current dialog or dialog state is not ESTABLISHED!\n");
+	    return;
+	}
+
+	pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE);
+	tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 );
+
+	if (tdata) {
+	    /* Create message body for the text. */
+	    pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body));
+	    body->content_type.type = pj_str("text");
+	    body->content_type.subtype = pj_str("plain");
+	    body->data = pj_pool_alloc(tdata->pool, text_msg.slen);
+	    pj_memcpy(body->data, text_msg.ptr, text_msg.slen);
+	    body->len = text_msg.slen;
+	    body->print_body = &pjsip_print_text_body;
+
+	    /* Assign body to message, and send the message! */
+	    tdata->msg->body = body;
+	    pjsip_dlg_send_msg( global.cur_dlg, tdata );
+	}
+
+    } else {
+	/* Send IM to buddy list. */
+	pjsip_method message;
+	static pj_str_t MESSAGE = { "MESSAGE", 7 };
+	pjsip_method_init_np(&message, &MESSAGE);
+	tdata = pjsip_endpt_create_request(global.endpt, &message, 
+					   &str,
+					   &global.real_contact,
+				           &str, &global.real_contact, NULL, -1, 
+					   &text_msg);
+	if (!tdata) {
+	    puts("Error creating request");
+	    return;
+	}
+	rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback);
+	if (rc == 0) {
+	    printf("Sending IM message %d\n", global.im_counter);
+	    ++global.im_counter;
+	} else {
+	    printf("Error: unable to send IM message!\n");
+	}
+    }
+}
+
+static void ui_send_options()
+{
+    char line[100];
+    pj_str_t str;
+    int selection, rc;
+    pjsip_tx_data *tdata;
+    pjsip_method options;
+
+    if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
+	return;
+
+    pjsip_method_set( &options, PJSIP_OPTIONS_METHOD );
+
+    if (selection == 0) {
+	/* Send OPTIONS to current dialog. */
+	tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1);
+	if (tdata)
+	    pjsip_dlg_send_msg( global.cur_dlg, tdata );
+    } else {
+	/* Send OPTIONS to arbitrary party. */
+	tdata = pjsip_endpt_create_request( global.endpt, &options,
+					    &str,
+					    &global.local_uri, &str, 
+					    &global.real_contact,
+					    NULL, -1, NULL);
+	if (tdata) {
+	    rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, 
+					   &generic_request_callback);
+	    if (rc != 0)
+		PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!"));
+	}
+    }
+}
+
+static void init_presence()
+{
+    const pjsip_presence_cb pres_cb = {
+	NULL,
+	&pres_on_received_request,
+	&pres_on_received_refresh,
+	&pres_on_received_update,
+	&pres_on_terminated
+    };
+
+    pjsip_presence_init(&pres_cb);
+}
+
+/* Subscribe presence information for all buddies. */
+static void subscribe_buddies_presence()
+{
+    int i;
+    for (i=0; i<global.buddy_cnt; ++i) {
+	pjsip_presentity *pres;
+	if (global.buddy_pres[i])
+	    continue;
+	pres = pjsip_presence_create( global.endpt, &global.local_uri,
+				      &global.buddy[i], PRESENCE_TIMEOUT, (void*)i);
+	if (pres) {
+	    pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
+	    pjsip_presence_subscribe( pres );
+	}
+	global.buddy_pres[i] = pres;
+    }
+}
+
+/* Unsubscribe presence information for all buddies. */
+static void unsubscribe_buddies_presence()
+{
+    int i;
+    for (i=0; i<global.buddy_cnt; ++i) {
+	pjsip_presentity *pres = global.buddy_pres[i];
+	if (pres) {
+	    pjsip_presence_unsubscribe(pres);
+	    pjsip_presence_destroy(pres);
+	    global.buddy_pres[i] = NULL;
+	}
+    }
+}
+
+/* Unsubscribe presence. */
+static void unsubscribe_presence()
+{
+    int i;
+
+    unsubscribe_buddies_presence();
+    for (i=0; i<global.pres_cnt; ++i) {
+	pjsip_presentity *pres = global.pres[i];
+	pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 0);
+	pjsip_presence_destroy( pres );
+    }
+}
+
+/* Advertise online status to subscribers. */
+static void update_im_status()
+{
+    int i;
+    for (i=0; i<global.pres_cnt; ++i) {
+	pjsip_presentity *pres = global.pres[i];
+	pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_ACTIVE, 
+			       !global.hide_status);
+    }
+}
+
+/*
+ * Main program.
+ */
+int main(int argc, char *argv[])
+{
+    /* set to WORKER_COUNT+1 to avoid zero size warning 
+     * when threading is disabled. */
+    pj_thread_t *thread[WORKER_COUNT+1];
+    pj_caching_pool cp;
+    int i;
+
+    global.sip_port = 5060;
+    global.auto_answer = -1;
+    global.auto_hangup = -1;
+    global.app_log_level = 3;
+
+    pj_log_set_level(4);
+    pj_log_set_log_func(&log_function);
+
+    /* Init PJLIB */
+    if (pj_init() != PJ_OK)
+	return 1;
+
+    /* Init caching pool. */
+    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+    global.pf = &cp.factory;
+
+    /* Create memory pool for application. */
+    global.pool = pj_pool_create(global.pf, "main", 1024, 0, NULL);
+
+    /* Parse command line arguments. */
+    if (parse_args(global.pool, argc, argv) != PJ_OK) {
+	pj_caching_pool_destroy(&cp);
+	return 1;
+    }
+
+    /* Init sockets */
+    if (init_sockets() != 0) {
+	pj_caching_pool_destroy(&cp);
+	return 1;
+    }
+
+    /* Initialize stack. */
+    if (init_stack() != PJ_OK) {
+	pj_caching_pool_destroy(&cp);
+	return 1;
+    }
+
+    /* Set callback to receive incoming IM */
+    pjsip_messaging_set_incoming_callback( &on_incoming_im_msg );
+
+    /* Set default worker count (can be zero) */
+    global.worker_cnt = WORKER_COUNT;
+
+    /* Create user worker thread(s), only when threading is enabled. */
+    for (i=0; i<global.worker_cnt; ++i) {
+	thread[i] = pj_thread_create( global.pool, "sip%p", 
+				      &worker_thread, 
+				      NULL, 0, NULL, 0);
+	if (thread == NULL) {
+	    global.worker_quit_flag = 1;
+	    for (--i; i>=0; --i) {
+		pj_thread_join(thread[i]);
+		pj_thread_destroy(thread[i]);
+	    }
+	    pj_caching_pool_destroy(&cp);
+	    return 1;
+	}
+    }
+
+    printf("Worker thread count: %d\n", global.worker_cnt);
+
+    /* Perform registration, if required. */
+    if (global.regc) {
+	update_registration(global.regc, 1);
+    }
+
+    /* Initialize media manager. */
+    global.mmgr = pj_med_mgr_create(global.pf);
+
+    /* Init presence. */
+    init_presence();
+
+    /* Subscribe presence information of all buddies. */
+    if (!global.no_presence)
+	subscribe_buddies_presence();
+
+    /* Initializatio completes, loop waiting for commands. */
+    for (;!global.worker_quit_flag;) {
+	pj_str_t str;
+	char line[128];
+
+#if WORKER_COUNT==0
+	/* If worker thread does not exist, main thread must poll for evetns. 
+	 * But this won't work very well since main thread is blocked by 
+	 * fgets(). So keep pressing the ENTER key to get the events!
+	 */
+	pj_time_val timeout = { 0, 100 };
+	pjsip_endpt_handle_events(global.endpt, &timeout);
+	puts("Keep pressing ENTER key to get the events!");
+#endif
+
+	printf("\nCurrent dialog: ");
+	print_dialog(global.cur_dlg);
+	puts("");
+
+	keystroke_help();
+
+	fgets(line, sizeof(line), stdin);
+
+	switch (*line) {
+	case 'm':
+	    puts("Make outgoing call");
+	    if (ui_input_url(&str, line, sizeof(line), &i) != NULL) {
+		pjsip_dlg *dlg = make_call(&str);
+		if (global.cur_dlg == NULL) {
+		    global.cur_dlg = dlg;
+		}
+	    }
+	    break;
+	case 'i':
+	    puts("Send Instant Messaging");
+	    ui_send_im_message();
+	    break;
+	case 'o':
+	    puts("Send OPTIONS");
+	    ui_send_options();
+	    break;
+	case 'a':
+	    if (global.cur_dlg) {
+		unsigned code;
+		pjsip_tx_data *tdata;
+		struct dialog_data *dlg_data = global.cur_dlg->user_data;
+
+		printf("Answer with status code (1xx-6xx): ");
+		fflush(stdout);
+		fgets(line, sizeof(line), stdin);
+		str = pj_str(line);
+		str.slen -= 1;
+
+		code = pj_strtoul(&str);
+		tdata = pjsip_dlg_answer(global.cur_dlg, code);
+		if (tdata) {
+		    if (code/100 == 2) {
+			tdata->msg->body = dlg_data->body;
+		    }
+		    pjsip_dlg_send_msg(global.cur_dlg, tdata);
+
+		}
+	    } else {
+		puts("No current dialog");
+	    }
+	    break;
+	case 'h':
+	    if (global.cur_dlg) {
+		pjsip_tx_data *tdata;
+		tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE);
+		if (tdata) {
+		    pjsip_dlg_send_msg(global.cur_dlg, tdata);
+		}
+	    } else {
+		puts("No current dialog");
+	    }
+	    break;
+	case ']':
+	    if (global.cur_dlg) {
+		global.cur_dlg = global.cur_dlg->next;
+		if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
+		    global.cur_dlg = global.cur_dlg->next;
+		}
+	    } else {
+		puts("No current dialog");
+	    }
+	    break;
+	case '[':
+	    if (global.cur_dlg) {
+		global.cur_dlg = global.cur_dlg->prev;
+		if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
+		    global.cur_dlg = global.cur_dlg->prev;
+		}
+	    } else {
+		puts("No current dialog");
+	    }
+	    break;
+	case 'd':
+	    pjsip_endpt_dump(global.endpt, *(line+1)=='1');
+	    pjsip_ua_dump(global.user_agent);
+	    break;
+	case 's':
+	    if (*(line+1) == 'u')
+		subscribe_buddies_presence();
+	    break;
+	case 'u':
+	    if (*(line+1) == 's')
+		unsubscribe_presence();
+	    break;
+	case 't':
+	    global.hide_status = !global.hide_status;
+	    update_im_status();
+	    break;
+	case 'q':
+	    goto on_exit;
+	case 'l':
+	    print_all_dialogs();
+	    break;
+	}
+    }
+
+on_exit:
+    /* Unregister, if required. */
+    if (global.regc) {
+	update_registration(global.regc, 0);
+    }
+
+    /* Unsubscribe presence. */
+    unsubscribe_presence();
+
+    /* Allow one second to get all events. */
+    if (1) {
+	pj_time_val end_time;
+
+	pj_gettimeofday(&end_time);
+	end_time.sec++;
+
+	PJ_LOG(3,(THIS_FILE, "Shutting down.."));
+	for (;;) {
+	    pj_time_val timeout = { 0, 20 }, now;
+	    pjsip_endpt_handle_events (global.endpt, &timeout);
+	    pj_gettimeofday(&now);
+	    PJ_TIME_VAL_SUB(now, end_time);
+	    if (now.sec >= 1)
+		break;
+	}
+    }
+
+    global.worker_quit_flag = 1;
+
+    pj_med_mgr_destroy(global.mmgr);
+
+    /* Wait all threads to quit. */
+    for (i=0; i<global.worker_cnt; ++i) {
+	pj_thread_join(thread[i]);
+	pj_thread_destroy(thread[i]);
+    }
+
+    /* Destroy endpoint. */
+    pjsip_endpt_destroy(global.endpt);
+
+    /* Destroy caching pool. */
+    pj_caching_pool_destroy(&cp);
+
+    /* Close log file, if any. */
+    if (global.log_file)
+	fclose(global.log_file);
+
+    return 0;
+}
+
+/*
+ * Register static modules to the endpoint.
+ */
+pj_status_t register_static_modules( pj_size_t *count,
+				     pjsip_module **modules )
+{
+    /* Reset count. */
+    *count = 0;
+
+    /* Register user agent module. */
+    modules[(*count)++] = pjsip_ua_get_module();
+    global.user_agent = modules[0]->mod_data;
+    modules[(*count)++] = pjsip_messaging_get_module();
+    modules[(*count)++] = pjsip_event_sub_get_module();
+
+    return PJ_OK;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/misc.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/misc.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/pjsua/misc.c	(revision 1753)
@@ -0,0 +1,490 @@
+/* $Header: /pjproject/pjsip/src/pjsua/misc.c 21    6/23/05 12:36a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+
+/*
+ * THIS FILE IS INCLUDED BY main.c.
+ * IT WON'T COMPILE BY ITSELF.
+ */
+
+#include "getopt.h"
+#include <stdio.h>
+ 
+
+/*
+ * Display program usage
+ */
+static void usage()
+{
+    puts("Usage:");
+    puts("  pjsua [options] [sip-url]");
+    puts("");
+    puts("  [sip-url]   Default URL to invite.");
+    puts("");
+    puts("General options:");
+    puts("  --config-file=file  Read the config/arguments from file.");
+    puts("  --log-file=fname    Log to filename (default stderr)");
+    puts("  --log-level=N       Set log max level to N (0(none) to 6(trace))");
+    puts("  --app-log-level=N   Set log max level for stdout display to N");
+    puts("  --help              Display this help screen");
+    puts("  --version           Display version info");
+    puts("");
+    puts("Media options:");
+    puts("  --null-audio        Use NULL audio device");
+    puts("");
+    puts("User Agent options:");
+    puts("  --auto-answer=sec   Auto-answer all incoming calls after sec seconds.");
+    puts("  --auto-hangup=sec   Auto-hangup all calls after sec seconds.");
+    puts("");
+    puts("SIP options:");
+    puts("  --local-port=port   Set TCP/UDP port");
+    puts("  --id=url            Set the URL of local ID (used in From header)");
+    puts("  --contact=url       Override the Contact information");
+    puts("  --proxy=url         Set the URL of proxy server");
+    puts("  --outbound=url      Set the URL of outbound proxy server");
+    puts("  --registrar=url     Set the URL of registrar server");
+    puts("  --reg-timeout=secs  Set registration interval to secs (default 3600)");
+    puts("");
+    puts("Authentication options:");
+    puts("  --realm=string      Set realm");
+    puts("  --username=string   Set authentication username");
+    puts("  --password=string   Set authentication password");
+    puts("");
+    puts("STUN options (all must be specified):");
+    puts("  --use-stun1=host[:port]");
+    puts("  --use-stun2=host[:port]  Use STUN and set host name and port of STUN servers");
+    puts("");
+    puts("SIMPLE options (may be specified more than once):");
+    puts("  --add-buddy url     Add the specified URL to the buddy list.");
+    puts("  --offer-x-ms-msg    Offer \"x-ms-message\" in outgoing INVITE");
+    puts("  --no-presence	Do not subscribe presence of buddies");
+    puts("");
+    fflush(stdout);
+}
+
+/* Display keystroke help. */
+static void keystroke_help()
+{
+    int i;
+
+    printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online"));
+    puts("");
+    puts("Buddy list:");
+    puts("-------------------------------------------------------------------------------");
+    for (i=0; i<global.buddy_cnt; ++i) {
+	printf(" %d\t%s  <%s>\n", i+1, global.buddy[i].ptr,
+		(global.buddy_status[i]?"Online":"Offline"));
+    }
+    //printf("-------------------------------------\n");
+    puts("");
+    //puts("Commands:");
+    puts("+=============================================================================+");
+    puts("|       Call Commands:         |      IM & Presence:      |   Misc:           |");
+    puts("|                              |                          |                   |");
+    puts("|  m  Make new call            |  i  Send IM              |  o  Send OPTIONS  |");
+    puts("|  a  Answer call              | su  Subscribe presence   |  d  Dump status   |");
+    puts("|  h  Hangup call              | us  Unsubscribe presence |  d1 Dump detailed |");
+    puts("|  ]  Select next dialog       |  t  Toggle Online status |                   |");
+    puts("|  [  Select previous dialog   |                          |                   |");
+    puts("+-----------------------------------------------------------------------------+");
+    puts("|  q  QUIT                                                                    |");
+    puts("+=============================================================================+");
+    puts("");
+
+
+    fflush(stdout);
+}
+
+/*
+ * Verify that valid SIP url is given.
+ */
+static pj_status_t verify_sip_url(char *url)
+{
+    pjsip_uri *p;
+    pj_pool_t *pool;
+    int len = (url ? strlen(url) : 0);
+
+    if (!len) return -1;
+
+    pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL);
+    if (!pool) return -1;
+
+    p = pjsip_parse_uri(pool, url, len, 0);
+    if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
+	p = NULL;
+
+    pj_pool_release(pool);
+    return p ? 0 : -1;
+}
+
+/*
+ * Read command arguments from config file.
+ */
+static int read_config_file(pj_pool_t *pool, const char *filename, 
+			    int *app_argc, char ***app_argv)
+{
+    int i;
+    FILE *fhnd;
+    char line[200];
+    int argc = 0;
+    char **argv;
+    enum { MAX_ARGS = 64 };
+
+    /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
+    argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
+    argv[argc++] = *app_argv[0];
+
+    /* Open config file. */
+    fhnd = fopen(filename, "rt");
+    if (!fhnd) {
+	printf("Unable to open config file %s\n", filename);
+	return -1;
+    }
+
+    /* Scan tokens in the file. */
+    while (argc < MAX_ARGS && !feof(fhnd)) {
+	char *token, *p = line;
+
+	if (fgets(line, sizeof(line), fhnd) == NULL) break;
+
+	for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS; 
+	     token = strtok(NULL, " \t\r\n"))
+	{
+	    int token_len;
+	    
+	    if (!token) break;
+	    if (*token == '#') break;
+
+	    token_len = strlen(token);
+	    if (!token_len)
+		continue;
+	    argv[argc] = pj_pool_alloc(pool, token_len+1);
+	    pj_memcpy(argv[argc], token, token_len+1);
+	    ++argc;
+	}
+    }
+
+    /* Copy arguments from command line */
+    for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
+	argv[argc++] = (*app_argv)[i];
+
+    if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
+	printf("Too many arguments specified in cmd line/config file\n");
+	fclose(fhnd);
+	return -1;
+    }
+
+    fclose(fhnd);
+
+    /* Assign the new command line back to the original command line. */
+    *app_argc = argc;
+    *app_argv = argv;
+    return 0;
+
+}
+
+/*
+ * Parse program arguments
+ */
+static int parse_args(pj_pool_t *pool, int argc, char *argv[])
+{
+    int c;
+    int option_index;
+    enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, 
+	   OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
+	   OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
+	   OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT, 
+	   OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
+	   OPT_USE_STUN1, OPT_USE_STUN2, 
+	   OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
+	   OPT_AUTO_ANSWER, OPT_AUTO_HANGUP};
+    struct option long_options[] = {
+	{ "config-file",1, 0, OPT_CONFIG_FILE},
+	{ "log-file",	1, 0, OPT_LOG_FILE},
+	{ "log-level",	1, 0, OPT_LOG_LEVEL},
+	{ "app-log-level",1,0,OPT_APP_LOG_LEVEL},
+	{ "help",	0, 0, OPT_HELP},
+	{ "version",	0, 0, OPT_VERSION},
+	{ "null-audio", 0, 0, OPT_NULL_AUDIO},
+	{ "local-port", 1, 0, OPT_LOCAL_PORT},
+	{ "proxy",	1, 0, OPT_PROXY},
+	{ "outbound",	1, 0, OPT_OUTBOUND_PROXY},
+	{ "registrar",	1, 0, OPT_REGISTRAR},
+	{ "reg-timeout",1, 0, OPT_REG_TIMEOUT},
+	{ "id",		1, 0, OPT_ID},
+	{ "contact",	1, 0, OPT_CONTACT},
+	{ "realm",	1, 0, OPT_REALM},
+	{ "username",	1, 0, OPT_USERNAME},
+	{ "password",	1, 0, OPT_PASSWORD},
+	{ "use-stun1",  1, 0, OPT_USE_STUN1},
+	{ "use-stun2",  1, 0, OPT_USE_STUN2},
+	{ "add-buddy",  1, 0, OPT_ADD_BUDDY},
+	{ "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
+	{ "no-presence", 0, 0, OPT_NO_PRESENCE},
+	{ "auto-answer",1, 0, OPT_AUTO_ANSWER},
+	{ "auto-hangup",1, 0, OPT_AUTO_HANGUP},
+	{ NULL, 0, 0, 0}
+    };
+    char *config_file = NULL;
+
+    /* Run getopt once to see if user specifies config file to read. */
+    while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+	switch (c) {
+	case 0:
+	    config_file = optarg;
+	    break;
+	}
+	if (config_file)
+	    break;
+    }
+
+    if (config_file) {
+	if (read_config_file(pool, config_file, &argc, &argv) != 0)
+	    return -1;
+    }
+
+    /* Reinitialize and re-run getopt again, possibly with new arguments
+     * read from config file.
+     */
+    optind = 0;
+    while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+	char *err, *p;
+
+	switch (c) {
+	case OPT_LOG_FILE:
+	    global.log_filename = optarg;
+	    break;
+	case OPT_LOG_LEVEL:
+	    c = strtoul(optarg, &err, 10);
+	    if (*err) {
+		printf("Error: expecting integer value 0-6 for --log-level\n");
+		return -1;
+	    }
+	    pj_log_set_level( c );
+	    break;
+	case OPT_APP_LOG_LEVEL:
+	    global.app_log_level = strtoul(optarg, &err, 10);
+	    if (*err) {
+		printf("Error: expecting integer value 0-6 for --app-log-level\n");
+		return -1;
+	    }
+	    break;
+	case OPT_HELP:
+	    usage();
+	    return -1;
+	case OPT_VERSION:   /* version */
+	    pj_dump_config();
+	    return -1;
+	case OPT_NULL_AUDIO:
+	    global.null_audio = 1;
+	    break;
+	case OPT_LOCAL_PORT:   /* local-port */
+	    global.sip_port = strtoul(optarg, &err, 10);
+	    if (*err) {
+		printf("Error: expecting integer value for --local-port\n");
+		return -1;
+	    }
+	    break;
+	case OPT_PROXY:   /* proxy */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg);
+		return -1;
+	    }
+	    global.proxy = pj_str(optarg);
+	    break;
+	case OPT_OUTBOUND_PROXY:   /* outbound proxy */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg);
+		return -1;
+	    }
+	    global.outbound_proxy = pj_str(optarg);
+	    break;
+	case OPT_REGISTRAR:   /* registrar */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg);
+		return -1;
+	    }
+	    global.registrar_uri = pj_str(optarg);
+	    break;
+	case OPT_REG_TIMEOUT:   /* reg-timeout */
+	    global.reg_timeout = strtoul(optarg, &err, 10);
+	    if (*err) {
+		printf("Error: expecting integer value for --reg-timeout\n");
+		return -1;
+	    }
+	    break;
+	case OPT_ID:   /* id */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in local id argument\n", optarg);
+		return -1;
+	    }
+	    global.local_uri = pj_str(optarg);
+	    break;
+	case OPT_CONTACT:   /* contact */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in contact argument\n", optarg);
+		return -1;
+	    }
+	    global.contact = pj_str(optarg);
+	    break;
+	case OPT_USERNAME:   /* Default authentication user */
+	    if (!global.cred_count) global.cred_count = 1;
+	    global.cred_info[0].username = pj_str(optarg);
+	    break;
+	case OPT_REALM:	    /* Default authentication realm. */
+	    if (!global.cred_count) global.cred_count = 1;
+	    global.cred_info[0].realm = pj_str(optarg);
+	    break;
+	case OPT_PASSWORD:   /* authentication password */
+	    if (!global.cred_count) global.cred_count = 1;
+	    global.cred_info[0].data_type = 0;
+	    global.cred_info[0].data = pj_str(optarg);
+	    break;
+	case OPT_USE_STUN1:   /* STUN server 1 */
+	    p = strchr(optarg, ':');
+	    if (p) {
+		*p = '\0';
+		global.stun_srv1 = pj_str(optarg);
+		global.stun_port1 = strtoul(p+1, &err, 10);
+		if (*err || global.stun_port1==0) {
+		    printf("Error: expecting port number with option --use-stun1\n");
+		    return -1;
+		}
+	    } else {
+		global.stun_port1 = 3478;
+		global.stun_srv1 = pj_str(optarg);
+	    }
+	    break;
+	case OPT_USE_STUN2:   /* STUN server 2 */
+	    p = strchr(optarg, ':');
+	    if (p) {
+		*p = '\0';
+		global.stun_srv2 = pj_str(optarg);
+		global.stun_port2 = strtoul(p+1, &err, 10);
+		if (*err || global.stun_port2==0) {
+		    printf("Error: expecting port number with option --use-stun2\n");
+		    return -1;
+		}
+	    } else {
+		global.stun_port2 = 3478;
+		global.stun_srv2 = pj_str(optarg);
+	    }
+	    break;
+	case OPT_ADD_BUDDY: /* Add to buddy list. */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid URL '%s' in --add-buddy option\n", optarg);
+		return -1;
+	    }
+	    if (global.buddy_cnt == MAX_BUDDIES) {
+		printf("Error: too many buddies in buddy list.\n");
+		return -1;
+	    }
+	    global.buddy[global.buddy_cnt++] = pj_str(optarg);
+	    break;
+	case OPT_OFFER_X_MS_MSG:
+	    global.offer_x_ms_msg = 1;
+	    break;
+	case OPT_NO_PRESENCE:
+	    global.no_presence = 1;
+	    break;
+	case OPT_AUTO_ANSWER:
+	    global.auto_answer = strtoul(optarg, &err, 10);
+	    if (*err) {
+		printf("Error: expecting integer value for --auto-answer option\n");
+		return -1;
+	    }
+	    break;
+	case OPT_AUTO_HANGUP:
+	    global.auto_hangup = strtoul(optarg, &err, 10);
+	    if (*err) {
+		printf("Error: expecting integer value for --auto-hangup option\n");
+		return -1;
+	    }
+	    break;
+	}
+    }
+
+    if (optind != argc) {
+	printf("Error: unknown options %s\n", argv[optind]);
+	return -1;
+    }
+
+    if (global.reg_timeout == 0)
+	global.reg_timeout = 3600;
+
+    return 0;
+}
+
+/* Print dialog. */
+static void print_dialog(pjsip_dlg *dlg)
+{
+    if (!dlg) {
+	puts("none");
+	return;
+    }
+
+    printf("%s: call-id=%.*s", dlg->obj_name, 
+			       (int)dlg->call_id->id.slen, 
+			       dlg->call_id->id.ptr);
+
+    printf(" (%s, %s)\n", pjsip_role_name(dlg->role),
+			  pjsip_dlg_state_str(dlg->state));
+}
+
+/* Dump media statistic */
+void dump_media_statistic(pjsip_dlg *dlg)
+{
+    struct dialog_data *dlg_data = dlg->user_data;
+    pj_media_stream_stat stat[2];
+    const char *statname[2] = { "TX", "RX" };
+    int i;
+
+    pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]);
+
+    printf("Media statistic:\n");
+    for (i=0; i<2; ++i) {
+	printf("  %s statistics:\n", statname[i]);
+	printf("    Pkt      TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx);
+	printf("    Octets   TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx);
+	printf("    Jitter   %d ms\n", stat[i].jitter);
+	printf("    Pkt lost %d\n", stat[i].pkt_lost);
+    }
+    printf("\n");
+}
+
+/* Print all dialogs. */
+static void print_all_dialogs()
+{
+    pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next;
+
+    puts("List all dialogs:");
+
+    while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) {
+	printf("%c", (dlg==global.cur_dlg ? '*' : ' '));
+	print_dialog(dlg);
+	dlg = dlg->next;
+    }
+
+    puts("");
+}
+
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/main.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/main.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/main.c	(revision 1753)
@@ -0,0 +1,37 @@
+/* $Header: /pjproject/pjsip/src/tests/pjsip_core/main.c 2     2/24/05 10:46a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include "test.h"
+#include <stdio.h>
+
+int main()
+{
+    test_uri();
+    test_msg();
+
+#if !IS_PROFILING
+    puts("Press <ENTER> to quit.");
+    fgets( s, sizeof(s), stdin);
+#endif
+    return 0;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/test.h
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/test.h	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/test.h	(revision 1753)
@@ -0,0 +1,31 @@
+/* $Header: /pjproject/pjsip/src/tests/pjsip_core/test.h 2     2/24/05 10:46a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pj/types.h>
+
+pj_status_t test_uri(void);
+pj_status_t test_msg(void);
+
+#define SILENT		1
+#define IS_PROFILING	1
+#define LOOP		2000
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/test_msg.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/test_msg.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/test_msg.c	(revision 1753)
@@ -0,0 +1,445 @@
+/* $Header: /pjproject/pjsip/src/tests/pjsip_core/test_msg.c 8     6/22/05 12:27a Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "test.h"
+
+#define ERR_SYNTAX_ERR	(-2)
+#define ERR_NOT_EQUAL	(-3)
+#define ERR_SYSTEM	(-4)
+
+
+static pjsip_msg *create_msg0(pj_pool_t *pool);
+
+struct test_msg
+{
+    char	 msg[1024];
+    pjsip_msg *(*creator)(pj_pool_t *pool);
+    pj_size_t	 len;
+} test_array[] = 
+{
+    {
+	/* 'Normal' message with all headers. */
+	"INVITE sip:user@foo SIP/2.0\n"
+	"From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r"
+	"To: Fellow User <sip:user@foo.bar.domain.com>\r\n"
+	"Call-ID: 12345678901234567890@bar\r\n"
+	"Content-Length: 0\r\n"
+	"CSeq: 123456 INVITE\n"
+	"Contact: <sip:joe@bar> ; q=0.5;expires=3600,sip:user@host;q=0.500\r"
+	"  ,sip:user2@host2\n"
+	"Content-Type: text/html ; charset=ISO-8859-4\r"
+	"Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n"
+	"  <sip:server10.biloxi.com;lr>\r"
+	"Record-Route: <sip:server10.biloxi.com>,\r\n"
+	"  <sip:bigbox3.site3.atlanta.com;lr>\n"
+	"Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n"
+	"Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
+	" ;received=192.0.2.1\r\n"
+	"Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n"
+	"Organization: \r"
+	"Max-Forwards: 70\n"
+	"X-Header: \r\n"
+	"\r",
+	&create_msg0
+    }
+};
+
+static pj_caching_pool cp;
+static pj_pool_factory *pf = &cp.factory;
+static pj_uint32_t parse_len, parse_time, print_time;
+
+static void pool_error(pj_pool_t *pool, pj_size_t sz)
+{
+    PJ_UNUSED_ARG(pool)
+    PJ_UNUSED_ARG(sz)
+
+    pj_assert(0);
+    exit(1);
+}
+
+static const char *STATUS_STR(pj_status_t status)
+{
+    switch (status) {
+    case 0: return "OK";
+    case ERR_SYNTAX_ERR: return "Syntax Error";
+    case ERR_NOT_EQUAL: return "Not Equal";
+    case ERR_SYSTEM: return "System Error";
+    }
+    return "???";
+}
+
+static pj_status_t test_entry( struct test_msg *entry )
+{
+    pjsip_msg *parsed_msg, *ref_msg;
+    pj_pool_t *pool;
+    pj_status_t status = PJ_OK;
+    int len;
+    pj_str_t str1, str2;
+    pjsip_hdr *hdr1, *hdr2;
+    pj_hr_timestamp t1, t2;
+    char *msgbuf;
+
+    enum { BUFLEN = 512 };
+    
+    pool = pj_pool_create( pf, "", 
+			   PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA, 
+			   &pool_error);
+
+    if (entry->len == 0) {
+	entry->len = strlen(entry->msg);
+    }
+
+    /* Parse message. */
+    parse_len += entry->len;
+    pj_hr_gettimestamp(&t1);
+    parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL);
+    if (parsed_msg == NULL) {
+	status = ERR_SYNTAX_ERR;
+	goto on_return;
+    }
+    pj_hr_gettimestamp(&t2);
+    parse_time += t2.u32.lo - t1.u32.lo;
+
+#if IS_PROFILING
+    goto print_msg;
+#endif
+
+    /* Create reference message. */
+    ref_msg = entry->creator(pool);
+
+    /* Create buffer for comparison. */
+    str1.ptr = pj_pool_alloc(pool, BUFLEN);
+    str2.ptr = pj_pool_alloc(pool, BUFLEN);
+
+    /* Compare message type. */
+    if (parsed_msg->type != ref_msg->type) {
+	status = ERR_NOT_EQUAL;
+	goto on_return;
+    }
+
+    /* Compare request or status line. */
+    if (parsed_msg->type == PJSIP_REQUEST_MSG) {
+	pjsip_method *m1 = &parsed_msg->line.req.method;
+	pjsip_method *m2 = &ref_msg->line.req.method;
+
+	if (m1->id != m2->id || pj_strcmp(&m1->name, &m2->name)) {
+	    status = ERR_NOT_EQUAL;
+	    goto on_return;
+	}
+    } else {
+
+    }
+
+    /* Compare headers. */
+    hdr1 = parsed_msg->hdr.next;
+    hdr2 = ref_msg->hdr.next;
+
+    while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) {
+	len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN);
+	if (len < 1) {
+	    status = ERR_SYSTEM;
+	    goto on_return;
+	}
+	str1.slen = len;
+
+	len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN);
+	if (len < 1) {
+	    status = ERR_SYSTEM;
+	    goto on_return;
+	}
+	str2.slen = len;
+
+	if (!SILENT) {
+	    printf("hdr1='%.*s'\n"
+		   "hdr2='%.*s'\n\n",
+		   str1.slen, str1.ptr,
+		   str2.slen, str2.ptr);
+	}
+	if (pj_strcmp(&str1, &str2) != 0) {
+	    status = ERR_NOT_EQUAL;
+	    goto on_return;
+	}
+
+	hdr1 = hdr1->next;
+	hdr2 = hdr2->next;
+    }
+
+    if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) {
+	status = ERR_NOT_EQUAL;
+	goto on_return;
+    }
+
+    /* Print message. */
+#if IS_PROFILING
+print_msg:
+#endif
+    msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN);
+    if (msgbuf == NULL) {
+	status = ERR_SYSTEM;
+	goto on_return;
+    }
+    pj_hr_gettimestamp(&t1);
+    len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN);
+    if (len < 1) {
+	status = ERR_SYSTEM;
+	goto on_return;
+    }
+    pj_hr_gettimestamp(&t2);
+    print_time += t2.u32.lo - t1.u32.lo;
+    status = PJ_OK;
+
+on_return:
+    pj_pool_release(pool);
+    return status;
+}
+
+static void warm_up()
+{
+    pj_pool_t *pool;
+    pool = pj_pool_create( pf, "", 
+			   PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA, 
+			   &pool_error);
+    pj_pool_release(pool);
+}
+
+
+pj_status_t test_msg(void)
+{
+    pj_status_t status;
+    unsigned i;
+
+    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+    warm_up();
+
+    for (i=0; i<LOOP; ++i) {
+	status = test_entry( &test_array[0] );
+    }
+    printf("%s\n", STATUS_STR(status));
+
+    printf("Total bytes: %u, parse time=%f/char, print time=%f/char\n", 
+	   parse_len, 
+	   parse_time*1.0/parse_len,
+	   print_time*1.0/parse_len);
+    return PJ_OK;
+}
+
+/*****************************************************************************/
+
+static pjsip_msg *create_msg0(pj_pool_t *pool)
+{
+
+    pjsip_msg *msg;
+    pjsip_name_addr *name_addr;
+    pjsip_url *url;
+    pjsip_fromto_hdr *fromto;
+    pjsip_cid_hdr *cid;
+    pjsip_clen_hdr *clen;
+    pjsip_cseq_hdr *cseq;
+    pjsip_contact_hdr *contact;
+    pjsip_ctype_hdr *ctype;
+    pjsip_routing_hdr *routing;
+    pjsip_via_hdr *via;
+    pjsip_generic_string_hdr *generic;
+    pj_str_t str;
+
+    msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
+
+    /* "INVITE sip:user@foo SIP/2.0\n" */
+    pjsip_method_set(&msg->line.req.method, PJSIP_INVITE_METHOD);
+    url = pjsip_url_create(pool, 0);
+    msg->line.req.uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->user, "user");
+    pj_strdup2(pool, &url->host, "foo");
+
+    /* "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" */
+    fromto = pjsip_from_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
+    pj_strdup2(pool, &fromto->tag, "1234578901234567890");
+    name_addr = pjsip_name_addr_create(pool);
+    fromto->uri = (pjsip_uri*)name_addr;
+    pj_strdup2(pool, &name_addr->display, "Hi I'm Joe");
+    url = pjsip_url_create(pool, 0);
+    name_addr->uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->user, "joe.user");
+    pj_strdup2(pool, &url->host, "bar.otherdomain.com");
+
+    /* "To: Fellow User <sip:user@foo.bar.domain.com>\r\n" */
+    fromto = pjsip_to_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
+    name_addr = pjsip_name_addr_create(pool);
+    fromto->uri = (pjsip_uri*)name_addr;
+    pj_strdup2(pool, &name_addr->display, "Fellow User");
+    url = pjsip_url_create(pool, 0);
+    name_addr->uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->user, "user");
+    pj_strdup2(pool, &url->host, "foo.bar.domain.com");
+
+    /* "Call-ID: 12345678901234567890@bar\r\n" */
+    cid = pjsip_cid_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)cid);
+    pj_strdup2(pool, &cid->id, "12345678901234567890@bar");
+
+    /* "Content-Length: 0\r\n" */
+    clen = pjsip_clen_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)clen);
+    clen->len = 0;
+
+    /* "CSeq: 123456 INVITE\n" */
+    cseq = pjsip_cseq_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)cseq);
+    cseq->cseq = 123456;
+    pjsip_method_set(&cseq->method, PJSIP_INVITE_METHOD);
+
+    /* "Contact: <sip:joe@bar>;q=0.5;expires=3600*/
+    contact = pjsip_contact_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
+    contact->q1000 = 500;
+    contact->expires = 3600;
+    name_addr = pjsip_name_addr_create(pool);
+    contact->uri = (pjsip_uri*)name_addr;
+    url = pjsip_url_create(pool, 0);
+    name_addr->uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->user, "joe");
+    pj_strdup2(pool, &url->host, "bar");
+
+    /*, sip:user@host;q=0.500\r" */
+    contact = pjsip_contact_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
+    contact->q1000 = 500;
+    url = pjsip_url_create(pool, 0);
+    contact->uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->user, "user");
+    pj_strdup2(pool, &url->host, "host");
+
+    /* "  ,sip:user2@host2\n" */
+    contact = pjsip_contact_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
+    url = pjsip_url_create(pool, 0);
+    contact->uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->user, "user2");
+    pj_strdup2(pool, &url->host, "host2");
+
+    /* "Content-Type: text/html; charset=ISO-8859-4\r" */
+    ctype = pjsip_ctype_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)ctype);
+    pj_strdup2(pool, &ctype->media.type, "text");
+    pj_strdup2(pool, &ctype->media.subtype, "html");
+    pj_strdup2(pool, &ctype->media.param, ";charset=ISO-8859-4");
+
+    /* "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n" */
+    routing = pjsip_route_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+    url = pjsip_url_create(pool, 0);
+    routing->name_addr.uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
+    url->lr_param = 1;
+
+    /* "  <sip:server10.biloxi.com;lr>\r" */
+    routing = pjsip_route_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+    url = pjsip_url_create(pool, 0);
+    routing->name_addr.uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->host, "server10.biloxi.com");
+    url->lr_param = 1;
+
+    /* "Record-Route: <sip:server10.biloxi.com>,\r\n" */
+    routing = pjsip_rr_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+    url = pjsip_url_create(pool, 0);
+    routing->name_addr.uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->host, "server10.biloxi.com");
+    url->lr_param = 0;
+
+    /* "  <sip:bigbox3.site3.atlanta.com;lr>\n" */
+    routing = pjsip_rr_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+    url = pjsip_url_create(pool, 0);
+    routing->name_addr.uri = (pjsip_uri*)url;
+    pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
+    url->lr_param = 1;
+
+    /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */
+    via = pjsip_via_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+    pj_strdup2(pool, &via->transport, "SCTP");
+    pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com");
+    pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1");
+
+    /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
+	" ;received=192.0.2.1\r\n" */
+    via = pjsip_via_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+    pj_strdup2(pool, &via->transport, "UDP");
+    pj_strdup2(pool, &via->sent_by.host, "pc33.atlanta.com");
+    pj_strdup2(pool, &via->branch_param, "z9hG4bKnashds8");
+    pj_strdup2(pool, &via->recvd_param, "192.0.2.1");
+
+
+    /* "Via: SIP/2.0/UDP 10.2.1.1, */ 
+    via = pjsip_via_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+    pj_strdup2(pool, &via->transport, "UDP");
+    pj_strdup2(pool, &via->sent_by.host, "10.2.1.1");
+    
+    
+    /*SIP/2.0/TCP 192.168.1.1\n" */
+    via = pjsip_via_hdr_create(pool);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+    pj_strdup2(pool, &via->transport, "TCP");
+    pj_strdup2(pool, &via->sent_by.host, "192.168.1.1");
+
+    /* "Organization: \r" */
+    str.ptr = "Organization";
+    str.slen = 12;
+    generic = pjsip_generic_string_hdr_create(pool, &str);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+    generic->hvalue.ptr = NULL;
+    generic->hvalue.slen = 0;
+
+    /* "Max-Forwards: 70\n" */
+    str.ptr = "Max-Forwards";
+    str.slen = 12;
+    generic = pjsip_generic_string_hdr_create(pool, &str);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+    str.ptr = "70";
+    str.slen = 2;
+    generic->hvalue = str;
+
+    /* "X-Header: \r\n" */
+    str.ptr = "X-Header";
+    str.slen = 8;
+    generic = pjsip_generic_string_hdr_create(pool, &str);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+    str.ptr = NULL;
+    str.slen = 0;
+    generic->hvalue = str;
+
+    return msg;
+}
Index: /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/test_uri.c
===================================================================
--- /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/test_uri.c	(revision 1753)
+++ /pjproject/branches/archives/pjproject-0.2/pjsip/src/tests/pjsip_core/test_uri.c	(revision 1753)
@@ -0,0 +1,665 @@
+/* $Header: /pjproject/pjsip/src/tests/pjsip_core/test_uri.c 7     6/19/05 6:12p Bennylp $ */
+/* 
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ *  Benny Prijono <bennylp@bulukucing.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
+ *
+ */
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_uri.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "test.h"
+
+#define ERR_SYNTAX_ERR	(-2)
+#define ERR_NOT_EQUAL	(-3)
+
+#define ALPHANUM    "abcdefghijklmnopqrstuvwxyz" \
+		    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+		    "0123456789"
+#define MARK	    "-_.!~*'()"
+#define USER	    "&=+$,;?/%"
+#define PASS	    "&=+$,%"
+#define PARAM_CHAR  "[]/:&+$" MARK "%"
+
+#define POOL_SIZE	4096
+
+static const char *STATUS_STR(pj_status_t status)
+{
+    switch (status) {
+    case 0: return "OK";
+    case ERR_SYNTAX_ERR: return "Syntax Error";
+    case ERR_NOT_EQUAL: return "Not Equal";
+    }
+    return "???";
+}
+
+static pj_uint32_t parse_len, parse_time, print_time;
+static pj_caching_pool cp;
+
+
+/* URI creator functions. */
+static pjsip_uri *create_uri1( pj_pool_t *pool );
+static pjsip_uri *create_uri2( pj_pool_t *pool );
+static pjsip_uri *create_uri3( pj_pool_t *pool );
+static pjsip_uri *create_uri4( pj_pool_t *pool );
+static pjsip_uri *create_uri5( pj_pool_t *pool );
+static pjsip_uri *create_uri6( pj_pool_t *pool );
+static pjsip_uri *create_uri7( pj_pool_t *pool );
+static pjsip_uri *create_uri8( pj_pool_t *pool );
+static pjsip_uri *create_uri9( pj_pool_t *pool );
+static pjsip_uri *create_uri10( pj_pool_t *pool );
+static pjsip_uri *create_uri11( pj_pool_t *pool );
+static pjsip_uri *create_uri12( pj_pool_t *pool );
+static pjsip_uri *create_uri13( pj_pool_t *pool );
+static pjsip_uri *create_uri14( pj_pool_t *pool );
+static pjsip_uri *create_uri15( pj_pool_t *pool );
+static pjsip_uri *create_uri16( pj_pool_t *pool );
+static pjsip_uri *create_uri17( pj_pool_t *pool );
+static pjsip_uri *create_uri18( pj_pool_t *pool );
+static pjsip_uri *create_uri19( pj_pool_t *pool );
+static pjsip_uri *create_dummy( pj_pool_t *pool );
+
+struct uri_test
+{
+    pj_status_t	     status;
+    char	     str[PJSIP_MAX_URL_SIZE];
+    pjsip_uri *(*creator)(pj_pool_t *pool);
+    pj_size_t	     len;
+} uri_test_array[] = 
+{
+    {
+	PJ_OK,
+	"sip:localhost",
+	&create_uri1
+    },
+    {
+	PJ_OK,
+	"sip:user@localhost",
+	&create_uri2
+    },
+    {
+	PJ_OK,
+	"sip:user:password@localhost:5060",
+	&create_uri3,
+    },
+    {
+	/* Port is specified should not match unspecified port. */
+	ERR_NOT_EQUAL,
+	"sip:localhost:5060",
+	&create_uri4
+    },
+    {
+	/* All recognized parameters. */
+	PJ_OK,
+	"sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK",
+	&create_uri5
+    },
+    {
+	/* Params mixed with other params and header params. */
+	PJ_OK,
+	"sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
+	"?Subject=Hello%20There&Server=SIP%20Server",
+	&create_uri6
+    },
+    {
+	/* SIPS. */
+	PJ_OK,
+	"sips:localhost",
+	&create_uri7,
+    },
+    {
+	/* Name address */
+	PJ_OK,
+	"<sip:localhost>",
+	&create_uri8
+    },
+    {
+	/* Name address with display name and SIPS scheme with some redundant
+	 * whitespaced.
+	 */
+	PJ_OK,
+	"  Power Administrator  <sips:localhost>",
+	&create_uri9
+    },
+    {
+	/* Name address. */
+	PJ_OK,
+	" \"User\" <sip:user@localhost:5071>",
+	&create_uri10
+    },
+    {
+	/* Escaped sequence in display name (display=Strange User\"\\\"). */
+	PJ_OK,
+	" \"Strange User\\\"\\\\\\\"\" <sip:localhost>",
+	&create_uri11,
+    },
+    {
+	/* Errorneous escaping in display name. */
+	ERR_SYNTAX_ERR,
+	" \"Rogue User\\\" <sip:localhost>",
+	&create_uri12,
+    },
+    {
+	/* Dangling quote in display name, but that should be OK. */
+	PJ_OK,
+	"Strange User\" <sip:localhost>",
+	&create_uri13,
+    },
+    {
+	/* Special characters in parameter value must be quoted. */
+	PJ_OK,
+	"sip:localhost;pvalue=\"hello world\"",
+	&create_uri14,
+    },
+    {
+	/* Excercise strange character sets allowed in display, user, password,
+	 * host, and port. 
+	 */
+	PJ_OK,
+	"This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@"
+	"my_proxy09.MY-domain.com:9801>",
+	&create_uri15,
+    },
+    {
+	/* Another excercise to the allowed character sets to the hostname. */
+	PJ_OK,
+	"sip:" ALPHANUM "-_.com",
+	&create_uri16,
+    },
+    {
+	/* Another excercise to the allowed character sets to the username 
+	 * and password.
+	 */
+	PJ_OK,
+	"sip:" ALPHANUM USER ":" ALPHANUM PASS "@host",
+	&create_uri17,
+    },
+    {
+	/* Excercise to the pname and pvalue, and mixup of other-param
+	 * between 'recognized' params.
+	 */
+	PJ_OK,
+	"sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR 
+	";lr;other=1;transport=sctp;other2",
+	&create_uri18,
+    },
+    {
+	/* This should trigger syntax error. */
+	ERR_SYNTAX_ERR,
+	"sip:",
+	&create_dummy,
+    },
+    {
+	/* Syntax error: whitespace after scheme. */
+	ERR_SYNTAX_ERR,
+	"sip :host",
+	&create_dummy,
+    },
+    {
+	/* Syntax error: whitespace before hostname. */
+	ERR_SYNTAX_ERR,
+	"sip: host",
+	&create_dummy,
+    },
+    {
+	/* Syntax error: invalid port. */
+	ERR_SYNTAX_ERR,
+	"sip:user:password",
+	&create_dummy,
+    },
+    {
+	/* Syntax error: no host. */
+	ERR_SYNTAX_ERR,
+	"sip:user@",
+	&create_dummy,
+    },
+    {
+	/* Syntax error: no user/host. */
+	ERR_SYNTAX_ERR,
+	"sip:@",
+	&create_dummy,
+    },
+    {
+	/* Syntax error: empty string. */
+	ERR_SYNTAX_ERR,
+	"",
+	&create_dummy,
+    },
+    {
+	PJ_OK,
+	"",
+	NULL,
+    },
+};
+
+static pjsip_uri *create_uri1(pj_pool_t *pool)
+{
+    /* "sip:localhost" */
+    pjsip_url *url = pjsip_url_create(pool, 0);
+
+    pj_strdup2(pool, &url->host, "localhost");
+    return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri2(pj_pool_t *pool)
+{
+    /* "sip:user@localhost" */
+    pjsip_url *url = pjsip_url_create(pool, 0);
+
+    pj_strdup2( pool, &url->user, "user");
+    pj_strdup2( pool, &url->host, "localhost");
+
+    return (pjsip_uri*) url;
+}
+
+static pjsip_uri *create_uri3(pj_pool_t *pool)
+{
+    /* "sip:user:password@localhost:5060" */
+    pjsip_url *url = pjsip_url_create(pool, 0);
+
+    pj_strdup2( pool, &url->user, "user");
+    pj_strdup2( pool, &url->passwd, "password");
+    pj_strdup2( pool, &url->host, "localhost");
+    url->port = 5060;
+
+    return (pjsip_uri*) url;
+}
+
+static pjsip_uri *create_uri4(pj_pool_t *pool)
+{
+    /* Like: "sip:localhost:5060", but without the port. */
+    pjsip_url *url = pjsip_url_create(pool, 0);
+
+    pj_strdup2(pool, &url->host, "localhost");
+    return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri5(pj_pool_t *pool)
+{
+    /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */
+    pjsip_url *url = pjsip_url_create(pool, 0);
+
+    pj_strdup2(pool, &url->host, "localhost");
+    pj_strdup2(pool, &url->transport_param, "tcp");
+    pj_strdup2(pool, &url->user_param, "ip");
+    url->ttl_param = 255;
+    url->lr_param = 1;
+    pj_strdup2(pool, &url->maddr_param, "127.0.0.1");
+    pj_strdup2(pool, &url->method_param, "ACK");
+
+    return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri6(pj_pool_t *pool)
+{
+    /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
+       "?Subject=Hello%20There&Server=SIP%20Server" 
+     */
+    pjsip_url *url = pjsip_url_create(pool, 0);
+
+    pj_strdup2(pool, &url->host, "localhost");
+    pj_strdup2(pool, &url->user_param, "phone");
+    pj_strdup2(pool, &url->other_param, ";pickup=hurry;message=I%20am%20sorry");
+    pj_strdup2(pool, &url->header_param, "?Subject=Hello%20There&Server=SIP%20Server");
+    return (pjsip_uri*)url;
+
+}
+
+static pjsip_uri *create_uri7(pj_pool_t *pool)
+{
+    /* "sips:localhost" */
+    pjsip_url *url = pjsip_url_create(pool, 1);
+
+    pj_strdup2(pool, &url->host, "localhost");
+    return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri8(pj_pool_t *pool)
+{
+    /* "<sip:localhost>" */
+    pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+    pjsip_url *url;
+
+    url = pjsip_url_create(pool, 0);
+    name_addr->uri = (pjsip_uri*) url;
+
+    pj_strdup2(pool, &url->host, "localhost");
+    return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri9(pj_pool_t *pool)
+{
+    /* "  Power Administrator <sips:localhost>" */
+    pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+    pjsip_url *url;
+
+    url = pjsip_url_create(pool, 1);
+    name_addr->uri = (pjsip_uri*) url;
+
+    pj_strdup2(pool, &name_addr->display, "Power Administrator");
+    pj_strdup2(pool, &url->host, "localhost");
+    return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri10(pj_pool_t *pool)
+{
+    /* " \"User\" <sip:user@localhost:5071>" */
+    pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+    pjsip_url *url;
+
+    url = pjsip_url_create(pool, 0);
+    name_addr->uri = (pjsip_uri*) url;
+
+    pj_strdup2(pool, &name_addr->display, "\"User\"");
+    pj_strdup2(pool, &url->user, "user");
+    pj_strdup2(pool, &url->host, "localhost");
+    url->port = 5071;
+    return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri11(pj_pool_t *pool)
+{
+    /* " \"Strange User\\\"\\\\\\\"\" <sip:localhost>" */
+    pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+    pjsip_url *url;
+
+    url = pjsip_url_create(pool, 0);
+    name_addr->uri = (pjsip_uri*) url;
+
+    pj_strdup2(pool, &name_addr->display, "\"Strange User\\\"\\\\\\\"\"");
+    pj_strdup2(pool, &url->host, "localhost");
+    return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri12(pj_pool_t *pool)
+{
+    /* " \"Rogue User\\\" <sip:localhost>" */
+    pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+    pjsip_url *url;
+
+    url = pjsip_url_create(pool, 0);
+    name_addr->uri = (pjsip_uri*) url;
+
+    pj_strdup2(pool, &name_addr->display, "\"Rogue User\\\"");
+    pj_strdup2(pool, &url->host, "localhost");
+    return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri13(pj_pool_t *pool)
+{
+    /* "Strange User\" <sip:localhost>" */
+    pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+    pjsip_url *url;
+
+    url = pjsip_url_create(pool, 0);
+    name_addr->uri = (pjsip_uri*) url;
+
+    pj_strdup2(pool, &name_addr->display, "Strange User\"");
+    pj_strdup2(pool, &url->host, "localhost");
+    return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri14(pj_pool_t *pool)
+{
+    /* "sip:localhost;pvalue=\"hello world\"" */
+    pjsip_url *url;
+    url = pjsip_url_create(pool, 0);
+    pj_strdup2(pool, &url->host, "localhost");
+    pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\"");
+    return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri15(pj_pool_t *pool)
+{
+    /* "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@my_proxy09.my-domain.com:9801>" */
+    pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+    pjsip_url *url;
+
+    url = pjsip_url_create(pool, 0);
+    name_addr->uri = (pjsip_uri*) url;
+
+    pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me");
+    pj_strdup2(pool, &url->user, "a19A&=+$,;?/%2c");
+    pj_strdup2(pool, &url->passwd, "%09a&Zz=+$,");
+    pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com");
+    url->port = 9801;
+    return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri16(pj_pool_t *pool)
+{
+    /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */
+    pjsip_url *url;
+    url = pjsip_url_create(pool, 0);
+    pj_strdup2(pool, &url->host, ALPHANUM "-_.com");
+    return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri17(pj_pool_t *pool)
+{
+    /* "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host" */
+    pjsip_url *url;
+    url = pjsip_url_create(pool, 0);
+    pj_strdup2(pool, &url->user, ALPHANUM USER);
+    pj_strdup2(pool, &url->passwd, ALPHANUM PASS);
+    pj_strdup2(pool, &url->host, "host");
+    return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri18(pj_pool_t *pool)
+{
+    /* "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";lr;other=1;transport=sctp;other2" */
+    pjsip_url *url;
+    url = pjsip_url_create(pool, 0);
+    pj_strdup2(pool, &url->host, "host");
+    pj_strdup2(pool, &url->user_param, "ip");
+    pj_strdup2(pool, &url->transport_param, "sctp");
+    pj_strdup2(pool, &url->other_param, ";" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";other=1;other2");    
+    url->lr_param = 1;
+    return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_dummy(pj_pool_t *pool)
+{
+    PJ_UNUSED_ARG(pool)
+    return NULL;
+}
+
+/*****************************************************************************/
+
+static void pool_error(pj_pool_t *pool, pj_size_t sz)
+{
+    PJ_UNUSED_ARG(pool)
+    PJ_UNUSED_ARG(sz)
+
+    pj_assert(0);
+    exit(1);
+}
+
+/*
+ * Test one test entry.
+ */
+static pj_status_t test_entry(struct uri_test *entry)
+{
+    pj_status_t status;
+    pj_pool_t *pool;
+    int len;
+    pjsip_uri *parsed_uri, *ref_uri;
+    pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0};
+    pj_hr_timestamp t1, t2;
+
+    pool = (*cp.factory.create_pool)( &cp.factory, "", POOL_SIZE, 0, &pool_error);
+
+    /* Parse URI text. */
+    pj_hr_gettimestamp(&t1);
+    parse_len += entry->len;
+    parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0);
+    if (!parsed_uri) {
+	/* Parsing failed. If the entry says that this is expected, then
+	 * return OK.
+	 */
+	status = entry->status==ERR_SYNTAX_ERR ? PJ_OK : ERR_SYNTAX_ERR;
+	goto on_return;
+    }
+    pj_hr_gettimestamp(&t2);
+    parse_time += t2.u32.lo - t1.u32.lo;
+
+    /* Create the reference URI. */
+    ref_uri = entry->creator(pool);
+
+    /* Print both URI. */
+    s1.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+    s2.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+
+    pj_hr_gettimestamp(&t1);
+    len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE);
+    if (len < 1) {
+	status = -1;
+	goto on_return;
+    }
+    s1.slen = len;
+
+    len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE);
+    if (len < 1) {
+	status = -1;
+	goto on_return;
+    }
+    s2.slen = len;
+    pj_hr_gettimestamp(&t2);
+    print_time += t2.u32.lo - t1.u32.lo;
+
+    /* Full comparison of parsed URI with reference URI. */
+    if (pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri) != 0) {
+	/* Not equal. See if this is the expected status. */
+	status = entry->status==ERR_NOT_EQUAL ? PJ_OK : ERR_NOT_EQUAL;
+	goto on_return;
+
+    } else {
+	/* Equal. See if this is the expected status. */
+	status = entry->status==PJ_OK ? PJ_OK : -1;
+	if (status != PJ_OK) {
+	    goto on_return;
+	}
+    }
+
+    /* Compare text. */
+    if (pj_strcmp(&s1, &s2) != 0) {
+	/* Not equal. */
+	status = ERR_NOT_EQUAL;
+    }
+
+on_return:
+    if (!SILENT) {
+	printf("%.2d %s (expected status=%s)\n"
+	       "   str=%s\n"
+	       "   uri=%.*s\n"
+	       "   ref=%.*s\n\n", 
+	       entry-uri_test_array, 
+	       STATUS_STR(status), 
+	       STATUS_STR(entry->status), 
+	       entry->str, 
+	       (int)s1.slen, s1.ptr, (int)s2.slen, s2.ptr);
+    }
+
+    pj_pool_release(pool);
+    return status;
+}
+
+static void warm_up(pj_pool_factory *pf)
+{
+    pj_pool_t *pool;
+    struct uri_test *entry;
+
+    pool = pj_pool_create(pf, "", POOL_SIZE, 0, &pool_error);
+    pjsip_parse_uri(pool, "sip:host", 8, 0);
+    entry = &uri_test_array[0];
+    while (entry->creator) {
+	entry->len = strlen(entry->str);
+	++entry;
+    }
+    pj_pool_release(pool);
+}
+
+//#if !IS_PROFILING
+#if 1
+pj_status_t test_uri()
+{
+    struct uri_test *entry;
+    int i=0, err=0;
+    pj_status_t status;
+    pj_hr_timestamp t1, t2;
+    pj_uint32_t total_time;
+
+    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+    warm_up(&cp.factory);
+
+    pj_hr_gettimestamp(&t1);
+    for (i=0; i<LOOP; ++i) {
+	entry = &uri_test_array[0];
+	while (entry->creator) {
+	    status = test_entry(entry);
+	    if (status != PJ_OK) {
+		++err;
+	    }
+	    ++entry;
+	}
+    }
+    pj_hr_gettimestamp(&t2);
+    total_time = t2.u32.lo - t1.u32.lo;
+
+    printf("Error=%d\n", err);
+    printf("Total parse len:  %u bytes\n", parse_len);
+    printf("Total parse time: %u (%f/char), print time: %u (%f/char)\n", 
+	    parse_time, parse_time*1.0/parse_len,
+	    print_time, print_time*1.0/parse_len);
+    printf("Total time: %u (%f/char)\n", total_time, total_time*1.0/parse_len);
+    return err;
+}
+
+#else
+
+pj_status_t test_uri()
+{
+    struct uri_test *entry;
+    unsigned i;
+
+    warm_up();
+    pj_caching_pool_init(&cp, 1024*1024);
+
+    for (i=0; i<LOOP; ++i) {
+	entry = &uri_test_array[0];
+	while (entry->creator) {
+	    pj_pool_t *pool;
+	    pjsip_uri *uri1, *uri2;
+
+	    pool = pj_pool_create( &cp.factory, "", POOL_SIZE, 0, &pool_error);
+	    uri1 = pjsip_parse_uri(pool, entry->str, strlen(entry->str));
+	    pj_pool_release(pool);
+	    ++entry;
+	}
+    }
+
+    return 0;
+}
+
+#endif
