/*
 * TLS Diffie Hellmann extension.
 *
 * climm TLS extension Copyright (C) © 2003-2007 Roman Hoog Antink
 *
 * This extension is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 dated June, 1991.
 *
 * This extension is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 * License for more details.
 *
 * In addition, as a special exception permission is granted to link the
 * code of this release of climm with the OpenSSL project's "OpenSSL"
 * library, and distribute the linked executables.  You must obey the GNU
 * General Public License in all respects for all of the code used other
 * than "OpenSSL".  If you modify this file, you may extend this exception
 * to your version of the file, but you are not obligated to do so.  If you
 * do not wish to do so, delete this exception statement from your version
 * of this file.
 *
 * You should have received a copy of the GNU General Public License
 * along with this package; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * $Id: util_ssl.c 2524 2008-03-08 21:08:36Z kuhlmann $
 */

#include "climm.h"
#include <assert.h>

#if ENABLE_SSL

#include "preferences.h"
#include "conv.h"
#include "util_ssl.h"
#include "util_str.h"
#include "util_tcl.h"
#include "util_ui.h"
#include "oscar_dc.h"
#include "connection.h"
#include "contact.h"
#include "packet.h"

#include <errno.h>

#ifndef ENABLE_TCL
#define TCLMessage(from, text) {}
#define TCLEvent(from, type, data) {}
#endif

static char ssl_init_ok = 0;

#if ENABLE_GNUTLS

#include <gnutls/gnutls.h>
#include <gcrypt.h>
#define SSL_FAIL(s, e)    { const char *t = s; \
                            if (prG->verbose & DEB_SSL || \
                                e != GNUTLS_E_UNEXPECTED_PACKET_LENGTH) \
                                  rl_printf (i18n (2374, "SSL error: %s [%d]\n"), \
                                    t ? t : "unknown", __LINE__);  \
                       }    
                        
#define SSL_CHECK_SUCCESS_1_OK(status, ret, msg1, msg2) SSL_CHECK_SUCCESS (status, ret, 1, msg1, msg2)
#define SSL_CHECK_SUCCESS_0_OK(status, ret, msg1, msg2) SSL_CHECK_SUCCESS (status, ret, 0, msg1, msg2)
#define SSL_CHECK_SUCCESS(status, ret, ok, msg1, msg2) { if (status != ok) { \
                                     SSL_FAIL (s_sprintf ("%s %s %s", \
                                        msg1 ? msg1 : "", msg2 ? msg2 : "", \
                                        gnutls_strerror (status)), status); \
                                     return ret; \
                                 } \
                               } 
static gnutls_anon_client_credentials client_cred;
static gnutls_anon_server_credentials server_cred;
static gnutls_dh_params dh_parm;

#else /* ENABLE_GNUTLS */

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/dh.h>
#include <openssl/opensslv.h>
#include <openssl/md5.h>

static SSL_CTX *gSSL_CTX;

/* AUTOGENERATED by openssl dhparam -5 -C */
static DH *get_dh512()
{
    static unsigned char dh512_p[]={
            0xA5,0x82,0x78,0x01,0x4D,0x7A,0x33,0x49,0x2A,0xD2,0x15,0x74,
            0x8E,0x95,0x67,0x26,0xB1,0x5D,0x8A,0x01,0xDA,0x0A,0x08,0x51,
            0x31,0x64,0x71,0xD7,0x38,0x53,0xEA,0xD2,0xCF,0x3D,0xB9,0x26,
            0x78,0x5F,0x75,0xF3,0x90,0xC5,0x63,0x7B,0x9A,0x4F,0x4D,0x03,
            0xAB,0x38,0x8D,0x79,0x58,0x19,0xD8,0x83,0x55,0x90,0xC8,0xEC,
            0xA0,0x8F,0x01,0x2F,
    };
    static unsigned char dh512_g[]={
            0x05,
    };

    DH *dh;

    if ((dh=DH_new()) == NULL) return(NULL);
    dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL);
    dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL);
    if ((dh->p == NULL) || (dh->g == NULL))
            { DH_free(dh); return(NULL); }
    return(dh);
}

