CVS: beepcore-c/threaded_os/profiles Makefile.in,NONE,1.1 cyrus-profiles.c,NONE,1.1 cyrus-profiles.h,NONE,1.1 null-profiles.c,NONE,1.1 null-profiles.h,NONE,1.1 sasl-profiles.c,NONE,1.1 sasl-profiles.h,NONE,1.1 syslog-raw.c,NONE,1.1 syslog-raw.h,NONE,1.1 tls-profile.c,NONE,1.1 tls-profile.h,NONE,1.1 tuning.h,NONE,1.1

Chris Hanson <[email protected]> Sat, 07 Sep 2002 20:02:35 -0700
Newsgroups gmane.network.beep.beepcore.c.cvs
Message-ID <[email protected]>
Update of /cvsroot/beepcore-c/beepcore-c/threaded_os/profiles
In directory usw-pr-cvs1:/tmp/cvs-serv32474/threaded_os/profiles

Added Files:
	Makefile.in cyrus-profiles.c cyrus-profiles.h null-profiles.c 
	null-profiles.h sasl-profiles.c sasl-profiles.h syslog-raw.c 
	syslog-raw.h tls-profile.c tls-profile.h tuning.h 
Log Message:
Move "profiles/" directory to "threaded_os/profiles/".


--- NEW FILE: Makefile.in ---
#
# Copyright (c) 2001 Invisible Worlds, Inc.  All rights reserved.
# 
# The contents of this file are subject to the Blocks Public License (the
# "License"); You may not use this file except in compliance with the License.
# 
# You may obtain a copy of the License at http://www.beepcore.org/
# 
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
# for the specific language governing rights and limitations under the
# License.
#
# $Id: Makefile.in,v 1.1 2002/09/08 03:02:33 cphmit Exp $
#
# Please notice that all dependencies are resolved by ordering, so
# that you are not allowed to screw around the order of targets!

### Generic settings
srcdir = @srcdir@
top_srcdir = @top_srcdir@
include $(top_srcdir)/Make.defs

### Target directories
DIRS =  unix
MOREDIRS = 

### Simple tree makefile targets
ifdef DIRS
all: treeall
clean: treeclean
install: treeinstall
endif

### Default tree handling targets
include $(top_srcdir)/Make.targets

--- NEW FILE: cyrus-profiles.c ---
/* profiles for CYRUS SASL (requires v2.1.0 or later) */

/* NB:

        please don't mess with the '#undef tuning', that part is still
        under development...

 */

/*
 * $Id: cyrus-profiles.c,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */


/*    includes */

#ifndef WIN32
#include <ctype.h>
#include <sys/time.h>
[...1935 lines suppressed...]
}

PROFILE_REGISTRATION *cyrus_profiles_Init2 (struct configobj *appconfig) {
    int      result;
    char    *cp;

    cyrus_first ();

    if (!(cp = config_get (appconfig, BEEP_IDENT)))
        cp = "beepd";
    if ((result = sasl_server_init (cyrus_server_callbacks (appconfig), cp))
            != SASL_OK) {
        log_line (LOG_PROF, 7, "sasl_server_init failed (cyrus code %d) %s",
                  result, sasl_errstring (result, NULL, NULL));

        return NULL;
    }

    return cyrus_profiles_Init (appconfig);
}

--- NEW FILE: cyrus-profiles.h ---
/* profiles for CYRUS SASL */

/*
 * $Id: cyrus-profiles.h,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */

/*

initiator usage:

   1. cyrus_first ()

   2. sasl_client_init (cyrus_client_callbacks (), pgmname)

   3. pr = cyrus_profiles_Init (appconfig)
      
   4. cyrus_tune (pr, ...)

   5. establish session using pr

   6. d = cyrus_login (...)

      if d is non-NULL, you lose


listener usage:

   1. cyrus_first ()

   2. sasl_server_init (cyrus_server_callbacks (appconfig), pgmname)

   3. pr = cyrus_profiles_Init (appconfig)
      
   4. cyrus_tune (pr, ...)

   5. establish session using pr

 */

#ifndef CYRUS_PROFILES_H
#define CYRUS_PROFILES_H 1

/* includes */

#include <beepcore-c/bp_wrapper.h>
#include <beepcore-c/bp_config.h>
#include <beepcore-c/tuning.h>
#include <sasl/sasl.h>


#if defined(__cplusplus)
extern "C"
{
#endif

/**
 * @name Cyrus SASL profiles
 **/

/*@{*/

/* typedefs */

/**
 * Prototype for the connection creation callback
 * 
 * This calls <i>sasl_client_new</i> or <i>sasl_server_new</i> with whatever
 * callback information is desired, and, on success, makes a call to
 * <i>sasl_setprop</i> to set the desired security properties with
 * <b>SASL_SEC_PROPS</b>.
 * <p>
 * Another reason to supply this callback, is it allows you to squirrel away
 * the connection pointer to later call <i>sasl_getprop</i>.
 *
 * @param bp A pointer to the connection structure.
 *
 * @param conn Must be filled-in by the callback, i.e., by calling
 *             <i>sasl_client_new</i> or <i>sasl_server_new</i>.
 *
 * @param clientData The user-supplied pointer provided to
 *                   {@link cyrus_tune cyrus_tune} or
 *                   {@link cyrus_login cyrus_login}.
 *
 * @param serverP If non-zero, call <i>sasl_server_new</i>
 *               (otherwise, call <i>sasl_client_new</i>).
 *
 * @return A SASL result code (e.g., <b>SASL_OK</b>).
 **/
typedef int (*sasl_conn_callback_t) (struct BP_CONNECTION* bp,
                                     sasl_conn_t** conn,
                                     void* clientData,
                                     int serverP);

/**
 * Prototype for the client interaction callback
 * 
 * This is called whenever a client interaction is necessary.
 *
 * @param bp A pointer to the connection structure.
 *
 * @param interact As provided by <i>sasl_client_start</i>  or
 *                 <i>sasl_client_step</i>.
 *
 * @param clientData The user-supplied pointer provided to
 *                   {@link cyrus_login cyrus_login}.
 *
 * @return On failure, a pointer to a {@link diagnostic diagnostic} structure
 *         explaining the reason
 *         (which will subsequently be destroyed by calling
 *         {@link bp_diagnostic_destroy bp_diagnostic_destroy}).
 **/
typedef struct diagnostic* (*sasl_interact_callback_t) (BP_CONNECTION* bp,
                                                        sasl_interact_t* interact,
                                                        void* clientData);



/* keys for configuration package */

/**
 * Enables debug mode for the SASL family of profiles,
 * defaults to <b>"0"</b>.
 **/
#define SASL_CYRUS_DEBUG        "beep profiles sasl debug"

/**
 * Specifies the registered name of the service using SASL,
 * defaults to <b>"beep"</b>.
 **/
#define SASL_CYRUS_SERVICE      "beep profiles sasl cyrus service"

/**
 * Specifies the minimum SSF that may be negotiated
 * defaults to <b>0</b>.
 * This is not consulted if {@link cyrus_login cyrus_login} was given
 * a non-NULL <i>callback1</i> parameter.
 **/
#define SASL_CYRUS_MINSSF      "beep profiles sasl cyrus min_ssf"

/**
 * Specifies the maximum SSF that may be negotiated
 * defaults to <b>256</b>.
 * This is not consulted if {@link cyrus_login cyrus_login} was given
 * a non-NULL <i>callback1</i> parameter.
 **/
#define SASL_CYRUS_MAXSSF      "beep profiles sasl cyrus max_ssf"

/**
 * Specifies the registered name of the service using SASL,
 * optional.
 **/
#define SASL_CYRUS_PLUGINPATH   "beep profiles sasl cyrus plugin_path"


/* module entry points */

/**
 * Well-known entry point for the SASL profiles implemented by Cyrus.
 * <p>
 * Note that you <b>must</b> call <i>sasl_server_init</i> or
 * <i>sasl_client_init</i> <b>before</b> you call this routine.
 * (But, first of all, you <b>must</b> call {@link cyrus_first cyrus_first}.)
 *
 * @param appconfig A pointer to the {@link configobj configuration}
 *                  structure used for configuration purposes.
 * <p>
 * Keys:
 * <blockquote><dl>
 * <dt>SASL_CYRUS_DEBUG</dt>
 * <dd>A non-zero value to enable debugging (optional).</dd>
 *
 * <dt>SASL_CYRUS_SERVICE</dt>
 * <dd>The registered name of the service using SASL,
 * defaults to <b>"beep"</b>.</dd>
 * </dl></blockquote>
 *
 * @return A pointer to a linked-list of profile-registration structures.
 **/
extern struct PROFILE_REGISTRATION* cyrus_profiles_Init(struct configobj* appconfig);


/* additional profile-related functions */

/**
 * The function to call before calling <i>sasl_client_init</i> or
 * <i>sasl_server_init</i>.
 **/
extern void cyrus_first (void);

/**
 * Returns a <i>sasl_callback_t</i> array, suitable for passing to
 * <i>sasl_client_init</i>.
 *
 * @param appconfig A pointer to the {@link configobj configuration}
 *                  structure used for configuration purposes.
 * <p>
 * Keys:
 * <blockquote><dl>
 * <dt>SASL_CYRUS_PLUGINPATH</dt>
 * <dd>A colon-separated search path (optional).</dd>
 * </dl></blockquote>
 *
 * @return A pointer to a static array.
 **/
extern sasl_callback_t* cyrus_client_callbacks (struct configobj *appconfig);

/**
 * Returns a <i>sasl_callback_t</i> array, suitable for passing to
 * <i>sasl_server_init</i>.
 *
 * @param appconfig A pointer to the {@link configobj configuration}
 *                  structure used for configuration purposes.
 * <p>
 * Keys:
 * <blockquote><dl>
 * <dt>SASL_CYRUS_PLUGINPATH</dt>
 * <dd>A colon-separated search path (optional).</dd>
 * </dl></blockquote>
 *
 * @return A pointer to a static array.
 **/
extern sasl_callback_t* cyrus_server_callbacks (struct configobj *appconfig);

/**
 * Sets the <i>SASL_CB_LOG</i> callback to use the logging package.
 *
 * @param bp A pointer to the connection structure.
 *
 * @param cb The callback array to update.
 **/
extern int cyrus_callback_setlog (BP_CONNECTION* bp,
                                  sasl_callback_t* cb);

/**
 * A default interaction-handler.
 * <p>
 * Consult the connection's {@link configobj configuration}
 * structure for the relevant information.
 * If absent, then it queries the user via <i>stdin</i>.
 * <p>
 * Input Keys, all optional:
 * <blockquote><dl>
 * <dt>SASL_LOCAL_REALM</dt>
 * <dd>The authentication realm.</dd>
 *
 * <dt>SASL_LOCAL_USERNAME</dt>
 * <dd>The SASL authentication-identity.</dd>
 *
 * <dt>SASL_LOCAL_TARGET</dt>
 * <dd>The SASL authorization-identity.</dd>
 *
 * <dt>SASL_LOCAL_PASSPHRASE</dt>
 * <dd>The passphrase for the authentication-identity.</dd>
 * </dl></blockquote>
 * <p>
 * Consult the {@link (*sasl_interact_callback_t) callback} information
 * for an explanation of the parameters and return value.
 *
 * @see cyrus_login
 **/
extern struct diagnostic* cyrus_fillin (BP_CONNECTION* bp,
                                        sasl_interact_t* interact,
                                        void* clientData);


/**
 * Configures a profile-registration structure to invoke a user-supplied
 * callback when it wants to create a <i>sasl_conn_t</i>.
 * <p>
 * Call this before registering the profile with the wrapper
 * (e.g., before calling {@link tcp_bp_listen tcp_bp_listen}.)
 *
 * @param pr A pointer to the profile-registration returned by
 *           {@link cyrus_profiles_Init cyrus_profiles_Init}.
 *
 * @param callback The routine to invoke to call <i>sasl_client_new</i>
 *                 or <i>sasl_server_new</i>
 *                 (cf., {@link (*sasl_conn_callback_t) callback}).
 *
 * @param clientData A user-supplied pointer, supplied to <i>callback</i>.
 * 
 * @param allP If non-zero, traverse the entire chain of profile-registration
 *              structures.
 **/
extern void cyrus_tune (PROFILE_REGISTRATION* pr,
                        sasl_conn_callback_t callback,
                        void* clientData,
                        int allP);

/**
 * Tune using SASL, and update the connection's
 * {@link configobj configuration} structure accordingly.
 *
 * @param bp A pointer to the connection structure.
 * <p>
 * Input Keys:
 * <blockquote><dl>
 * <dt>SASL_CYRUS_SERVICE</dt>
 * <dd>The "service name" for this application,
 * defaults to <b>"beep"</b>.</dd>
 *
 * <dt>SASL_LOCAL_MECHANISM</dt>
 * <dd>The SASL mechanism to use; if not present, this is negotiated.</dd>
 * </dl></blockquote>
 * <p>
 * Output Keys:
 * <blockquote><dl>
 * <dt>SASL_LOCAL_MECHANISM</dt>
 * <dd>The SASL mechanism used for authentication.</dd>
 *
 * <dt>SASL_LOCAL_CODE</dt>
 * <dd>The resulting status code.
 * Consult \URL[Section 8 of RFC 3080]{http://www.beepcore.org/beepcore/docs/rfc3080.jsp#reply-codes}
 * for an (incomplete) list.</dd>
 *
 * <dt>SASL_LOCAL_REASON</dt>
 * <dd>A textual message corresponding to <b>SASL_LOCAL_CODE</b></dd>
 * </dl></blockquote>
 *
 * @param serverName The value to use for the <i>serverName</i> attribute
 *                    of the &lt;start&gt; element (may not be <b>NULL</b>).
 *
 * @param callback1 The routine to invoke to call <i>sasl_client_new</i>
 *                 or <i>sasl_server_new</i>
 *                 (cf., {@link (*sasl_conn_callback_t) callback}).
 *
 * @param clientData1 A user-supplied pointer, supplied to <i>callback1</i>.
 * 
 * @param callback2 The routine to invoke when a client interaction is needed
 *                 (cf., {@link (*sasl_interact_callback_t) callback}),
 *                 if <b>NULL</b>,
 *                 then {@link cyrus_fillin cyrus_fillin} is used.
 *
 * @param clientData2 A user-supplied pointer, supplied to the <i>callback</i>.
 * 
 * @return On failure, a pointer to a {@link diagnostic diagnostic} structure
 *         explaining the reason
 *         (which should subsequently be destroyed by calling
 *         {@link bp_diagnostic_destroy bp_diagnostic_destroy}).
 *
 * @see cyrus_fillin
 **/
extern struct diagnostic* cyrus_login(BP_CONNECTION* bp,
                                      char* serverName,
                                      sasl_conn_callback_t callback1,
                                      void* clientData1,
                                      sasl_interact_callback_t callback2,
                                      void* clientData2);

/*@}*/

#if defined(__cplusplus)
}
#endif
#endif

--- NEW FILE: null-profiles.c ---
/* profiles for NULL ECHO/SINK */

/*
 * $Id: null-profiles.c,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */


/*    includes */

#ifndef WIN32
#include <string.h>
#else
#include <windows.h>
#endif
#include "null-profiles.h"
#include <beepcore-c/tuning.h>
#include <beepcore-c/logutil.h>
#include <beepcore-c/semutex.h>


/*    defines and typedefs */

#define PRO_ECHO_URI            "http://xml.resource.org/profiles/NULL/ECHO"
#define PRO_SINK_URI            "http://xml.resource.org/profiles/NULL/SINK"

#define EMPTY_RPY \
        "Content-Type: application/beep+xml\r\n\r\n<null />"


typedef struct pro_localdata {
    int          pl_flags;              /* mode flags                   */
#define PRO_ECHOMODE    (1<<0)          /* doing echo, not sink         */
#define PRO_START       (1<<1)          /* did null_start               */
#define PRO_READY       (1<<2)          /* ready for null_send          */
#define PRO_RSPWAIT     (1<<3)          /* waiting for peer's response  */
#define PRO_CLSWAIT     (1<<4)          /* waiting for close            */
#define PRO_DENIED      (1<<5)          /* close request was denied     */
#define PRO_DEBUG       (1<<6)          /* debugging                    */

    PROFILE_INSTANCE
                *pl_pi;                 /* return value for null_start  */

    char        *pl_rbuf;               /* out parameters for null_trip */
    int          pl_rlen;               /*   ..                         */

    sem_t        pl_sem;                /* semaphore used for receive */
}  PRO_LOCALDATA;


/*    statics */

static char    *pro_name (char *uri);


/*    profile methods */

static char *
pro_name (char *uri) {
    char        *cp;

    if ((cp = strrchr (uri, '/')) != NULL)
        return ++cp;
    return uri;
}


/* first four methods are invoked when channels are not-yet/no-longer
   instantiated

   success: return NULL
   failure: return char *reason
 */


/* invoked when profile is registered with the wrapper

   do process-wide initialization

   on failure, the wrapper makes no further calls to the profile for
               the duration of the session
 */

static char *
pro_connection_init (PROFILE_REGISTRATION  *pr,
                     BP_CONNECTION         *bp) {
    if (((PRO_LOCALDATA *) pr -> user_ptr) -> pl_flags & PRO_DEBUG) {
        fprintf (stderr, "%s connection_init\n", pro_name (pr -> uri));
        fflush (stderr);
    }

    return NULL;
}


/* invoked if profile is about to be made available for the session

   do session-wide initialization

   on failure, the wrapper makes no further calls to the profile
 */

static char *
pro_session_init (PROFILE_REGISTRATION  *pr,
                  BP_CONNECTION         *bp) {
    if (((PRO_LOCALDATA *) pr -> user_ptr) -> pl_flags & PRO_DEBUG) {
        fprintf (stderr, "%s session_init: role='%c' mode=\"%s\"\n",
                 pro_name (pr -> uri), bp -> role, bp -> mode);
        fflush (stderr);
    }

    return NULL;
}


/* invoked just before the session is released

   do session-wide finalization

   return value is ignored by wrapper
 */

static char *
pro_session_fin (PROFILE_REGISTRATION   *pr,
                 BP_CONNECTION          *bp) {
    if (((PRO_LOCALDATA *) pr -> user_ptr) -> pl_flags & PRO_DEBUG) {
        fprintf (stderr, "%s session_fin\n", pro_name (pr -> uri));
        fflush (stderr);
    }

    return NULL;
}


/* invoked just before wrapper is destroyed

   do process-wide finalization

   return value is ignored by wrapper
 */

static char *
pro_connection_fin (PROFILE_REGISTRATION   *pr,
                    BP_CONNECTION          *bp) {
    if (((PRO_LOCALDATA *) pr -> user_ptr) -> pl_flags & PRO_DEBUG) {
        fprintf (stderr, "%s connection_fin\n", pro_name (pr -> uri));
        fflush (stderr);
    }

    return NULL;
}


