Compare commits
2 Commits
master
...
working/tn
Author | SHA1 | Date | |
---|---|---|---|
|
0c8f79a6a0 | ||
|
b9330e5850 |
@ -39,6 +39,7 @@ DIST_SUBDIRS = \
|
|||||||
src/protocols/rdp \
|
src/protocols/rdp \
|
||||||
src/protocols/ssh \
|
src/protocols/ssh \
|
||||||
src/protocols/telnet \
|
src/protocols/telnet \
|
||||||
|
src/protocols/tn5250 \
|
||||||
src/protocols/vnc
|
src/protocols/vnc
|
||||||
|
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
@ -73,6 +74,10 @@ if ENABLE_TELNET
|
|||||||
SUBDIRS += src/protocols/telnet
|
SUBDIRS += src/protocols/telnet
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if ENABLE_TN5250
|
||||||
|
SUBDIRS += src/protocols/tn5250
|
||||||
|
endif
|
||||||
|
|
||||||
if ENABLE_VNC
|
if ENABLE_VNC
|
||||||
SUBDIRS += src/protocols/vnc
|
SUBDIRS += src/protocols/vnc
|
||||||
endif
|
endif
|
||||||
|
@ -1004,6 +1004,9 @@ fi
|
|||||||
AM_CONDITIONAL([ENABLE_TELNET], [test "x${have_libtelnet}" = "xyes" \
|
AM_CONDITIONAL([ENABLE_TELNET], [test "x${have_libtelnet}" = "xyes" \
|
||||||
-a "x${have_terminal}" = "xyes"])
|
-a "x${have_terminal}" = "xyes"])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_TN5250], [test "x${have_libtelnet}" = "xyes" \
|
||||||
|
-a "x${have_terminal}" = "xyes"])
|
||||||
|
|
||||||
AC_SUBST(TELNET_LIBS)
|
AC_SUBST(TELNET_LIBS)
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1190,6 +1193,7 @@ AC_CONFIG_FILES([Makefile
|
|||||||
src/protocols/rdp/tests/Makefile
|
src/protocols/rdp/tests/Makefile
|
||||||
src/protocols/ssh/Makefile
|
src/protocols/ssh/Makefile
|
||||||
src/protocols/telnet/Makefile
|
src/protocols/telnet/Makefile
|
||||||
|
src/protocols/tn5250/Makefile
|
||||||
src/protocols/vnc/Makefile])
|
src/protocols/vnc/Makefile])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|
||||||
@ -1201,6 +1205,7 @@ AM_COND_IF([ENABLE_KUBERNETES], [build_kubernetes=yes], [build_kubernetes=no])
|
|||||||
AM_COND_IF([ENABLE_RDP], [build_rdp=yes], [build_rdp=no])
|
AM_COND_IF([ENABLE_RDP], [build_rdp=yes], [build_rdp=no])
|
||||||
AM_COND_IF([ENABLE_SSH], [build_ssh=yes], [build_ssh=no])
|
AM_COND_IF([ENABLE_SSH], [build_ssh=yes], [build_ssh=no])
|
||||||
AM_COND_IF([ENABLE_TELNET], [build_telnet=yes], [build_telnet=no])
|
AM_COND_IF([ENABLE_TELNET], [build_telnet=yes], [build_telnet=no])
|
||||||
|
AM_COND_IF([ENABLE_TN5250], [build_tn5250=yes], [build_tn5250=no])
|
||||||
AM_COND_IF([ENABLE_VNC], [build_vnc=yes], [build_vnc=no])
|
AM_COND_IF([ENABLE_VNC], [build_vnc=yes], [build_vnc=no])
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1262,6 +1267,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
|
|||||||
RDP ........... ${build_rdp}
|
RDP ........... ${build_rdp}
|
||||||
SSH ........... ${build_ssh}
|
SSH ........... ${build_ssh}
|
||||||
Telnet ........ ${build_telnet}
|
Telnet ........ ${build_telnet}
|
||||||
|
TN5250 ........ ${build_tn5250}
|
||||||
VNC ........... ${build_vnc}
|
VNC ........... ${build_vnc}
|
||||||
|
|
||||||
Services / tools:
|
Services / tools:
|
||||||
|
67
src/protocols/tn5250/Makefile.am
Normal file
67
src/protocols/tn5250/Makefile.am
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
# NOTE: Parts of this file (Makefile.am) are automatically transcluded verbatim
|
||||||
|
# into Makefile.in. Though the build system (GNU Autotools) automatically adds
|
||||||
|
# its own license boilerplate to the generated Makefile.in, that boilerplate
|
||||||
|
# does not apply to the transcluded portions of Makefile.am which are licensed
|
||||||
|
# to you by the ASF under the Apache License, Version 2.0, as described above.
|
||||||
|
#
|
||||||
|
|
||||||
|
AUTOMAKE_OPTIONS = foreign
|
||||||
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
|
|
||||||
|
lib_LTLIBRARIES = libguac-client-tn5250.la
|
||||||
|
|
||||||
|
libguac_client_tn5250_la_SOURCES = \
|
||||||
|
argv.c \
|
||||||
|
client.c \
|
||||||
|
clipboard.c \
|
||||||
|
input.c \
|
||||||
|
pipe.c \
|
||||||
|
settings.c \
|
||||||
|
tn5250.c \
|
||||||
|
user.c \
|
||||||
|
lib5250/lib5250.c
|
||||||
|
|
||||||
|
noinst_HEADERS = \
|
||||||
|
argv.h \
|
||||||
|
client.h \
|
||||||
|
clipboard.h \
|
||||||
|
input.h \
|
||||||
|
pipe.h \
|
||||||
|
settings.h \
|
||||||
|
tn5250.h \
|
||||||
|
user.h \
|
||||||
|
lib5250/lib5250.h
|
||||||
|
|
||||||
|
libguac_client_tn5250_la_CFLAGS = \
|
||||||
|
-Werror -Wall -Iinclude \
|
||||||
|
@LIBGUAC_INCLUDE@ \
|
||||||
|
@TERMINAL_INCLUDE@
|
||||||
|
|
||||||
|
libguac_client_tn5250_la_LIBADD = \
|
||||||
|
@COMMON_LTLIB@ \
|
||||||
|
@LIBGUAC_LTLIB@ \
|
||||||
|
@TERMINAL_LTLIB@
|
||||||
|
|
||||||
|
libguac_client_tn5250_la_LDFLAGS = \
|
||||||
|
-version-info 0:0:0 \
|
||||||
|
@PTHREAD_LIBS@ \
|
||||||
|
@TELNET_LIBS@
|
||||||
|
|
84
src/protocols/tn5250/argv.c
Normal file
84
src/protocols/tn5250/argv.c
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "argv.h"
|
||||||
|
#include "tn5250.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
|
||||||
|
#include <guacamole/protocol.h>
|
||||||
|
#include <guacamole/socket.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int guac_tn5250_argv_callback(guac_user* user, const char* mimetype,
|
||||||
|
const char* name, const char* value, void* data) {
|
||||||
|
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_terminal* terminal = tn5250_client->term;
|
||||||
|
|
||||||
|
/* Update color scheme */
|
||||||
|
if (strcmp(name, GUAC_TN5250_ARGV_COLOR_SCHEME) == 0)
|
||||||
|
guac_terminal_apply_color_scheme(terminal, value);
|
||||||
|
|
||||||
|
/* Update font name */
|
||||||
|
else if (strcmp(name, GUAC_TN5250_ARGV_FONT_NAME) == 0)
|
||||||
|
guac_terminal_apply_font(terminal, value, -1, 0);
|
||||||
|
|
||||||
|
/* Update only if font size is sane */
|
||||||
|
else if (strcmp(name, GUAC_TN5250_ARGV_FONT_SIZE) == 0) {
|
||||||
|
int size = atoi(value);
|
||||||
|
if (size > 0)
|
||||||
|
guac_terminal_apply_font(terminal, NULL, size,
|
||||||
|
tn5250_client->settings->resolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void* guac_tn5250_send_current_argv(guac_user* user, void* data) {
|
||||||
|
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) data;
|
||||||
|
guac_terminal* terminal = tn5250_client->term;
|
||||||
|
|
||||||
|
/* Send current color scheme */
|
||||||
|
guac_user_stream_argv(user, user->socket, "text/plain",
|
||||||
|
GUAC_TN5250_ARGV_COLOR_SCHEME,
|
||||||
|
guac_terminal_get_color_scheme(terminal));
|
||||||
|
|
||||||
|
/* Send current font name */
|
||||||
|
guac_user_stream_argv(user, user->socket, "text/plain",
|
||||||
|
GUAC_TN5250_ARGV_FONT_NAME,
|
||||||
|
guac_terminal_get_font_name(terminal));
|
||||||
|
|
||||||
|
/* Send current font size */
|
||||||
|
char font_size[64];
|
||||||
|
sprintf(font_size, "%i", guac_terminal_get_font_size(terminal));
|
||||||
|
guac_user_stream_argv(user, user->socket, "text/plain",
|
||||||
|
GUAC_TN5250_ARGV_FONT_SIZE, font_size);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
73
src/protocols/tn5250/argv.h
Normal file
73
src/protocols/tn5250/argv.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUAC_TN5250_ARGV_H
|
||||||
|
#define GUAC_TN5250_ARGV_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <guacamole/argv.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the parameter that specifies/updates the color scheme used by
|
||||||
|
* the terminal emulator.
|
||||||
|
*/
|
||||||
|
#define GUAC_TN5250_ARGV_COLOR_SCHEME "color-scheme"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the parameter that specifies/updates the name of the font used
|
||||||
|
* by the terminal emulator.
|
||||||
|
*/
|
||||||
|
#define GUAC_TN5250_ARGV_FONT_NAME "font-name"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the parameter that specifies/updates the font size used by the
|
||||||
|
* terminal emulator.
|
||||||
|
*/
|
||||||
|
#define GUAC_TN5250_ARGV_FONT_SIZE "font-size"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a received argument value from a Guacamole "argv" instruction,
|
||||||
|
* updating the given connection parameter.
|
||||||
|
*/
|
||||||
|
guac_argv_callback guac_tn5250_argv_callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the current values of all non-sensitive parameters which may be set
|
||||||
|
* while the connection is running to the given user. Note that the user
|
||||||
|
* receiving these values will not necessarily be able to set new values
|
||||||
|
* themselves if their connection is read-only. This function can be used as
|
||||||
|
* the callback for guac_client_foreach_user() and guac_client_for_owner()
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The user that should receive the values of all non-sensitive parameters
|
||||||
|
* which may be set while the connection is running.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* The guac_n5250_client instance associated with the current connection.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Always NULL.
|
||||||
|
*/
|
||||||
|
void* guac_tn5250_send_current_argv(guac_user* user, void* data);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
105
src/protocols/tn5250/client.c
Normal file
105
src/protocols/tn5250/client.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "argv.h"
|
||||||
|
#include "client.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "tn5250.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
#include <langinfo.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <guacamole/argv.h>
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/recording.h>
|
||||||
|
|
||||||
|
int guac_client_init(guac_client* client) {
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_INFO, "%s: Starting initialization of TN5250 client.", __func__);
|
||||||
|
|
||||||
|
/* Set client args */
|
||||||
|
client->args = GUAC_TN5250_CLIENT_ARGS;
|
||||||
|
|
||||||
|
/* Allocate client instance data */
|
||||||
|
guac_tn5250_client* tn5250_client = calloc(1, sizeof(guac_tn5250_client));
|
||||||
|
client->data = tn5250_client;
|
||||||
|
|
||||||
|
/* Init tn5250 client */
|
||||||
|
tn5250_client->socket_fd = -1;
|
||||||
|
tn5250_client->echo_enabled = 1;
|
||||||
|
|
||||||
|
/* Set handlers */
|
||||||
|
client->join_handler = guac_tn5250_user_join_handler;
|
||||||
|
client->free_handler = guac_tn5250_client_free_handler;
|
||||||
|
client->leave_handler = guac_tn5250_user_leave_handler;
|
||||||
|
|
||||||
|
/* Register handlers for argument values that may be sent after the handshake */
|
||||||
|
guac_argv_register(GUAC_TN5250_ARGV_COLOR_SCHEME, guac_tn5250_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO);
|
||||||
|
guac_argv_register(GUAC_TN5250_ARGV_FONT_NAME, guac_tn5250_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO);
|
||||||
|
guac_argv_register(GUAC_TN5250_ARGV_FONT_SIZE, guac_tn5250_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO);
|
||||||
|
|
||||||
|
/* Set locale and warn if not UTF-8 */
|
||||||
|
setlocale(LC_CTYPE, "");
|
||||||
|
if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0) {
|
||||||
|
guac_client_log(client, GUAC_LOG_INFO,
|
||||||
|
"Current locale does not use UTF-8. Some characters may "
|
||||||
|
"not render correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_tn5250_client_free_handler(guac_client* client) {
|
||||||
|
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
|
||||||
|
/* Close tn5250 connection */
|
||||||
|
if (tn5250_client->socket_fd != -1)
|
||||||
|
close(tn5250_client->socket_fd);
|
||||||
|
|
||||||
|
/* Clean up recording, if in progress */
|
||||||
|
if (tn5250_client->recording != NULL)
|
||||||
|
guac_recording_free(tn5250_client->recording);
|
||||||
|
|
||||||
|
/* Kill terminal */
|
||||||
|
guac_terminal_free(tn5250_client->term);
|
||||||
|
|
||||||
|
/* Wait for and free telnet session, if connected */
|
||||||
|
if (tn5250_client->tn5250 != NULL) {
|
||||||
|
pthread_join(tn5250_client->client_thread, NULL);
|
||||||
|
telnet_free(tn5250_client->tn5250);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free settings */
|
||||||
|
if (tn5250_client->settings != NULL)
|
||||||
|
guac_tn5250_settings_free(tn5250_client->settings);
|
||||||
|
|
||||||
|
free(tn5250_client);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
39
src/protocols/tn5250/client.h
Normal file
39
src/protocols/tn5250/client.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_TN5250_CLIENT_H
|
||||||
|
#define GUAC_TN5250_CLIENT_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <libtelnet.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free handler. Required by libguac and called when the guac_client is
|
||||||
|
* disconnected and must be cleaned up.
|
||||||
|
*/
|
||||||
|
guac_client_free_handler guac_tn5250_client_free_handler;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
61
src/protocols/tn5250/clipboard.c
Normal file
61
src/protocols/tn5250/clipboard.c
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "clipboard.h"
|
||||||
|
#include "tn5250.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/stream.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
int guac_tn5250_clipboard_handler(guac_user* user, guac_stream* stream,
|
||||||
|
char* mimetype) {
|
||||||
|
|
||||||
|
/* Clear clipboard and prepare for new data */
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_terminal_clipboard_reset(tn5250_client->term, mimetype);
|
||||||
|
|
||||||
|
/* Set handlers for clipboard stream */
|
||||||
|
stream->blob_handler = guac_tn5250_clipboard_blob_handler;
|
||||||
|
stream->end_handler = guac_tn5250_clipboard_end_handler;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_tn5250_clipboard_blob_handler(guac_user* user, guac_stream* stream,
|
||||||
|
void* data, int length) {
|
||||||
|
|
||||||
|
/* Append new data */
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_terminal_clipboard_append(tn5250_client->term, data, length);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_tn5250_clipboard_end_handler(guac_user* user, guac_stream* stream) {
|
||||||
|
|
||||||
|
/* Nothing to do - clipboard is implemented within client */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
43
src/protocols/tn5250/clipboard.h
Normal file
43
src/protocols/tn5250/clipboard.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_TN5250_CLIPBOARD_H
|
||||||
|
#define GUAC_TN5250_CLIPBOARD_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for inbound clipboard streams.
|
||||||
|
*/
|
||||||
|
guac_user_clipboard_handler guac_tn5250_clipboard_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for data received along clipboard streams.
|
||||||
|
*/
|
||||||
|
guac_user_blob_handler guac_tn5250_clipboard_blob_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for end-of-stream related to clipboard.
|
||||||
|
*/
|
||||||
|
guac_user_end_handler guac_tn5250_clipboard_end_handler;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
138
src/protocols/tn5250/input.c
Normal file
138
src/protocols/tn5250/input.c
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
#include "tn5250.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/recording.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
#include <libtelnet.h>
|
||||||
|
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int guac_tn5250_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
||||||
|
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_tn5250_settings* settings = tn5250_client->settings;
|
||||||
|
guac_terminal* term = tn5250_client->term;
|
||||||
|
|
||||||
|
/* Skip if terminal not yet ready */
|
||||||
|
if (term == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Report mouse position within recording */
|
||||||
|
if (tn5250_client->recording != NULL)
|
||||||
|
guac_recording_report_mouse(tn5250_client->recording, x, y,
|
||||||
|
mask);
|
||||||
|
|
||||||
|
/* Send mouse if not searching for password or username */
|
||||||
|
if (settings->password_regex == NULL && settings->username_regex == NULL)
|
||||||
|
guac_terminal_send_mouse(term, user, x, y, mask);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_tn5250_user_key_handler(guac_user* user, int keysym, int pressed) {
|
||||||
|
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_tn5250_settings* settings = tn5250_client->settings;
|
||||||
|
guac_terminal* term = tn5250_client->term;
|
||||||
|
|
||||||
|
/* Report key state within recording */
|
||||||
|
if (tn5250_client->recording != NULL)
|
||||||
|
guac_recording_report_key(tn5250_client->recording,
|
||||||
|
keysym, pressed);
|
||||||
|
|
||||||
|
/* Skip if terminal not yet ready */
|
||||||
|
if (term == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Stop searching for password */
|
||||||
|
if (settings->password_regex != NULL) {
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"Stopping password prompt search due to user input.");
|
||||||
|
|
||||||
|
regfree(settings->password_regex);
|
||||||
|
free(settings->password_regex);
|
||||||
|
settings->password_regex = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop searching for username */
|
||||||
|
if (settings->username_regex != NULL) {
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"Stopping username prompt search due to user input.");
|
||||||
|
|
||||||
|
regfree(settings->username_regex);
|
||||||
|
free(settings->username_regex);
|
||||||
|
settings->username_regex = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Intercept and handle Pause / Break / Ctrl+0 as "IAC BRK" */
|
||||||
|
if (pressed && (
|
||||||
|
keysym == 0xFF13 /* Pause */
|
||||||
|
|| keysym == 0xFF6B /* Break */
|
||||||
|
|| (
|
||||||
|
guac_terminal_get_mod_ctrl(term)
|
||||||
|
&& keysym == '0'
|
||||||
|
) /* Ctrl + 0 */
|
||||||
|
)) {
|
||||||
|
|
||||||
|
/* Send IAC BRK */
|
||||||
|
telnet_iac(tn5250_client->tn5250, TELNET_BREAK);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send key */
|
||||||
|
guac_terminal_send_key(term, keysym, pressed);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_tn5250_user_size_handler(guac_user* user, int width, int height) {
|
||||||
|
|
||||||
|
/* Get terminal */
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_terminal* terminal = tn5250_client->term;
|
||||||
|
|
||||||
|
/* Skip if terminal not yet ready */
|
||||||
|
if (terminal == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Resize terminal */
|
||||||
|
guac_terminal_resize(terminal, width, height);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
46
src/protocols/tn5250/input.h
Normal file
46
src/protocols/tn5250/input.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_TN5250_INPUT_H
|
||||||
|
#define GUAC_TN5250_INPUT_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for key events. Required by libguac and called whenever key events
|
||||||
|
* are received.
|
||||||
|
*/
|
||||||
|
guac_user_key_handler guac_tn5250_user_key_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for mouse events. Required by libguac and called whenever mouse
|
||||||
|
* events are received.
|
||||||
|
*/
|
||||||
|
guac_user_mouse_handler guac_tn5250_user_mouse_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for size events. Required by libguac and called whenever the remote
|
||||||
|
* display (window) is resized.
|
||||||
|
*/
|
||||||
|
guac_user_size_handler guac_tn5250_user_size_handler;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
105
src/protocols/tn5250/lib5250/lib5250.c
Normal file
105
src/protocols/tn5250/lib5250/lib5250.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "lib5250.h"
|
||||||
|
#include "tn5250.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
int guac_lib5250_process_packet(const char* data, guac_client* client) {
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "%s: Processing expected TN5250 SNA data.", __func__);
|
||||||
|
|
||||||
|
guac_lib5250_packet* packet = (guac_lib5250_packet*) data;
|
||||||
|
|
||||||
|
if (ntohs(packet->type) != GUAC_LIB5250_SNA_GDS) {
|
||||||
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
||||||
|
"%s: Unexpected SNA packet typing parsing TN5250 packet: 0x%x",
|
||||||
|
__func__, ntohs(packet->type));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (packet->opcode) {
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_NOOP:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode no-op.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_INVITE:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode invite.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_OUTPUT:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode output.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_PUTGET:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode put/get.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_SAVESCREEN:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode save screen.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_RESTORESCREEN:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode restore screen.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_READIMMEDIATE:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode read immediate.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_READSCREEN:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode read screen.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_CANCELINVITE:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode cancel invite.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_MSGON:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode msg on.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GUAC_LIB5250_OPCODE_MSGOFF:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received opcode msg off.", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Received unknown opcode.", __func__);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: SNA data length: %d bytes.", __func__, ntohs(packet->len));
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Packet type: 0x%x", __func__, ntohs(packet->type));
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Reserved space: 0x%x", __func__, ntohs(packet->reserved));
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Variable header length: 0x%x.", __func__, packet->varlen);
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: SNA Flags: 0x%x", __func__, packet->flags);
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: SNA Rerved flags: 0x%x", __func__, packet->reserved_flags);
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: SNA Opcode: 0x%x", __func__, packet->opcode);
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: WHOLE ENCHILADA: 0x%x", __func__, packet);
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_WARNING, "%s: Everything else is unimplemented, now.", __func__);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
207
src/protocols/tn5250/lib5250/lib5250.h
Normal file
207
src/protocols/tn5250/lib5250/lib5250.h
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_LIB5250_H
|
||||||
|
#define GUAC_LIB5250_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "tn5250.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The General Data Stream SNA packet type.
|
||||||
|
*/
|
||||||
|
#define GUAC_LIB5250_SNA_GDS 0x12a0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enum that represents all of the available terminal types according to
|
||||||
|
* RFC1205, the specification that documents the Telnet 5250 (TN5250)
|
||||||
|
* standard.
|
||||||
|
*/
|
||||||
|
typedef enum guac_lib5250_terminal {
|
||||||
|
|
||||||
|
/* 24 x 80 Double-Byte Character Set color display */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_5555_C01,
|
||||||
|
|
||||||
|
/* 24 x 80 Double-Byte Character Set (DBCS) */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_5555_B01,
|
||||||
|
|
||||||
|
/* 27 x 132 color display */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_3477_FC,
|
||||||
|
|
||||||
|
/* 27 x 132 monochrome display */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_3477_FG,
|
||||||
|
|
||||||
|
/* 27 x 132 monochrome display */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_3180_2,
|
||||||
|
|
||||||
|
/* 24 x 80 color display */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_3179_2,
|
||||||
|
|
||||||
|
/* 24 x 80 monochrome display */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_3196_A1,
|
||||||
|
|
||||||
|
/* 24 x 80 color display */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_5292_2,
|
||||||
|
|
||||||
|
/* 24 x 80 monochrome display */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_5291_1,
|
||||||
|
|
||||||
|
/* 24 x 80 monochrome display */
|
||||||
|
GUAC_LIB5250_TERMINAL_IBM_5251_11
|
||||||
|
|
||||||
|
} guac_lib5250_terminal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A data type that captures a terminal type, from the guac_lib5250_terminal
|
||||||
|
* enum, and the capabilities associated with that terminal.
|
||||||
|
*/
|
||||||
|
typedef struct guac_lib5250_terminal_cap {
|
||||||
|
|
||||||
|
/* The name of the terminal. */
|
||||||
|
guac_lib5250_terminal name;
|
||||||
|
|
||||||
|
/* The number of rows supported on the terminal. */
|
||||||
|
unsigned int rows;
|
||||||
|
|
||||||
|
/* The number of columns supports on the terminal. */
|
||||||
|
unsigned int cols;
|
||||||
|
|
||||||
|
/* 1 if the terminal supports colors, 0 if it is monochrome. */
|
||||||
|
unsigned int color;
|
||||||
|
|
||||||
|
/* The number of bytes-per-character the terminal supports. This must
|
||||||
|
* be either 1 or 2. */
|
||||||
|
unsigned int charbytes;
|
||||||
|
|
||||||
|
} guac_lib5250_terminal_cap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of the possible TN5250 opcodes, which can be sent to the client to
|
||||||
|
* direct it to do something with the terminal. These are documented in RFC1205.
|
||||||
|
*/
|
||||||
|
typedef enum guac_lib5250_opcode {
|
||||||
|
|
||||||
|
/* No Operation */
|
||||||
|
GUAC_LIB5250_OPCODE_NOOP = 0x00,
|
||||||
|
|
||||||
|
/* Invite terminal */
|
||||||
|
GUAC_LIB5250_OPCODE_INVITE = 0x01,
|
||||||
|
|
||||||
|
/* Output only */
|
||||||
|
GUAC_LIB5250_OPCODE_OUTPUT = 0x02,
|
||||||
|
|
||||||
|
/* Put/Get */
|
||||||
|
GUAC_LIB5250_OPCODE_PUTGET = 0x03,
|
||||||
|
|
||||||
|
/* Save screen */
|
||||||
|
GUAC_LIB5250_OPCODE_SAVESCREEN = 0x04,
|
||||||
|
|
||||||
|
/* Restore screen */
|
||||||
|
GUAC_LIB5250_OPCODE_RESTORESCREEN = 0x05,
|
||||||
|
|
||||||
|
/* Read immediate */
|
||||||
|
GUAC_LIB5250_OPCODE_READIMMEDIATE = 0x06,
|
||||||
|
|
||||||
|
/* Read screen */
|
||||||
|
GUAC_LIB5250_OPCODE_READSCREEN = 0x08,
|
||||||
|
|
||||||
|
/* Cancel invite */
|
||||||
|
GUAC_LIB5250_OPCODE_CANCELINVITE = 0x0A,
|
||||||
|
|
||||||
|
/* Message light on */
|
||||||
|
GUAC_LIB5250_OPCODE_MSGON = 0x0B,
|
||||||
|
|
||||||
|
/* Message light off */
|
||||||
|
GUAC_LIB5250_OPCODE_MSGOFF = 0x0C
|
||||||
|
|
||||||
|
} guac_lib5250_opcode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A data type that defines the structure of the packet for 5250 communication
|
||||||
|
* across telnet. The structure of this packet is based off of the definition
|
||||||
|
* in RFC1205.
|
||||||
|
*/
|
||||||
|
typedef struct guac_lib5250_packet {
|
||||||
|
|
||||||
|
/* The length of the entire logical record. */
|
||||||
|
uint16_t len;
|
||||||
|
|
||||||
|
/* The SNA record type. This should always be 0x12A0, which is the
|
||||||
|
* General Data Stream record. */
|
||||||
|
uint16_t type;
|
||||||
|
|
||||||
|
/* Empty space, should be zerod. */
|
||||||
|
uint16_t reserved;
|
||||||
|
|
||||||
|
/* The length of the variable portion of the header, in octets. According
|
||||||
|
* to the RFC, this should always be 0x04. */
|
||||||
|
uint8_t varlen;
|
||||||
|
|
||||||
|
/* Flags associated with the header, as documented in the RFC. The
|
||||||
|
* bits are as follows:
|
||||||
|
* Bit 0: ERR - Data stream output error.
|
||||||
|
* Bit 1: ATN - Indicates Attention key was pressed.
|
||||||
|
* Bit 2-4: Reserved, zeros.
|
||||||
|
* Bit 5: SRQ - System Request key has been pressed.
|
||||||
|
* Bit 6: TRQ - Test Request key has been pressed.
|
||||||
|
* Bit 7: HLP - Help in Error State, with actual help code sent as data after
|
||||||
|
* the header.
|
||||||
|
*/
|
||||||
|
uint8_t flags;
|
||||||
|
|
||||||
|
/* At present, the remaining 8 bits of flags are not used at all. As old
|
||||||
|
* as RFC1205 is, I doubt that's likely to change. These bits should be
|
||||||
|
* all zeroes.
|
||||||
|
*/
|
||||||
|
uint8_t reserved_flags;
|
||||||
|
|
||||||
|
/* The operation code, as requested by the sender. Valid values for this
|
||||||
|
* are in the gauc_lib5250_opcode enum.
|
||||||
|
*/
|
||||||
|
uint8_t opcode;
|
||||||
|
|
||||||
|
/* What remains is the data in the packet, which should consume the
|
||||||
|
* remainder of the space that the header did not take up. The packet
|
||||||
|
* should also be terminated with a Telnet IAC EOR record, 0xffef.
|
||||||
|
*/
|
||||||
|
void* data;
|
||||||
|
|
||||||
|
} guac_lib5250_packet __attribute__ ((aligned));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the packet that libtelnet has received and then takes action
|
||||||
|
* based on the contents of the packet.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* The data buffer delivered by libtelnet, which should be binary telnet
|
||||||
|
* data that is the actual TN5250 protocol.
|
||||||
|
*
|
||||||
|
* @param client
|
||||||
|
* The Guacamole client associated with this connection.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Zero if the data was successfully processed, non-zero otherwise.
|
||||||
|
*/
|
||||||
|
int guac_lib5250_process_packet(const char* data, guac_client* client);
|
||||||
|
|
||||||
|
#endif // GUAC_LIB5250_H
|
50
src/protocols/tn5250/pipe.c
Normal file
50
src/protocols/tn5250/pipe.c
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "pipe.h"
|
||||||
|
#include "tn5250.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
|
||||||
|
#include <guacamole/protocol.h>
|
||||||
|
#include <guacamole/socket.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int guac_tn5250_pipe_handler(guac_user* user, guac_stream* stream,
|
||||||
|
char* mimetype, char* name) {
|
||||||
|
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
|
||||||
|
/* Redirect STDIN if pipe has required name */
|
||||||
|
if (strcmp(name, GUAC_TN5250_STDIN_PIPE_NAME) == 0) {
|
||||||
|
guac_terminal_send_stream(tn5250_client->term, user, stream);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No other inbound pipe streams are supported */
|
||||||
|
guac_protocol_send_ack(user->socket, stream, "No such input stream.",
|
||||||
|
GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND);
|
||||||
|
guac_socket_flush(user->socket);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
42
src/protocols/tn5250/pipe.h
Normal file
42
src/protocols/tn5250/pipe.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUAC_TN5250_PIPE_H
|
||||||
|
#define GUAC_TN5250_PIPE_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name reserved for the inbound pipe stream which forces the terminal
|
||||||
|
* emulator's STDIN to be received from the pipe.
|
||||||
|
*/
|
||||||
|
#define GUAC_TN5250_STDIN_PIPE_NAME "STDIN"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an incoming stream from a Guacamole "pipe" instruction. If the pipe
|
||||||
|
* is named "STDIN", the the contents of the pipe stream are redirected to
|
||||||
|
* STDIN of the terminal emulator for as long as the pipe is open.
|
||||||
|
*/
|
||||||
|
guac_user_pipe_handler guac_tn5250_pipe_handler;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
501
src/protocols/tn5250/settings.c
Normal file
501
src/protocols/tn5250/settings.c
Normal file
@ -0,0 +1,501 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "argv.h"
|
||||||
|
#include "common/defaults.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
#include <guacamole/wol-constants.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* Client plugin arguments */
|
||||||
|
const char* GUAC_TN5250_CLIENT_ARGS[] = {
|
||||||
|
"hostname",
|
||||||
|
"port",
|
||||||
|
"username",
|
||||||
|
"username-regex",
|
||||||
|
"password",
|
||||||
|
"password-regex",
|
||||||
|
GUAC_TN5250_ARGV_FONT_NAME,
|
||||||
|
GUAC_TN5250_ARGV_FONT_SIZE,
|
||||||
|
GUAC_TN5250_ARGV_COLOR_SCHEME,
|
||||||
|
"typescript-path",
|
||||||
|
"typescript-name",
|
||||||
|
"create-typescript-path",
|
||||||
|
"recording-path",
|
||||||
|
"recording-name",
|
||||||
|
"recording-exclude-output",
|
||||||
|
"recording-exclude-mouse",
|
||||||
|
"recording-include-keys",
|
||||||
|
"create-recording-path",
|
||||||
|
"read-only",
|
||||||
|
"backspace",
|
||||||
|
"terminal-type",
|
||||||
|
"scrollback",
|
||||||
|
"login-success-regex",
|
||||||
|
"login-failure-regex",
|
||||||
|
"disable-copy",
|
||||||
|
"disable-paste",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TN5250_ARGS_IDX {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hostname to connect to. Required.
|
||||||
|
*/
|
||||||
|
IDX_HOSTNAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The port to connect to. Optional.
|
||||||
|
*/
|
||||||
|
IDX_PORT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the user to login as. Optional.
|
||||||
|
*/
|
||||||
|
IDX_USERNAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for the username/login
|
||||||
|
* prompt. Optional.
|
||||||
|
*/
|
||||||
|
IDX_USERNAME_REGEX,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password to use when logging in. Optional.
|
||||||
|
*/
|
||||||
|
IDX_PASSWORD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for the password prompt.
|
||||||
|
* Optional.
|
||||||
|
*/
|
||||||
|
IDX_PASSWORD_REGEX,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the font to use within the terminal.
|
||||||
|
*/
|
||||||
|
IDX_FONT_NAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the font to use within the terminal, in points.
|
||||||
|
*/
|
||||||
|
IDX_FONT_SIZE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color scheme to use, as a series of semicolon-separated color-value
|
||||||
|
* pairs: "background: <color>", "foreground: <color>", or
|
||||||
|
* "color<n>: <color>", where <n> is a number from 0 to 255, and <color> is
|
||||||
|
* "color<n>" or an X11 color code (e.g. "aqua" or "rgb:12/34/56").
|
||||||
|
* The color scheme can also be one of the special values: "black-white",
|
||||||
|
* "white-black", "gray-black", or "green-black".
|
||||||
|
*/
|
||||||
|
IDX_COLOR_SCHEME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The full absolute path to the directory in which typescripts should be
|
||||||
|
* written.
|
||||||
|
*/
|
||||||
|
IDX_TYPESCRIPT_PATH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name that should be given to typescripts which are written in the
|
||||||
|
* given path. Each typescript will consist of two files: "NAME" and
|
||||||
|
* "NAME.timing".
|
||||||
|
*/
|
||||||
|
IDX_TYPESCRIPT_NAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the specified typescript path should automatically be created
|
||||||
|
* if it does not yet exist.
|
||||||
|
*/
|
||||||
|
IDX_CREATE_TYPESCRIPT_PATH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The full absolute path to the directory in which screen recordings
|
||||||
|
* should be written.
|
||||||
|
*/
|
||||||
|
IDX_RECORDING_PATH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name that should be given to screen recordings which are written in
|
||||||
|
* the given path.
|
||||||
|
*/
|
||||||
|
IDX_RECORDING_NAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether output which is broadcast to each connected client (graphics,
|
||||||
|
* streams, etc.) should NOT be included in the session recording. Output
|
||||||
|
* is included by default, as it is necessary for any recording which must
|
||||||
|
* later be viewable as video.
|
||||||
|
*/
|
||||||
|
IDX_RECORDING_EXCLUDE_OUTPUT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether changes to mouse state, such as position and buttons pressed or
|
||||||
|
* released, should NOT be included in the session recording. Mouse state
|
||||||
|
* is included by default, as it is necessary for the mouse cursor to be
|
||||||
|
* rendered in any resulting video.
|
||||||
|
*/
|
||||||
|
IDX_RECORDING_EXCLUDE_MOUSE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether keys pressed and released should be included in the session
|
||||||
|
* recording. Key events are NOT included by default within the recording,
|
||||||
|
* as doing so has privacy and security implications. Including key events
|
||||||
|
* may be necessary in certain auditing contexts, but should only be done
|
||||||
|
* with caution. Key events can easily contain sensitive information, such
|
||||||
|
* as passwords, credit card numbers, etc.
|
||||||
|
*/
|
||||||
|
IDX_RECORDING_INCLUDE_KEYS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the specified screen recording path should automatically be
|
||||||
|
* created if it does not yet exist.
|
||||||
|
*/
|
||||||
|
IDX_CREATE_RECORDING_PATH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "true" if this connection should be read-only (user input should be
|
||||||
|
* dropped), "false" or blank otherwise.
|
||||||
|
*/
|
||||||
|
IDX_READ_ONLY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASCII code, as an integer to use for the backspace key, or
|
||||||
|
* GUAC_TERMINAL_DEFAULT_BACKSPACE if not specified.
|
||||||
|
*/
|
||||||
|
IDX_BACKSPACE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The terminal emulator type that is passed to the remote system (e.g.
|
||||||
|
* "xterm" or "xterm-256color"). "linux" is used if unspecified.
|
||||||
|
*/
|
||||||
|
IDX_TERMINAL_TYPE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum size of the scrollback buffer in rows.
|
||||||
|
*/
|
||||||
|
IDX_SCROLLBACK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for whether login was
|
||||||
|
* successful. This parameter is optional. If given, the
|
||||||
|
* "login-failure-regex" parameter must also be specified, and the first
|
||||||
|
* frame of the Guacamole connection will be withheld until login
|
||||||
|
* success/failure has been determined.
|
||||||
|
*/
|
||||||
|
IDX_LOGIN_SUCCESS_REGEX,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for whether login was
|
||||||
|
* unsuccessful. This parameter is optional. If given, the
|
||||||
|
* "login-success-regex" parameter must also be specified, and the first
|
||||||
|
* frame of the Guacamole connection will be withheld until login
|
||||||
|
* success/failure has been determined.
|
||||||
|
*/
|
||||||
|
IDX_LOGIN_FAILURE_REGEX,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether outbound clipboard access should be blocked. If set to "true",
|
||||||
|
* it will not be possible to copy data from the terminal to the client
|
||||||
|
* using the clipboard. By default, clipboard access is not blocked.
|
||||||
|
*/
|
||||||
|
IDX_DISABLE_COPY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether inbound clipboard access should be blocked. If set to "true", it
|
||||||
|
* will not be possible to paste data from the client to the terminal using
|
||||||
|
* the clipboard. By default, clipboard access is not blocked.
|
||||||
|
*/
|
||||||
|
IDX_DISABLE_PASTE,
|
||||||
|
|
||||||
|
TN5250_ARGS_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles the given regular expression, returning NULL if compilation fails
|
||||||
|
* or of the given regular expression is NULL. The returned regex_t must be
|
||||||
|
* freed with regfree() AND free(), or with guac_tn5250_regex_free().
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The user who provided the setting associated with the given regex
|
||||||
|
* pattern. Error messages will be logged on behalf of this user.
|
||||||
|
*
|
||||||
|
* @param pattern
|
||||||
|
* The regular expression pattern to compile.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The compiled regular expression, or NULL if compilation fails or NULL
|
||||||
|
* was originally provided for the pattern.
|
||||||
|
*/
|
||||||
|
static regex_t* guac_tn5250_compile_regex(guac_user* user, char* pattern) {
|
||||||
|
|
||||||
|
/* Nothing to compile if no pattern provided */
|
||||||
|
if (pattern == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
int compile_result;
|
||||||
|
regex_t* regex = malloc(sizeof(regex_t));
|
||||||
|
|
||||||
|
/* Compile regular expression */
|
||||||
|
compile_result = regcomp(regex, pattern,
|
||||||
|
REG_EXTENDED | REG_NOSUB | REG_ICASE | REG_NEWLINE);
|
||||||
|
|
||||||
|
/* Notify of failure to parse/compile */
|
||||||
|
if (compile_result != 0) {
|
||||||
|
guac_user_log(user, GUAC_LOG_ERROR, "Regular expression '%s' "
|
||||||
|
"could not be compiled.", pattern);
|
||||||
|
free(regex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_tn5250_regex_free(regex_t** regex) {
|
||||||
|
if (*regex != NULL) {
|
||||||
|
regfree(*regex);
|
||||||
|
free(*regex);
|
||||||
|
*regex = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guac_tn5250_settings* guac_tn5250_parse_args(guac_user* user,
|
||||||
|
int argc, const char** argv) {
|
||||||
|
|
||||||
|
/* Validate arg count */
|
||||||
|
if (argc != TN5250_ARGS_COUNT) {
|
||||||
|
guac_user_log(user, GUAC_LOG_WARNING, "Incorrect number of connection "
|
||||||
|
"parameters provided: expected %i, got %i.",
|
||||||
|
TN5250_ARGS_COUNT, argc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
guac_tn5250_settings* settings = calloc(1, sizeof(guac_tn5250_settings));
|
||||||
|
|
||||||
|
/* Read parameters */
|
||||||
|
settings->hostname =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_HOSTNAME, "");
|
||||||
|
|
||||||
|
/* Read username */
|
||||||
|
settings->username =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_USERNAME, NULL);
|
||||||
|
|
||||||
|
/* Read username regex only if username is specified */
|
||||||
|
if (settings->username != NULL) {
|
||||||
|
settings->username_regex = guac_tn5250_compile_regex(user,
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_USERNAME_REGEX, GUAC_TN5250_DEFAULT_USERNAME_REGEX));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read password */
|
||||||
|
settings->password =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_PASSWORD, NULL);
|
||||||
|
|
||||||
|
/* Read password regex only if password is specified */
|
||||||
|
if (settings->password != NULL) {
|
||||||
|
settings->password_regex = guac_tn5250_compile_regex(user,
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_PASSWORD_REGEX, GUAC_TN5250_DEFAULT_PASSWORD_REGEX));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read optional login success detection regex */
|
||||||
|
settings->login_success_regex = guac_tn5250_compile_regex(user,
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_LOGIN_SUCCESS_REGEX, NULL));
|
||||||
|
|
||||||
|
/* Read optional login failure detection regex */
|
||||||
|
settings->login_failure_regex = guac_tn5250_compile_regex(user,
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_LOGIN_FAILURE_REGEX, NULL));
|
||||||
|
|
||||||
|
/* Both login success and login failure regexes must be provided if either
|
||||||
|
* is present at all */
|
||||||
|
if (settings->login_success_regex != NULL
|
||||||
|
&& settings->login_failure_regex == NULL) {
|
||||||
|
guac_tn5250_regex_free(&settings->login_success_regex);
|
||||||
|
guac_user_log(user, GUAC_LOG_WARNING, "Ignoring provided value for "
|
||||||
|
"\"%s\" as \"%s\" must also be provided.",
|
||||||
|
GUAC_TN5250_CLIENT_ARGS[IDX_LOGIN_SUCCESS_REGEX],
|
||||||
|
GUAC_TN5250_CLIENT_ARGS[IDX_LOGIN_FAILURE_REGEX]);
|
||||||
|
}
|
||||||
|
else if (settings->login_failure_regex != NULL
|
||||||
|
&& settings->login_success_regex == NULL) {
|
||||||
|
guac_tn5250_regex_free(&settings->login_failure_regex);
|
||||||
|
guac_user_log(user, GUAC_LOG_WARNING, "Ignoring provided value for "
|
||||||
|
"\"%s\" as \"%s\" must also be provided.",
|
||||||
|
GUAC_TN5250_CLIENT_ARGS[IDX_LOGIN_FAILURE_REGEX],
|
||||||
|
GUAC_TN5250_CLIENT_ARGS[IDX_LOGIN_SUCCESS_REGEX]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read-only mode */
|
||||||
|
settings->read_only =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_READ_ONLY, false);
|
||||||
|
|
||||||
|
/* Read maximum scrollback size */
|
||||||
|
settings->max_scrollback =
|
||||||
|
guac_user_parse_args_int(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_SCROLLBACK, GUAC_TERMINAL_DEFAULT_MAX_SCROLLBACK);
|
||||||
|
|
||||||
|
/* Read font name */
|
||||||
|
settings->font_name =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_FONT_NAME, GUAC_TERMINAL_DEFAULT_FONT_NAME);
|
||||||
|
|
||||||
|
/* Read font size */
|
||||||
|
settings->font_size =
|
||||||
|
guac_user_parse_args_int(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_FONT_SIZE, GUAC_TERMINAL_DEFAULT_FONT_SIZE);
|
||||||
|
|
||||||
|
/* Copy requested color scheme */
|
||||||
|
settings->color_scheme =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_COLOR_SCHEME, GUAC_TERMINAL_DEFAULT_COLOR_SCHEME);
|
||||||
|
|
||||||
|
/* Pull width/height/resolution directly from user */
|
||||||
|
settings->width = user->info.optimal_width;
|
||||||
|
settings->height = user->info.optimal_height;
|
||||||
|
settings->resolution = user->info.optimal_resolution;
|
||||||
|
|
||||||
|
/* Read port */
|
||||||
|
settings->port =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_PORT, GUAC_TN5250_DEFAULT_PORT);
|
||||||
|
|
||||||
|
/* Read typescript path */
|
||||||
|
settings->typescript_path =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_TYPESCRIPT_PATH, NULL);
|
||||||
|
|
||||||
|
/* Read typescript name */
|
||||||
|
settings->typescript_name =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_TYPESCRIPT_NAME, GUAC_TN5250_DEFAULT_TYPESCRIPT_NAME);
|
||||||
|
|
||||||
|
/* Parse path creation flag */
|
||||||
|
settings->create_typescript_path =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_CREATE_TYPESCRIPT_PATH, false);
|
||||||
|
|
||||||
|
/* Read recording path */
|
||||||
|
settings->recording_path =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_RECORDING_PATH, NULL);
|
||||||
|
|
||||||
|
/* Read recording name */
|
||||||
|
settings->recording_name =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_RECORDING_NAME, GUAC_TN5250_DEFAULT_RECORDING_NAME);
|
||||||
|
|
||||||
|
/* Parse output exclusion flag */
|
||||||
|
settings->recording_exclude_output =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_RECORDING_EXCLUDE_OUTPUT, false);
|
||||||
|
|
||||||
|
/* Parse mouse exclusion flag */
|
||||||
|
settings->recording_exclude_mouse =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_RECORDING_EXCLUDE_MOUSE, false);
|
||||||
|
|
||||||
|
/* Parse key event inclusion flag */
|
||||||
|
settings->recording_include_keys =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_RECORDING_INCLUDE_KEYS, false);
|
||||||
|
|
||||||
|
/* Parse path creation flag */
|
||||||
|
settings->create_recording_path =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_CREATE_RECORDING_PATH, false);
|
||||||
|
|
||||||
|
/* Parse backspace key code */
|
||||||
|
settings->backspace =
|
||||||
|
guac_user_parse_args_int(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_BACKSPACE, GUAC_TERMINAL_DEFAULT_BACKSPACE);
|
||||||
|
|
||||||
|
/* Read terminal emulator type. */
|
||||||
|
settings->terminal_type =
|
||||||
|
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_TERMINAL_TYPE, "linux");
|
||||||
|
|
||||||
|
/* Parse clipboard copy disable flag */
|
||||||
|
settings->disable_copy =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_DISABLE_COPY, false);
|
||||||
|
|
||||||
|
/* Parse clipboard paste disable flag */
|
||||||
|
settings->disable_paste =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||||
|
IDX_DISABLE_PASTE, false);
|
||||||
|
|
||||||
|
/* Parsing was successful */
|
||||||
|
return settings;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_tn5250_settings_free(guac_tn5250_settings* settings) {
|
||||||
|
|
||||||
|
/* Free network connection information */
|
||||||
|
free(settings->hostname);
|
||||||
|
free(settings->port);
|
||||||
|
|
||||||
|
/* Free credentials */
|
||||||
|
free(settings->username);
|
||||||
|
free(settings->password);
|
||||||
|
|
||||||
|
/* Free various regexes */
|
||||||
|
guac_tn5250_regex_free(&settings->username_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->password_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->login_success_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->login_failure_regex);
|
||||||
|
|
||||||
|
/* Free display preferences */
|
||||||
|
free(settings->font_name);
|
||||||
|
free(settings->color_scheme);
|
||||||
|
|
||||||
|
/* Free typescript settings */
|
||||||
|
free(settings->typescript_name);
|
||||||
|
free(settings->typescript_path);
|
||||||
|
|
||||||
|
/* Free screen recording settings */
|
||||||
|
free(settings->recording_name);
|
||||||
|
free(settings->recording_path);
|
||||||
|
|
||||||
|
/* Free terminal emulator type. */
|
||||||
|
free(settings->terminal_type);
|
||||||
|
|
||||||
|
/* Free overall structure */
|
||||||
|
free(settings);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
321
src/protocols/tn5250/settings.h
Normal file
321
src/protocols/tn5250/settings.h
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_TN5250_SETTINGS_H
|
||||||
|
#define GUAC_TN5250_SETTINGS_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
/**
|
||||||
|
* The port to connect to when initiating any tn5250 connection, if no other
|
||||||
|
* port is specified.
|
||||||
|
*/
|
||||||
|
#define GUAC_TN5250_DEFAULT_PORT "23"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filename to use for the typescript, if not specified.
|
||||||
|
*/
|
||||||
|
#define GUAC_TN5250_DEFAULT_TYPESCRIPT_NAME "typescript"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filename to use for the screen recording, if not specified.
|
||||||
|
*/
|
||||||
|
#define GUAC_TN5250_DEFAULT_RECORDING_NAME "recording"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for the username/login prompt
|
||||||
|
* if no other regular expression is specified.
|
||||||
|
*/
|
||||||
|
#define GUAC_TN5250_DEFAULT_USERNAME_REGEX "[Ll]ogin:"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for the password prompt if no
|
||||||
|
* other regular expression is specified.
|
||||||
|
*/
|
||||||
|
#define GUAC_TN5250_DEFAULT_PASSWORD_REGEX "[Pp]assword:"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings for the TN5250 connection. The values for this structure are parsed
|
||||||
|
* from the arguments given during the Guacamole protocol handshake using the
|
||||||
|
* guac_tn5250_parse_args() function.
|
||||||
|
*/
|
||||||
|
typedef struct guac_tn5250_settings {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hostname of the tn5250 server to connect to.
|
||||||
|
*/
|
||||||
|
char* hostname;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The port of the tn5250 server to connect to.
|
||||||
|
*/
|
||||||
|
char* port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the user to login as, if any. If no username is specified,
|
||||||
|
* this will be NULL.
|
||||||
|
*/
|
||||||
|
char* username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for the username/login
|
||||||
|
* prompt. If no username is specified, this will be NULL. If a username
|
||||||
|
* is specified, this will either be the specified username regex, or the
|
||||||
|
* default username regex.
|
||||||
|
*/
|
||||||
|
regex_t* username_regex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password to give when authenticating, if any. If no password is
|
||||||
|
* specified, this will be NULL.
|
||||||
|
*/
|
||||||
|
char* password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for the password prompt. If
|
||||||
|
* no password is specified, this will be NULL. If a password is specified,
|
||||||
|
* this will either be the specified password regex, or the default
|
||||||
|
* password regex.
|
||||||
|
*/
|
||||||
|
regex_t* password_regex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for whether login was
|
||||||
|
* successful. If no such regex is specified, or if no login failure regex
|
||||||
|
* was specified, this will be NULL.
|
||||||
|
*/
|
||||||
|
regex_t* login_success_regex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The regular expression to use when searching for whether login failed.
|
||||||
|
* If no such regex is specified, or if no login success regex was
|
||||||
|
* specified, this will be NULL.
|
||||||
|
*/
|
||||||
|
regex_t* login_failure_regex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this connection is read-only, and user input should be dropped.
|
||||||
|
*/
|
||||||
|
bool read_only;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum size of the scrollback buffer in rows.
|
||||||
|
*/
|
||||||
|
int max_scrollback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the font to use for display rendering.
|
||||||
|
*/
|
||||||
|
char* font_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the font to use, in points.
|
||||||
|
*/
|
||||||
|
int font_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the color scheme to use.
|
||||||
|
*/
|
||||||
|
char* color_scheme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The desired width of the terminal display, in pixels.
|
||||||
|
*/
|
||||||
|
int width;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The desired height of the terminal display, in pixels.
|
||||||
|
*/
|
||||||
|
int height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The desired screen resolution, in DPI.
|
||||||
|
*/
|
||||||
|
int resolution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether outbound clipboard access should be blocked. If set, it will not
|
||||||
|
* be possible to copy data from the terminal to the client using the
|
||||||
|
* clipboard.
|
||||||
|
*/
|
||||||
|
bool disable_copy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether inbound clipboard access should be blocked. If set, it will not
|
||||||
|
* be possible to paste data from the client to the terminal using the
|
||||||
|
* clipboard.
|
||||||
|
*/
|
||||||
|
bool disable_paste;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path in which the typescript should be saved, if enabled. If no
|
||||||
|
* typescript should be saved, this will be NULL.
|
||||||
|
*/
|
||||||
|
char* typescript_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filename to use for the typescript, if enabled.
|
||||||
|
*/
|
||||||
|
char* typescript_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the typescript path should be automatically created if it does
|
||||||
|
* not already exist.
|
||||||
|
*/
|
||||||
|
bool create_typescript_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path in which the screen recording should be saved, if enabled. If
|
||||||
|
* no screen recording should be saved, this will be NULL.
|
||||||
|
*/
|
||||||
|
char* recording_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filename to use for the screen recording, if enabled.
|
||||||
|
*/
|
||||||
|
char* recording_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the screen recording path should be automatically created if it
|
||||||
|
* does not already exist.
|
||||||
|
*/
|
||||||
|
bool create_recording_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether output which is broadcast to each connected client (graphics,
|
||||||
|
* streams, etc.) should NOT be included in the session recording. Output
|
||||||
|
* is included by default, as it is necessary for any recording which must
|
||||||
|
* later be viewable as video.
|
||||||
|
*/
|
||||||
|
bool recording_exclude_output;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether changes to mouse state, such as position and buttons pressed or
|
||||||
|
* released, should NOT be included in the session recording. Mouse state
|
||||||
|
* is included by default, as it is necessary for the mouse cursor to be
|
||||||
|
* rendered in any resulting video.
|
||||||
|
*/
|
||||||
|
bool recording_exclude_mouse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether keys pressed and released should be included in the session
|
||||||
|
* recording. Key events are NOT included by default within the recording,
|
||||||
|
* as doing so has privacy and security implications. Including key events
|
||||||
|
* may be necessary in certain auditing contexts, but should only be done
|
||||||
|
* with caution. Key events can easily contain sensitive information, such
|
||||||
|
* as passwords, credit card numbers, etc.
|
||||||
|
*/
|
||||||
|
bool recording_include_keys;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ASCII code, as an integer, that the tn5250 client will use when the
|
||||||
|
* backspace key is pressed. By default, this is 127, ASCII delete, if
|
||||||
|
* not specified in the client settings.
|
||||||
|
*/
|
||||||
|
int backspace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The terminal emulator type that is passed to the remote system.
|
||||||
|
*/
|
||||||
|
char* terminal_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to send the magic Wake-on-LAN (WoL) packet prior to
|
||||||
|
* continuing the connection.
|
||||||
|
*/
|
||||||
|
bool wol_send_packet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MAC address to put in the magic WoL packet for the remote host to
|
||||||
|
* wake.
|
||||||
|
*/
|
||||||
|
char* wol_mac_addr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The broadcast address to which to send the magic WoL packet to wake
|
||||||
|
* the remote host.
|
||||||
|
*/
|
||||||
|
char* wol_broadcast_addr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The UDP port to use when sending the WoL packet.
|
||||||
|
*/
|
||||||
|
unsigned short wol_udp_port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of seconds to wait after sending the magic WoL packet before
|
||||||
|
* continuing the connection.
|
||||||
|
*/
|
||||||
|
int wol_wait_time;
|
||||||
|
|
||||||
|
} guac_tn5250_settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses all given args, storing them in a newly-allocated settings object. If
|
||||||
|
* the args fail to parse, NULL is returned.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The user who submitted the given arguments while joining the
|
||||||
|
* connection.
|
||||||
|
*
|
||||||
|
* @param argc
|
||||||
|
* The number of arguments within the argv array.
|
||||||
|
*
|
||||||
|
* @param argv
|
||||||
|
* The values of all arguments provided by the user.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A newly-allocated settings object which must be freed with
|
||||||
|
* guac_tn5250_settings_free() when no longer needed. If the arguments fail
|
||||||
|
* to parse, NULL is returned.
|
||||||
|
*/
|
||||||
|
guac_tn5250_settings* guac_tn5250_parse_args(guac_user* user,
|
||||||
|
int argc, const char** argv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the regex pointed to by the given pointer, assigning the value NULL to
|
||||||
|
* that pointer once the regex is freed. If the pointer already contains NULL,
|
||||||
|
* this function has no effect.
|
||||||
|
*
|
||||||
|
* @param regex
|
||||||
|
* The address of the pointer to the regex that should be freed.
|
||||||
|
*/
|
||||||
|
void guac_tn5250_regex_free(regex_t** regex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the given guac_tn5250_settings object, having been previously
|
||||||
|
* allocated via guac_tn5250_parse_args().
|
||||||
|
*
|
||||||
|
* @param settings
|
||||||
|
* The settings object to free.
|
||||||
|
*/
|
||||||
|
void guac_tn5250_settings_free(guac_tn5250_settings* settings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NULL-terminated array of accepted client args.
|
||||||
|
*/
|
||||||
|
extern const char* GUAC_TN5250_CLIENT_ARGS[];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
770
src/protocols/tn5250/tn5250.c
Normal file
770
src/protocols/tn5250/tn5250.c
Normal file
@ -0,0 +1,770 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "argv.h"
|
||||||
|
#include "tn5250.h"
|
||||||
|
#include "lib5250/lib5250.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/protocol.h>
|
||||||
|
#include <guacamole/recording.h>
|
||||||
|
#include <guacamole/timestamp.h>
|
||||||
|
#include <guacamole/wol.h>
|
||||||
|
#include <libtelnet.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support levels for various telnet options, required for connection
|
||||||
|
* negotiation by telnet_init(), part of libtelnet. According to RFC1205,
|
||||||
|
* support for BINARY, TERMINAL TYPE (TTYPE), and END OF RECORD (EOR) must be
|
||||||
|
* enabled for TN5250 to operate. TERMINAL is only supported
|
||||||
|
* client-side (WILL), while BINARY and END OF RECORD should be agreed upon
|
||||||
|
* between client and server.
|
||||||
|
*/
|
||||||
|
static const telnet_telopt_t __telnet_options[] = {
|
||||||
|
{ TELNET_TELOPT_BINARY, TELNET_WILL, TELNET_DO },
|
||||||
|
{ TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DO },
|
||||||
|
{ TELNET_TELOPT_EOR, TELNET_WILL, TELNET_DO },
|
||||||
|
{ TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT },
|
||||||
|
{ TELNET_TELOPT_NEW_ENVIRON, TELNET_WILL, TELNET_DONT },
|
||||||
|
{ -1, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the entire buffer given to the specified file descriptor, retrying
|
||||||
|
* the write automatically if necessary. This function will return a value
|
||||||
|
* not equal to the buffer's size if an error occurs which prevents all
|
||||||
|
* future writes.
|
||||||
|
*
|
||||||
|
* @param fd
|
||||||
|
* The file descriptor to write to.
|
||||||
|
*
|
||||||
|
* @param buffer
|
||||||
|
* The buffer to write.
|
||||||
|
*
|
||||||
|
* @param size
|
||||||
|
* The number of bytes from the buffer to write.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Return a value equal to the buffer's size on success, or a value not
|
||||||
|
* equal to the buffer's size or a negative value on failure.
|
||||||
|
*/
|
||||||
|
static int __guac_tn5250_write_all(int fd, const char* buffer, int size) {
|
||||||
|
|
||||||
|
int remaining = size;
|
||||||
|
while (remaining > 0) {
|
||||||
|
|
||||||
|
/* Attempt to write data */
|
||||||
|
int ret_val = write(fd, buffer, remaining);
|
||||||
|
if (ret_val <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* If successful, contine with what data remains (if any) */
|
||||||
|
remaining -= ret_val;
|
||||||
|
buffer += ret_val;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the given line against the given regex, returning true and sending
|
||||||
|
* the given value if a match is found. An enter keypress is automatically
|
||||||
|
* sent after the value is sent.
|
||||||
|
*
|
||||||
|
* @param client
|
||||||
|
* The guac_client associated with the tn5250 session.
|
||||||
|
*
|
||||||
|
* @param regex
|
||||||
|
* The regex to search for within the given line buffer.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The string value to send through STDIN of the tn5250 session if a
|
||||||
|
* match is found, or NULL if no value should be sent.
|
||||||
|
*
|
||||||
|
* @param line_buffer
|
||||||
|
* The line of character data to test.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if a match is found, false otherwise.
|
||||||
|
*/
|
||||||
|
static bool guac_tn5250_regex_exec(guac_client* client, regex_t* regex,
|
||||||
|
const char* value, const char* line_buffer) {
|
||||||
|
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
|
||||||
|
/* Send value upon match */
|
||||||
|
if (regexec(regex, line_buffer, 0, NULL, 0) == 0) {
|
||||||
|
|
||||||
|
/* Send value */
|
||||||
|
if (value != NULL) {
|
||||||
|
guac_terminal_send_string(tn5250_client->term, value);
|
||||||
|
guac_terminal_send_string(tn5250_client->term, "\x0D");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop searching for prompt */
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the given line against the various stored regexes, automatically
|
||||||
|
* sending the configured username, password, or reporting login
|
||||||
|
* success/failure depending on context. If no search is in progress, either
|
||||||
|
* because no regexes have been defined or because all applicable searches have
|
||||||
|
* completed, this function has no effect.
|
||||||
|
*
|
||||||
|
* @param client
|
||||||
|
* The guac_client associated with the tn5250 session.
|
||||||
|
*
|
||||||
|
* @param line_buffer
|
||||||
|
* The line of character data to test.
|
||||||
|
*/
|
||||||
|
static void guac_tn5250_search_line(guac_client* client, const char* line_buffer) {
|
||||||
|
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_tn5250_settings* settings = tn5250_client->settings;
|
||||||
|
|
||||||
|
/* Continue search for username prompt */
|
||||||
|
if (settings->username_regex != NULL) {
|
||||||
|
if (guac_tn5250_regex_exec(client, settings->username_regex,
|
||||||
|
settings->username, line_buffer)) {
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "Username sent");
|
||||||
|
guac_tn5250_regex_free(&settings->username_regex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue search for password prompt */
|
||||||
|
if (settings->password_regex != NULL) {
|
||||||
|
if (guac_tn5250_regex_exec(client, settings->password_regex,
|
||||||
|
settings->password, line_buffer)) {
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "Password sent");
|
||||||
|
|
||||||
|
/* Do not continue searching for username/password once password is sent */
|
||||||
|
guac_tn5250_regex_free(&settings->username_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->password_regex);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue search for login success */
|
||||||
|
if (settings->login_success_regex != NULL) {
|
||||||
|
if (guac_tn5250_regex_exec(client, settings->login_success_regex,
|
||||||
|
NULL, line_buffer)) {
|
||||||
|
|
||||||
|
/* Allow terminal to render now that login has been deemed successful */
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "Login successful");
|
||||||
|
guac_terminal_start(tn5250_client->term);
|
||||||
|
|
||||||
|
/* Stop all searches */
|
||||||
|
guac_tn5250_regex_free(&settings->username_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->password_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->login_success_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->login_failure_regex);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue search for login failure */
|
||||||
|
if (settings->login_failure_regex != NULL) {
|
||||||
|
if (guac_tn5250_regex_exec(client, settings->login_failure_regex,
|
||||||
|
NULL, line_buffer)) {
|
||||||
|
|
||||||
|
/* Advise that login has failed and connection should be closed */
|
||||||
|
guac_client_abort(client,
|
||||||
|
GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
||||||
|
"Login failed");
|
||||||
|
|
||||||
|
/* Stop all searches */
|
||||||
|
guac_tn5250_regex_free(&settings->username_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->password_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->login_success_regex);
|
||||||
|
guac_tn5250_regex_free(&settings->login_failure_regex);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for a line matching the various stored regexes, automatically
|
||||||
|
* sending the configured username, password, or reporting login
|
||||||
|
* success/failure depending on context. If no search is in progress, either
|
||||||
|
* because no regexes have been defined or because all applicable searches
|
||||||
|
* have completed, this function has no effect.
|
||||||
|
*
|
||||||
|
* @param client
|
||||||
|
* The guac_client associated with the tn5250 session.
|
||||||
|
*
|
||||||
|
* @param buffer
|
||||||
|
* The buffer of received data to search through.
|
||||||
|
*
|
||||||
|
* @param size
|
||||||
|
* The size of the given buffer, in bytes.
|
||||||
|
*/
|
||||||
|
static void guac_tn5250_search(guac_client* client, const char* buffer, int size) {
|
||||||
|
|
||||||
|
static char line_buffer[1024] = {0};
|
||||||
|
static int length = 0;
|
||||||
|
|
||||||
|
/* Append all characters in buffer to current line */
|
||||||
|
const char* current = buffer;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
|
||||||
|
char c = *(current++);
|
||||||
|
|
||||||
|
/* Attempt pattern match and clear buffer upon reading newline */
|
||||||
|
if (c == '\n') {
|
||||||
|
if (length > 0) {
|
||||||
|
line_buffer[length] = '\0';
|
||||||
|
guac_tn5250_search_line(client, line_buffer);
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append all non-newline characters to line buffer as long as space
|
||||||
|
* remains */
|
||||||
|
else if (length < sizeof(line_buffer) - 1)
|
||||||
|
line_buffer[length++] = c;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt pattern match if an unfinished line remains (may be a prompt) */
|
||||||
|
if (length > 0) {
|
||||||
|
line_buffer[length] = '\0';
|
||||||
|
guac_tn5250_search_line(client, line_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler, as defined by libtelnet. This function is passed to
|
||||||
|
* telnet_init() and will be called for every event fired by libtelnet,
|
||||||
|
* including feature enable/disable and receipt/transmission of data.
|
||||||
|
*
|
||||||
|
* @param tn5250
|
||||||
|
* The telnet_t object associated with the event.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The telnet_event_t that has fired.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* User data provided to the event.
|
||||||
|
*/
|
||||||
|
static void __guac_tn5250_event_handler(telnet_t* tn5250, telnet_event_t* event, void* data) {
|
||||||
|
|
||||||
|
guac_client* client = (guac_client*) data;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_tn5250_settings* settings = tn5250_client->settings;
|
||||||
|
|
||||||
|
switch (event->type) {
|
||||||
|
|
||||||
|
/* Terminal output received */
|
||||||
|
case TELNET_EV_DATA:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE,
|
||||||
|
"%s: Received %d bytes of data from remote side.", __func__, event->data.size);
|
||||||
|
if (tn5250_client->binary_mode) {
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE,
|
||||||
|
"%s: Receiving binary data, which will not go directly to the terminal.",
|
||||||
|
__func__);
|
||||||
|
if (guac_lib5250_process_packet(event->data.buffer, client)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE,
|
||||||
|
"%s: Writing ASCII output directly to terminal.", __func__);
|
||||||
|
guac_terminal_write(tn5250_client->term, event->data.buffer, event->data.size);
|
||||||
|
guac_tn5250_search(client, event->data.buffer, event->data.size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Data destined for remote end */
|
||||||
|
case TELNET_EV_SEND:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE,
|
||||||
|
"%s: Sending %d bytes of data to remote side.", __func__, event->data.size);
|
||||||
|
if (__guac_tn5250_write_all(tn5250_client->socket_fd, event->data.buffer, event->data.size)
|
||||||
|
!= event->data.size)
|
||||||
|
guac_client_stop(client);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Remote feature enabled */
|
||||||
|
case TELNET_EV_WILL:
|
||||||
|
if (event->neg.telopt == TELNET_TELOPT_ECHO)
|
||||||
|
tn5250_client->echo_enabled = 0; /* Disable local echo, as remote will echo */
|
||||||
|
|
||||||
|
if (event->neg.telopt == TELNET_TELOPT_EOR) {
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"%s: Received End of Record support from remote side.",
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
|
if (event->neg.telopt == TELNET_TELOPT_BINARY) {
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"%s: Received Binary support from remote side.",
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Remote feature disabled */
|
||||||
|
case TELNET_EV_WONT:
|
||||||
|
if (event->neg.telopt == TELNET_TELOPT_ECHO)
|
||||||
|
tn5250_client->echo_enabled = 1; /* Enable local echo, as remote won't echo */
|
||||||
|
|
||||||
|
/* END OF RECORD are require for TN5250 - stop if remote
|
||||||
|
* side does not support it. */
|
||||||
|
if (event->neg.telopt == TELNET_TELOPT_EOR) {
|
||||||
|
guac_client_log(client, GUAC_LOG_ERROR, "%s: Unexpected lack of "
|
||||||
|
" END OF RECORD support on the remote side.", __func__);
|
||||||
|
guac_client_stop(client);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->neg.telopt == TELNET_TELOPT_BINARY) {
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "%s: End of binary data.",
|
||||||
|
__func__);
|
||||||
|
tn5250_client->binary_mode = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Local feature enable */
|
||||||
|
case TELNET_EV_DO:
|
||||||
|
if (event->neg.telopt == TELNET_TELOPT_EOR)
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"%s: Local EOR support enabled.",
|
||||||
|
__func__);
|
||||||
|
|
||||||
|
/* Sending a DO BINARY means the next data we receive from the
|
||||||
|
* server should be binary data. */
|
||||||
|
if (event->neg.telopt == TELNET_TELOPT_BINARY) {
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"%s: Local BINARY support enabled, "
|
||||||
|
"expecting next data to be binary.",
|
||||||
|
__func__);
|
||||||
|
tn5250_client->binary_mode = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Terminal type request */
|
||||||
|
case TELNET_EV_TTYPE:
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"%s: Received TERMINAL TYPE request from the remote side.", __func__);
|
||||||
|
if (event->ttype.cmd == TELNET_TTYPE_SEND) {
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"%s: Sending terminal type \"%s\" to remote side.", __func__, settings->terminal_type);
|
||||||
|
|
||||||
|
/* Apparently sending the TERMINAL TYPE to an IBMi system over
|
||||||
|
* telnet requires both sending the TERM environment variable
|
||||||
|
* and responding to the actual telnet TTYPE inquiry. */
|
||||||
|
telnet_begin_newenviron(tn5250, TELNET_ENVIRON_IS);
|
||||||
|
telnet_newenviron_value(tn5250, TELNET_ENVIRON_VAR, "TERM");
|
||||||
|
telnet_newenviron_value(tn5250, TELNET_ENVIRON_VALUE, settings->terminal_type);
|
||||||
|
telnet_finish_sb(tn5250);
|
||||||
|
telnet_ttype_is(tn5250_client->tn5250, settings->terminal_type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Environment request */
|
||||||
|
case TELNET_EV_ENVIRON:
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"%s: Received environment request from the remote side.",
|
||||||
|
__func__);
|
||||||
|
/* Only send USER if entire environment was requested */
|
||||||
|
if (event->environ.size == 0) {
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Sending username to remote server: %s", __func__, settings->username);
|
||||||
|
guac_tn5250_send_user(tn5250, settings->username);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Generic IAC received */
|
||||||
|
case TELNET_EV_IAC:
|
||||||
|
if (event->iac.cmd == TELNET_EOR) {
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE, "%s: Receive END OF RECORD from remote side.", __func__);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"%s: Received generic IAC data: 0x%x",
|
||||||
|
__func__, event->iac.cmd);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* For subnegotiation, we need to break and let the libtelnet internals
|
||||||
|
* finish handling the request. */
|
||||||
|
case TELNET_EV_SUBNEGOTIATION:
|
||||||
|
guac_client_log(client, GUAC_LOG_TRACE,
|
||||||
|
"%s: Received subnegotiation, sending on to libtelnet.",
|
||||||
|
__func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Connection warnings */
|
||||||
|
case TELNET_EV_WARNING:
|
||||||
|
guac_client_log(client, GUAC_LOG_WARNING, "%s", event->error.msg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Connection errors */
|
||||||
|
case TELNET_EV_ERROR:
|
||||||
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
||||||
|
"Telnet connection closing with error: %s", event->error.msg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Ignore other events */
|
||||||
|
default:
|
||||||
|
guac_client_log(client, GUAC_LOG_WARNING,
|
||||||
|
"%s: Unknown or unsupported telnet command received: 0x%x",
|
||||||
|
__func__, event->type);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input thread, started by the main tn5250 client thread. This thread
|
||||||
|
* continuously reads from the terminal's STDIN and transfers all read
|
||||||
|
* data to the tn5250 connection.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* The current guac_client instance.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Always NULL.
|
||||||
|
*/
|
||||||
|
static void* __guac_tn5250_input_thread(void* data) {
|
||||||
|
|
||||||
|
guac_client* client = (guac_client*) data;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
|
||||||
|
char buffer[8192];
|
||||||
|
int bytes_read;
|
||||||
|
|
||||||
|
/* Write all data read */
|
||||||
|
while ((bytes_read = guac_terminal_read_stdin(tn5250_client->term, buffer, sizeof(buffer))) > 0) {
|
||||||
|
telnet_send(tn5250_client->tn5250, buffer, bytes_read);
|
||||||
|
if (tn5250_client->echo_enabled)
|
||||||
|
guac_terminal_write(tn5250_client->term, buffer, bytes_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to the tn5250 server specified within the data associated
|
||||||
|
* with the given guac_client, which will have been populated by
|
||||||
|
* guac_client_init.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The connected tn5250 instance, if successful, or NULL if the
|
||||||
|
* connection fails for any reason.
|
||||||
|
*/
|
||||||
|
static telnet_t* __guac_tn5250_create_session(guac_client* client) {
|
||||||
|
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
struct addrinfo* addresses;
|
||||||
|
struct addrinfo* current_address;
|
||||||
|
|
||||||
|
char connected_address[1024];
|
||||||
|
char connected_port[64];
|
||||||
|
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_tn5250_settings* settings = tn5250_client->settings;
|
||||||
|
|
||||||
|
struct addrinfo hints = {
|
||||||
|
.ai_family = AF_UNSPEC,
|
||||||
|
.ai_socktype = SOCK_STREAM,
|
||||||
|
.ai_protocol = IPPROTO_TCP
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Get socket */
|
||||||
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
/* Get addresses connection */
|
||||||
|
if ((retval = getaddrinfo(settings->hostname, settings->port,
|
||||||
|
&hints, &addresses))) {
|
||||||
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error parsing given address or port: %s",
|
||||||
|
gai_strerror(retval));
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt connection to each address until success */
|
||||||
|
current_address = addresses;
|
||||||
|
while (current_address != NULL) {
|
||||||
|
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
/* Resolve hostname */
|
||||||
|
if ((retval = getnameinfo(current_address->ai_addr,
|
||||||
|
current_address->ai_addrlen,
|
||||||
|
connected_address, sizeof(connected_address),
|
||||||
|
connected_port, sizeof(connected_port),
|
||||||
|
NI_NUMERICHOST | NI_NUMERICSERV)))
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "Unable to resolve host: %s", gai_strerror(retval));
|
||||||
|
|
||||||
|
/* Connect */
|
||||||
|
if (connect(fd, current_address->ai_addr,
|
||||||
|
current_address->ai_addrlen) == 0) {
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "Successfully connected to "
|
||||||
|
"host %s, port %s", connected_address, connected_port);
|
||||||
|
|
||||||
|
/* Done if successful connect */
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise log information regarding bind failure */
|
||||||
|
else
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "Unable to connect to "
|
||||||
|
"host %s, port %s: %s",
|
||||||
|
connected_address, connected_port, strerror(errno));
|
||||||
|
|
||||||
|
current_address = current_address->ai_next;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If unable to connect to anything, fail */
|
||||||
|
if (current_address == NULL) {
|
||||||
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND,
|
||||||
|
"Unable to connect to any addresses.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free addrinfo */
|
||||||
|
freeaddrinfo(addresses);
|
||||||
|
|
||||||
|
/* Open tn5250 session */
|
||||||
|
telnet_t* tn5250 = telnet_init(__telnet_options, __guac_tn5250_event_handler, 0, client);
|
||||||
|
if (tn5250 == NULL) {
|
||||||
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Telnet client allocation failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save file descriptor */
|
||||||
|
tn5250_client->socket_fd = fd;
|
||||||
|
|
||||||
|
return tn5250;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a 16-bit value over the given tn5250 connection with the byte order
|
||||||
|
* required by the tn5250 protocol.
|
||||||
|
*
|
||||||
|
* @param tn5250
|
||||||
|
* The tn5250 connection to use.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The value to send.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
static void __guac_tn5250_send_uint16(telnet_t* tn5250, uint16_t value) {
|
||||||
|
|
||||||
|
unsigned char buffer[2];
|
||||||
|
buffer[0] = (value >> 8) & 0xFF;
|
||||||
|
buffer[1] = value & 0xFF;
|
||||||
|
|
||||||
|
telnet_send(tn5250, (char*) buffer, 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an 8-bit value over the given tn5250 connection.
|
||||||
|
*
|
||||||
|
* @param tn5250
|
||||||
|
* The tn5250 connection to use.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The value to send.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
static void __guac_tn5250_send_uint8(telnet_t* tn5250, uint8_t value) {
|
||||||
|
telnet_send(tn5250, (char*) (&value), 1);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void guac_tn5250_send_user(telnet_t* tn5250, const char* username) {
|
||||||
|
|
||||||
|
/* IAC SB NEW-ENVIRON IS */
|
||||||
|
telnet_begin_newenviron(tn5250, TELNET_ENVIRON_IS);
|
||||||
|
|
||||||
|
/* Only send username if defined */
|
||||||
|
if (username != NULL) {
|
||||||
|
|
||||||
|
telnet_newenviron_value(tn5250, TELNET_ENVIRON_VAR, "USER");
|
||||||
|
telnet_newenviron_value(tn5250, TELNET_ENVIRON_VALUE, username);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IAC SB */
|
||||||
|
telnet_finish_sb(tn5250);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for data on the given file descriptor for up to one second. The
|
||||||
|
* return value is identical to that of select(): 0 on timeout, < 0 on
|
||||||
|
* error, and > 0 on success.
|
||||||
|
*
|
||||||
|
* @param socket_fd The file descriptor to wait for.
|
||||||
|
* @return A value greater than zero on success, zero on timeout, and
|
||||||
|
* less than zero on error.
|
||||||
|
*/
|
||||||
|
static int __guac_tn5250_wait(int socket_fd) {
|
||||||
|
|
||||||
|
/* Build array of file descriptors */
|
||||||
|
struct pollfd fds[] = {{
|
||||||
|
.fd = socket_fd,
|
||||||
|
.events = POLLIN,
|
||||||
|
.revents = 0,
|
||||||
|
}};
|
||||||
|
|
||||||
|
/* Wait for one second */
|
||||||
|
return poll(fds, 1, 1000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void* guac_tn5250_client_thread(void* data) {
|
||||||
|
|
||||||
|
guac_client* client = (guac_client*) data;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
guac_tn5250_settings* settings = tn5250_client->settings;
|
||||||
|
|
||||||
|
pthread_t input_thread;
|
||||||
|
char buffer[8192];
|
||||||
|
int wait_result;
|
||||||
|
|
||||||
|
/* Set up screen recording, if requested */
|
||||||
|
if (settings->recording_path != NULL) {
|
||||||
|
tn5250_client->recording = guac_recording_create(client,
|
||||||
|
settings->recording_path,
|
||||||
|
settings->recording_name,
|
||||||
|
settings->create_recording_path,
|
||||||
|
!settings->recording_exclude_output,
|
||||||
|
!settings->recording_exclude_mouse,
|
||||||
|
0, /* Touch events not supported */
|
||||||
|
settings->recording_include_keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create terminal options with required parameters */
|
||||||
|
guac_terminal_options* options = guac_terminal_options_create(
|
||||||
|
settings->width, settings->height, settings->resolution);
|
||||||
|
|
||||||
|
/* Set optional parameters */
|
||||||
|
options->disable_copy = settings->disable_copy;
|
||||||
|
options->max_scrollback = settings->max_scrollback;
|
||||||
|
options->font_name = settings->font_name;
|
||||||
|
options->font_size = settings->font_size;
|
||||||
|
options->color_scheme = settings->color_scheme;
|
||||||
|
options->backspace = settings->backspace;
|
||||||
|
|
||||||
|
/* Create terminal */
|
||||||
|
tn5250_client->term = guac_terminal_create(client, options);
|
||||||
|
|
||||||
|
/* Free options struct now that it's been used */
|
||||||
|
free(options);
|
||||||
|
|
||||||
|
/* Fail if terminal init failed */
|
||||||
|
if (tn5250_client->term == NULL) {
|
||||||
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||||
|
"Terminal initialization failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send current values of exposed arguments to owner only */
|
||||||
|
guac_client_for_owner(client, guac_tn5250_send_current_argv,
|
||||||
|
tn5250_client);
|
||||||
|
|
||||||
|
/* Set up typescript, if requested */
|
||||||
|
if (settings->typescript_path != NULL) {
|
||||||
|
guac_terminal_create_typescript(tn5250_client->term,
|
||||||
|
settings->typescript_path,
|
||||||
|
settings->typescript_name,
|
||||||
|
settings->create_typescript_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open tn5250 session */
|
||||||
|
tn5250_client->tn5250 = __guac_tn5250_create_session(client);
|
||||||
|
if (tn5250_client->tn5250 == NULL) {
|
||||||
|
/* Already aborted within __guac_tn5250_create_session() */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logged in */
|
||||||
|
guac_client_log(client, GUAC_LOG_INFO, "Telnet connection successful.");
|
||||||
|
|
||||||
|
/* Allow terminal to render if login success/failure detection is not
|
||||||
|
* enabled */
|
||||||
|
if (settings->login_success_regex == NULL
|
||||||
|
&& settings->login_failure_regex == NULL)
|
||||||
|
guac_terminal_start(tn5250_client->term);
|
||||||
|
|
||||||
|
/* Start input thread */
|
||||||
|
if (pthread_create(&(input_thread), NULL, __guac_tn5250_input_thread, (void*) client)) {
|
||||||
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start input thread");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* While data available, write to terminal */
|
||||||
|
while ((wait_result = __guac_tn5250_wait(tn5250_client->socket_fd)) >= 0) {
|
||||||
|
|
||||||
|
/* Resume waiting of no data available */
|
||||||
|
if (wait_result == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int bytes_read = read(tn5250_client->socket_fd, buffer, sizeof(buffer));
|
||||||
|
if (bytes_read <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
telnet_recv(tn5250_client->tn5250, buffer, bytes_read);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kill client and Wait for input thread to die */
|
||||||
|
guac_client_stop(client);
|
||||||
|
pthread_join(input_thread, NULL);
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_INFO, "Telnet connection ended.");
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
95
src/protocols/tn5250/tn5250.h
Normal file
95
src/protocols/tn5250/tn5250.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_TN5250_H
|
||||||
|
#define GUAC_TN5250_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
|
||||||
|
#include <guacamole/recording.h>
|
||||||
|
#include <libtelnet.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TN5250-specific client data.
|
||||||
|
*/
|
||||||
|
typedef struct guac_tn5250_client {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Telnet connection settings.
|
||||||
|
*/
|
||||||
|
guac_tn5250_settings* settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The TN5250 client thread.
|
||||||
|
*/
|
||||||
|
pthread_t client_thread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file descriptor of the socket connected to the tn5250 server,
|
||||||
|
* or -1 if no connection has been established.
|
||||||
|
*/
|
||||||
|
int socket_fd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TN5250 connection, used by the telnet client thread.
|
||||||
|
*/
|
||||||
|
telnet_t* tn5250;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether all user input should be automatically echoed to the
|
||||||
|
* terminal.
|
||||||
|
*/
|
||||||
|
int echo_enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The terminal which will render all output from the tn5250 client.
|
||||||
|
*/
|
||||||
|
guac_terminal* term;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The in-progress session recording, or NULL if no recording is in
|
||||||
|
* progress.
|
||||||
|
*/
|
||||||
|
guac_recording* recording;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether or not the TN5250 client is now in binary mode and
|
||||||
|
* subsequent data should be binary.
|
||||||
|
*/
|
||||||
|
int binary_mode;
|
||||||
|
|
||||||
|
} guac_tn5250_client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main tn5250 client thread, handling transfer of TN5250 output to STDOUT.
|
||||||
|
*/
|
||||||
|
void* guac_tn5250_client_thread(void* data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the given username by setting the remote USER environment variable
|
||||||
|
* using the telnet NEW-ENVIRON option.
|
||||||
|
*/
|
||||||
|
void guac_tn5250_send_user(telnet_t* tn5250, const char* username);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
122
src/protocols/tn5250/user.c
Normal file
122
src/protocols/tn5250/user.c
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "argv.h"
|
||||||
|
#include "clipboard.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "pipe.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "tn5250.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/socket.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int guac_tn5250_user_join_handler(guac_user* user, int argc, char** argv) {
|
||||||
|
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||||
|
|
||||||
|
/* Parse provided arguments */
|
||||||
|
guac_tn5250_settings* settings = guac_tn5250_parse_args(user,
|
||||||
|
argc, (const char**) argv);
|
||||||
|
|
||||||
|
/* Fail if settings cannot be parsed */
|
||||||
|
if (settings == NULL) {
|
||||||
|
guac_user_log(user, GUAC_LOG_INFO,
|
||||||
|
"Badly formatted client arguments.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store settings at user level */
|
||||||
|
user->data = settings;
|
||||||
|
|
||||||
|
/* Connect via tn5250 if owner */
|
||||||
|
if (user->owner) {
|
||||||
|
|
||||||
|
/* Store owner's settings at client level */
|
||||||
|
tn5250_client->settings = settings;
|
||||||
|
|
||||||
|
/* Start client thread */
|
||||||
|
if (pthread_create(&(tn5250_client->client_thread), NULL,
|
||||||
|
guac_tn5250_client_thread, (void*) client)) {
|
||||||
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||||
|
"Unable to start tn5250 client thread");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If not owner, synchronize with current display */
|
||||||
|
else {
|
||||||
|
guac_terminal_dup(tn5250_client->term, user, user->socket);
|
||||||
|
guac_tn5250_send_current_argv(user, tn5250_client);
|
||||||
|
guac_socket_flush(user->socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only handle events if not read-only */
|
||||||
|
if (!settings->read_only) {
|
||||||
|
|
||||||
|
/* General mouse/keyboard events */
|
||||||
|
user->key_handler = guac_tn5250_user_key_handler;
|
||||||
|
user->mouse_handler = guac_tn5250_user_mouse_handler;
|
||||||
|
|
||||||
|
/* Inbound (client to server) clipboard transfer */
|
||||||
|
if (!settings->disable_paste)
|
||||||
|
user->clipboard_handler = guac_tn5250_clipboard_handler;
|
||||||
|
|
||||||
|
/* STDIN redirection */
|
||||||
|
user->pipe_handler = guac_tn5250_pipe_handler;
|
||||||
|
|
||||||
|
/* Updates to connection parameters */
|
||||||
|
user->argv_handler = guac_argv_handler;
|
||||||
|
|
||||||
|
/* Display size change events */
|
||||||
|
user->size_handler = guac_tn5250_user_size_handler;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_tn5250_user_leave_handler(guac_user* user) {
|
||||||
|
|
||||||
|
guac_tn5250_client* tn5250_client =
|
||||||
|
(guac_tn5250_client*) user->client->data;
|
||||||
|
|
||||||
|
/* Remove the user from the terminal */
|
||||||
|
guac_terminal_remove_user(tn5250_client->term, user);
|
||||||
|
|
||||||
|
/* Free settings if not owner (owner settings will be freed with client) */
|
||||||
|
if (!user->owner) {
|
||||||
|
guac_tn5250_settings* settings = (guac_tn5250_settings*) user->data;
|
||||||
|
guac_tn5250_settings_free(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
38
src/protocols/tn5250/user.h
Normal file
38
src/protocols/tn5250/user.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_TN5250_USER_H
|
||||||
|
#define GUAC_TN5250_USER_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for joining users.
|
||||||
|
*/
|
||||||
|
guac_user_join_handler guac_tn5250_user_join_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for leaving users.
|
||||||
|
*/
|
||||||
|
guac_user_leave_handler guac_tn5250_user_leave_handler;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue
Block a user