static void openssl_info_callback (SSL *s, int where, int ret)
{
    const char *str;
    int w;

    w = where & ~SSL_ST_MASK;

    if (w & SSL_ST_CONNECT) str = "SSL_connect";
    else if (w & SSL_ST_ACCEPT) str = "SSL_accept";
    else str = "undefined";

    if (where & SSL_CB_LOOP)
    {
        rl_printf ("%s:%s\n", str, SSL_state_string_long (s));
    }
    else if (where & SSL_CB_ALERT)
    {
        str = (where & SSL_CB_READ) ? "read" : "write";
        rl_printf ("SSL3 alert %s:%s:%s\n",
            str,
            SSL_alert_type_string_long (ret),
            SSL_alert_desc_string_long (ret));
    }
    else if (where & SSL_CB_EXIT)
    {
        if (ret == 0)
            rl_printf ("%s:failed in %s\n",
                str, SSL_state_string_long(s));
        else if (ret < 0)
        {
            rl_printf ("%s:%s\n",str,SSL_state_string_long(s));
        }
    }
    else if (where & SSL_CB_ALERT)
    {
        str = (where & SSL_CB_READ) ? "read" : "write";
        rl_printf ("SSL3 alert %s:%s:%s\n",
            str,
            SSL_alert_type_string_long (ret),
            SSL_alert_desc_string_long (ret));
    }
    else if (where & SSL_CB_EXIT)
    {
        if (ret == 0)
            rl_printf ("%s:failed in %s\n",
                str, SSL_state_string_long(s));
        else if (ret < 0)
        {
            rl_printf ("%s:error in %s\n",
                str, SSL_state_string_long(s));
        }
    }
}
#endif /* ENABLE_GNUTLS */

const char *ssl_strerror (Connection *conn, ssl_errno_t err, int e)
{
#if ENABLE_GNUTLS
    if (conn->ssl_status == SSL_STATUS_OK && err)
        return gnutls_strerror (err);
#else
    if (conn->ssl_status == SSL_STATUS_OK && err)
        return "OpenSSL error";
#endif
    return strerror (e);
}

/* 
 * Initialize ssl library
 *
 * Return -1 means failure, 0 means ok.
 */
int SSLInit ()
{
#if ENABLE_GNUTLS

#if !HAVE_DH_GENPARAM2
    gnutls_datum p1, p2;
#endif
    int ret;

    ssl_init_ok = 0;
    
    if (!libgnutls_is_present)
    {
        rl_printf (i18n (2581, "Install the GnuTLS library and enjoy encrypted connections to peers!\n"));
        return -1;
    }

    ret = gnutls_global_init ();
    SSL_CHECK_SUCCESS_0_OK (ret, -1, "gnutls_global_init", NULL);

    ret = gnutls_anon_allocate_client_credentials (&client_cred);
    SSL_CHECK_SUCCESS_0_OK (ret, -1, "allocate_credentials", "[client]");

    ret = gnutls_anon_allocate_server_credentials (&server_cred);
    SSL_CHECK_SUCCESS_0_OK (ret, -1, "allocate_credentials", "[server]");

    ret = gnutls_dh_params_init (&dh_parm);
    SSL_CHECK_SUCCESS_0_OK (ret, -1, "DH param init", "[server]");

#if HAVE_DH_GENPARAM2
    ret = gnutls_dh_params_generate2 (dh_parm, DH_OFFER_BITS);
    SSL_CHECK_SUCCESS_0_OK (ret, -1, "DH param generate2", "[server]");
#else
    ret = gnutls_dh_params_generate (&p1, &p2, DH_OFFER_BITS);
    SSL_CHECK_SUCCESS_0_OK (ret, -1, "DH param generate", "[server]");
    ret = gnutls_dh_params_set (dh_parm, p1, p2, DH_OFFER_BITS);
    SSL_CHECK_SUCCESS_0_OK (ret, -1, "DH param set", "[server]");
    free (p1.data);
    free (p2.data);
#endif

    gnutls_anon_set_server_dh_params (server_cred, dh_parm);

#else /* ENABLE_GNUTLS */

    DH *dh;
    SSL_library_init();
    gSSL_CTX = SSL_CTX_new (TLSv1_method ());
#if OPENSSL_VERSION_NUMBER >= 0x00905000L
    SSL_CTX_set_cipher_list (gSSL_CTX, "ADH:@STRENGTH");
#else
    SSL_CTX_set_cipher_list (gSSL_CTX, "ADH");
#endif

    if (prG->verbose & DEB_SSL)
        SSL_CTX_set_info_callback (gSSL_CTX, (void (*)())openssl_info_callback);

    dh = get_dh512 ();
    if (!dh)
        return -1;
    SSL_CTX_set_tmp_dh (gSSL_CTX, dh);
    DH_free (dh);

#endif /* ENABLE_GNUTLS */

    ssl_init_ok = 1;
    return 0;
}