/* next three methods are invoked when a session/channel starts */


/* invoked when the peer asks to start a channel

   if not echoing, ignore any piggyback'd data  
   clone PRO_LOCALDATA and accept it
 */

static void
pro_start_indication (PROFILE_INSTANCE  *pi,
                      PROFILE           *po) {
    DIAGNOSTIC           ds,
                        *d = &ds;
    PRO_LOCALDATA       *pl = pi -> channel -> profile_registration
                                 -> user_ptr,
                        *il;
    PROFILE              ps,
                        *p = &ps;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);

    if (pl -> pl_flags & PRO_DEBUG) {
        char    *cp,
               **pp;
        static char *pairs[] = {
            PRIVACY_REMOTE_SUBJECTNAME, "PRIVACY_REMOTE_SUBJECTNAME",
            PRIVACY_CIPHER_NAME,        "PRIVACY_CIPHER_NAME",
            PRIVACY_CIPHER_PROTOCOL,    "PRIVACY_CIPHER_PROTOCOL",
            PRIVACY_CIPHER_SBITS,       "PRIVACY_CIPHER_SBITS",
            SASL_LOCAL_MECHANISM,       "SASL_LOCAL_MECHANISM",
            SASL_LOCAL_CODE,            "SASL_LOCAL_CODE",
            SASL_LOCAL_REASON,          "SASL_LOCAL_REASON",
            SASL_REMOTE_MECHANISM,      "SASL_REMOTE_MECHANISM",
            SASL_REMOTE_USERNAME,       "SASL_REMOTE_USERNAME",
            SASL_REMOTE_TARGET,         "SASL_REMOTE_TARGET",
            SASL_REMOTE_CODE,           "SASL_REMOTE_CODE",
            SASL_REMOTE_REASON,         "SASL_REMOTE_REASON",
          NULL
        };      

        fprintf (stderr, "%s start_indication: piggyback=\"%s\"\n",
                 pro_name (po -> uri),
                 po -> piggyback ? po -> piggyback : "<NULL>");
        for (pp = pairs; *pp; pp++)
            if ((cp = config_get (appconfig, *pp++)) != NULL)
                fprintf (stderr, "    %s: \"%s\"\n", *pp, cp);

        fflush (stderr);
    }
               
    if (!(il = (PRO_LOCALDATA *) lib_malloc (sizeof *il))) {
        memset (d, 0, sizeof *d);
        d -> code = 421;
        d -> message = "out of memory";
        bpc_start_response (pi -> channel, po, d);
        return;
    }
    memcpy (il, pl, sizeof *il);
    il -> pl_pi = pi, pi -> user_ptr1 = il;

    if (il -> pl_flags & PRO_ECHOMODE)
        p = po;
    else {
        memset (p, 0, sizeof *p);
        p -> uri = po -> uri;
    }

    bpc_start_response (pi -> channel, p, NULL);
}


/* invoked when a our request to start a channel has resulted in the channel starting

   remember PRO_LOCALDATA passed as clientData
 */

static void
pro_start_confirmation (void             *clientData,
                        PROFILE_INSTANCE        *pi,
                        PROFILE                 *po) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) clientData;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);

    if (il -> pl_flags & PRO_DEBUG) {
        char    *cp,
               **pp;
        static char *pairs[] = {
            PRIVACY_REMOTE_SUBJECTNAME, "PRIVACY_REMOTE_SUBJECTNAME",
            PRIVACY_CIPHER_NAME,        "PRIVACY_CIPHER_NAME",
            PRIVACY_CIPHER_PROTOCOL,    "PRIVACY_CIPHER_PROTOCOL",
            PRIVACY_CIPHER_SBITS,       "PRIVACY_CIPHER_SBITS",
            SASL_LOCAL_MECHANISM,       "SASL_LOCAL_MECHANISM",
            SASL_LOCAL_CODE,            "SASL_LOCAL_CODE",
            SASL_LOCAL_REASON,          "SASL_LOCAL_REASON",
            SASL_REMOTE_MECHANISM,      "SASL_REMOTE_MECHANISM",
            SASL_REMOTE_USERNAME,       "SASL_REMOTE_USERNAME",
            SASL_REMOTE_TARGET,         "SASL_REMOTE_TARGET",
            SASL_REMOTE_CODE,           "SASL_REMOTE_CODE",
            SASL_REMOTE_REASON,         "SASL_REMOTE_REASON",
          NULL
        };      

        fprintf (stderr, "%s start_confirmation: piggyback=\"%s\"\n",
                 pro_name (po -> uri),
                 po -> piggyback ? po -> piggyback : "<NULL>");
        for (pp = pairs; *pp; pp++)
            if ((cp = config_get (appconfig, *pp++)) != NULL)
                fprintf (stderr, "    %s: \"%s\"\n", *pp, cp);

        fflush (stderr);
    }

    il -> pl_pi = pi, pi -> user_ptr1 = il;
}


/* invoked when a response is received to null_start's request to start a channel

   note failure
*/

static void
pro_start_callback (void                *clientData,
                    CHANNEL_INSTANCE    *ci,
                    DIAGNOSTIC          *error) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) clientData;

    if (il -> pl_flags & PRO_DEBUG) {
      if (error)
          fprintf (stderr, "%s pro_start_callback: error=[%d] \"%s\" \n",
                   pro_name (ci -> profile_registration -> uri),
                   error -> code,
                   error -> message ? error -> message : "<NULL>");
      else
          fprintf (stderr, "%s pro_start_callback: no error\n",
                   pro_name (ci -> profile_registration -> uri));
        fflush (stderr);
    }

    if (error)
        printf ("unable to start channel: [%d] %s\n",
                   error -> code,
                   error -> message ? error -> message : "<NULL>");
}


/* next three methods are invoked when a session/channel closes */


/*
   invoked when we or the peer asks to close a session/channel
   (or perhaps due to transport problem, etc.)

   if the remote peer is asking, reject it

   otherwise, accept it
 */

static void
pro_close_indication (PROFILE_INSTANCE  *pi,
                      DIAGNOSTIC        *request,
                      char               origin,
                      char               scope) {
    BP_CONNECTION       *bp = pi -> channel -> conn;
    DIAGNOSTIC          *d;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (il -> pl_flags & PRO_DEBUG) {
        fprintf (stderr,
                 "%s close_indication: request=[%d] \"%s\" origin=%c scope=%c\n",
                 pro_name (pi -> channel -> profile_registration -> uri),
                 request -> code, request -> message ? request -> message
                                                     : "<NULL>",
                 origin, scope);
        fflush (stderr);
    }
               
    d = ((il -> pl_flags & PRO_START) && (origin == PRO_ACTION_REMOTE))
            ? bp_diagnostic_new (bp, 500, NULL, "no thanks")
            : NULL;

    bpc_close_response (pi -> channel, d);

    if (d)
        bp_diagnostic_destroy (bp, d);
}


/* invoked when we know whether the channel has finally closed or not
   (regardless of who requested it)

   if nothing happened, do nothing

   if we requested the close, leave PRO_LOCALDATA for null_close

   if we're doing a round-trip, signal null_trip and release the semaphore

   otherwise, lib_free PRO_LOCALDATA
 */

static void
pro_close_confirmation (PROFILE_INSTANCE        *pi,
                        char                     status,
                        DIAGNOSTIC              *error,
                        char                     origin,
                        char                     scope) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (il -> pl_flags & PRO_DEBUG) {
        if (error)
            fprintf (stderr,
                     "%s close_confirmation: status=%c error=[%d] \"%s\" origin=%c scope=%c\n",
                     pro_name (pi -> channel -> profile_registration -> uri),
                     status, error -> code, error -> message ? error -> message
                                                             : "<NULL>",
                     origin, scope);
        else 
            fprintf (stderr,
                     "%s close_confirmation: status=%c no error origin=%c scope=%c\n",
                     pro_name (pi -> channel -> profile_registration -> uri),
                     status, origin, scope);
        fflush (stderr);
    }
    
    if (status != PRO_ACTION_SUCCESS)
        return;

    pi -> user_ptr1 = NULL;

    if (il -> pl_flags & PRO_CLSWAIT)
        il -> pl_flags &= ~PRO_CLSWAIT;
    else if (il -> pl_flags & PRO_START) {
        il -> pl_pi = NULL;
        if (il -> pl_flags & PRO_RSPWAIT) {
            il -> pl_flags &= ~PRO_RSPWAIT;
            SEM_POST (&il -> pl_sem);
        }
    }
    else
        lib_free (il);
}


/* invoked when a response is received to null_close's request to close
   the channel

   note failure
*/

static void
pro_close_callback (void                *clientData,
                    CHANNEL_INSTANCE    *ci,
                    DIAGNOSTIC          *error) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) clientData;

    if (il -> pl_flags & PRO_DEBUG) {
      if (error)
          fprintf (stderr, "%s pro_close_callback: error=[%d] \"%s\" \n",
                   pro_name (ci -> profile_registration -> uri),
                   error -> code,
                   error -> message ? error -> message : "<NULL>");
      else
          fprintf (stderr, "%s pro_close_callback: no error\n",
                   pro_name (ci -> profile_registration -> uri));
        fflush (stderr);
    }

    if (error) {
        il -> pl_flags |= PRO_DENIED, il -> pl_flags &= ~PRO_CLSWAIT;

        printf ("unable to close channel: [%d] %s\n",
                error -> code,
                error -> message ? error -> message : "<NULL>");
    }
}


/* next two methods are invoked on tuning resets */


/* invoked when the local peer is about to do a tuning reset */

static void
pro_tuning_reset_indication (PROFILE_INSTANCE  *pi) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (il -> pl_flags & PRO_DEBUG) {
        fprintf (stderr, "%s tuning_reset_indication\n",
                 pro_name (pi -> channel -> profile_registration -> uri));
        fflush (stderr);
    }
}


/* invoked when we know whether the tuning reset occurred */

static void
pro_tuning_reset_confirmation (PROFILE_INSTANCE        *pi,
                               char                     status) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (il -> pl_flags & PRO_DEBUG) {
        fprintf (stderr, "%s tuning_reset_confirmation: status=%c\n",
                 pro_name (pi -> channel -> profile_registration -> uri),
                 status);
        fflush (stderr);
    }

    pro_close_confirmation (pi, status, NULL, PRO_ACTION_LOCAL,
                            PRO_ACTION_SESSION);
}


/* invoked when a frame is available to be read

      frame         actions taken by
   received     ECHO                SINK
   --------     ----                ----
        MSG     send echo RPY       partial: ignore
                                    complete: send empty RPY

        RPY     update out parameters for null_trip
                if complete, leave RSPWAIT

    ERR/NUL     if complete, leave RSPWAIT

        ANS     ignore              ignore  

 */

static void
pro_frame_available (PROFILE_INSTANCE   *pi) {
    int                  size;
    char                *buffer,
                        *payload;
    FRAME               *f;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (!(f = bpc_query_frame (pi -> channel, BLU_QUERY_ANYTYPE,
                               BLU_QUERY_ANYMSG, BLU_QUERY_ANYANS)))
      return;

    if (il -> pl_flags & PRO_DEBUG) {
        fprintf (stderr,
                 "%s frame_available: type=%c number=%ld answer=%ld more=%c size=%ld\n",
                 pro_name (pi -> channel -> profile_registration -> uri),
                 f -> msg_type, f -> message_number, f -> answer_number,
                 f -> more, f -> size);
        fprintf (stderr, f -> size > 75 ? "%-75.75s...\n" : "%s\n",
                 f -> payload);
        fflush (stderr);
    }

    switch (f -> msg_type) {
        case BLU_FRAME_TYPE_MSG:
            if (il -> pl_flags & PRO_ECHOMODE)
                payload = f -> payload, size = f -> size;
            else if (f -> more == BLU_FRAME_COMPLETE)
                payload = EMPTY_RPY, size = sizeof EMPTY_RPY - 1;
            else
                break;
            if ((buffer = bpc_buffer_allocate (pi -> channel, size)) != NULL) {
                memcpy (buffer, payload, size);
                bpc_send (pi -> channel, BLU_FRAME_TYPE_RPY,
                          f -> message_number, BLU_FRAME_IGNORE_ANSNO,
                          f -> more, buffer, size);
            } else
                bp_log (pi -> channel -> conn, LOG_PROF, 5,
                        "%s frame_available: out of memory",
                        pro_name (pi -> channel -> profile_registration -> uri));
            break;

        case BLU_FRAME_TYPE_RPY:
            if (f -> size <= il -> pl_rlen) {
                memcpy (il -> pl_rbuf, f -> payload, f -> size);
                il -> pl_rbuf += f -> size, il -> pl_rlen -= f -> size;
            } else
                il -> pl_rlen = -1;
            /* and fall... */
        case BLU_FRAME_TYPE_ERR:
        case BLU_FRAME_TYPE_NUL:
            if (f -> more == BLU_FRAME_COMPLETE) {
                il -> pl_flags &= ~PRO_RSPWAIT;
                SEM_POST (&il -> pl_sem);
            }
            break;

        case BLU_FRAME_TYPE_ANS:
        default:
            break;
    }

    bpc_frame_destroy (pi -> channel, f);
}


/*    module entry points */

static PROFILE_REGISTRATION *
pro_init (struct configobj  *appconfig,
          int                echoP) {
    int                   debugP;
    char                 *cp;
    PRO_LOCALDATA        *pl;
    PROFILE_REGISTRATION *pr;

    if (!(pr = (PROFILE_REGISTRATION *) lib_malloc (sizeof *pr +
                                                    sizeof *pl))) {
        log_line (LOG_PROF, 6, "unable to allocate PR");
        return NULL;
    }
    memset (pr, 0, sizeof *pr);

    cp = echoP ? NULL_ECHO_DEBUG : NULL_SINK_DEBUG;
    debugP = 0;
    if ((cp = config_get (appconfig, cp)) != NULL)
        debugP = atoi (cp);

    cp = echoP ? NULL_ECHO_URI : NULL_SINK_URI;
    if (!(pr -> uri = config_get (appconfig, cp)))
        pr -> uri = echoP ? PRO_ECHO_URI : PRO_SINK_URI;

    cp = echoP ? NULL_ECHO_IMODES : NULL_SINK_IMODES;
    if (!(pr -> initiator_modes = config_get (appconfig, cp)))
        pr -> initiator_modes = "plaintext,encrypted";
    cp = echoP ? NULL_ECHO_LMODES : NULL_SINK_LMODES;
    if (!(pr -> listener_modes = config_get (appconfig, cp)))
        pr -> listener_modes = "plaintext,encrypted";

    pr -> full_messages = 0;
    pr -> thread_per_channel = 0;

    pr -> proreg_connection_init = pro_connection_init;
    pr -> proreg_connection_fin  = pro_connection_fin;
    pr -> proreg_session_init = pro_session_init;
    pr -> proreg_session_fin  = pro_session_fin;
    pr -> proreg_start_indication   = pro_start_indication;
    pr -> proreg_start_confirmation = pro_start_confirmation;
    pr -> proreg_close_indication   = pro_close_indication;
    pr -> proreg_close_confirmation = pro_close_confirmation;
    pr -> proreg_tuning_reset_indication   = pro_tuning_reset_indication;
    pr -> proreg_tuning_reset_confirmation = pro_tuning_reset_confirmation;
    pr -> proreg_frame_available = pro_frame_available;

    pr -> user_ptr = pl = (PRO_LOCALDATA *) (((char *) pr) + sizeof *pr);
    memset (pl, 0, sizeof *pl);
    if (echoP)
        pl -> pl_flags |= PRO_ECHOMODE;
    if (debugP)
        pl -> pl_flags |= PRO_DEBUG;

    return pr;
}

PROFILE_REGISTRATION *
null_echo_Init (struct configobj  *appconfig) {
    return pro_init (appconfig, 1);
}

PROFILE_REGISTRATION *
null_sink_Init (struct configobj  *appconfig) {
    return pro_init (appconfig, 0);
}


/* initiator: start channel */

void *
null_start (BP_CONNECTION               *bp,
            PROFILE_REGISTRATION        *pr,
            char                        *serverName) {
    DIAGNOSTIC          *d;
    PRO_LOCALDATA       *pl = pr -> user_ptr,
                        *il;
    PROFILE              ps,
                        *p = &ps;

    memset (p, 0, sizeof *p);
    p -> uri = pr -> uri;
    p -> piggyback_length = strlen (p -> piggyback = "");

    if (!(il = (PRO_LOCALDATA *) lib_malloc (sizeof *il)))
        return NULL;
    memcpy (il, pl, sizeof *il);
    il -> pl_flags |= PRO_START;

    /* this blocks! */
    d = bp_start_request (bp, BLU_CHAN0_CHANO_DEFAULT, BLU_CHAN0_MSGNO_DEFAULT,
                          p, serverName, pro_start_callback, (void *) il);

    if (d) {
        printf ("unable to start channel: [%d] %s\n",
                d -> code, d -> message);
        bp_diagnostic_destroy (bp, d);
    }

    if (il -> pl_pi) {
        SEM_INIT (&il -> pl_sem, 0);
        il -> pl_flags |= PRO_READY;
    } else
        lib_free (il), il = NULL;

    return ((void *) il);
}


/* initiator: send a message, get back reply */

extern int
null_trip (void                         *v,
           char                         *ibuf,
           int                           ilen,
           char                         *obuf,
           int                           omaxlen) {
    char                *buffer;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) v;
    PROFILE_INSTANCE    *pi;

    if (!(pi = il -> pl_pi)) {
        SEM_DESTROY (&il -> pl_sem);
        lib_free (il);
        return NULL_DONE;
    }
    if (!(il -> pl_flags & PRO_READY))
        return NULL_BUSY;

    if (!(buffer = bpc_buffer_allocate (pi -> channel, ilen)))
        return NULL_ERROR;
    memcpy (buffer, ibuf, ilen);

    il -> pl_rbuf = obuf, il -> pl_rlen = omaxlen;
    il -> pl_flags |= PRO_RSPWAIT, il -> pl_flags &= ~PRO_READY;

    bpc_send (pi -> channel, BLU_FRAME_TYPE_MSG, BLU_FRAME_MSGNO_UNUSED,
              BLU_FRAME_IGNORE_ANSNO, BLU_FRAME_COMPLETE, buffer, ilen);

    SEM_WAIT (&il -> pl_sem);

    if (!il -> pl_pi) {
        SEM_DESTROY (&il -> pl_sem);
        lib_free (il);
        return NULL_DONE;
    }

    il -> pl_flags |= PRO_READY;

    if (il -> pl_rlen < 0)
        return NULL_ERROR;

    return (omaxlen - il -> pl_rlen);
}


/* initiator: close channel */

