From f4e5ca2f1444f9db50886640ac03e7825d5a5f2e Mon Sep 17 00:00:00 2001
From: Jernej Kovacic <jkovacic@gmail.com>
Date: Tue, 25 Oct 2011 23:50:44 +0200
Subject: [PATCH] libssh2_session_supported_algs: added

---
 docs/Makefile.am                      |    1 +
 docs/libssh2_session_supported_algs.3 |   48 ++++++++++++++
 include/libssh2.h                     |   16 +++++-
 src/kex.c                             |  110 +++++++++++++++++++++++++++++++++
 4 files changed, 174 insertions(+), 1 deletions(-)
 create mode 100644 docs/libssh2_session_supported_algs.3

diff --git a/docs/Makefile.am b/docs/Makefile.am
index bcd9faa..58dc6c8 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -112,6 +112,7 @@ dist_man_MANS = \
 	libssh2_session_set_blocking.3 \
 	libssh2_session_set_timeout.3 \
 	libssh2_session_startup.3 \
+	libssh2_session_supported_algs.3 \
 	libssh2_sftp_close.3 \
 	libssh2_sftp_close_handle.3 \
 	libssh2_sftp_closedir.3 \
diff --git a/docs/libssh2_session_supported_algs.3 b/docs/libssh2_session_supported_algs.3
new file mode 100644
index 0000000..ec54246
--- /dev/null
+++ b/docs/libssh2_session_supported_algs.3
@@ -0,0 +1,48 @@
+.TH libssh2_session_supported_algs 3 "23 Oct 2011" "libssh2 1.3.1" "libssh2 manual"
+.SH NAME
+libssh2_session_supported_algs - get list of supported algorithms
+.SH SYNOPSIS
+.nf
+#include <libssh2.h>
+
+int libssh2_session_supported_algs(LIBSSH2_SESSION* session,
+                                   int method_type,
+                                   const char*** algs);
+.SH DESCRIPTION
+\fIsession\fP - An instance of initialized LIBSSH2_SESSION (the function will
+use its pointer to the memory allocation function).  \fImethod_type\fP - Method
+type. See .BR \fIlibssh2_session_method_pref(3)\fP.  \fIalgs\fP - Address of a
+pointer that will point to an array af returned algorithms
+
+Get a list of supported algorithms for the given \fImethod_type\fP. The
+method_type parameter is equivalent to method_type in
+\fIlibssh2_session_method_pref(3)\fP. If successful, the function will
+allocate the appropriate amount of memory. When not needed anymore, it must be
+deallocated by calling \fIlibssh2_free(3)\fP. When this function is
+unsuccessful, this must not be done.
+
+In order to get a list of all supported compression algorithms,
+libssh2_session_flag(session, LIBSSH2_FLAG_COMPRESS, 1) must be called before
+calling this function, otherwise only "none" will be returned.
+
+If successful, the function will allocate and fill the array with supported
+algorithms (the same names as defined in RFC 4253).  The array is not NULL
+terminated.
+.SH RETURN VALUE
+On success, a number of returned algorithms (i.e a positive number will be
+returned).  In case of a failure, an error code (a negative number, see below)
+is returned.  0 should never be returned.
+.SH ERRORS
+\fILIBSSH2_ERROR_BAD_USE\fP - Invalid address of algs.
+
+\fILIBSSH2_ERROR_METHOD_NOT_SUPPORTED\fP -  Unknown method type.
+
+\fILIBSSH2_ERROR_INVAL\fP - Internal error (normally should not occur).
+
+\fILIBSSH2_ERROR_ALLOC\fP - Allocation of memory failed.
+.SH AVAILABILITY
+Added in 1.3.1
+.SH SEE ALSO
+.BR libssh2_session_methods(3),
+.BR libssh2_session_method_pref(3)
+.BR libssh2_free(3)
diff --git a/include/libssh2.h b/include/libssh2.h
index 8dc547c..65182cf 100644
--- a/include/libssh2.h
+++ b/include/libssh2.h
@@ -441,6 +441,20 @@ LIBSSH2_API void libssh2_exit(void);
  */
 LIBSSH2_API void libssh2_free(LIBSSH2_SESSION *session, void *ptr);
 