#if ENABLE_GNUTLS
struct ssl_md5ctx_s {
    gcry_md_hd_t h;
};

ssl_md5ctx_t *ssl_md5_init ()
{
    ssl_md5ctx_t *ctx = malloc (sizeof *ctx);
    if (!ctx)
        return NULL;
    gcry_error_t err = gcry_md_open (&ctx->h, GCRY_MD_MD5, 0);
    if (gcry_err_code (err))
    {
        free (ctx);
        return NULL;
    }
    return ctx;
}

void ssl_md5_write (ssl_md5ctx_t *ctx, char *buf, size_t len)
{
    if (!ctx)
        return;
    gcry_md_write (ctx->h, buf, len);
}

int ssl_md5_final (ssl_md5ctx_t *ctx, char *buf)
{
    if (!ctx)
        return -1;
    gcry_md_final (ctx->h);
    unsigned char *hash = gcry_md_read (ctx->h, 0);
    assert (hash);
    memcpy (buf, hash, 16);
    gcry_md_close (ctx->h);
    free (ctx);
    return 0;
}
#else
struct ssl_md5ctx_s {
    MD5_CTX ctx;
};

ssl_md5ctx_t *ssl_md5_init ()
{
    ssl_md5ctx_t *ctx = malloc (sizeof *ctx);
    if (!ctx)
        return NULL;
    MD5_Init (&ctx->ctx);
    return ctx;
}

void ssl_md5_write (ssl_md5ctx_t *ctx, char *buf, size_t len)
{
    if (!ctx)
        return;
    MD5_Update (&ctx->ctx, buf, len);
}

int ssl_md5_final (ssl_md5ctx_t *ctx, char *buf)
{
    if (!ctx)
        return -1;
    MD5_Final ((unsigned char *)buf, &ctx->ctx);
    free (ctx);
    return 0;
}
#endif


/*
 * Check whether peer supports SSL
 *
 * Returns 0 if SSL/TLS not supported by peer.
 */
#undef ssl_supported
int ssl_supported (Connection *conn DEBUGPARAM)
{
    Contact *cont;
    UBYTE status_save = conn->ssl_status;
    
    if (!ssl_init_ok)
        return 0;   /* our SSL core is not working :( */
    
    if (conn->ssl_status == SSL_STATUS_OK)
        return 1;   /* SSL session already established */

    if (conn->ssl_status == SSL_STATUS_FAILED)
        return 0;   /* ssl handshake with peer already failed. So don't try again */

    conn->ssl_status = SSL_STATUS_FAILED;
    
    if (!(conn->type & TYPEF_ANY_PEER))
        return 0;
        
    cont = conn->cont;
    
    /* check for peer capabilities
     * Note: we never initialize SSL for incoming direct connections yet
     *        in order to avoid mutual SSL init trials among climm peers.
     */
    if (!cont)
        return 0;

    if (!(HAS_CAP (cont->caps, CAP_SIMNEW) || HAS_CAP (cont->caps, CAP_MICQ)
          || HAS_CAP (cont->caps, CAP_CLIMM) || HAS_CAP (cont->caps, CAP_LICQNEW)
          || (cont->dc && (cont->dc->id1 & 0xFFFF0000) == LICQ_WITHSSL)))
    {
        Debug (DEB_SSL, "%s (%s) is no SSL candidate", cont->nick, cont->screen);
        TCLEvent (cont, "ssl", "no_candidate");
        return 0;
    }

    conn->ssl_status = status_save;
    Debug (DEB_SSL, "%s (%s) is an SSL candidate", cont->nick, cont->screen);
    TCLEvent (cont, "ssl", "candidate");
    return 1;
}