extern int
null_close (void        *v) {
    BP_CONNECTION       *bp;
    DIAGNOSTIC          *d;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) v;
    PROFILE_INSTANCE    *pi;

    if (!(pi = il -> pl_pi)) {
        SEM_DESTROY (&il -> pl_sem);
        lib_free (il);
        return NULL_DONE;
    }
    if (!(il -> pl_flags & PRO_READY))
        return NULL_BUSY;

    il -> pl_flags |= PRO_CLSWAIT, il -> pl_flags &= ~PRO_READY;

    bp = pi -> channel -> conn;
    if ((d = bpc_close_request (pi -> channel, BLU_CHAN0_MSGNO_DEFAULT, 200,
                                NULL, NULL, pro_close_callback,
                                (void *) il)) != NULL) {
        printf ("unable to close channel: [%d] %s\n",
                d -> code, d -> message);
        bp_diagnostic_destroy (bp, d);

        il -> pl_flags |= PRO_READY, il -> pl_flags &= ~PRO_CLSWAIT;
        return NULL_ERROR;
    }

    /* could use a semaphore instead of a spinlock, but it's an example... */
    while (il -> pl_flags & PRO_CLSWAIT)
        YIELD ();

    if (il -> pl_flags & PRO_DENIED) {
        il -> pl_flags |= PRO_READY, il -> pl_flags &= ~PRO_DENIED;
        return NULL_DENIED;
    }

    SEM_DESTROY (&il -> pl_sem);
    lib_free (il);
    return NULL_OK;
}

--- NEW FILE: null-profiles.h ---
/* profiles for NULL ECHO/SINK */

/*
 * $Id: null-profiles.h,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */

/*

initiator usage:

   1. pr = null_echo_Init (appconfig)    or null_sink_Init ...
      
   2. establish session, bp, using pr

   3. v = null_start (bp, pr)

      if v is NULL, you lose

   4. for 0..I: cc = null_trip (v, ibuf, ilen, obuf, omaxlen)

       if (cc < 0) release session, etc.

       otherwise cc octets were stored in obuf

   5. i = null_close (v)

       if (i < 0) release session, etc.

       otherwise channel is closed


listener usage:

   1. pr = null_echo_Init (appconfig)    or null_sink_Init ...
      
   2. establish session using pr

   3. sr_Fin (pr) prior to destroying pr

 */

#ifndef NULL_PROFILES_H
#define NULL_PROFILES_H 1


/* includes */

#include <beepcore-c/bp_config.h>
#include <beepcore-c/bp_wrapper.h>


#if defined(__cplusplus)
extern "C"
{
#endif

/**
 * @name The NULL/ECHO and NULL/SINK profiles
 **/

/*@{*/

/* keys for configuration package */

/**
 * URI to use for the NULL ECHO profile,
 * defaults to <b>"http://xml.resource.org/profiles/NULL/ECHO"</b>.
 **/
#define NULL_ECHO_URI           "beep profiles null_echo uri"

/**
 * Initiator modes for the NULL ECHO profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define NULL_ECHO_IMODES        "beep profiles null_echo initiator_modes"

/**
 * Listener modes for the NULL ECHO profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define NULL_ECHO_LMODES        "beep profiles null_echo listener_modes"

/**
 * Enables debug mode for the NULL ECHO profile,
 * defaults to <b>"0"</b>.
 **/
#define NULL_ECHO_DEBUG         "beep profiles null_echo debug"

/**
 * URI to use for the NULL SINK profile,
 * defaults to <b>"http://xml.resource.org/profiles/NULL/SINK"</b>.
 **/
#define NULL_SINK_URI           "beep profiles null_sink uri"

/**
 * Initiator modes for the NULL SINK profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define NULL_SINK_IMODES        "beep profiles null_sink initiator_modes"

/**
 * Listener modes for the NULL SINK profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define NULL_SINK_LMODES        "beep profiles null_sink listener_modes"

/**
 * Enables debug mode for the NULL SINK profile,
 * defaults to <b>"0"</b>.
 **/
#define NULL_SINK_DEBUG         "beep profiles null_sink debug"



/* module entry points */

/**
 * Well-known entry point for the NULL/ECHO profile.
 *
 * @param appconfig A pointer to the {@link configobj configuration}
 *                  structure used for configuration purposes.
 * <p>
 * Keys:
 * <blockquote><dl>
 * <dt>NULL_ECHO_URI</dt>
 * <dd>The registration URI for the profile (optional).</dd>
 *
 * <dt>NULL_ECHO_IMODES</dt>
 * <dd>The <i>initiator_modes</i> to use when registering the profile
 * (optional).</dd>
 *
 * <dt>NULL_ECHO_LMODES</dt>
 * <dd>The <i>listener_modes</i> to use when registering the profile
 * (optional).</dd>
 * </dl></blockquote>
 *
 * @return A pointer to a profile-registration structure.
 **/
extern struct PROFILE_REGISTRATION* null_echo_Init (struct configobj* appconfig);

/**
 * Well-known entry point for the NULL/SINK profile.
 *
 * @param appconfig A pointer to the {@link configobj configuration}
 *                  structure that may be used for configuration purposes.
 * <p>
 * Keys:
 * <blockquote><dl>
 * <dt>NULL_SINK_URI</dt>
 * <dd>The registration URI for the profile (optional).</dd>
 *
 * <dt>NULL_SINK_IMODES</dt>
 * <dd>The <i>initiator_modes</i> to use when registering the profile
 * (optional).</dd>
 *
 * <dt>NULL_SINK_LMODES</dt>
 * <dd>The <i>listener_modes</i> to use when registering the profile
 * (optional).</dd>
 * </dl></blockquote>
 *
 * @return A pointer to a profile-registration structure.
 **/
extern struct PROFILE_REGISTRATION* null_sink_Init (struct configobj* appconfig);


/* initiator routines */

/**
 * Starts an echo or sink channel.
 *
 * @param bp A pointer to the connection structure.
 *
 * @param pr A pointer to the profile-registration returned by either
 *          {@link null_echo_Init null_echo_Init} or
 *           {@link null_sink_Init null_sink_Init}.
 *
 * @param serverName The value to use for the <i>serverName</i> attribute
 *                    of the &lt;start&gt; element, or <b>NULL</b>.
 *
 * @return An opaque pointer for use with {@link null_trip null_trip} and
 *         {@link null_close null_close}.
 **/
extern void* null_start (struct BP_CONNECTION* bp,
                         struct PROFILE_REGISTRATION* pr,
                         char* serverName);

/**
 * Initiates a round-trip transaction.
 *
 * @param v An opaque pointer returned by {@link null_start null_start}.
 *
 * @param ibuf A character pointer to the send buffer.
 *
 * @param ilen The length of the send buffer, in octets.
 *
 * @param obuf A character pointer to the receive buffer.
 *
 * @param omaxlen The length of the receive buffer, in octets.
 *
 * @return The number of octets received, or one of:
 *         <ul>
 *         <li>{@link NULL_ERROR NULL_ERROR}</li>
 *         <li>{@link NULL_BUSY NULL_BUSY}</li>
 *         <li>{@link NULL_DONE NULL_DONE}</li>
 *         </ul>
 **/
extern int null_trip (void* v,
                      char* ibuf,
                      int ilen,
                      char* obuf,
                      int omaxlen);

/**
 * Closes an echo or sink channel.
 *
 * @param v An opaque pointer returned by {@link null_start null_start}.
 *
 * @return One of:
 *         <ul>
 *         <li>{@link NULL_OK NULL_OK}</li>
 *         <li>{@link NULL_ERROR NULL_ERROR}</li>
 *         <li>{@link NULL_BUSY NULL_BUSY}</li>
 *         <li>{@link NULL_DONE NULL_DONE}</li>
 *         <li>{@link NULL_DENIED NULL_DENIED}</li>
 *         </ul>
 **/
extern int null_close (void* v);

/**
 * no problema
 **/
#define NULL_OK         0

/**
 * error performing task
 **/
#define NULL_ERROR      (-1)

/**
 * still doing {@link null_trip null_trip}
 **/
#define NULL_BUSY       (-2)

/**
 * channel is closed
 **/
#define NULL_DONE       (-3)

/**
 * remote peer refused to close
 **/
#define NULL_DENIED     (-4)

/*@}*/

#if defined(__cplusplus)
}
#endif

#endif

--- NEW FILE: sasl-profiles.c ---
/*
 * Copyright (c) 2001 Invisible Worlds, Inc.  All rights reserved.
 *
 * The contents of this file are subject to the Blocks Public License (the
 * "License"); You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at http://www.beepcore.org/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 */

/*
 * $Id: sasl-profiles.c,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */


/*    includes */

#ifndef WIN32
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#else
#include <windows.h>
#define sem_t HANDLE
#endif
#include <beepcore-c/sasl-profiles.h>
#include <beepcore-c/logutil.h>
#include <beepcore-c/CBEEPint.h>
#include "SASLutils/sasl_general.h"


/*    defines and typedefs */

#define PRO_ANONYMOUS_URI       "http://iana.org/beep/SASL/ANONYMOUS"
#define PRO_OTP_URI             "http://iana.org/beep/SASL/OTP"

#define DEFAULT_CONTENT_TYPE    "Content-Type: application/beep+xml\r\n\r\n"


typedef struct pro_localdata {
    int                  pl_flags;
#define PRO_INITIATOR   (1<<0)
#define PRO_STARTING    (1<<1)
#define PRO_CLOSED      (1<<2)

    CHANNEL_INSTANCE    *pl_channel;
    DIAGNOSTIC          *pl_status;

    int                   pl_task;
#define PRO_SERVER_ANON         0x00
#define PRO_SERVER_OTP          0x01
#define PRO_CLIENT_ANON         0x80
#define PRO_CLIENT_OTP          0x81

    int                  pl_sent;
    struct sasl_data_block
                         pl_sdb;
}  PRO_LOCALDATA;


/*    statics */

static int      pro_debug;
static char    *pro_name (char *uri);

static void     sasl_error (PROFILE_INSTANCE *pi,
                            char             *data,
                            int              size);

static char    *sasl_response (PROFILE_INSTANCE *pi,
                               int               result);

static void     sasl_set_local_success (PROFILE_INSTANCE *pi);
static void     sasl_set_local_failure (PROFILE_INSTANCE *pi,
                                        DIAGNOSTIC       *d);
static void     sasl_set_remote_success (PROFILE_INSTANCE *pi);
static void     sasl_set_remote_failure (PROFILE_INSTANCE *pi,
                                         int               code,
                                         char             *reason);


/*    profile methods */

static char *
pro_name (char *uri) {
    char        *cp;

    if ((cp = strrchr (uri, '/')) != NULL)
        return ++cp;
    return uri;
}

static char *
pro_connection_init (PROFILE_REGISTRATION *pr,
                     BP_CONNECTION        *bp) {
    if (pro_debug) {
        fprintf (stderr, "%s connection_init\n", pro_name (pr -> uri));
        fflush (stderr);
    }

    return NULL;
}

static char *
pro_session_init (PROFILE_REGISTRATION *pr,
                  BP_CONNECTION        *bp) {
    if (pro_debug) {
        fprintf (stderr, "%s session_init: role='%c' mode=\"%s\"\n",
                 pro_name (pr -> uri), bp -> role, bp -> mode);
        fflush (stderr);
    }

    return NULL;
}

static char *
pro_session_fin (PROFILE_REGISTRATION *pr,
                 BP_CONNECTION        *bp) {
    if (pro_debug) {
        fprintf (stderr, "%s session_fin\n", pro_name (pr -> uri));
        fflush (stderr);
    }

    return NULL;
}

static char *
pro_connection_fin (PROFILE_REGISTRATION *pr,
                    BP_CONNECTION        *bp) {
    if (pro_debug) {
        fprintf (stderr, "%s connection_fin\n", pro_name (pr -> uri));
        fflush (stderr);
    }

    return NULL;
}

static void
pro_start_indication (PROFILE_INSTANCE *pi,
                      PROFILE          *po) {
    char                *cp,
                        *otp;
    DIAGNOSTIC           ds,
                        *d = &ds;
    PRO_LOCALDATA       *il;
    PROFILE              ps,
                        *p = &ps;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);
    struct cfgsearchobj *cs;

    if (pro_debug) {
        fprintf (stderr, "%s start_indication: piggyback=\"%s\"\n",
                 pro_name (po -> uri),
                 po -> piggyback ? po -> piggyback : "<NULL>");
        fflush (stderr);
    }

    memset (p, 0, sizeof *p);
    p -> uri = po -> uri;

    memset (d, 0, sizeof *d);
    d -> code = 421;

    if (((cp = config_get (appconfig, SASL_LOCAL_CODE)) != NULL)
            && (*cp == '2')) {
        d -> code = 520;
        d -> message = "already tuned for authentication";

        bpc_start_response (pi -> channel, p, d);
        return;
    }

    if (!(il = (PRO_LOCALDATA *) lib_malloc (sizeof *il))) {
        d -> message = "out of memory";

        bpc_start_response (pi -> channel, p, d);
        return;
    }
    pi -> user_ptr1 = il;

    memset (il, 0, sizeof *il);

    if (!(otp = config_get (appconfig, SASL_OTP_URI)))
        otp = PRO_OTP_URI;
    il -> pl_task = strcmp (po -> uri, otp) ? PRO_SERVER_ANON : PRO_SERVER_OTP;
    if (!(il -> pl_sdb.path = config_get (appconfig, SASL_OTP_DIRECTORY)))
        il -> pl_sdb.path = "/tmp";

    cs = config_search_init (appconfig, SASL_REMOTE_PREFIX, "");
    for (cp = config_search_string_firstkey (cs);
             cp;
             cp = config_search_string_nextkey (cs))
        config_delete (appconfig, cp);
    config_search_fin (&cs);
    switch (il -> pl_task) {
        case PRO_SERVER_ANON:
            config_set (appconfig, SASL_REMOTE_MECHANISM, "anonymous");
            break;

        case PRO_SERVER_OTP:
            config_set (appconfig, SASL_REMOTE_MECHANISM, "otp");
            break;
    }

    if (po -> piggyback_length > 0) {
        int result = SASL_COMPLETE;

        switch (il -> pl_task) {
            case PRO_SERVER_ANON:
                if ((result = sasl_anonymous_server_guts (po -> piggyback,
                                                          &il -> pl_sdb))
                        == SASL_COMPLETE)
                    config_set (appconfig, SASL_REMOTE_USERNAME,
                                il -> pl_sdb.parse_blob_data);
                break;

            case PRO_SERVER_OTP:
                result = sasl_otp_server_guts_1 (po -> piggyback,
                                                 &il -> pl_sdb);
                break;
        }
        p -> piggyback_length = strlen (p -> piggyback =
                                            sasl_response (pi, result));
        il -> pl_sent = 1;
    }

    bpc_start_response (pi -> channel, p, NULL);
}

static void
pro_start_confirmation (void             *clientData,
                        PROFILE_INSTANCE *pi,
                        PROFILE          *po) {
    int                  problem,
                         size;
    char                *buffer,
                        *payload;
    PRO_LOCALDATA       *il = clientData;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);

    if (pro_debug) {
        fprintf (stderr, "%s start_confirmation: piggyback=\"%s\"\n",
                 pro_name (po -> uri),
                 po -> piggyback ? po -> piggyback : "<NULL>");
        fflush (stderr);
    }

    pi -> user_ptr1 = il;

    il -> pl_flags &= ~(PRO_STARTING | PRO_CLOSED);
    il -> pl_channel = pi -> channel;

    if (po -> piggyback_length == 0) {
        il -> pl_status = bp_diagnostic_new (pi -> channel -> conn, 504, NULL,
                                             "expecting blob element");
        return;
    }

    if (strstr (po -> piggyback, "<error")) {
        sasl_error (pi, po -> piggyback, po -> piggyback_length);
        return;
    }
    
    switch (il -> pl_task) {
        case PRO_CLIENT_ANON:
            parse_blob (po -> piggyback, &il -> pl_sdb);
            if (!strcmp (il -> pl_sdb.parse_blob_status, "complete"))
                sasl_set_local_success (pi);
            else
                sasl_set_local_failure (pi, NULL);
            break;

        case PRO_CLIENT_OTP:
            if (!(payload = sasl_otp_client_guts_2
                                (po -> piggyback,
                                 config_get (appconfig, SASL_LOCAL_PASSPHRASE),
                                 &problem, &il -> pl_sdb))) {
                if (problem == SASL_ERROR_ALGORITHM) {
                    config_set (appconfig, SASL_LOCAL_CODE, "504");
                    config_set (appconfig, SASL_LOCAL_REASON,
                                "unsupported algorithm");
                } else {
                    config_set (appconfig, SASL_LOCAL_CODE, "501");
                    config_set (appconfig, SASL_LOCAL_REASON,
                                "unable to parse blob");
                }

                payload = "<blob status='abort' />";
            }
            size = strlen (payload) + sizeof DEFAULT_CONTENT_TYPE - 1;
            if ((buffer = bpc_buffer_allocate (pi -> channel, size)) != NULL) {
                sprintf (buffer, "%s%s", DEFAULT_CONTENT_TYPE, payload);

                bpc_send (pi -> channel, BLU_FRAME_TYPE_MSG,
                          BLU_FRAME_MSGNO_UNUSED, BLU_FRAME_IGNORE_ANSNO,
                          BLU_FRAME_COMPLETE, buffer, size);
                il -> pl_sent++;
            } else
                bp_log (pi -> channel -> conn, LOG_PROF, 5,
                        "%s start_confirmation: out of memory",
                        pro_name (po -> uri));
            break;
    }
}

static void
pro_close_indication (PROFILE_INSTANCE *pi,
                      DIAGNOSTIC       *request,
                      char              origin,
                      char              scope) {
    if (pro_debug) {
        fprintf (stderr,
                 "%s close_indication: request=[%d] \"%s\" origin=%c scope=%c\n",
                 pro_name (pi -> channel -> profile_registration -> uri),
                 request -> code, request -> message ? request -> message
                                                     : "<NULL>",
                 origin, scope);
        fflush (stderr);
    }
               
    bpc_close_response (pi -> channel, NULL);
}

static void
pro_close_confirmation (PROFILE_INSTANCE *pi,
                        char              status,
                        DIAGNOSTIC       *error,
                        char              origin,
                        char              scope) {
    PRO_LOCALDATA *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (pro_debug) {
        if (error)
            fprintf (stderr,
                     "%s close_confirmation: status=%c error=[%d] \"%s\" origin=%c scope=%c\n",
                     pro_name (pi -> channel -> profile_registration -> uri),
                     status, error -> code, error -> message ? error -> message
                                                             : "<NULL>",
                     origin, scope);
        else 
            fprintf (stderr,
                     "%s close_confirmation: status=%c no error origin=%c scope=%c\n",
                     pro_name (pi -> channel -> profile_registration -> uri),
                     status, origin, scope);
        fflush (stderr);
    }

    if (status != PRO_ACTION_SUCCESS)
        return;

    if (il) {
        if (il -> pl_flags & PRO_INITIATOR) {
            il -> pl_flags |= PRO_CLOSED;
        } else {
            pi -> user_ptr1 = NULL;

            lib_free (il);
        }
    }
}