+/*
+ * libssh2_session_supported_algs()
+ *
+ * Fills algs with a list of supported acryptographic algorithms. Returns a
+ * non-negative number (number of supported algorithms) on success or a
+ * negative number (an eror code) on failure.
+ *
+ * NOTE: on success, algs must be deallocated (by calling libssh2_free) when
+ * not needed anymore
+ */
+LIBSSH2_API int libssh2_session_supported_algs(LIBSSH2_SESSION* session,
+                                               int method_type,
+                                               const char*** algs);
+
 /* Session API */
 LIBSSH2_API LIBSSH2_SESSION *
 libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
@@ -1027,7 +1041,7 @@ libssh2_knownhost_get(LIBSSH2_KNOWNHOSTS *hosts,
 
 struct libssh2_agent_publickey {
     unsigned int magic;              /* magic stored by the library */
-    void *node;	    /* handle to the internal representation of key */
+    void *node;     /* handle to the internal representation of key */
     unsigned char *blob;           /* public key blob */
     size_t blob_len;               /* length of the public key blob */
     char *comment;                 /* comment in printable format */
diff --git a/src/kex.c b/src/kex.c
index d26b5f3..6d2e31e 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -1896,3 +1896,113 @@ libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type,
     return 0;
 }
 
+/*
+ * libssh2_session_supported_algs()
+ * returns a number of returned algorithms (a positive number) on success,
+ * a negative number on failure
+ */
+
+LIBSSH2_API int libssh2_session_supported_algs(LIBSSH2_SESSION* session,
+                                               int method_type,
+                                               const char*** algs)
+{
+    unsigned int i;
+    unsigned int j;
+    unsigned int ialg;
+    const LIBSSH2_COMMON_METHOD **mlist;
+
+    /* to prevent coredumps due to dereferencing of NULL */
+    if (NULL == algs)
+        return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+                              "algs must not be NULL");
+
+    switch (method_type) {
+    case LIBSSH2_METHOD_KEX:
+        mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_kex_methods;
+        break;
+
+    case LIBSSH2_METHOD_HOSTKEY:
+        mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_hostkey_methods();
+        break;
+
+    case LIBSSH2_METHOD_CRYPT_CS:
+    case LIBSSH2_METHOD_CRYPT_SC:
+        mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods();
+        break;
+
+    case LIBSSH2_METHOD_MAC_CS:
+    case LIBSSH2_METHOD_MAC_SC:
+        mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods();
+        break;
+
+    case LIBSSH2_METHOD_COMP_CS:
+    case LIBSSH2_METHOD_COMP_SC:
+        mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_comp_methods(session);
+        break;
+
+    default:
+        return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                              "Unknown method type");
+    }  /* switch */
+
+    /* weird situation */
+    if (NULL==mlist)
+        return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+                              "No algorithm found");
+
+    /*
+      mlist is looped through twice. The first time to find the number od
+      supported algorithms (needed to allocate the proper size of array) and
+      the second time to actually copy the pointers.  Typically this function
+      will not be called often (typically at the beginning of a session) and
+      the number of algorithms (i.e. niumber of iterations in one loop) will
+      not be high (typically it will not exceed 20) for quite a long time.
+
+      So double looping really shouldn't be an issue and it is definitely a
+      better solution than reallocation several times.
+    */
+
+    /* count the number of supported algorithms */
+    for ( i=0, ialg=0; NULL!=mlist[i]; i++) {
+        /* do not count fields with NULL name */
+        if (mlist[i]->name)
+            ialg++;
+    }
+
+    /* weird situation, no algorithm found */
+    if (0==ialg)
+        return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+                              "No algorithm found");
+
+    /* allocate buffer */
+    *algs = (const char**) LIBSSH2_ALLOC(session, ialg*sizeof(const char*));
+    if ( NULL==*algs ) {
+        return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+                              "Memory allocation failed");
+    }
+    /* Past this point *algs must be deallocated in case of an error!! */
+
+    /* copy non-NULL pointers only */
+    for ( i=0, j=0; NULL!=mlist[i] && j<ialg; i++ ) {
+        if ( NULL==mlist[i]->name ){
+            /* maybe a weird situation but if it occurs, do not include NULL
+               pointers */
+            continue;
+        }
+
+        /* note that [] has higher priority than * (dereferencing) */
+        (*algs)[j++] = mlist[i]->name;
+    }
+
+    /* correct number of pointers copied? (test the code above) */
+    if ( j!=ialg ) {
+        /* deallocate buffer */
+        LIBSSH2_FREE(session, *algs);
+        *algs = NULL;
+
+        return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+                              "Internal error");
+    }
+
+    return ialg;
+}
-- 
1.7.7