/*
 * ssl_connect
 *
 * execute SSL handshake
 *
 * return: 1 means ok. 0 failed.
 */
#undef ssl_connect
int ssl_connect (Connection *conn, BOOL is_client DEBUGPARAM)
{
#if ENABLE_GNUTLS

    int ret;
    int kx_prio[2] = { GNUTLS_KX_ANON_DH, 0 };
#ifdef ENABLE_TCL
    Contact *cont = conn->cont;
#endif

    Debug (DEB_SSL, "ssl_connect");
    if (!ssl_init_ok || (conn->ssl_status != SSL_STATUS_NA && conn->ssl_status != SSL_STATUS_INIT && conn->ssl_status != SSL_STATUS_REQUEST))
    {
        TCLEvent (cont, "ssl", "failed precondition");
        return 0;
    }    
    conn->ssl_status = SSL_STATUS_FAILED;
    conn->connect = 1;

    ret = gnutls_init (&conn->ssl, is_client ? GNUTLS_CLIENT : GNUTLS_SERVER);
    if (ret)
        TCLEvent (cont, "ssl", "failed init");

    SSL_CHECK_SUCCESS_0_OK (ret, 0, "init", is_client ? "[client]" : "[server]");
    
    gnutls_set_default_priority (conn->ssl);
    gnutls_kx_set_priority (conn->ssl, kx_prio);

    if (is_client)
        ret = gnutls_credentials_set (conn->ssl, GNUTLS_CRD_ANON, client_cred);
    else
        ret = gnutls_credentials_set (conn->ssl, GNUTLS_CRD_ANON, server_cred);
    if (ret)
        TCLEvent (cont, "ssl", "failed key");

    SSL_CHECK_SUCCESS_0_OK (ret, 0, "credentials_set", is_client ? "[client]" : "[server]");
    if (is_client)
        /* reduce minimal prime bits expected for licq interoperability */
        gnutls_dh_set_prime_bits (conn->ssl, DH_EXPECT_BITS);
    
    gnutls_transport_set_ptr (conn->ssl, (gnutls_transport_ptr)conn->sok); /* return type void */

#else /* ENABLE_GNUTLS */

    conn->ssl = SSL_new (gSSL_CTX);
    SSL_set_session (conn->ssl, NULL);
    SSL_set_fd (conn->ssl, conn->sok);

    if (is_client)
        SSL_set_connect_state (conn->ssl);
    else
        SSL_set_accept_state (conn->ssl);

#endif /* ENABLE_GNUTLS */

    return ssl_handshake (conn) != 0;
}

/*
 * sockread wrapper for network connections
 *
 * Calls default sockread() for non-SSL connections.
 */
ssl_errno_t ssl_read (Connection *conn, UBYTE *data, UWORD len_p)
{
    int len, rc;
#if !ENABLE_GNUTLS
    int tmp;
#endif
    Contact *cont = conn->cont;
    
    if (conn->ssl_status == SSL_STATUS_HANDSHAKE)
    {
        rc = ssl_handshake (conn);
        DebugH (DEB_SSL, "ssl_sockread calling handshake [ret=%d errno=%d]", rc, errno);
        errno = EAGAIN;
        return -1;
    }
    if (conn->ssl_status != SSL_STATUS_OK)
    {
        len = sockread (conn->sok, data, len_p);
        rc = errno;
        if (!len && !rc)
            rc = ECONNRESET;
        errno = rc;
        return len;
    }

#if ENABLE_GNUTLS
    len = gnutls_record_recv (conn->ssl, data, len_p);
    if (len > 0)
        DebugH (DEB_SSL, "read %d bytes from %s", len, cont->nick);
    if (len < 0)
    {
        SSL_FAIL (s_sprintf (i18n (2376, "SSL read from %s [ERR=%d]: %s"), 
                  cont->nick, len, gnutls_strerror (len)), len);
        ssl_disconnect (conn);
    }
#else
    DebugH (DEB_SSL, "SSL_read pre");
    len = SSL_read (conn->ssl, data, len_p);
    DebugH (DEB_SSL, "SSL_read post");
    if (len > 0)
    {
        DebugH (DEB_SSL, "read %d bytes from %s", len, cont->nick);
        /* setting _W to trigger more reading */
        conn->connect |= CONNECT_SELECT_R | CONNECT_SELECT_W;
        return len;
    }
    tmp = SSL_get_error (conn->ssl, len);
    if (tmp != SSL_ERROR_NONE)
    {
        int line = -1;
        const char *file = "";
        switch (tmp)
        {
            case SSL_ERROR_WANT_WRITE:
                conn->connect |= CONNECT_SELECT_W;
                conn->connect &= ~CONNECT_SELECT_R;
                len = 0;
                break;
            case SSL_ERROR_WANT_READ:
                conn->connect |= CONNECT_SELECT_R;
                conn->connect &= ~CONNECT_SELECT_W;
                len = 0;
                break;
            case SSL_ERROR_SSL:
                ERR_get_error_line (&file, &line);
                rl_printf (i18n (2527, "OpenSSL internal error: %s:%i\n"), file, line);
                return -1;
            case SSL_ERROR_SYSCALL:
                rl_printf (i18n (2528, "OpenSSL read error: %s\n"), strerror (errno));
                ssl_disconnect (conn);
                return -1;
            case SSL_ERROR_ZERO_RETURN:
                ssl_disconnect (conn);
                return -1;
            default:
                rl_printf (i18n (2529, "OpenSSL read error %d (unknown)\n"), tmp);
                ssl_disconnect (conn);
                return -1;
        }
    }
#endif
    if (!len)
    {
        errno = EAGAIN;
        len = -1;
    }
    return len;
}