static void
pro_tuning_reset_indication (PROFILE_INSTANCE  *pi) {
    if (pro_debug) {
        fprintf (stderr, "%s tuning_reset_indication\n",
                 pro_name (pi -> channel -> profile_registration -> uri));
        fflush (stderr);
    }
}

static void
pro_tuning_reset_confirmation (PROFILE_INSTANCE        *pi,
                               char                     status) {
    if (pro_debug) {
        fprintf (stderr, "%s tuning_reset_confirmation: status=%c\n",
                 pro_name (pi -> channel -> profile_registration -> uri),
                 status);
        fflush (stderr);
    }

    pro_close_confirmation (pi, status, NULL, PRO_ACTION_LOCAL,
                             PRO_ACTION_SESSION);
}

static void
pro_message_available (PROFILE_INSTANCE *pi) {
    int                  result,
                         size;
    char                *buffer,
                        *payload,
                        *response = NULL;
    FRAME               *f;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    result = SASL_ALREADY_DONE;

    if (!(f = bpc_query_message (pi -> channel, BLU_QUERY_ANYTYPE,
                                 BLU_QUERY_ANYMSG, BLU_QUERY_ANYANS)))
        return;

    if (pro_debug) {
        fprintf (stderr,
                 "%s message_available: type=%c number=%ld answer=%ld size=%ld\n",
                 pro_name (pi -> channel -> profile_registration -> uri),
                 f -> msg_type, f -> message_number, f -> answer_number,
                 f -> size);
        fprintf (stderr, f -> size > 150 ? "%-150.150s...\n" : "%s\n",
                 f -> payload);
        fflush (stderr);
    }

    size = bpc_frame_aggregate (pi -> channel, f, &buffer);
    if (buffer == NULL) {
        bp_log (pi -> channel -> conn, LOG_PROF, 5,
                "%s message_available: out of memory aggregating message",
                 pro_name (pi -> channel -> profile_registration -> uri));
        bpc_frame_destroy (pi -> channel, f);
        return;
    }

    switch (il -> pl_task) {
        case PRO_CLIENT_ANON:
            break;

        case PRO_CLIENT_OTP:
            if (il -> pl_sent != 2)
                break;

            if (strstr (buffer, "<error") != NULL)
                sasl_error (pi, buffer, size);
            else {
                parse_blob (buffer, &il -> pl_sdb);
                if (!strcmp (il -> pl_sdb.parse_blob_status, "complete"))
                    sasl_set_local_success (pi);
                else
                    sasl_set_local_failure (pi, NULL);
            }
            break;

        case PRO_SERVER_ANON:
            if (il -> pl_sent != 0)
                break;

            if ((result = sasl_anonymous_server_guts (buffer, &il -> pl_sdb))
                    == SASL_COMPLETE)
                config_set (appconfig, SASL_REMOTE_USERNAME,
                            il -> pl_sdb.parse_blob_data);
            break;

        case PRO_SERVER_OTP:
            switch (il -> pl_sent) {
                case 0:
                    result = sasl_otp_server_guts_1 (buffer, &il -> pl_sdb);
                    break;

                case 1:
                    if ((result = sasl_otp_server_guts_2 (buffer, &il -> pl_sdb))
                            == SASL_COMPLETE) {
                        config_set (appconfig, SASL_REMOTE_USERNAME,
                                    il -> pl_sdb.authenID);
                            config_set (appconfig, SASL_REMOTE_TARGET,
                                        (il -> pl_sdb.authorID[0] != '\0')
                                            ? il -> pl_sdb.authorID
                                            : il -> pl_sdb.authenID);
                    }
                    break;
            }
            break;
    }

    bpc_buffer_destroy (pi -> channel, buffer);

    switch (il -> pl_task) {
        case PRO_CLIENT_ANON:
        case PRO_CLIENT_OTP:
            response = NULL;
            break;

        case PRO_SERVER_ANON:
        case PRO_SERVER_OTP:
            response = sasl_response (pi, result);
            break;
    }

    if (f -> msg_type == BLU_FRAME_TYPE_MSG) {
        payload = response ? response
                           : "<error code='521'>Client-side only</error>";
        size = strlen (payload) + sizeof DEFAULT_CONTENT_TYPE - 1;

        if ((buffer = bpc_buffer_allocate(pi -> channel, size)) != NULL) {
            sprintf (buffer, "%s%s", DEFAULT_CONTENT_TYPE, payload);

            bpc_send (pi -> channel,
                      (payload[1] == 'e')
                          ? BLU_FRAME_TYPE_ERR : BLU_FRAME_TYPE_RPY,
                      f -> message_number, f -> answer_number,
                      BLU_FRAME_COMPLETE, buffer, size);
            il -> pl_sent++;
        } else
            bp_log (pi -> channel -> conn, LOG_PROF, 5,
                    "%s message: out of memory sending response",
                    pro_name (pi -> channel -> profile_registration -> uri));
    }

    bpc_frame_destroy (pi -> channel, f);
}

static void
pro_window_full (PROFILE_INSTANCE *pi) {
    DIAGNOSTIC    *d;

    if (pro_debug) {
        fprintf (stderr, "%s window_full\n",
                 pro_name (pi -> channel -> profile_registration -> uri));
        fflush (stderr);
    }

    bp_log (pi -> channel -> conn, LOG_PROF, 5, "%s channel window full!?!",
            pro_name (pi -> channel -> profile_registration -> uri));
    if (!(d = bpc_close_request (pi -> channel, BLU_CHAN0_MSGNO_DEFAULT, 550,
                                 NULL, "stop misbehaving", NULL, NULL))) {
        bp_log (pi -> channel -> conn, LOG_PROF, 4,
                "unable to close SASL channel: [%d] %s", d -> code,
                d -> message);

        bp_diagnostic_destroy (pi -> channel -> conn, d);
    }    
}


/*    helper functions */

static void
sasl_error (PROFILE_INSTANCE       *pi,
            char                   *data,
            int                     size) {
    char            *cp;
    DIAGNOSTIC       ds,
                    *d = &ds;
    FRAME           *f;
        
    if ((cp = strstr (data, "<error")) != NULL)
        size -= cp - data, data = cp;

    memset (d, 0, sizeof *d);
    if (!(f = bpc_frame_allocate (pi -> channel, size))) {
        d -> code = 421;
        d -> message = "out of memory decoding SASL error";
    } else {
        f -> msg_type = BLU_FRAME_TYPE_ERR;
        strcpy (f -> payload, data);

        if (!(d = bpc_parse_error (pi -> channel, f))) {
            d -> code = 500;
            d -> message = "unable to parse error message from server";
        }
    }
            
    sasl_set_local_failure (pi, d);

    if (d != &ds)
        bp_diagnostic_destroy (pi -> channel -> conn, d);
    if (f)
        bpc_frame_destroy (pi -> channel, f);
}

static char *
sasl_response (PROFILE_INSTANCE       *pi,
               int                     result) {
    char                *response;
    struct sasl_data_block
                        *sdb = &(((PRO_LOCALDATA *) pi -> user_ptr1) -> pl_sdb);

    switch (result) {
        case SASL_COMPLETE:
            response = "<blob status='complete' />";
            sasl_set_remote_success (pi);
            break;

        case SASL_CONTINUE:
            response = sdb -> payload;
            break;

        case SASL_ABORTED:
            response = "<blob status='complete' />";
            sasl_set_remote_failure (pi, 550, "Aborted by client");
            break;

        case SASL_ERROR_BLOB:
            response = "<error code='501'>Invalid blob format</error>";
            sasl_set_remote_failure (pi, 501, "Invalid blob format");
            break;

        case SASL_ERROR_TRACE:
            response = "<error code='551'>Invalid trace information</error>";
            sasl_set_remote_failure (pi, 551, "Invalid trace information");
            break;

        case SASL_ERROR_PROXY:
            response = "<error code='551'>Proxying not supported</error>";
            sasl_set_remote_failure (pi, 551, "Proxying not supported");
            break;

        case SASL_ERROR_USERNAME:
            response = "<error code='535'>Invalid user name</error>";
            sasl_set_remote_failure (pi, 535, "Invalid user name");
            break;

        case SASL_ERROR_USERBUSY:
            response = "<error code='450'>Database busy</error>";
            sasl_set_remote_failure (pi, 450, "Database busy");
            break;

        case SASL_ERROR_SEQUENCE:
            response = "<error code='534'>Sequence exhausted</error>";
            sasl_set_remote_failure (pi, 534, "Sequence exhausted");
            break;

        case SASL_ERROR_HEXONLY:
            response = "<error code='554'>Expecting hex: response</error>";
            sasl_set_remote_failure (pi, 554, "Expecting hex: response");
            break;

        case SASL_ERROR_BADHASH:
            response = "<error code='535'>Invalid user name</error>";
            sasl_set_remote_failure (pi, 535, "Bad response");
            break;

        default:
            response = "<error code='504'>Internal error</error>";
            sasl_set_remote_failure (pi, 504, "Internal error");
            break;
    }

    return response;
}

static void
sasl_set_local_success (PROFILE_INSTANCE       *pi) {
    int                  code;
    char                *reason;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);

    if (pro_debug) {
        fprintf (stderr, "sasl_set_local_success\n");
        fflush (stderr);
    }

    if (config_test_and_set (appconfig, SASL_LOCAL_CODE, "200") != CONFIG_OK) {
        code = atoi (config_get (appconfig, SASL_LOCAL_CODE));
        reason = config_get (appconfig, SASL_LOCAL_REASON);
    } else {
        code = 200;
        config_set (appconfig, SASL_LOCAL_REASON, reason = "login successful");
    }

    il -> pl_status = bp_diagnostic_new (pi -> channel -> conn, code, NULL,
                                         "%s", reason);
}

static void
sasl_set_local_failure (PROFILE_INSTANCE       *pi,
                        DIAGNOSTIC             *d) {
    int                  code;
    char                 buffer[BUFSIZ],
                        *reason;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);

    if (d) {
        code = d -> code;
        reason = d -> message ? d -> message : "";
    } else if (!il -> pl_sdb.parse_blob_status[0])
        code = 501, reason = "unable to parse blob from server";
    else if (!strcmp (il -> pl_sdb.parse_blob_status, "abort"))
        code = 450, reason = "server returned 'abort' status";
    else if (!strcmp (il -> pl_sdb.parse_blob_status, "continue"))
        code = 300, reason = "server returned 'continue' status";
    else {
        code = 555;
        sprintf (reason = buffer, "server returned '%s' status",
                 il -> pl_sdb.parse_blob_status);
    }

    if (pro_debug) {
        fprintf (stderr, "sasl_set_local_failure: code=%d reason=\"%s\"\n",
                 code, reason);
        fflush (stderr);
    }

    config_set (appconfig, SASL_LOCAL_REASON, reason);

    sprintf (buffer, "%d", code);
    config_set (appconfig, SASL_LOCAL_CODE, buffer);

    il -> pl_status = bp_diagnostic_new (pi -> channel -> conn, code, NULL,
                                         "%s", reason);
}

static void
sasl_set_remote_success (PROFILE_INSTANCE       *pi) {
    char    *cp;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);

    if (pro_debug) {
        fprintf (stderr, "sasl_set_remote_success\n");
        fflush (stderr);
    }

    if (config_test_and_set (appconfig, SASL_REMOTE_CODE, "200")
            == CONFIG_OK) {
        config_set (appconfig, SASL_REMOTE_REASON, "login successful");

        cp = config_get (appconfig, SASL_REMOTE_MECHANISM);
        bp_log (pi -> channel -> conn, LOG_PROF, 2, "sasl 200 %s %s", cp,
                config_get (appconfig, SASL_REMOTE_USERNAME));
    }
}

static void
sasl_set_remote_failure (PROFILE_INSTANCE       *pi,
                         int                     code,
                         char                   *reason) {
    char        buffer[BUFSIZ];
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);

    if (pro_debug) {
        fprintf (stderr, "sasl_set_remote_failure: code=%d reason=\"%s\"\n",
                 code, reason);
        fflush (stderr);
    }

    sprintf (buffer, "%d", code);
    config_set (appconfig, SASL_REMOTE_CODE, buffer);
    config_set (appconfig, SASL_REMOTE_REASON, reason);

    bp_log (pi -> channel -> conn, LOG_PROF, 3, "sasl %d %s", code, reason);
}


/*
 * Additional profile-related functions.
 */

DIAGNOSTIC *
sasl_login (BP_CONNECTION             *bp,
            char                      *serverName) {
    char                *cp,
                        *mechanism;
    DIAGNOSTIC          *d;
    PRO_LOCALDATA       *il;
    PROFILE              ps,
                        *p = &ps;
    struct configobj    *appconfig = bp_get_config (bp);

    if (((cp = config_get (appconfig, SASL_REMOTE_CODE)) != NULL)
            && (*cp == '2'))
        return bp_diagnostic_new (bp, 520, NULL,
                                  "already tuned for authentication");

    if (!(mechanism = config_get (appconfig,
                                  SASL_LOCAL_MECHANISM)))
        return bp_diagnostic_new (bp, 501, NULL, "mechanism undefined");

    if (!(il = (PRO_LOCALDATA *) lib_malloc (sizeof *il)))
        return bp_diagnostic_new (bp, 421, NULL, "out of memory");

    memset (il, 0, sizeof *il);
    il -> pl_flags = PRO_INITIATOR | PRO_STARTING | PRO_CLOSED;
    il -> pl_sent = 1;

    memset (p, 0, sizeof *p);

    if (!strcasecmp (mechanism, "anonymous")) {
        il -> pl_task = PRO_CLIENT_ANON;
        if (!(p -> uri = config_get (appconfig, SASL_ANONYMOUS_URI)))
            p -> uri = PRO_ANONYMOUS_URI;
        p -> piggyback =
              sasl_anonymous_client_guts (config_get (appconfig,
                                                      SASL_LOCAL_TRACEINFO),
                                          &il -> pl_sdb);
    } else if (!strcasecmp (mechanism, "otp")) {
        char    *authenID,
                *authorID;

        il -> pl_task = PRO_CLIENT_OTP;
        if (!(p -> uri = config_get (appconfig, SASL_OTP_URI)))
            p -> uri = PRO_OTP_URI;
        if (!(authenID = config_get (appconfig, SASL_LOCAL_USERNAME))) {
            lib_free (il);
            return bp_diagnostic_new (bp, 501, NULL,
                                      "authentication ID undefined");
        }
        authorID = config_get (appconfig, SASL_LOCAL_TARGET);

        p -> piggyback = sasl_otp_client_guts_1 (authenID, authorID,
                                                 &il -> pl_sdb);
    } else {
        lib_free (il);
        return bp_diagnostic_new (bp, 501, NULL, "mechanism unknown: %s",
                                  mechanism);
    }
    p -> piggyback_length = strlen (p -> piggyback);

    config_delete (appconfig, SASL_LOCAL_CODE);
    config_delete (appconfig, SASL_LOCAL_REASON);
    config_set (appconfig, SASL_LOCAL_MECHANISM, mechanism);

    if ((d = bp_start_request (bp, BLU_CHAN0_CHANO_DEFAULT,
                               BLU_CHAN0_MSGNO_DEFAULT, p, serverName, NULL,
                               (void *) il)) != NULL)
        goto out;

    while ((il -> pl_status == NULL)
               && ((il -> pl_flags & (PRO_STARTING|PRO_CLOSED)) != PRO_CLOSED)
               && (bp -> status != INST_STATUS_EXIT))
        YIELD ();

    if (!il -> pl_status) {
        d =  bp_diagnostic_new (bp, 450, NULL,
                                bp -> status != INST_STATUS_EXIT
                                    ? "lost channel during tuning"
                                    : "lost session during tuning");
        goto out;
    }

    if (il -> pl_status -> code >= 400)
        d = il -> pl_status;
    else
        bp_diagnostic_destroy (NULL, il -> pl_status);

out: ;
    if (!(il -> pl_flags & PRO_CLOSED))
        il -> pl_channel -> profile -> user_ptr1 = NULL;

    lib_free (il);

    return d;
}


/*
 * Well-known shared library entry point.
 */

PROFILE_REGISTRATION *sasl_profiles_Init (struct configobj *appconfig) {
    char                *cp;
    PROFILE_REGISTRATION anons,
                        *anon = &anons,
                         otps,
                        *otp = &otps;

    pro_debug = 0;
    if ((cp = config_get (appconfig, SASL_FAMILY_DEBUG)) != NULL)
        pro_debug = atoi (cp);

    memset (anon, 0, sizeof *anon);

    if (!(anon -> uri = config_get (appconfig, SASL_ANONYMOUS_URI)))
        anon -> uri = PRO_ANONYMOUS_URI;

    if (!(anon -> initiator_modes = config_get (appconfig,
                                                SASL_ANONYMOUS_IMODES)))
        anon -> initiator_modes = "plaintext,encrypted";
    if (!(anon -> listener_modes = config_get (appconfig,
                                                SASL_ANONYMOUS_LMODES)))
        anon -> listener_modes = "plaintext,encrypted";

    anon -> full_messages = 1;
    anon -> thread_per_channel = 0;

    anon -> proreg_connection_init = pro_connection_init;
    anon -> proreg_connection_fin  = pro_connection_fin;
    anon -> proreg_session_init = pro_session_init;
    anon -> proreg_session_fin  = pro_session_fin;
    anon -> proreg_start_indication = pro_start_indication;
    anon -> proreg_start_confirmation = pro_start_confirmation;
    anon -> proreg_close_indication   = pro_close_indication;
    anon -> proreg_close_confirmation = pro_close_confirmation;
    anon -> proreg_tuning_reset_indication   = pro_tuning_reset_indication;
    anon -> proreg_tuning_reset_confirmation = pro_tuning_reset_confirmation;
    anon -> proreg_message_available = pro_message_available;
    anon -> proreg_window_full       = pro_window_full;

    memcpy (otp, anon, sizeof *otp);

    if (!(otp -> uri = config_get (appconfig, SASL_OTP_URI)))
        otp -> uri = PRO_OTP_URI;

    if (!(otp -> initiator_modes = config_get (appconfig, SASL_OTP_IMODES)))
        otp -> initiator_modes = "plaintext,encrypted";
    if (!(otp -> listener_modes = config_get (appconfig, SASL_OTP_LMODES)))
        otp -> listener_modes = "plaintext,encrypted";

    if ((anon = bp_profile_registration_clone (NULL, anon)) != NULL)
        anon -> next = bp_profile_registration_clone (NULL, otp);

    return anon;
}

--- NEW FILE: sasl-profiles.h ---
/*
 * Copyright (c) 2001 Invisible Worlds, Inc.  All rights reserved.
 *
 * The contents of this file are subject to the Blocks Public License (the
 * "License"); You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at http://www.beepcore.org/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 */

/*
 * $Id: sasl-profiles.h,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */

/*

initiator usage:

   1. pr = sasl_profiles_Init (appconfig)

   2. establish session using pr

   3. d = sasl_login (...)

      if d is non-NULL, you lose


listener usage:

   1. pr = sasl_profiles_Init (appconfig)

   2. establish session using pr

 */

#ifndef SASL_PROFILES_H
#define SASL_PROFILES_H 1

/* includes */

#include <beepcore-c/bp_wrapper.h>
#include <beepcore-c/bp_config.h>
#include <beepcore-c/tuning.h>


#if defined(__cplusplus)
extern "C"
{
#endif

/**
 * @name The SASL ANONYMOUS and OTP profiles
 **/

/*@{*/

/* keys for configuration package */

/**
 * URI to use for the SASL ANONYMOUS profile,
 * defaults to <b>"http://iana.org/beep/SASL/ANONYMOUS"</b>.
 **/
#define SASL_ANONYMOUS_URI      "beep profiles sasl_anon uri"

/**
 * Initiator modes for the SASL ANONYMOUS profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define SASL_ANONYMOUS_IMODES   "beep profiles sasl_anon initiator_modes"

/**
 * Listener modes for the SASL ANONYMOUS profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define SASL_ANONYMOUS_LMODES   "beep profiles sasl_anon listener_modes"

/**
 * URI to use for the SASL OTP profile,
 * defaults to <b>"http://iana.org/beep/SASL/OTP"</b>.
 **/
#define SASL_OTP_URI            "beep profiles sasl_otp uri"

/**
 * Initiator modes for the SASL OTP profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define SASL_OTP_IMODES         "beep profiles sasl_otp initiator_modes"

/**
 * Listener modes for the SASL OTP profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define SASL_OTP_LMODES         "beep profiles sasl_otp listener_modes"

/**
 * The directory where the OTP database is kept,
 * defaults to <b>"/tmp"</b>.
 **/
#define SASL_OTP_DIRECTORY      "beep profiles sasl_otp directory"

/**
 * Enables debug mode for the SASL family of profiles,
 * defaults to <b>"0"</b>.
 **/
#define SASL_FAMILY_DEBUG       "beep profiles sasl debug"


/**
 * Trace information for SASL ANONYMOUS.
 **/
#define SASL_LOCAL_TRACEINFO    "beep identity local traceinfo"


/* module entry points */

/**
 * Well-known entry point for the SASL ANONYMOUS and OTP profiles.
 *
 * @param appconfig A pointer to the {@link configobj configuration}
 *                  structure used for configuration purposes.
 * <p>
 * Keys:
 * <blockquote><dl>
 * <dt>SASL_ANONYMOUS_URI/SASL_OTP_URI</dt>
 * <dd>The registration URI for the profile (optional).</dd>
 *
 * <dt>SASL_ANONYMOUS_IMODES/SASL_OTP_IMODES</dt>
 * <dd>The <i>initiator_modes</i> to use when registering the profile
 * (optional).</dd>
 *
 * <dt>SASL_ANONYMOUS_LMODES/SASL_OTP_LMODES</dt>
 * <dd>The <i>listener_modes</i> to use when registering the profile
 * (optional).</dd>
 *
 * <dt>SASL_OTP_DIRECTORY</dt>
 * <dd>The directory where OTP information is kept,
 * defaults to <b>"/tmp"</b>.</dd>
 *
 * <dt>SASL_FAMILY_DEBUG</dt>
 * <dd>A non-zero value to enable debugging (optional).</dd>
 * </dl></blockquote>
 *
 * @return A pointer to a linked-list of profile-registration structures.
 **/
extern struct PROFILE_REGISTRATION* sasl_profiles_Init(struct configobj* appconfig);

/**
 * Tune using SASL, and update the connection's
 * {@link configobj configuration} structure accordingly.
 *
 * @param bp A pointer to the connection structure.
 * <p>
 * Input Keys:
 * <blockquote><dl>
 * <dt>SASL_LOCAL_MECHANISM</dt>
 * <dd>One of: <b>"anonymous"</b>, <b>"otp"</b>.</dd>
 *
 * <dt>SASL_LOCAL_TRACEINFO</t>
 * <dd>Trace information for SASL ANONYMOUS.</dd>
 *
 * <dt>SASL_LOCAL_USERNAME</dt>
 * <dd>The SASL authentication-identity.</dd>
 *
 * <dt>SASL_LOCAL_TARGET</dt>
 * <dd>The SASL authorization-identity.</dd>
 *
 * <dt>SASL_LOCAL_PASSPHRASE</dt>
 * <dd>The passphrase for the authentication-identity.</dd>
 * </dl></blockquote>
 * <p>
 * Output Keys:
 * <blockquote><dl>
 * <dt>SASL_LOCAL_MECHANISM</dt>
 * <dd>The SASL mechanism used for authentication.</dd>
 *
 * <dt>SASL_LOCAL_CODE</dt>
 * <dd>The resulting status code.
 * Consult \URL[Section 8 of RFC 3080]{http://www.beepcore.org/beepcore/docs/rfc3080.jsp#reply-codes}
 * for an (incomplete) list.</dd>
 *
 * <dt>SASL_LOCAL_REASON</dt>
 * <dd>A textual message corresponding to <b>SASL_LOCAL_CODE</b></dd>
 * </dl></blockquote>
 *
 * @param serverName The value to use for the <i>serverName</i> attribute
 *                    of the &lt;start&gt; element, or <b>NULL</b>.
 *
 * @return On failure, a pointer to a {@link diagnostic diagnostic} structure
 *         explaining the reason
 *         (which should subsequently be destroyed by calling
 *         {@link bp_diagnostic_destroy bp_diagnostic_destroy}).
 **/
extern struct diagnostic* sasl_login(BP_CONNECTION* bp,
                                     char* serverName);

/*@}*/

#if defined(__cplusplus)
}
#endif
#endif

--- NEW FILE: syslog-raw.c ---
/* profile for SYSLOG reliable RAW */

/*
 * $Id: syslog-raw.c,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */

#define BEEPD


/*    includes */
#include <string.h>
#include "syslog-raw.h"
#include <beepcore-c/logutil.h>


/*    defines and typedefs */

#define PRO_RAW_URI             "http://iana.org/beep/SYSLOG/RAW"

#define DEFAULT_CONTENT_TYPE    "\r\n"


typedef struct pro_localdata {
    int          pl_flags;              /* mode flags                   */
#define PRO_INITIATOR   (1<<0)          /* either initiator             */
#define PRO_LISTENER    (1<<1)          /*   or listener                */
#define PRO_ONCEONLY    (1<<2)          /* report only first problem    */
#define PRO_READY       (1<<3)          /* ready for sr_log/sr_fin      */
#define PRO_ABORTED     (1<<4)          /* the session is aborting      */

    long         pl_msgNo;              /* when answering a MSG         */
    long         pl_ansNo;              /*   ..                         */

    PROFILE_INSTANCE
                *pl_pi;                 /* hook to wrappers             */

    char        *pl_serverName;         /* what you'd think             */

    sr_callback  pl_callback;           /* application callback         */
    void        *pl_clientData;         /*   ..                         */
}  PRO_LOCALDATA;


/*    statics */

static int      pro_debug;

static void     pro_upcall (PRO_LOCALDATA       *il,
                            int                  code,
                            char                *message);

static DIAGNOSTIC *
                pro_send_start (BP_CONNECTION   *bp,
                                PRO_LOCALDATA   *il);

static void     pro_send_error (PROFILE_INSTANCE *pi,
                                long            msgNo,
                                int             code,
                                char            *language,
                                char            *diagnostic);

static void     pro_syslog (PRO_LOCALDATA       *il,
                            char                *buffer,
                            int                  size);

/*    profile methods */

/*
   first four methods are invoked when channels are not-yet/no-longer
   instantiated

   success: return NULL
   failure: return char *reason
 */



/* invoked when wrapper tries to register the profile

   do process-wide initialization

   on failure, the wrapper doesn't register the profile
 */

static char *
pro_connection_init (PROFILE_REGISTRATION  *pr,
                     BP_CONNECTION         *bp) {
    if (pro_debug) {
        fprintf (stderr, "SR connection_init\n");
        fflush (stderr);
    }

    return NULL;
}


/* invoked if profile is about to be made available for the session

   do session-wide initialization

   on failure, the wrapper makes no further calls to the profile for
               the duration of the session
 */

static char *
pro_session_init (PROFILE_REGISTRATION  *pr,
                  BP_CONNECTION         *bp) {
    if (pro_debug) {
        fprintf (stderr, "SR session_init: role='%c' mode=\"%s\"\n",
                 bp -> role, bp -> mode);
        fflush (stderr);
    }

    return NULL;
}


/* invoked just before the session is released

   do session-wide finalization

   return value is ignored by wrapper
 */

static char *
pro_session_fin (PROFILE_REGISTRATION   *pr,
                 BP_CONNECTION          *bp) {
    if (pro_debug) {
        fprintf (stderr, "SR session_fin\n");
        fflush (stderr);
    }

    return NULL;
}


/* invoked just before wrapper is destroyed

   do process-wide finalization

   return value is ignored by wrapper
 */

static char *
pro_connection_fin (PROFILE_REGISTRATION   *pr,
                    BP_CONNECTION          *bp) {
    if (pro_debug) {
        fprintf (stderr, "SR connection_fin\n");
        fflush (stderr);
    }

    return NULL;
}

/* next four methods are invoked when a session/channel starts */

/* invoked when the peer sends a greeting

   listener: do nothing

   initiator: clone PRO_LOCALDATA and start a channel,
              or tell the application why not
 */

static void
pro_greeting_notification (PROFILE_REGISTRATION *pr,
                           BP_CONNECTION        *bp,
                           char                  status) {
    DIAGNOSTIC          *d;
    PRO_LOCALDATA       *pl = pr -> user_ptr,
                        *il;

    if (pro_debug) {
        fprintf (stderr, "SR greeting_notification status=%c\n",
                 status);
        fflush (stderr);
    }

    if (pl -> pl_flags & PRO_LISTENER)
        return;

    switch (status) {
        case PROFILE_PRESENT:
            break;

        case PROFILE_ABSENT:
            pro_upcall (pl, 500, "greeting: profile not present");
            return;

        case GREETING_ERROR:
            pro_upcall (pl, 500, "greeting: got error");
            return;
    }

    if (!(il = (PRO_LOCALDATA *) lib_malloc (sizeof *il))) {
        pro_upcall (pl, 500, "SR greeting: out of memory");
        return;
    }
    memcpy (il, pl, sizeof *il);

    if ((d = pro_send_start (bp, il)) != NULL) {
        pro_upcall (pl, d -> code, d -> message);
        bp_diagnostic_destroy (bp, d);
        lib_free (il);
    }
}


/* invoked when the peer asks to start a channel

   listener: ignore any piggyback'd data
             clone PRO_LOCALDATA and accept it
             or tell the application why not

   initiator: decline it
 */

static void
pro_start_indication (PROFILE_INSTANCE  *pi,
                      PROFILE           *po) {
    long                wsize;
    char                *buffer,
                        *cp;
    DIAGNOSTIC           ds,
                        *d = &ds;
    PRO_LOCALDATA       *pl = pi -> channel -> profile_registration
                                 -> user_ptr,
                        *il;
    struct configobj *appconfig = bp_get_config (pi -> channel -> conn);

    if (pro_debug) {
        fprintf (stderr, "SR start_indication: piggyback=\"%s\"\n",
                 po -> piggyback ? po -> piggyback : "<NULL>");
        fflush (stderr);
    }

    memset (d, 0, sizeof *d);

    if (pl -> pl_flags & PRO_INITIATOR) {
        memset (d, 0, sizeof *d);
        d -> code = 521;
        d -> message = "not available";
        bpc_start_response (pi -> channel, po, d);
        return;
    }

    if (!(il = (PRO_LOCALDATA *) lib_malloc (sizeof *il))) {
        pro_upcall (pl, d -> code = 421, d -> message = "out of memory");
        bpc_start_response (pi -> channel, po, d);
        return;
    }
    memcpy (il, pl, sizeof *il);
    il -> pl_pi = pi, pi -> user_ptr1 = il;

    bpc_start_response (pi -> channel, po, NULL);

    if ((cp = config_get (appconfig, SYSLOG_RAW_WINDOWSIZE))
            && (wsize = (long) atol (cp)) > 4096)
      (void) bpc_set_channel_window (pi -> channel, wsize);

    if (!(buffer = bpc_buffer_allocate (pi -> channel,
                                        sizeof DEFAULT_CONTENT_TYPE - 1))) {
        pro_upcall (il, 500, "SR start: out of memory");
        return;
    }

    strcpy (buffer, DEFAULT_CONTENT_TYPE);
    bpc_send (pi -> channel, BLU_FRAME_TYPE_MSG, BLU_FRAME_MSGNO_UNUSED,
              BLU_FRAME_IGNORE_ANSNO, BLU_FRAME_COMPLETE, buffer,
              strlen (buffer));

    (*il -> pl_callback) ((void *) il, 350, NULL, il -> pl_clientData);
}


/* invoked when a our request to start a channel has resulted in the channel starting

   remember PRO_LOCALDATA passed as clientData
 */

static void
pro_start_confirmation (void                    *clientData,
                        PROFILE_INSTANCE        *pi,
                        PROFILE                 *po) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) clientData;

    if (pro_debug) {
        fprintf (stderr, "SR start_confirmation: piggyback=\"%s\"\n",
                 po -> piggyback ? po -> piggyback : "<NULL>");
        fflush (stderr);
    }

    il -> pl_pi = pi, pi -> user_ptr1 = il;
}

/* initiator only: invoked when a response is received to our request to
                   start a channel, either from pro_greeting_notification
                   (the first time we start the channel) or
                   pro_close_confirmation (when we start the channel again
                   after sr_sync)

   failure: tell the application
*/

static void
pro_start_callback (void                    *clientData,
                    CHANNEL_INSTANCE        *ci,
                    DIAGNOSTIC              *error) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) clientData;

    if (error) {
        pro_upcall (il, error -> code, error -> message);
        lib_free (il);
    }
}


/* next three methods are invoked when a session/channel closes */


/*
   invoked when we or the peer asks to close a session/channel
   (or perhaps due to transport problem, etc.)

   always accept it
 */

static void
pro_close_indication (PROFILE_INSTANCE  *pi,
                      DIAGNOSTIC        *request,
                      char               origin,
                      char               scope) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (pro_debug) {
        fprintf (stderr,
                 "SR close_indication: request=[%d] \"%s\" origin=%c scope=%c\n",
                 request -> code, request -> message ? request -> message
                                                     : "<NULL>",
                 origin, scope);
        fflush (stderr);
    }
               
    switch (scope) {
        case PRO_ACTION_SESSION:
        case PRO_ACTION_CHANNEL:
            break;

        case PRO_ACTION_ABORT:
        default:
            il -> pl_flags |= PRO_ABORTED;
            break;
    }

    il -> pl_flags &= ~PRO_READY;
    bpc_close_response (pi -> channel, NULL);
}


/* invoked when we know whether the channel has finally closed or not
   (regardless of who requested it)

   if nothing happened, do nothing

   initiator: if flushing a channel, start another one

   otherwise, tell the application we're done
 */

static void
pro_close_confirmation (PROFILE_INSTANCE        *pi,
                        char                     status,
                        DIAGNOSTIC              *error,
                        char                     origin,
                        char                     scope) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (pro_debug) {
        if (error)
            fprintf (stderr,
                     "SR close_confirmation: status=%c error=[%d] \"%s\" origin=%c scope=%c\n",
                     status, error -> code, error -> message ? error -> message
                                                             : "<NULL>",
                     origin, scope);
        else 
            fprintf (stderr,
                     "SR close_confirmation: status=%c no error origin=%c scope=%c\n",
                     status, origin, scope);
        fflush (stderr);
    }

    if (status != PRO_ACTION_SUCCESS) {
        if (il -> pl_flags & PRO_INITIATOR)
            il -> pl_flags |= PRO_READY;
        return;
    }

    pi -> user_ptr1 = NULL;

    if (il -> pl_flags & PRO_ABORTED)
        pro_upcall (il, 421, "session aborted");
    else
        pro_upcall (il, 220, NULL);

    lib_free (il);
}


/* listener: invoked when a response is received to pro_message_available's
   request to close the channel

   success: do nothing

   failure: tell the application
*/

static void
pro_close_callback (void                *clientData,
                    CHANNEL_INSTANCE    *ci,
                    DIAGNOSTIC          *error) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) clientData;

    if (error)
        pro_upcall (il, error -> code, error -> message);
}


/* next two methods are invoked on tuning resets */


/* invoked when the local peer is about to do a tuning reset */

static void
pro_tuning_reset_indication (PROFILE_INSTANCE  *pi) {
    if (pro_debug) {
        fprintf (stderr, "SR tuning_reset_indication\n");
        fflush (stderr);
    }
}


/* invoked when we know whether the tuning reset occurred */

static void
pro_tuning_reset_confirmation (PROFILE_INSTANCE        *pi,
                               char                     status) {
    if (pro_debug) {
        fprintf (stderr, "SR tuning_reset_confirmation: status=%c\n", status);
        fflush (stderr);
    }

    pro_close_confirmation (pi, status, NULL, PRO_ACTION_LOCAL,
                            PRO_ACTION_SESSION);
}


/* next two methods are invoked on read events */


/* invoked when a message is available to be read

   message              actions taken by
   received     listener                initiator
   --------     --------                ---------
   MSG          send ERR                tell the application we're ready
                                        for sr_log

   ANS          parse and pass up       n/a

   NUL          close channel           n/a

   RPY/ERR      abort channel           n/a

 */

static void
pro_message_available (PROFILE_INSTANCE *pi) {
    int                  code = 200,
                         size;
    char                *buffer;
    DIAGNOSTIC          *d;
    FRAME               *f;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;
    BP_CONNECTION       *bp = pi -> channel -> conn;

    if (!(f = bpc_query_message (pi -> channel, BLU_QUERY_ANYTYPE,
                                 BLU_QUERY_ANYMSG, BLU_QUERY_ANYANS)))
      return;

    if (pro_debug) {
        fprintf (stderr,
                 "SR message_available: type=%c number=%ld answer=%ld more=%c size=%ld\n",
                 f -> msg_type, f -> message_number, f -> answer_number,
                 f -> more, f -> size);
        fprintf (stderr, f -> size > 75 ? "%-75.75s...\n" : "%s\n",
                 f -> payload);
        fflush (stderr);
    }

    switch (f -> msg_type) {
        case BLU_FRAME_TYPE_MSG:
            if (il -> pl_flags & PRO_INITIATOR) {
                il -> pl_msgNo = f -> message_number;
                il -> pl_ansNo = -1;

                il -> pl_flags |= PRO_READY;
                (*il -> pl_callback) ((void *) il, 350, NULL, il -> pl_clientData);
            } else
                pro_send_error (pi, f -> message_number, 500, NULL,
                                "not expecting MSG");
            break;

        case BLU_FRAME_TYPE_ANS:
            size = bpc_frame_aggregate (pi -> channel, f, &buffer);
            if (buffer) {
                pro_syslog (il, buffer, size);
                bpc_buffer_destroy (pi -> channel, buffer);
            } else
                bp_log (bp, LOG_PROF, 5,
                        "SR message: out of memory reading ANS");
            break;

        case BLU_FRAME_TYPE_RPY:
        case BLU_FRAME_TYPE_ERR:
            code = 500;
            il -> pl_flags |= PRO_ABORTED;
            /* and fall... */
        case BLU_FRAME_TYPE_NUL:
            if ((d = bpc_close_request (pi -> channel, BLU_CHAN0_MSGNO_DEFAULT,
                                        code, NULL,
                                        code == 500 ? "protocol error" : NULL,
                                        pro_close_callback,
                                        (void *) il)) != NULL) {
                pro_upcall (il, d -> code, d -> message);
                bp_diagnostic_destroy (bp, d);
            }
            break;

        default:
            break;
    }

    bpc_message_destroy (pi -> channel, f);
}