/*
 * sockwrite wrapper for network connections
 *
 * Calls default sockwrite() for non-SSL connections.
 */
ssl_errno_t ssl_write (Connection *conn, UBYTE *data, UWORD len_p)
{
    int len = 0;
#if !ENABLE_GNUTLS
    int tmp;
#endif
    Contact *cont = conn->cont;
    
    if (conn->ssl_status == SSL_STATUS_HANDSHAKE)
    {
        ssl_handshake (conn);
        return 0;
    }
    if (conn->ssl_status != SSL_STATUS_OK)
        return sockwrite (conn->sok, data, len_p);

#if ENABLE_GNUTLS
    len = gnutls_record_send (conn->ssl, data, len_p);
    if (len > 0)
        DebugH (DEB_SSL, "write %d bytes to %s", len, cont->nick);
    if (len < 0)
    {
        SSL_FAIL (s_sprintf (i18n (2377, "SSL write to %s [ERR=%d]: %s"), 
                 cont->nick, len, gnutls_strerror (len)), len);
        ssl_disconnect (conn);
    }
#else
    len = SSL_write (conn->ssl, data, len_p);
    if (len > 0)
    {
        DebugH (DEB_SSL, "sent %d/%d bytes to %s", len, len_p, cont->nick);
        return len;
    }
    tmp = SSL_get_error (conn->ssl, len);
    if (tmp != SSL_ERROR_NONE)
    {
        switch (tmp)
        {
            case SSL_ERROR_WANT_WRITE:
                conn->connect |= CONNECT_SELECT_W;
                conn->connect &= ~CONNECT_SELECT_R;
                errno = EAGAIN;
                len = -1;
                break;
            case SSL_ERROR_WANT_READ:
                conn->connect |= CONNECT_SELECT_R;
                conn->connect &= ~CONNECT_SELECT_W;
                errno = EAGAIN;
                len = -1;
                break;
            case SSL_ERROR_SYSCALL:
                rl_printf (i18n (2530, "OpenSSL write error: %s\n"), strerror (tmp));
                ssl_disconnect (conn);
                return -1;
            default:
                rl_printf (i18n (2531, "OpenSSL write error %d (unknown)\n"), tmp);
                ssl_disconnect (conn);
                return -1;
        }
    }
#endif
    return len;
}

/*
 *  call handshake in SSL lib.
 *  return: 0=failed, 1=ongoing (EAGAIN), 2=ok
 */