/* invoked when input queue is full, and the message is incomplete

   could avoid this if we used the frame-based interface

   however that would mean we'd have to be able to re-assemble multiple
   ANS messages and add a lot of state to pro_syslog...
 */

static void
pro_window_full (PROFILE_INSTANCE       *pi) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (pro_debug) {
        fprintf (stderr, "SR window_full\n");
        fflush (stderr);
    }

    pro_upcall (il, 421, "session blocked, increase window size");
}


/*    helper methods */


/* invoke the application's callback, at most once */

static void
pro_upcall (PRO_LOCALDATA       *il,
            int                  code,
            char                *message) {
    if (!(il -> pl_flags & PRO_ONCEONLY)) {
        il -> pl_flags |= PRO_ONCEONLY;

        (*il -> pl_callback) (NULL, code, message, il -> pl_clientData);
    }
}


/* initiator: start a channel */

static DIAGNOSTIC *
pro_send_start (BP_CONNECTION   *bp,
                PRO_LOCALDATA   *il) {
    PROFILE              ps,
                        *p = &ps;
    struct configobj *appconfig = bp_get_config (bp);

    memset (p, 0, sizeof *p);
    if (!(p -> uri = config_get (appconfig, SYSLOG_RAW_URI)))
        p -> uri = PRO_RAW_URI;
    p -> piggyback_length = strlen (p -> piggyback = "");

    return bp_start_request (bp, BLU_CHAN0_CHANO_DEFAULT,
                             BLU_CHAN0_MSGNO_DEFAULT, p, il -> pl_serverName,
                             pro_start_callback, (void *) il);
}


/* listener: send an ERR if we receive a MSG */

static void
pro_send_error (PROFILE_INSTANCE        *pi,
                long                     msgNo,
                int                      code,
                char                    *language,
                char                    *diagnostic) {
    char *buffer = bpc_error_allocate (pi -> channel, code, language,
                                       diagnostic);

    if (buffer)
        bpc_send (pi -> channel, BLU_FRAME_TYPE_ERR, msgNo,
                  BLU_FRAME_IGNORE_ANSNO, BLU_FRAME_COMPLETE, buffer,
                  strlen (buffer));
    else
        bp_log (pi -> channel -> conn, LOG_PROF, 5,
                "SR message: out of memory sending ERR");
}


/* listener: parse syslog entries and pass to application */

static void
pro_syslog (PRO_LOCALDATA       *il,
            char                *buffer,
            int                  size) {
    int          twiceP;
    char        *cp,
                *dp;

    /* skip past MIME headers to find beginning of data, two possibilities:

       Content-Type: ...\r\n[...\r\n]\r\n

       \r\n

    */

    cp = buffer;
    twiceP = 1;
    while (size-- > 0) {
        char    c = *cp++;

        switch (c) {
            case '\r':
                if ((size > 0) && (*cp == '\n'))
                    cp++, size--;
                twiceP++;
                break;

            case '\n':
                twiceP++;
                break;

            default:
              twiceP = 0;
                break;
        }
        if (twiceP > 1)
            break;
    }
            
    dp = cp;
    while (size > 0) {
        char    c = *cp;

        switch (c) {
            case '\r':
            case '\n':
                *cp = '\0';
                if (cp > dp)
                    (*il -> pl_callback) ((void *) il, cp - dp, dp,
                                          il -> pl_clientData);
                cp++, size--;
                if ((size > 0) && (*cp == '\n'))
                    cp++, size--;
                dp = cp;
                break;

            default:
                cp++, size--;
                break;
        }
    }
    if (cp > dp)
        (*il -> pl_callback) ((void *) il, cp - dp, dp, il -> pl_clientData);
}


/*
 * Well-known shared library entry point.
 */

PROFILE_REGISTRATION *
sr_Init (struct configobj  *appconfig) {
    char                 *cp;
    PRO_LOCALDATA        *pl;
    PROFILE_REGISTRATION *pr;

    pro_debug = 0;
    if ((cp = config_get (appconfig, SYSLOG_RAW_DEBUG)) != NULL)
        pro_debug = atoi (cp);

    if (!(pr = (PROFILE_REGISTRATION *) lib_malloc (sizeof *pr +
                                                    sizeof *pl))) {
        log_line (LOG_PROF, 6, "unable to allocate PR");
        return NULL;
    }
    memset (pr, 0, sizeof *pr);

    if (!(pr -> uri = config_get (appconfig, SYSLOG_RAW_URI)))
        pr -> uri = PRO_RAW_URI;

    if (!(pr -> initiator_modes = config_get (appconfig, SYSLOG_RAW_IMODES)))
        pr -> initiator_modes = "plaintext,encrypted";
    if (!(pr -> listener_modes = config_get (appconfig, SYSLOG_RAW_LMODES)))
        pr -> listener_modes = "plaintext,encrypted";

    pr -> full_messages = 1;
    pr -> thread_per_channel = 0;

    pr -> proreg_connection_init = pro_connection_init;
    pr -> proreg_connection_fin  = pro_connection_fin;
    pr -> proreg_session_init = pro_session_init;
    pr -> proreg_session_fin  = pro_session_fin;
    pr -> proreg_greeting_notification = pro_greeting_notification;
    pr -> proreg_start_indication   = pro_start_indication;
    pr -> proreg_start_confirmation = pro_start_confirmation;
    pr -> proreg_close_indication   = pro_close_indication;
    pr -> proreg_close_confirmation = pro_close_confirmation;
    pr -> proreg_tuning_reset_indication   = pro_tuning_reset_indication;
    pr -> proreg_tuning_reset_confirmation = pro_tuning_reset_confirmation;
    pr -> proreg_message_available = pro_message_available;
    pr -> proreg_window_full       = pro_window_full;

    pr -> user_ptr = pl = (PRO_LOCALDATA *) (((char *) pr) + sizeof *pr);
    memset (pl, 0, sizeof *pl);

    return pr;
}
 

/* invoked by a listening application 

               pr - non-NULL result from sr_Init
         callback - pointer to callback procedure
       clientData - opaque pointer
 */

void
sr_listener (PROFILE_REGISTRATION       *pr,
             sr_callback                 callback,
             void                       *clientData) {
    PRO_LOCALDATA       *pl = pr -> user_ptr;

    pl -> pl_flags = PRO_LISTENER;
    pl -> pl_callback = callback;
    pl -> pl_clientData = clientData;
}


/* invoked by an initiating application 

               pr - non-NULL result from sr_Init
         callback - pointer to callback procedure
       clientData - opaque pointer
 */

void
sr_initiator (PROFILE_REGISTRATION      *pr,
              char                      *serverName,
              sr_callback                callback,
              void                      *clientData) {
    PRO_LOCALDATA       *pl = pr -> user_ptr;

    pl -> pl_flags = PRO_INITIATOR;
    pl -> pl_serverName = serverName;
    pl -> pl_callback = callback;
    pl -> pl_clientData = clientData;
}


/* initiator: send syslog entry */

int
sr_log (void                    *v,
        char                    *entry) {
    int                  i;
    char                *buffer,
                        *ep;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) v;
    PROFILE_INSTANCE    *pi;

    if (!(pi = il -> pl_pi)) {
        lib_free (il);
        return SR_DONE;
    }
    if (!(il -> pl_flags & PRO_READY))
        return SR_BUSY;

    if ((i = strlen (ep = entry)) > 0) {
        ep += i - 1;
        if (*ep == '\n')
           ep--, i--;
        if ((i > 0) && (*ep == '\r'))
           i--;
    }
    if (i == 0)
        return SR_OK;
    
    if (!(buffer = bpc_buffer_allocate (pi -> channel,
                                         i
                                       + (sizeof DEFAULT_CONTENT_TYPE - 1))))
        return SR_ERROR;
    sprintf (buffer, "%s%*.*s", DEFAULT_CONTENT_TYPE, i, i, entry);

    if (++il -> pl_ansNo > 2147483647)
        il -> pl_ansNo = 0;
    bpc_send (pi -> channel, BLU_FRAME_TYPE_ANS, il -> pl_msgNo,
              il -> pl_ansNo, BLU_FRAME_COMPLETE, buffer, strlen (buffer));

    return SR_OK;
}


/* initiator: drain entries, optionally closing channel */

int
sr_fin (void                  *v) {
    char                *buffer;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) v;
    PROFILE_INSTANCE    *pi;

    if (!(pi = il -> pl_pi)) {
        lib_free (il);
        return SR_DONE;
    }
    if (!(il -> pl_flags & PRO_READY))
        return SR_BUSY;

    if (!(buffer = bpc_buffer_allocate (pi -> channel, 0)))
        return SR_ERROR;

    il -> pl_flags &= ~PRO_READY;
    bpc_send (pi -> channel, BLU_FRAME_TYPE_NUL, il -> pl_msgNo,
              BLU_FRAME_IGNORE_ANSNO, BLU_FRAME_COMPLETE, buffer, 0);

    return SR_OK;
}

#ifdef  BEEPD
static void
callback_listener (void *v,
                   int   code,
                   char *diagnostic,
                   void *clientData) {
    fprintf (stderr,
             "SR callback_listener v=0x%x code=%d\n   diagnostic=\"%s\"\n",
             (unsigned int) v, code, diagnostic);
    fflush (stderr);
}

PROFILE_REGISTRATION *
sr_Init_listener (struct configobj  *appconfig) {
    PROFILE_REGISTRATION *pr = sr_Init (appconfig);

    if (pr)
        sr_listener (pr, callback_listener, NULL);

    return pr;
}
#endif

--- NEW FILE: syslog-raw.h ---
/* profile for SYSLOG reliable RAW */

/*
 * $Id: syslog-raw.h,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */

/*

initiator usage:

   1. pr = sr_Init (appconfig)
      sr_initiator (pr, callback, clientData)

   2. establish session using pr

   3. await invocation of callback (v, code, diagnostic, clientData)

       if v is NULL:
           look at code/diagnostic for reason
           release session, etc.

       otherwise:
            remember v

   4. for 0..I: sr_log (v, msg)

   5. sr_fin (v)

   6. await invocation of callback (v, code, diagnostic, clientData)

      look at code/diagnostic for success/failure


listener usage:

   1. pr = sr_Init (appconfig)
      sr_listener (pr, callback, clientData)

   2. establish session using pr

   3. forever: await invocation of callback (v, code, diagnostic, clientData)

       if v is NULL:
           look at code/diagnostic for reason
           release session, etc.

       if diagnostic is NULL:
           we're now plugged in (may happen more than once)
       
       otherwise:
            give diagnostic (of length code) to logging system

 */

#ifndef SR_PROFILE_H
#define SR_PROFILE_H    1


/* includes */

#include <beepcore-c/bp_config.h>
#include <beepcore-c/bp_wrapper.h>


#if defined(__cplusplus)
extern "C"
{
#endif

/**
 * @name The SYSLOG reliable RAW profile
 **/

/*@{*/

/* typedefs */

/**
 * Prototype for the application callback
 *
 * @param v A profile-provided pointer to be used for future calls to
 *         {@link sr_log sr_log} or {@link sr_fin sr_fin}.
 *         If <b>NULL</b>, then the channel has closed, and the session may
 *         be released.
 *
 * @param code A numeric result code.
 * Consult \URL[Section 8 of RFC 3080]{http://www.beepcore.org/beepcore/docs/rfc3080.jsp#reply-codes}
 * for an (incomplete) list.
 *
 * @param diagnostic A textual message corresopnding to the <i>code</i> parameter.
 *
 * @param clientData The user-supplied pointer provided to either
 *                   {@link sr_initiator sr_initiator} or
 *                   {@link sr_listener sr_listener}.
 **/
typedef void (*sr_callback) (void *v,
                             int code,
                             char *message,
                             void *clientData);


/* keys for configuration package */

/**
 * URI to use for the SYSLOG reliable RAW profile,
 * defaults to <b>"http://iana.org/beep/SYSLOG/RAW"</b>.
 **/
#define SYSLOG_RAW_URI           "beep profiles syslog_raw uri"

/**
 * Initiator modes for the SYSLOG reliable RAW profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define SYSLOG_RAW_IMODES        "beep profiles syslog_raw initiator_modes"

/**
 * Listener modes for the SYSLOG reliable RAW profile,
 * defaults to <b>"plaintext,encrypted"</b>.
 **/
#define SYSLOG_RAW_LMODES        "beep profiles syslog_raw listener_modes"

/**
 * User-specified window size for the SYSLOG reliable RAW profile.
 **/
#define SYSLOG_RAW_WINDOWSIZE    "beep profiles syslog_raw window_size"

/**
 * Enables debug mode for the SYSLOG reliable RAW profile,
 * defaults to <b>"0"</b>.
 **/
#define SYSLOG_RAW_DEBUG         "beep profiles syslog_raw debug"


/* module entry points */

/**
 * Well-known entry point for the SYSLOG reliable RAW profile.
 *
 * @param appconfig A pointer to the {@link configobj configuration}
 *                  structure used for configuration purposes.
 * <p>
 * Keys:
 * <blockquote><dl>
 * <dt>SYSLOG_RAW_URI</dt>
 * <dd>The registration URI for the profile (optional).</dd>
 *
 * <dt>SYSLOG_RAW_IMODES</dt>
 * <dd>The <i>initiator_modes</i> to use when registering the profile
 * (optional).</dd>
 *
 * <dt>SYSLOG_RAW_LMODES</dt>
 * <dd>The <i>listener_modes</i> to use when registering the profile
 * (optional).</dd>
 *
 * <dt>SYSLOG_RAW_DEBUG</dt>
 * <dd>A non-zero value to enable debugging (optional).</dd>
 * </dl></blockquote>
 *
 * @return A pointer to a profile-registration structure.
 **/
extern PROFILE_REGISTRATION* sr_Init (struct configobj* appconfig);


/* listener routines */

/**
 * Configures a profile-registration structure to behave as a listener.
 * <p>
 * Call this before registering the profile with the wrapper
 * (e.g., before calling {@link tcp_bp_listen tcp_bp_listen}.)
 *
 * @param pr A pointer to the profile-registration returned by
 *           {@link sr_Init sr_Init}.
 *
 * @param callback The routine to invoke on an event
 *                 (cf., {@link (*sr_callback) callback}).
 * 
 * @param clientData A user-supplied pointer.
 **/
extern void sr_listener (PROFILE_REGISTRATION* pr,
                         sr_callback callback,
                         void* clientData);


/* initiator routines */

/**
 * Configures a profile-registration structure to behave as an initiator.
 * <p>
 * Call this before registering the profile with the wrapper
 * (e.g., before calling {@link tcp_bp_connect tcp_bp_connect}.)
 *
 * @param pr A pointer to the profile-registration returned by
 *           {@link sr_Init sr_Init}.
 *
 * @param serverName The value to use for the <i>serverName</i> attribute
 *                    of the &lt;start&gt; element, or <b>NULL</b>.
 *
 * @param callback The routine to invoke on an event
 *                 (cf., {@link (*sr_callback) callback}).
 * 
 * @param clientData A user-supplied pointer.
 **/
extern void sr_initiator (PROFILE_REGISTRATION* pr,
                          char* serverName,
                          sr_callback callback,
                          void* clientData);

/**
 * Sends an entry to log.
 *
 * @param v An opaque pointer returned by the last
 * {@link (*sr_callback) callback}.
 *
 * @param entry The entries to log.
 *
 * @return One of:
 *         <ul>
 *         <li>{@link SR_OK SR_OK}</li>
 *         <li>{@link SR_ERROR SR_ERROR}</li>
 *         <li>{@link SR_BUSY SR_BUSY}</li>
 *         <li>{@link SR_DONE SR_DONE}</li>
 *         </ul>
 **/
extern int sr_log (void* v,
                   char* entry);


/**
 * Closes the SYSLOG reliable RAW channel.
 *
 * @param v An opaque pointer returned by the last
 * {@link (*sr_callback) callback}.
 *
 * @return One of:
 *         <ul>
 *         <li>{@link SR_OK SR_OK}</li>
 *         <li>{@link SR_ERROR SR_ERROR}</li>
 *         <li>{@link SR_BUSY SR_BUSY}</li>
 *         <li>{@link SR_DONE SR_DONE}</li>
 *         </ul>
 **/
extern int sr_fin (void* v);

/**
 * no problema
 **/
#define SR_OK         0

/**
 * error performing task
 **/
#define SR_ERROR      (-1)

/**
 * still doing {@link sr_log sr_log}.
 **/
#define SR_BUSY       (-2)

/**
 * channel is closed
 **/
#define SR_DONE       (-3)

/*@}*/

#if defined(__cplusplus)
}
#endif

#endif

--- NEW FILE: tls-profile.c ---
/*
 * Copyright (c) 2001 Invisible Worlds, Inc.  All rights reserved.
 *
 * The contents of this file are subject to the Blocks Public License (the
 * "License"); You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at http://www.beepcore.org/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 */

/*
 * $Id: tls-profile.c,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */


/*    includes */

#ifndef WIN32
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#else
#define sem_t HANDLE
#endif
#include <beepcore-c/tls-profile.h>
#include <beepcore-c/bp_notify.h>
#include <beepcore-c/logutil.h>
#include <beepcore-c/workthread.h>
#include <beepcore-c/bp_slist_utility.h>
#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <beepcore-c/bp_fiostate.h>
#include <beepcore-c/bp_fpollmgr.h>


/*    defines and typedefs */

#define PRO_TLS_URI             "http://iana.org/beep/TLS"


typedef struct pro_localdata {
    int                  pl_flags;
#define PRO_INITIATOR   (1<<0)
#define PRO_STARTING    (1<<1)
#define PRO_CLOSED      (1<<2)

    CHANNEL_INSTANCE    *pl_channel;
    DIAGNOSTIC          *pl_status;

    SSL_CTX             *pl_ctx;
}  PRO_LOCALDATA;


/*    statics */

static int       pro_debug;
static WORKQUEUE* notifyqueue = NULL;

static void     tls_tuning_reset_server_handshake (PROFILE_INSTANCE *pi);
static void     tls_tuning_reset_notify (void* data);
static void     tls_tuning_reset_client_handshake (PROFILE_INSTANCE *pi);

static void     tls_set_success (PROFILE_INSTANCE *pi,
                                 SSL              *ssl);
static void     tls_set_failure (PROFILE_INSTANCE *pi,
                                 int               error);

static int      tls_reader (void *handle,
                            char *buffer,
                            int  size);
static int      tls_writer (void *handle,
                            char *buffer,
                            int  size);
#if 0
static int      tls_destroy (void *handle);
#endif


/*    profile methods */

static char *
pro_connection_init (PROFILE_REGISTRATION *pr,
                     BP_CONNECTION        *bp) {
    if (pro_debug) {
        fprintf (stderr, "TLS connection_init\n");
        fflush (stderr);
    }

    return NULL;
}

static char *
pro_session_init (PROFILE_REGISTRATION *pr,
                  BP_CONNECTION        *bp) {
    if (pro_debug) {
        fprintf (stderr, "TLS session_init: role='%c' mode=\"%s\"\n",
                 bp -> role, bp -> mode);
        fflush (stderr);
    }

    return NULL;
}

static char *
pro_session_fin (PROFILE_REGISTRATION *pr,
                 BP_CONNECTION        *bp) {
    if (pro_debug) {
        fprintf (stderr, "TLS session_fin\n");
        fflush (stderr);
    }

    return NULL;
}

static char *
pro_connection_fin (PROFILE_REGISTRATION *pr,
                    BP_CONNECTION        *bp) {
    if (pro_debug) {
        fprintf (stderr, "TLS connection_fin\n");
        fflush (stderr);
    }

    return NULL;
}

static void
pro_start_indication (PROFILE_INSTANCE *pi,
                      PROFILE          *po) {
    char                 certbuf[BUFSIZ],
                        *certfile,
                        *cp,
                        *dir,
                         keybuf[BUFSIZ],
                        *keyfile,
                        *serverName;
    DIAGNOSTIC           ds,
                        *d = &ds;
    PROFILE              ps,
                        *p = &ps;
    PRO_LOCALDATA       *il;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);
    struct cfgsearchobj *cs;

    if (!(serverName = bp_server_name (pi -> channel -> conn)))
        serverName = pi -> channel -> inbound -> server_name;
    if (pro_debug) {
        fprintf (stderr,
                 "TLS start_indication: servername=\"%s\" piggyback=\"%s\"\n",
                 serverName ? serverName : "<NULL>",
                 po -> piggyback ? po -> piggyback : "<NULL>");
        fflush (stderr);
    }
               
    memset (p, 0, sizeof *p);
    p -> uri = po -> uri;

    memset (d, 0, sizeof *d);
    d -> code = 421;

    if (((cp = config_get (appconfig, SASL_LOCAL_CODE)) != NULL)
            && (*cp == '2')) {
        d -> code = 520;
        d -> message = "already tuned for authentication";

        bpc_start_response (pi -> channel, p, d);
        return;
    }

    if (!(il = (PRO_LOCALDATA *) lib_malloc (sizeof *il))) {
        d -> message = "out of memory";

        bpc_start_response (pi -> channel, p, d);
        return;
    }

    certfile = keyfile = NULL;
    if ((dir = config_get (appconfig, TLS_CERTDIR))
            && !(strstr (serverName, "/"))
            && !(strstr (serverName, ".."))) {
        sprintf (certbuf, "%s/%s", dir, serverName);
#ifndef WIN32
        if (access (certbuf, R_OK) >= 0) {
            certfile = keyfile = certbuf;
            if ((dir = config_get (appconfig, TLS_KEYDIR)) != NULL) {
                sprintf (keybuf, "%s/%s", dir, serverName);
                if (access (keybuf, R_OK) >= 0)
                    keyfile = keybuf;
                else
                    certfile = keyfile = NULL;
            }
        }
#else
        certfile = keyfile = certbuf;
        if ((dir = config_get (appconfig, TLS_KEYDIR)) != NULL) {
            sprintf (keybuf, "%s/%s", dir, serverName);
            keyfile = keybuf;
        }
#endif
    }

    memset (il, 0, sizeof *il);
    if (!(il -> pl_ctx = SSL_CTX_new (SSLv23_server_method ()))) {
        if (pro_debug)
            ERR_print_errors_fp (stderr);
        d -> message = "unable to create server context";
    } else if (!certfile
                   && !(certfile = config_get (appconfig, TLS_CERTFILE))) {
        d -> message = "TLS_CERTFILE not present in configuration";
    } else if (SSL_CTX_use_certificate_file (il -> pl_ctx, certfile,
                                             SSL_FILETYPE_PEM) <= 0) {
        if (pro_debug)
            ERR_print_errors_fp (stderr);
        d -> message = "unable to use certificate file";
    } else if (!keyfile && !(keyfile = config_get (appconfig, TLS_KEYFILE))) {
        d -> message = "TLS_KEYFILE not present in configuration";
    } else if (SSL_CTX_use_PrivateKey_file (il -> pl_ctx, keyfile,
                                            SSL_FILETYPE_PEM) <= 0) {
        if (pro_debug)
            ERR_print_errors_fp (stderr);
        d -> message =  "unable to use private key file";
    } else if (!SSL_CTX_check_private_key (il -> pl_ctx)) {
        d -> message = "private key does not match certificate's public key";
    } else if (po -> piggyback_length == 0)
        d = NULL;
    else if (strstr (po -> piggyback, "<ready")) {
        bp_slist *list;
        CHANNEL_INSTANCE *inst;

        for (list = pi -> channel -> conn -> channel_instance;
                 list;
                 list = list -> next)
        {
            if ((inst = list -> data) -> channel_number == 0) {
                bpc_tuning_reset_request (inst);
                break;
            }
        }

        p -> piggyback_length = strlen (p -> piggyback = "<proceed />");

#if 0
        err = workqueue_add_item(notifyqueue, tls_tuning_reset_notify, pi);
        if (err == 0) {
            fiostate_block_read (GET_WRAPPER(pi->channel->conn) -> iostate);
            d = NULL;
        } else {
            d -> message = "unable to wait for channels to become quiescent";
        }
#else
        fiostate_block_read (GET_WRAPPER(pi->channel->conn) -> iostate);
        d = NULL;
#endif
    } else
        d = bp_diagnostic_new (pi -> channel -> conn, 504, NULL,
                               "expecting ready element in piggyback");

    if (d && d -> message) {
        lib_free (il);
    } else {
        pi -> user_ptr1 = il;
        cs = config_search_init (appconfig, PRIVACY_PREFIX, "");
        for (cp = config_search_string_firstkey (cs);
                cp;
                cp = config_search_string_nextkey (cs))
            config_delete (appconfig, cp);
        config_search_fin (&cs);
    }
    
    bpc_start_response (pi -> channel, p, d);
#if 1
    workqueue_add_item(notifyqueue, tls_tuning_reset_notify, pi);
#endif

    if ((d != NULL) && (d != &ds))
        bp_diagnostic_destroy (pi -> channel -> conn, d);
}

static void
pro_start_confirmation (void             *clientData,
                        PROFILE_INSTANCE *pi,
                        PROFILE          *po) {
    PRO_LOCALDATA *il = (PRO_LOCALDATA *) clientData;

    if (pro_debug) {
        fprintf (stderr, "TLS start_confirmation: piggyback=\"%s\"\n",
                 po -> piggyback ? po -> piggyback : "<NULL>");
        fflush (stderr);
    }
               
    pi -> user_ptr1 = il;

    il -> pl_flags &= ~(PRO_STARTING | PRO_CLOSED);
    il -> pl_channel = pi -> channel;

    if ((po -> piggyback_length > 0) && strstr (po -> piggyback, "<proceed"))
#if 1
        tls_tuning_reset_client_handshake (pi);
#else
        workqueue_add_item(notifyqueue, tls_tuning_reset_client_handshake, pi);
#endif
    else
      il -> pl_status = bp_diagnostic_new (pi -> channel -> conn, 504, NULL,
                                           "expecting proceed element");
}

static void
pro_close_indication (PROFILE_INSTANCE *pi,
                      DIAGNOSTIC       *request,
                      char              origin,
                      char              scope) {
    if (pro_debug) {
        fprintf (stderr,
                 "TLS close_indication: request=[%d] \"%s\" origin=%c scope=%c\n",
                 request -> code, request -> message ? request -> message
                                                     : "<NULL>",
                 origin, scope);
        fflush (stderr);
    }
               
    bpc_close_response (pi -> channel, NULL);
}

static void
pro_close_confirmation(PROFILE_INSTANCE *pi,
                       char              status,
                       DIAGNOSTIC       *error,
                       char              origin,
                       char              scope) {
    PRO_LOCALDATA *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (pro_debug) {
        if (error)
            fprintf (stderr,
                     "TLS close_confirmation: status=%c error=[%d] \"%s\" origin=%c scope=%c\n",
                     status, error -> code, error -> message ? error -> message
                                                             : "<NULL>",
                     origin, scope);
        else 
            fprintf (stderr,
                     "TLS close_confirmation: status=%c no error origin=%c scope=%c\n",
                     status, origin, scope);
        fflush (stderr);
    }

    if (status != PRO_ACTION_SUCCESS)
        return;

    if (il) {
        if (il -> pl_flags & PRO_INITIATOR)
            il -> pl_flags |= PRO_CLOSED;
        else {
            pi -> user_ptr1 = NULL;

            SSL_CTX_free (il -> pl_ctx);
            lib_free (il);
        }
    }
}

static void
pro_tuning_reset_indication (PROFILE_INSTANCE *pi) {
    if (pro_debug) {
        fprintf (stderr, "TLS tuning_reset_indication\n");
        fflush (stderr);
    }
}

static void
pro_tuning_reset_confirmation (PROFILE_INSTANCE *pi,
                               char              status) {
    if (pro_debug) {
        fprintf (stderr, "TLS tuning_reset_confirmation: status=%c\n", status);
        fflush (stderr);
    }

    pro_close_confirmation (pi, status, NULL, PRO_ACTION_LOCAL,
                            PRO_ACTION_SESSION);
}

static void
pro_message_available(PROFILE_INSTANCE *pi) {
    int            type;
    char          *buffer;
    FRAME         *f;
    PRO_LOCALDATA *il = (PRO_LOCALDATA *) pi -> user_ptr1;
    
    if (!(f = bpc_query_message (pi -> channel, BLU_QUERY_ANYTYPE,
                                  BLU_QUERY_ANYMSG, BLU_QUERY_ANYANS)))
        return;

    if (pro_debug) {
        fprintf (stderr,
                 "TLS message_available: type=%c number=%ld answer=%ld size=%ld\n",
                 f -> msg_type, f -> message_number, f -> answer_number,
                 f -> size);
        fprintf (stderr, f -> size > 75 ? "%-75.75s...\n" : "%s\n",
                 f -> payload);
        fflush (stderr);
    }

    switch (f -> msg_type) {
        case BLU_FRAME_TYPE_MSG:
            type = BLU_FRAME_TYPE_ERR;
            if (il -> pl_flags & PRO_INITIATOR)
                buffer = bpc_error_allocate (pi -> channel, 521, NULL,
                                             "client-side only");
            else if (!strstr (f -> payload, "<ready")) {
                buffer = bpc_error_allocate (pi -> channel, 504, NULL,
                                              "expecting ready element");
            } else if ((buffer = bpc_buffer_allocate (pi -> channel,
                                                       sizeof "<proceed />"
                                                           - 1)) != NULL) {
                type = BLU_FRAME_TYPE_RPY;
                strcpy (buffer, "<proceed />");
                bpc_tuning_reset_request (pi -> channel);
                fiostate_block_read(GET_WRAPPER(pi->channel->conn) -> iostate);
            }
            if (buffer != NULL) {
                bpc_send (pi -> channel, type, f -> message_number,
                          BLU_FRAME_IGNORE_ANSNO, BLU_FRAME_COMPLETE, buffer,
                          strlen (buffer));
                if (type == BLU_FRAME_TYPE_RPY) {
                    bpc_frame_destroy (pi -> channel, f);
                    tls_tuning_reset_server_handshake (pi);
                    return;
                }
            } else
                bp_log (pi -> channel -> conn, LOG_PROF, 5,
                        "TLS message: out of memory allocating buffer/error");
            break;

        case BLU_FRAME_TYPE_RPY:
            if (strstr (f -> payload, "<proceed")) {
                bpc_frame_destroy (pi -> channel, f);
                tls_tuning_reset_client_handshake (pi);
                return;
            }
            /* and fall... */
        case BLU_FRAME_TYPE_ANS:
        case BLU_FRAME_TYPE_NUL:
        case BLU_FRAME_TYPE_ERR:
            bpc_tuning_reset_complete (pi -> channel, PRO_ACTION_FAILURE,
                                       NULL);
            break;
    }

    bpc_frame_destroy (pi -> channel, f);
}

static void
pro_window_full (PROFILE_INSTANCE *pi) {
    DIAGNOSTIC    *d;

    if (pro_debug) {
        fprintf (stderr, "TLS window_full\n");
        fflush (stderr);
    }

    bp_log (pi -> channel -> conn, LOG_PROF, 5, "TLS channel window full!?!");
    if (!(d = bpc_close_request (pi -> channel, BLU_CHAN0_MSGNO_DEFAULT, 550,
                                 NULL, "stop misbehaving", NULL, NULL))) {
        bp_log (pi -> channel -> conn, LOG_PROF, 4,
                "unable to close TLS channel: [%d] %s", d -> code,
                d -> message);
        bp_diagnostic_destroy (pi -> channel -> conn, d);
    }    
}


/*    helper functions */

static void
tls_tuning_reset_server_handshake (PROFILE_INSTANCE *pi) {
    int                  error;
    char                *cp;
    BIO                 *bio;
    SSL                 *ssl;
    BP_CONNECTION       *bp = pi -> channel -> conn;
    WRAPPER             *w = GET_WRAPPER (bp);
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;
    struct configobj    *appconfig = bp_get_config (bp);
    struct cfgsearchobj *cs;

    if (pro_debug) {
        fprintf (stderr, "TLS server_handshake\n");
        fflush (stderr);
    }

    fiostate_unblock_write (w -> iostate);
    fiostate_block_read (w -> iostate);

    bpc_wait_state_quiescent (pi -> channel);

    fiostate_block_read (w -> iostate);
    fiostate_block_write (w -> iostate);

    ssl = SSL_new (il -> pl_ctx);
    bio = BIO_new (BIO_s_socket ());
    BIO_set_fd (bio, w -> iostate -> socket, BIO_NOCLOSE);
    BIO_set_nbio (bio, 1);
    SSL_set_bio (ssl, bio, bio);

    do {
        error = SSL_get_error (ssl, SSL_accept (ssl));
    } while ((error == SSL_ERROR_WANT_READ)
                 || (error == SSL_ERROR_WANT_WRITE));

    cs = config_search_init (appconfig, SASL_REMOTE_PREFIX, "");
    for (cp = config_search_string_firstkey (cs);
             cp;
             cp = config_search_string_nextkey (cs))
        config_delete (appconfig, cp);
    config_search_fin (&cs);

    if (error != SSL_ERROR_NONE) {
        tls_set_failure (pi, error);
        SSL_free (ssl);
        bpc_tuning_reset_complete (pi -> channel, PRO_ACTION_SUCCESS,
                                   "plaintext");
    } else {
        tls_set_success (pi, ssl);
        bp_new_reader_writer (w -> iostate, tls_reader, ssl, tls_writer,
                              ssl, 4096, NULL /*tls_destroy*/, NULL);
        bpc_tuning_reset_complete (pi -> channel, PRO_ACTION_SUCCESS,
                                    "encrypted");
    }
}

static void
tls_tuning_reset_notify (void* data) {
    PROFILE_INSTANCE *pi = (PROFILE_INSTANCE*) data;

    if (pi -> channel -> conn -> status == INST_STATUS_EXIT)
        return;

    bp_wait_channel_0_state_quiescent (pi -> channel -> conn);

    tls_tuning_reset_server_handshake (pi);
}

static void
tls_tuning_reset_client_handshake (PROFILE_INSTANCE *pi) {
    int                  error;
    char                *cp;
    BIO                 *bio;
    SSL                 *ssl;
    BP_CONNECTION       *bp = pi -> channel -> conn;
    WRAPPER             *w = GET_WRAPPER (bp);
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;
    struct configobj    *appconfig = bp_get_config (bp);
    struct cfgsearchobj *cs;

    if (pro_debug) {
        fprintf (stderr, "TLS client_handshake\n");
        fflush (stderr);
    }

    fiostate_block_read(w->iostate);
    fiostate_block_write(w->iostate);
    ssl = SSL_new (il -> pl_ctx);
    bio = BIO_new (BIO_s_socket ());
    BIO_set_fd (bio, w -> iostate -> socket, BIO_NOCLOSE);
    BIO_set_nbio (bio, 1);
    SSL_set_bio (ssl, bio, bio);

    do
    {
        error = SSL_get_error (ssl, SSL_connect (ssl));
    } while ((error == SSL_ERROR_WANT_READ)
                 || (error == SSL_ERROR_WANT_WRITE));

    il -> pl_flags |= PRO_CLOSED;
    cs = config_search_init (appconfig, SASL_REMOTE_PREFIX, "");
    for (cp = config_search_string_firstkey (cs);
             cp;
             cp = config_search_string_nextkey (cs))
        config_delete (appconfig, cp);
    config_search_fin (&cs);

    if (error != SSL_ERROR_NONE) {
        tls_set_failure (pi, error);
        SSL_free (ssl);
        bpc_tuning_reset_complete (pi -> channel, PRO_ACTION_SUCCESS,
                                   "plaintext"); 
    } else {
        tls_set_success (pi, ssl);
        bp_new_reader_writer(w -> iostate, tls_reader, ssl, tls_writer,
                             ssl, 4096, NULL /* tls_destroy */, NULL);
        bpc_tuning_reset_complete (pi -> channel, PRO_ACTION_SUCCESS,
                                    "encrypted"); 
    }
}