#undef ssl_handshake
int ssl_handshake (Connection *conn DEBUGPARAM)
{
    Contact *cont = conn->cont;
    int ret = 0;

#if ENABLE_GNUTLS
    ret = gnutls_handshake (conn->ssl);
    DebugH (DEB_SSL, "handshake %d (%d,%d)", ret, GNUTLS_E_AGAIN, GNUTLS_E_INTERRUPTED);
    if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
    {
        conn->ssl_status = SSL_STATUS_HANDSHAKE;
        conn->connect &= ~CONNECT_SELECT_R & ~CONNECT_SELECT_W;
        if (gnutls_record_get_direction (conn->ssl))
           conn->connect |= CONNECT_SELECT_W;
        else
           conn->connect |= CONNECT_SELECT_R;
        return 1;
    }
#else
    int err_i = SSL_do_handshake (conn->ssl);
    int err_j = SSL_get_error (conn->ssl, err_i);
    if (err_j != SSL_ERROR_NONE)
    {
        const char *file = "";
        int line = -1;
        switch (err_j)
        {
#ifdef SSL_ERROR_WANT_ACCEPT
            case SSL_ERROR_WANT_ACCEPT:
#endif
            case SSL_ERROR_WANT_READ:
                conn->ssl_status = SSL_STATUS_HANDSHAKE;
                conn->connect |= CONNECT_SELECT_R;
                return 1;

            case SSL_ERROR_WANT_CONNECT:
            case SSL_ERROR_WANT_WRITE:
            case SSL_ERROR_WANT_X509_LOOKUP:
                conn->ssl_status = SSL_STATUS_HANDSHAKE;
                conn->connect |= CONNECT_SELECT_R | CONNECT_SELECT_W;
                return 1;
            case SSL_ERROR_SSL:
                ERR_get_error_line (&file, &line);
                rl_printf (i18n (2527, "OpenSSL internal error: %s:%i\n"), file, line);
                ret = 1;
            default:
                rl_printf (i18n (2532, "OpenSSL error: SSL_RET=%d, SSL_ERR=%d\n"), err_i, err_j);
                ret = 1;
        }
    }
#endif

    if (ret)
    {
        TCLEvent (cont, "ssl", "failed handshake");

#if ENABLE_GNUTLS
        SSL_FAIL (s_sprintf ("%s %s", "handshake", gnutls_strerror (ret)), ret);
#else
        rl_printf (i18n (2533, "SSL handshake failed.\n"));
#endif
        conn->ssl_status = SSL_STATUS_FAILED;
        ssl_disconnect (conn);
        conn->connect = 0;
        return 0;
    }

    conn->ssl_status = SSL_STATUS_OK;
    conn->connect = CONNECT_OK | CONNECT_SELECT_R;
    if (prG->verbose)
    {
        rl_log_for (cont->nick, COLCONTACT);
        rl_printf (i18n (2375, "SSL handshake ok.\n"));
    }
    TCLEvent (cont, "ssl", "ok");

    return 2;
}

/*
 * Shutdown SSL session and release SSL memory
 */
#undef ssl_close
void ssl_close (Connection *conn DEBUGPARAM)
{
    DebugH (DEB_SSL, "ssl_close");

    if (conn->ssl_status == SSL_STATUS_OK)
    {
        DebugH (DEB_SSL, "ssl_close calling ssl_disconnect");
        ssl_disconnect (conn);
    }
    if (conn->sok != -1)
        sockclose (conn->sok);
    conn->sok = -1;
}

/*
 * Stop SSL traffic on given Connection.
 *
 * Note: does not close socket. Frees SSL data only.
 */
#undef ssl_disconnect
void ssl_disconnect (Connection *conn DEBUGPARAM)
{
    DebugH (DEB_SSL, "ssl_disconnect");

    if (conn->ssl_status == SSL_STATUS_OK)
    {
        conn->ssl_status = SSL_STATUS_NA;
        if (conn->ssl) 
#if ENABLE_GNUTLS
            free (conn->ssl);
#else
        {
            SSL_shutdown (conn->ssl);
            SSL_free (conn->ssl);
        }
#endif
        conn->ssl = NULL;
    }
}

/* 
 * Request secure channel in licq's way.
 */
BOOL TCPSendSSLReq (Connection *list, Contact *cont)
{
    Connection *peer;
    UBYTE ret;

    ret = PeerSendMsg (list, cont, MSG_SSL_OPEN, "");
    if ((peer = ServerFindChild (list->serv, cont, TYPE_MSGDIRECT)))
        peer->ssl_status = SSL_STATUS_REQUEST;
    return ret;
}                      
#endif