static void
tls_set_success (PROFILE_INSTANCE *pi,
                 SSL              *ssl) {
    int                  bits;
    char                 buffer[BUFSIZ],
                        *cp;
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;
    SSL_CIPHER          *cipher = SSL_get_current_cipher (ssl);
    X509                *x509;
    struct configobj    *appconfig = bp_get_config (pi -> channel -> conn);

    if (pro_debug) {
        fprintf (stderr, "tls_set_success\n");
        fflush (stderr);
    }

    if ((x509 = SSL_get_peer_certificate (ssl)) != NULL) {
        cp = X509_NAME_oneline (X509_get_subject_name (x509), NULL, 0);
        config_set (appconfig, PRIVACY_REMOTE_SUBJECTNAME, cp);
#if 0
        /** @todo fix this */
        free (cp);
#endif
    }
    config_set (appconfig, PRIVACY_CIPHER_NAME,
                (char *) SSL_CIPHER_get_name (cipher));
    config_set (appconfig, PRIVACY_CIPHER_PROTOCOL,
                SSL_CIPHER_get_version (cipher));
    sprintf (buffer, "%d", SSL_CIPHER_get_bits (cipher, &bits));
    config_set (appconfig, PRIVACY_CIPHER_SBITS, buffer);

    if (il -> pl_flags & PRO_INITIATOR)
        il -> pl_status = bp_diagnostic_new (pi -> channel -> conn, 200, NULL,
                                             "tuning successful");
}

static void
tls_set_failure (PROFILE_INSTANCE *pi,
                 int               error) {
    PRO_LOCALDATA       *il = (PRO_LOCALDATA *) pi -> user_ptr1;

    if (il -> pl_flags & PRO_INITIATOR)
        il -> pl_status = bp_diagnostic_new (pi -> channel -> conn, 550, NULL,
                                             "openssl returned %d", error);
    else
        log_line (LOG_PROF, 5, "openssl returned %d", error);
}

static int
tls_reader (void *handle,
            char *buffer,
            int   size) {
    SSL *ssl = (SSL *) handle;

    return SSL_read (ssl, buffer, size);
}

static int
tls_writer (void *handle,
            char *buffer,
            int   size) {
    SSL *ssl = (SSL *) handle;

    return SSL_write (ssl, buffer, size);
}

#if 0
static int
tls_destroy (void *handle) {
    SSL *ssl = (SSL *) handle;

    if (ssl)
        SSL_free (ssl);

    return 0;
}
#endif


/*
 * Additional profile-related functions.
 */

DIAGNOSTIC *
tls_privatize (BP_CONNECTION           *bp,
               char                    *serverName) {
    char                *cp;
    bp_slist            *list;
    CHANNEL_INSTANCE    *ci;
    DIAGNOSTIC          *d;
    PROFILE              ps,
                        *p = &ps;
    PRO_LOCALDATA       *il;
    struct configobj    *appconfig = bp_get_config (bp);
    struct cfgsearchobj *cs;

    if (((cp = config_get (appconfig, SASL_REMOTE_CODE)) != NULL)
            && (*cp == '2'))
        return bp_diagnostic_new (bp, 520, NULL,
                                  "already tuned for authentication");

    if (!(il = (PRO_LOCALDATA *) lib_malloc (sizeof *il)))
        return bp_diagnostic_new (bp, 421, NULL, "out of memory");

    memset (il, 0, sizeof *il);
    il -> pl_flags = PRO_INITIATOR | PRO_STARTING | PRO_CLOSED;
    if (!(il -> pl_ctx = SSL_CTX_new (TLSv1_client_method ()))) {
        if (pro_debug)
            ERR_print_errors_fp (stderr);
        lib_free (il);
        
        return bp_diagnostic_new (bp, 421, NULL,
                                  "unable to create client context");
    }

    for (list = bp -> channel_instance; list; list = list -> next)
        if ((ci = list -> data) -> channel_number == 0) {
            bpc_tuning_reset_request (ci);
            break;
        }

    memset (p, 0, sizeof *p);
    p -> uri = PRO_TLS_URI;
    p -> piggyback_length = strlen (p -> piggyback = "<ready />");

    cs = config_search_init (appconfig, PRIVACY_PREFIX, "");
    for (cp = config_search_string_firstkey (cs);
             cp;
             cp = config_search_string_nextkey (cs))
        config_delete (appconfig, cp);
    config_search_fin (&cs);

    if ((d = bp_start_request(bp, BLU_CHAN0_CHANO_DEFAULT,
                              BLU_CHAN0_MSGNO_DEFAULT, p, serverName, NULL,
                              (void *) il)) != NULL)
        goto out;

    while ((il -> pl_status == NULL)
               && ((il -> pl_flags & (PRO_STARTING|PRO_CLOSED)) != PRO_CLOSED)
               && (bp -> status != INST_STATUS_EXIT))
        YIELD ();

    while (bp->status == INST_STATUS_TUNING)
        YIELD();

    if (!il -> pl_status) {
        d = bp_diagnostic_new (bp, 400, NULL,
                               bp -> status != INST_STATUS_EXIT
                                    ? "lost channel during tuning"
                                    : "lost session during tuning");
        goto out;
    }

    if (il -> pl_status -> code >= 400) {
        d = il -> pl_status;
        goto out;
    }
    bp_diagnostic_destroy (bp, il -> pl_status);

    if ((!(d = bp_wait_for_greeting (bp)))
            && (bp -> status == INST_STATUS_EXIT))
        d = bp_diagnostic_new (bp, 400, NULL, "lost session after tuning");

out: ;
    if (!(il -> pl_flags & PRO_CLOSED))
        il -> pl_channel -> profile -> user_ptr1 = NULL;

    SSL_CTX_free (il -> pl_ctx);
    lib_free (il);

    return d;
}


/*
 * Well-known shared library entry point.
 */

PROFILE_REGISTRATION *tls_profile_Init(struct configobj *appconfig) {
    char                *cp;
    PROFILE_REGISTRATION prs,
                        *pr = &prs;
  
    pro_debug = 0;
    if ((cp = config_get (appconfig, TLS_DEBUG)) != NULL)
        pro_debug = atoi (cp);

    memset (pr, 0, sizeof *pr);

    if (!(pr -> uri = config_get (appconfig, TLS_URI)))
        pr -> uri = PRO_TLS_URI;

    pr -> initiator_modes = "plaintext";
    pr -> listener_modes = "plaintext";

    pr -> full_messages = 1;
    pr -> thread_per_channel = 0;

    pr -> proreg_connection_init = pro_connection_init;
    pr -> proreg_connection_fin  = pro_connection_fin;
    pr -> proreg_session_init = pro_session_init;
    pr -> proreg_session_fin  = pro_session_fin;
    pr -> proreg_start_indication = pro_start_indication;
    pr -> proreg_start_confirmation = pro_start_confirmation;
    pr -> proreg_close_indication   = pro_close_indication;
    pr -> proreg_close_confirmation = pro_close_confirmation;
    pr -> proreg_tuning_reset_indication   = pro_tuning_reset_indication;
    pr -> proreg_tuning_reset_confirmation = pro_tuning_reset_confirmation;
    pr -> proreg_message_available = pro_message_available;
    pr -> proreg_window_full       = pro_window_full;

    SSL_library_init ();
    SSL_load_error_strings ();

    if (!(cp = config_get (appconfig, TLS_CERTFILE))) {
        log_line (LOG_PROF, 6, "TLS_CERTFILE not present in configuration");
        return NULL;
    }
    if (!strstr (cp, ".pem")) {
        log_line (LOG_PROF, 6, "TLS_CERTFILE doesn't end in \".pem\"");
        return NULL;
    }
    if (!(cp = config_get (appconfig, TLS_KEYFILE))) {
        log_line (LOG_PROF, 6, "TLS_KEYFILE not present in configuration");
        return NULL;
    }
    if (!strstr (cp,".pem")) {
        log_line (LOG_PROF, 6, "TLS_KEYFILE doesn't end in \".pem\"");
        return NULL;
    }

    notifyqueue = workqueue_create();
    if (notifyqueue == NULL) {
        log_line (LOG_PROF, 6, "Unable to initalize worker threads");
        return NULL;
    }

    return bp_profile_registration_clone (NULL, pr);
}

--- NEW FILE: tls-profile.h ---
/*
 * Copyright (c) 2001 Invisible Worlds, Inc.  All rights reserved.
 *
 * The contents of this file are subject to the Blocks Public License (the
 * "License"); You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at http://www.beepcore.org/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 */

/*
 * $Id: tls-profile.h,v 1.1 2002/09/08 03:02:33 cphmit Exp $
 */

/*

initiator usage:

   1. pr = tls_profile_Init (appconfig)

   2. establish session using pr

   3. d = tls_privatize (...)

      if d is non-NULL, you lose


listener usage:

   1. pr = tls_profile_Init (appconfig)

   2. establish session using pr

 */

#ifndef TLS_PROFILE_H
#define TLS_PROFILE_H 1

/* includes */

#include <beepcore-c/bp_wrapper.h>
#include <beepcore-c/bp_config.h>
#include <beepcore-c/tuning.h>


#if defined(__cplusplus)
extern "C"
{
#endif

/**
 * @name The TLS profile
 **/

/*@{*/

/* keys for configuration package */

/**
 * URI to use for the TLS profile,
 * defaults to <b>"http://iana.org/beep/TLS"</b>.
 **/
#define TLS_URI                 "beep profiles tls uri"

/**
 * File containing the public key certificate,
 * must be present.
 **/
#define TLS_CERTFILE            "beep profiles tls certfile"

/**
 * Directory containing public key certificates,
 * each file named according to the <i>serverName</i> attribute.
 * If this key or the resulting file isn't present,
 * <i>TLS_CERTFILE</i> is used instead.
 **/
#define TLS_CERTDIR            "beep profiles tls certdir"

/**
 * File containing the secret key,
 * must be present.
 **/
#define TLS_KEYFILE             "beep profiles tls keyfile"

/**
 * Directory containing secret keys,
 * each file named according to the <i>serverName</i> attribute.
 * If this key or the resulting file isn't present,
 * <i>TLS_KEYFILE</i> is used instead.
 **/
#define TLS_KEYDIR              "beep profiles tls keydir"

/**
 * Enables debug mode for the TLS profile,
 * defaults to <b>"0"</b>.
 **/
#define TLS_DEBUG               "beep profiles tls debug"



/* module entry points */

/**
 * Well-known entry point for the TLS profile.
 *
 * @param appconfig A pointer to the {@link configobj configuration}
 *                  structure used for configuration purposes.
 * <p>
 * Keys:
 * <blockquote><dl>
 * <dt>TLS_URI</dt>
 * <dd>The registration URI for the profile.</dd>
 *
 * <dt>TLS_DEBUG</dt>
 * <dd>A non-zero value to enable debugging (optional).</dd>
 *
 * <dt>TLS_CERTDIR</dt>
 * <dd>The name of the directory containing public key certificates,
 * each file named according to the <i>serverName</i> attribute
 * (optional, server-side only).
 * If this key or the resulting file isn't present,
 * <i>TLS_CERTFILE</i> is used instead.</dd>
 *
 * <dt>TLS_KEYFILE</dt>
 * <dd>The name of the file containing the private key.</dd>
 *
 * <dt>TLS_KEYDIR</dt>
 * <dd>The name of the directory containing secret keys,
 * each file named according to the <i>serverName</i> attribute
 * (optional, server-side only).
 * If this key or the resulting file isn't present,
 * <i>TLS_KEYFILE</i> is used instead.</dd>
 * </dl></blockquote>
 *
 * @return A pointer to a profile-registration structure.
 **/
extern struct PROFILE_REGISTRATION* tls_profile_Init(struct configobj* appconfig);

/**
 * Tune using TLS, and update the connection's
 * {@link configobj configuration} structure accordingly.
 * <p>
 * On success, transport privacy is in effect and no user channels are open.
 *
 * @param bp A pointer to the connection structure. 
 * <p>
 * Input Keys:
 * <blockquote><dl>
 * <dt>TLS_CERTFILE</dt>
 * <dd>The name of the file containing the public key certificate.</dd>
 *
 * <dt>TLS_KEYFILE</dt>
 * <dd>The name of the file containing the private key.</dd>
 * </dl></blockquote>
 *
 * <p>
 * Output Keys:
 * <blockquote><dl>
 * <dt>PRIVACY_REMOTE_SUBJECTNAME</dt>
 * <dd>The subject name from the peer's certificate.</dd>
 *
 * <dt>PRIVACY_CIPHER_NAME</dt>
 * <dd>The name of the cipher in use.</dd>
 *
 * <dt>PRIVACY_CIPHER_PROTOCOL</dt>
 * <dd>The name of the privacy protocol in use.</dd>
 *
 * <dt>PRIVACY_CIPHER_SBITS</dt>
 * <dd>The number of bits in the secret key in use.</dd>
 * </dl></blockquote>
 *
 * @param serverName The value to use for the <i>serverName</i> attribute
 *                    of the &lt;start&gt; element, or <b>NULL</b>.
 *
 * @return On failure, a pointer to a {@link diagnostic diagnostic} structure
 *         explaining the reason
 *         (which should subsequently be destroyed by calling
 *         {@link bp_diagnostic_destroy bp_diagnostic_destroy}).
 **/
extern struct diagnostic* tls_privatize(struct BP_CONNECTION* bp,
                                        char* serverName);

/*@}*/

#if defined(__cplusplus)
}
#endif

#endif

--- NEW FILE: tuning.h ---
/* tuning parameters */

#ifndef TUNING_H
#define TUNING_H        1

/* includes */

#include <beepcore-c/bp_config.h>


#if defined(__cplusplus)
extern "C"
{
#endif


/**
 * @name Tuning Parameters
 **/

/*@{*/

/**
 * The prefix used for identity properties associated with the local peer.
 **/
#define SASL_LOCAL_PREFIX      "beep identity local "

/**
 * The SASL authentication-identity.
 **/
#define SASL_LOCAL_USERNAME     "beep identity local username"

/**
 * The SASL authorization-identity.
 **/
#define SASL_LOCAL_TARGET       "beep identity local target"

/**
 * The authentication realm.
 **/
#define SASL_LOCAL_REALM        "beep identity local target"

/**
 * The passphrase for the authentication-identity.
 **/
#define SASL_LOCAL_PASSPHRASE   "beep identity local passphrase"

/**
 * The mechanism used during the peer's attempt to authenticate.
 * <p>
 * Typically, this is set by {@link sasl_login sasl_login}
 * or {@link cyrus_login cyrus_login}.
 **/
#define SASL_LOCAL_MECHANISM   "beep identity local mechanism"

/**
 * The reply code resulting from a peer's attempt to authenticate.
 * <p>
 * Typically, this is set by {@link sasl_login sasl_login}
 * or {@link cyrus_login cyrus_login}.
 * Consult \URL[Section 8 of RFC 3080]{http://www.beepcore.org/beepcore/docs/rfc3080.jsp#reply-codes}
 * for an (incomplete) list.
 **/
#define SASL_LOCAL_CODE         "beep identity local reply_code"

/**
 * A textual message corresponding to the
 * {@link SASL_LOCAL_CODE SASL_LOCAL_CODE}.
 * <p>
 * Typically, this is set by {@link sasl_login sasl_login}
 * or {@link cyrus_login cyrus_login}.
 **/
#define SASL_LOCAL_REASON       "beep identity local reply_text"

/**
 * The prefix used for identity properties associated with the remote peer.
 **/
#define SASL_REMOTE_PREFIX      "beep identity remote "

/**
 * The mechanism used during the peer's attempt to authenticate.
 **/
#define SASL_REMOTE_MECHANISM   "beep identity remote mechanism"

/**
 * The SASL authentication-identity of the peer.
 * <p>
 * After registering the result from
 * {@link sasl_profiles_Init sasl_profiles_Init}
 * or {@link cyrus_profiles_Init cyrus_profiles_Init},
 * this parameter gets set when appropriate.
 **/
#define SASL_REMOTE_USERNAME    "beep identity remote username"

/**
 * The SASL authorization-identity of the peer.
 * <p>
 * After registering the result from
 * {@link sasl_profiles_Init sasl_profiles_Init}
 * or {@link cyrus_profiles_Init cyrus_profiles_Init},
 * this parameter gets set when appropriate.
 **/
#define SASL_REMOTE_TARGET      "beep identity remote target"

/**
 * The numeric result of the peer's attempt to authenticate.
 * <p>
 * After registering the result from
 * {@link sasl_profiles_Init sasl_profiles_Init}
 * or {@link cyrus_profiles_Init cyrus_profiles_Init},
 * this parameter gets set when appropriate.
 * Consult \URL[Section 8 of RFC 3080]{http://www.beepcore.org/beepcore/docs/rfc3080.jsp#reply-codes}
 * for an (incomplete) list.
 **/
#define SASL_REMOTE_CODE        "beep identity remote reply_code"

/**
 * A textual message corresponding to the
 * {@link SASL_REMOTE_CODE SASL_REMOTE_CODE}.
 * <p>
 * After registering the result from
 * {@link sasl_profiles_Init sasl_profiles_Init}
 * or {@link cyrus_profiles_Init cyrus_profiles_Init},
 * this parameter gets set when appropriate.
 **/
#define SASL_REMOTE_REASON      "beep identity remote reply_text"


/**
 * The prefix used for privacy properties associated with the session.
 **/
#define PRIVACY_PREFIX          "beep identity privacy "

/**
 * The name of the cipher in use.
 * <p>
 * On the client-side, this is typically set as a result of calling
 * {@link tls_privatize tls_privatize}; whilst on the server-side,
 * after registering the result from {@link tls_profile_Init tls_profile_Init},
 * this parameter gets set when appropriate.
 * If the Cyrus SASL package is in use, then this may also be set as a result
 * of calling {@link cyrus_login cyrus_login}; whilst on the server-side,
 * after registering the result from
 * {@link cyrus_profiles_Init cyrus_profiles_Init}.
 **/
#define PRIVACY_CIPHER_NAME     "beep identity privacy cipher name"

/**
 * The number of bits in the session key in use.
 * <p>
 * On the client-side, this is typically set as a result of calling
 * {@link tls_privatize tls_privatize}; whilst on the server-side,
 * after registering the result from {@link tls_profile_Init tls_profile_Init},
 * this parameter gets set when appropriate.
 * If the Cyrus SASL package is in use, then this may also be set as a result
 * of calling {@link cyrus_login cyrus_login}; whilst on the server-side,
 * after registering the result from
 * {@link cyrus_profiles_Init cyrus_profiles_Init}.
 **/
#define PRIVACY_CIPHER_SBITS    "beep identity privacy cipher session_bits"

/**
 * The name of the privacy protocol in use.
 * <p>
 * On the client-side, this is typically set as a result of calling
 * {@link tls_privatize tls_privatize}; whilst on the server-side,
 * after registering the result from {@link tls_profile_Init tls_profile_Init},
 * this parameter gets set when appropriate.
 **/
#define PRIVACY_CIPHER_PROTOCOL "beep identity privacy cipher protocol"

/**
 * The subject name from the peer's certificate.
 * <p>
 * On the client-side, this is typically set as a result of calling
 * {@link tls_privatize tls_privatize}; whilst on the server-side,
 * after registering the result from {@link tls_profile_Init tls_profile_Init},
 * this parameter gets set when appropriate.
 **/
#define PRIVACY_REMOTE_SUBJECTNAME \
                                "beep identity privacy remote subject_name"

/*@}*/

#if defined(__cplusplus)
}
#endif

#endif



-------------------------------------------------------
In remembrance
www.osdn.com/911/