Merge libguac-client-ssh.

This commit is contained in:
Michael Jumper 2013-06-04 15:52:49 -07:00
commit d9cfd35647
34 changed files with 6767 additions and 0 deletions

36
protocols/ssh/.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# Object code
*.o
*.so
*.lo
*.la
# Backup files
*~
# Release files
*.tar.gz
# Files currently being edited by vim or vi
*.swp
# automake/autoconf
.deps/
.libs/
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
m4/*
!README
config.guess
config.log
config.status
config.sub
configure
depcomp
install-sh
libtool
ltmain.sh
missing

1
protocols/ssh/AUTHORS Normal file
View File

@ -0,0 +1 @@
Michael Jumper <zhangmaike@users.sourceforge.net>

1
protocols/ssh/ChangeLog Normal file
View File

@ -0,0 +1 @@

470
protocols/ssh/LICENSE Normal file
View File

@ -0,0 +1,470 @@
MOZILLA PUBLIC LICENSE
Version 1.1
---------------
1. Definitions.
1.0.1. "Commercial Use" means distribution or otherwise making the
Covered Code available to a third party.
1.1. "Contributor" means each entity that creates or contributes to
the creation of Modifications.
1.2. "Contributor Version" means the combination of the Original
Code, prior Modifications used by a Contributor, and the Modifications
made by that particular Contributor.
1.3. "Covered Code" means the Original Code or Modifications or the
combination of the Original Code and Modifications, in each case
including portions thereof.
1.4. "Electronic Distribution Mechanism" means a mechanism generally
accepted in the software development community for the electronic
transfer of data.
1.5. "Executable" means Covered Code in any form other than Source
Code.
1.6. "Initial Developer" means the individual or entity identified
as the Initial Developer in the Source Code notice required by Exhibit
A.
1.7. "Larger Work" means a work which combines Covered Code or
portions thereof with code not governed by the terms of this License.
1.8. "License" means this document.
1.8.1. "Licensable" means having the right to grant, to the maximum
extent possible, whether at the time of the initial grant or
subsequently acquired, any and all of the rights conveyed herein.
1.9. "Modifications" means any addition to or deletion from the
substance or structure of either the Original Code or any previous
Modifications. When Covered Code is released as a series of files, a
Modification is:
A. Any addition to or deletion from the contents of a file
containing Original Code or previous Modifications.
B. Any new file that contains any part of the Original Code or
previous Modifications.
1.10. "Original Code" means Source Code of computer software code
which is described in the Source Code notice required by Exhibit A as
Original Code, and which, at the time of its release under this
License is not already Covered Code governed by this License.
1.10.1. "Patent Claims" means any patent claim(s), now owned or
hereafter acquired, including without limitation, method, process,
and apparatus claims, in any patent Licensable by grantor.
1.11. "Source Code" means the preferred form of the Covered Code for
making modifications to it, including all modules it contains, plus
any associated interface definition files, scripts used to control
compilation and installation of an Executable, or source code
differential comparisons against either the Original Code or another
well known, available Covered Code of the Contributor's choice. The
Source Code can be in a compressed or archival form, provided the
appropriate decompression or de-archiving software is widely available
for no charge.
1.12. "You" (or "Your") means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this
License or a future version of this License issued under Section 6.1.
For legal entities, "You" includes any entity which controls, is
controlled by, or is under common control with You. For purposes of
this definition, "control" means (a) the power, direct or indirect,
to cause the direction or management of such entity, whether by
contract or otherwise, or (b) ownership of more than fifty percent
(50%) of the outstanding shares or beneficial ownership of such
entity.
2. Source Code License.
2.1. The Initial Developer Grant.
The Initial Developer hereby grants You a world-wide, royalty-free,
non-exclusive license, subject to third party intellectual property
claims:
(a) under intellectual property rights (other than patent or
trademark) Licensable by Initial Developer to use, reproduce,
modify, display, perform, sublicense and distribute the Original
Code (or portions thereof) with or without Modifications, and/or
as part of a Larger Work; and
(b) under Patents Claims infringed by the making, using or
selling of Original Code, to make, have made, use, practice,
sell, and offer for sale, and/or otherwise dispose of the
Original Code (or portions thereof).
(c) the licenses granted in this Section 2.1(a) and (b) are
effective on the date Initial Developer first distributes
Original Code under the terms of this License.
(d) Notwithstanding Section 2.1(b) above, no patent license is
granted: 1) for code that You delete from the Original Code; 2)
separate from the Original Code; or 3) for infringements caused
by: i) the modification of the Original Code or ii) the
combination of the Original Code with other software or devices.
2.2. Contributor Grant.
Subject to third party intellectual property claims, each Contributor
hereby grants You a world-wide, royalty-free, non-exclusive license
(a) under intellectual property rights (other than patent or
trademark) Licensable by Contributor, to use, reproduce, modify,
display, perform, sublicense and distribute the Modifications
created by such Contributor (or portions thereof) either on an
unmodified basis, with other Modifications, as Covered Code
and/or as part of a Larger Work; and
(b) under Patent Claims infringed by the making, using, or
selling of Modifications made by that Contributor either alone
and/or in combination with its Contributor Version (or portions
of such combination), to make, use, sell, offer for sale, have
made, and/or otherwise dispose of: 1) Modifications made by that
Contributor (or portions thereof); and 2) the combination of
Modifications made by that Contributor with its Contributor
Version (or portions of such combination).
(c) the licenses granted in Sections 2.2(a) and 2.2(b) are
effective on the date Contributor first makes Commercial Use of
the Covered Code.
(d) Notwithstanding Section 2.2(b) above, no patent license is
granted: 1) for any code that Contributor has deleted from the
Contributor Version; 2) separate from the Contributor Version;
3) for infringements caused by: i) third party modifications of
Contributor Version or ii) the combination of Modifications made
by that Contributor with other software (except as part of the
Contributor Version) or other devices; or 4) under Patent Claims
infringed by Covered Code in the absence of Modifications made by
that Contributor.
3. Distribution Obligations.
3.1. Application of License.
The Modifications which You create or to which You contribute are
governed by the terms of this License, including without limitation
Section 2.2. The Source Code version of Covered Code may be
distributed only under the terms of this License or a future version
of this License released under Section 6.1, and You must include a
copy of this License with every copy of the Source Code You
distribute. You may not offer or impose any terms on any Source Code
version that alters or restricts the applicable version of this
License or the recipients' rights hereunder. However, You may include
an additional document offering the additional rights described in
Section 3.5.
3.2. Availability of Source Code.
Any Modification which You create or to which You contribute must be
made available in Source Code form under the terms of this License
either on the same media as an Executable version or via an accepted
Electronic Distribution Mechanism to anyone to whom you made an
Executable version available; and if made available via Electronic
Distribution Mechanism, must remain available for at least twelve (12)
months after the date it initially became available, or at least six
(6) months after a subsequent version of that particular Modification
has been made available to such recipients. You are responsible for
ensuring that the Source Code version remains available even if the
Electronic Distribution Mechanism is maintained by a third party.
3.3. Description of Modifications.
You must cause all Covered Code to which You contribute to contain a
file documenting the changes You made to create that Covered Code and
the date of any change. You must include a prominent statement that
the Modification is derived, directly or indirectly, from Original
Code provided by the Initial Developer and including the name of the
Initial Developer in (a) the Source Code, and (b) in any notice in an
Executable version or related documentation in which You describe the
origin or ownership of the Covered Code.
3.4. Intellectual Property Matters
(a) Third Party Claims.
If Contributor has knowledge that a license under a third party's
intellectual property rights is required to exercise the rights
granted by such Contributor under Sections 2.1 or 2.2,
Contributor must include a text file with the Source Code
distribution titled "LEGAL" which describes the claim and the
party making the claim in sufficient detail that a recipient will
know whom to contact. If Contributor obtains such knowledge after
the Modification is made available as described in Section 3.2,
Contributor shall promptly modify the LEGAL file in all copies
Contributor makes available thereafter and shall take other steps
(such as notifying appropriate mailing lists or newsgroups)
reasonably calculated to inform those who received the Covered
Code that new knowledge has been obtained.
(b) Contributor APIs.
If Contributor's Modifications include an application programming
interface and Contributor has knowledge of patent licenses which
are reasonably necessary to implement that API, Contributor must
also include this information in the LEGAL file.
(c) Representations.
Contributor represents that, except as disclosed pursuant to
Section 3.4(a) above, Contributor believes that Contributor's
Modifications are Contributor's original creation(s) and/or
Contributor has sufficient rights to grant the rights conveyed by
this License.
3.5. Required Notices.
You must duplicate the notice in Exhibit A in each file of the Source
Code. If it is not possible to put such notice in a particular Source
Code file due to its structure, then You must include such notice in a
location (such as a relevant directory) where a user would be likely
to look for such a notice. If You created one or more Modification(s)
You may add your name as a Contributor to the notice described in
Exhibit A. You must also duplicate this License in any documentation
for the Source Code where You describe recipients' rights or ownership
rights relating to Covered Code. You may choose to offer, and to
charge a fee for, warranty, support, indemnity or liability
obligations to one or more recipients of Covered Code. However, You
may do so only on Your own behalf, and not on behalf of the Initial
Developer or any Contributor. You must make it absolutely clear than
any such warranty, support, indemnity or liability obligation is
offered by You alone, and You hereby agree to indemnify the Initial
Developer and every Contributor for any liability incurred by the
Initial Developer or such Contributor as a result of warranty,
support, indemnity or liability terms You offer.
3.6. Distribution of Executable Versions.
You may distribute Covered Code in Executable form only if the
requirements of Section 3.1-3.5 have been met for that Covered Code,
and if You include a notice stating that the Source Code version of
the Covered Code is available under the terms of this License,
including a description of how and where You have fulfilled the
obligations of Section 3.2. The notice must be conspicuously included
in any notice in an Executable version, related documentation or
collateral in which You describe recipients' rights relating to the
Covered Code. You may distribute the Executable version of Covered
Code or ownership rights under a license of Your choice, which may
contain terms different from this License, provided that You are in
compliance with the terms of this License and that the license for the
Executable version does not attempt to limit or alter the recipient's
rights in the Source Code version from the rights set forth in this
License. If You distribute the Executable version under a different
license You must make it absolutely clear that any terms which differ
from this License are offered by You alone, not by the Initial
Developer or any Contributor. You hereby agree to indemnify the
Initial Developer and every Contributor for any liability incurred by
the Initial Developer or such Contributor as a result of any such
terms You offer.
3.7. Larger Works.
You may create a Larger Work by combining Covered Code with other code
not governed by the terms of this License and distribute the Larger
Work as a single product. In such a case, You must make sure the
requirements of this License are fulfilled for the Covered Code.
4. Inability to Comply Due to Statute or Regulation.
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Code due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description
must be included in the LEGAL file described in Section 3.4 and must
be included with all distributions of the Source Code. Except to the
extent prohibited by statute or regulation, such description must be
sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
5. Application of this License.
This License applies to code to which the Initial Developer has
attached the notice in Exhibit A and to related Covered Code.
6. Versions of the License.
6.1. New Versions.
Netscape Communications Corporation ("Netscape") may publish revised
and/or new versions of the License from time to time. Each version
will be given a distinguishing version number.
6.2. Effect of New Versions.
Once Covered Code has been published under a particular version of the
License, You may always continue to use it under the terms of that
version. You may also choose to use such Covered Code under the terms
of any subsequent version of the License published by Netscape. No one
other than Netscape has the right to modify the terms applicable to
Covered Code created under this License.
6.3. Derivative Works.
If You create or use a modified version of this License (which you may
only do in order to apply it to code which is not already Covered Code
governed by this License), You must (a) rename Your license so that
the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
"MPL", "NPL" or any confusingly similar phrase do not appear in your
license (except to note that your license differs from this License)
and (b) otherwise make it clear that Your version of the license
contains terms which differ from the Mozilla Public License and
Netscape Public License. (Filling in the name of the Initial
Developer, Original Code or Contributor in the notice described in
Exhibit A shall not of themselves be deemed to be modifications of
this License.)
7. DISCLAIMER OF WARRANTY.
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
8. TERMINATION.
8.1. This License and the rights granted hereunder will terminate
automatically if You fail to comply with terms herein and fail to cure
such breach within 30 days of becoming aware of the breach. All
sublicenses to the Covered Code which are properly granted shall
survive any termination of this License. Provisions which, by their
nature, must remain in effect beyond the termination of this License
shall survive.
8.2. If You initiate litigation by asserting a patent infringement
claim (excluding declatory judgment actions) against Initial Developer
or a Contributor (the Initial Developer or Contributor against whom
You file such action is referred to as "Participant") alleging that:
(a) such Participant's Contributor Version directly or indirectly
infringes any patent, then any and all rights granted by such
Participant to You under Sections 2.1 and/or 2.2 of this License
shall, upon 60 days notice from Participant terminate prospectively,
unless if within 60 days after receipt of notice You either: (i)
agree in writing to pay Participant a mutually agreeable reasonable
royalty for Your past and future use of Modifications made by such
Participant, or (ii) withdraw Your litigation claim with respect to
the Contributor Version against such Participant. If within 60 days
of notice, a reasonable royalty and payment arrangement are not
mutually agreed upon in writing by the parties or the litigation claim
is not withdrawn, the rights granted by Participant to You under
Sections 2.1 and/or 2.2 automatically terminate at the expiration of
the 60 day notice period specified above.
(b) any software, hardware, or device, other than such Participant's
Contributor Version, directly or indirectly infringes any patent, then
any rights granted to You by such Participant under Sections 2.1(b)
and 2.2(b) are revoked effective as of the date You first made, used,
sold, distributed, or had made, Modifications made by that
Participant.
8.3. If You assert a patent infringement claim against Participant
alleging that such Participant's Contributor Version directly or
indirectly infringes any patent where such claim is resolved (such as
by license or settlement) prior to the initiation of patent
infringement litigation, then the reasonable value of the licenses
granted by such Participant under Sections 2.1 or 2.2 shall be taken
into account in determining the amount or value of any payment or
license.
8.4. In the event of termination under Sections 8.1 or 8.2 above,
all end user license agreements (excluding distributors and resellers)
which have been validly granted by You or any distributor hereunder
prior to termination shall survive termination.
9. LIMITATION OF LIABILITY.
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
10. U.S. GOVERNMENT END USERS.
The Covered Code is a "commercial item," as that term is defined in
48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
software" and "commercial computer software documentation," as such
terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
all U.S. Government End Users acquire Covered Code with only those
rights set forth herein.
11. MISCELLANEOUS.
This License represents the complete agreement concerning subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. This License shall be governed by
California law provisions (except to the extent applicable law, if
any, provides otherwise), excluding its conflict-of-law provisions.
With respect to disputes in which at least one party is a citizen of,
or an entity chartered or registered to do business in the United
States of America, any litigation relating to this License shall be
subject to the jurisdiction of the Federal Courts of the Northern
District of California, with venue lying in Santa Clara County,
California, with the losing party responsible for costs, including
without limitation, court costs and reasonable attorneys' fees and
expenses. The application of the United Nations Convention on
Contracts for the International Sale of Goods is expressly excluded.
Any law or regulation which provides that the language of a contract
shall be construed against the drafter shall not apply to this
License.
12. RESPONSIBILITY FOR CLAIMS.
As between Initial Developer and the Contributors, each party is
responsible for claims and damages arising, directly or indirectly,
out of its utilization of rights under this License and You agree to
work with Initial Developer and Contributors to distribute such
responsibility on an equitable basis. Nothing herein is intended or
shall be deemed to constitute any admission of liability.
13. MULTIPLE-LICENSED CODE.
Initial Developer may designate portions of the Covered Code as
"Multiple-Licensed". "Multiple-Licensed" means that the Initial
Developer permits you to utilize portions of the Covered Code under
Your choice of the NPL or the alternative licenses, if any, specified
by the Initial Developer in the file described in Exhibit A.
EXHIBIT A -Mozilla Public License.
``The contents of this file are subject to the Mozilla Public License
Version 1.1 (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.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.
The Original Code is ______________________________________.
The Initial Developer of the Original Code is ________________________.
Portions created by ______________________ are Copyright (C) ______
_______________________. All Rights Reserved.
Contributor(s): ______________________________________.
Alternatively, the contents of this file may be used under the terms
of the _____ license (the "[___] License"), in which case the
provisions of [______] License are applicable instead of those
above. If you wish to allow use of your version of this file only
under the terms of the [____] License and not to allow others to use
your version of this file under the MPL, indicate your decision by
deleting the provisions above and replace them with the notice and
other provisions required by the [___] License. If you do not delete
the provisions above, a recipient may use your version of this file
under either the MPL or the [___] License."
[NOTE: The text of this Exhibit A may differ slightly from the text of
the notices in the Source Code files of the Original Code. You should
use the text of this Exhibit A rather than the text found in the
Original Code Source Code for Your Modifications.]

78
protocols/ssh/Makefile.am Normal file
View File

@ -0,0 +1,78 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is libguac-client-ssh.
#
# The Initial Developer of the Original Code is
# Michael Jumper.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
lib_LTLIBRARIES = libguac-client-ssh.la
libguac_client_ssh_la_SOURCES = \
src/blank.c \
src/buffer.c \
src/char_mappings.c \
src/client.c \
src/common.c \
src/cursor.c \
src/display.c \
src/guac_handlers.c \
src/ibar.c \
src/ssh_client.c \
src/terminal.c \
src/terminal_handlers.c
noinst_HEADERS = \
include/blank.h \
include/buffer.h \
include/char_mappings.h \
include/client.h \
include/common.h \
include/cursor.h \
include/display.h \
include/guac_handlers.h \
include/ibar.h \
include/libssh_compat.h \
include/ssh_client.h \
include/terminal.h \
include/terminal_handlers.h \
include/types.h
libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@
libguac_client_ssh_la_LIBADD = @PANGO_LIBS@ @PANGOCAIRO_LIBS@
libguac_client_ssh_la_LDFLAGS = -version-info 0:0:0
EXTRA_DIST = LICENSE

74
protocols/ssh/README Normal file
View File

@ -0,0 +1,74 @@
------------------------------------------------------------
About this README
------------------------------------------------------------
This README is intended to provide quick and to-the-point documentation for
technical users intending to compile parts of Guacamole themselves.
Distribution-specific packages are available from the files section of the main
project page:
http://sourceforge.net/projects/guacamole/files/
Distribution-specific documentation is provided on the Guacamole wiki:
http://guac-dev.org/
------------------------------------------------------------
What is libguac-client-ssh?
------------------------------------------------------------
libguac-client-ssh is a protocol support plugin for the Guacamole proxy (guacd)
which provides support for SSH, the secure shell.
------------------------------------------------------------
Compiling and installing libguac-client-ssh
------------------------------------------------------------
Please note that distribution-specific pre-compiled packages are available from
the files section of the main project site:
http://sourceforge.net/projects/guacamole/files/
libguac-client-ssh is built using the popular GNU Automake, and thus provides
the standard configure script.
1) Run configure
$ ./configure
Assuming all dependencies have been installed, this should succeed without
errors.
2) Run make
$ make
libguac-client-ssh will now compile.
3) Install (as root)
# make install
libguac-client-ssh will install to your /usr/local/lib directory by default.
You can change the install location by using the --prefix option for
configure.
You will need to run ldconfig (as root) so that guacd can find the library
when needed:
# ldconfig
------------------------------------------------------------
Reporting problems
------------------------------------------------------------
Please report any bugs encountered by opening a new ticket at the Trac system
hosted at:
http://guac-dev.org/trac/

View File

@ -0,0 +1,66 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is libguac-client-ssh.
#
# The Initial Developer of the Original Code is
# Michael Jumper.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
AC_INIT(src/client.c)
AM_INIT_AUTOMAKE([libguac-client-ssh], 0.8.0)
AC_CONFIG_MACRO_DIR([m4])
# Checks for programs.
AC_PROG_CC
AC_PROG_CC_C99
AC_PROG_LIBTOOL
# Checks for libraries.
AC_CHECK_LIB([guac], [guac_client_plugin_open],, AC_MSG_ERROR("libguac must be installed first"))
AC_CHECK_LIB([cairo], [cairo_create],, AC_MSG_ERROR("cairo is required for drawing instructions"))
AC_CHECK_LIB([ssh], [ssh_new],, AC_MSG_ERROR("libssh is required"))
AC_CHECK_LIB([pthread], [pthread_create])
PKG_CHECK_MODULES([PANGO], pango);
PKG_CHECK_MODULES([PANGOCAIRO], pangocairo);
# Check for SSH functions
AC_CHECK_FUNC(ssh_channel_close, AC_DEFINE(HAVE_SSH_CHANNEL_CLOSE))
AC_CHECK_FUNC(ssh_channel_send_eof, AC_DEFINE(HAVE_SSH_CHANNEL_SEND_EOF))
AC_CHECK_FUNC(ssh_channel_free, AC_DEFINE(HAVE_SSH_CHANNEL_FREE))
# Checks for header files.
AC_CHECK_HEADERS([guacamole/client.h guacamole/guacio.h guacamole/protocol.h])
# Checks for library functions.
AC_FUNC_MALLOC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View File

@ -0,0 +1,52 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _GUAC_SSH_BLANK_H
#define _GUAC_SSH_BLANK_H
#include <cairo/cairo.h>
#include <guacamole/client.h>
/**
* Creates a new blank cursor, returning the corresponding cursor object.
*
* @param client The guac_client to send the cursor to.
* @return A new cursor which must be free'd via guac_ssh_cursor_free()/
*/
guac_ssh_cursor* guac_ssh_create_blank(guac_client* client);
#endif

View File

@ -0,0 +1,144 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_BUFFER_H
#define _SSH_GUAC_BUFFER_H
#include "types.h"
/**
* A single variable-length row of terminal data.
*/
typedef struct guac_terminal_buffer_row {
/**
* Array of guac_terminal_char representing the contents of the row.
*/
guac_terminal_char* characters;
/**
* The length of this row in characters. This is the number of initialized
* characters in the buffer, usually equal to the number of characters
* in the screen width at the time this row was created.
*/
int length;
/**
* The number of elements in the characters array. After the length
* equals this value, the array must be resized.
*/
int available;
} guac_terminal_buffer_row;
/**
* A buffer containing a constant number of arbitrary-length rows.
* New rows can be appended to the buffer, with the oldest row replaced with
* the new row.
*/
typedef struct guac_terminal_buffer {
/**
* The character to assign to newly-allocated cells.
*/
guac_terminal_char default_character;
/**
* Array of buffer rows. This array functions as a ring buffer.
* When a new row needs to be appended, the top reference is moved down
* and the old top row is replaced.
*/
guac_terminal_buffer_row* rows;
/**
* The row to replace when adding a new row to the buffer.
*/
int top;
/**
* The number of rows currently stored in the buffer.
*/
int length;
/**
* The number of rows in the buffer. This is the total capacity
* of the buffer.
*/
int available;
} guac_terminal_buffer;
/**
* Allocates a new buffer having the given maximum number of rows. New character cells will
* be initialized to the given character.
*/
guac_terminal_buffer* guac_terminal_buffer_alloc(int rows, guac_terminal_char* default_character);
/**
* Frees the given buffer.
*/
void guac_terminal_buffer_free(guac_terminal_buffer* buffer);
/**
* Returns the row at the given location. The row returned is guaranteed to be at least the given
* width.
*/
guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row, int width);
/**
* Copies the given range of columns to a new location, offset from
* the original by the given number of columns.
*/
void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row,
int start_column, int end_column, int offset);
/**
* Copies the given range of rows to a new location, offset from the
* original by the given number of rows.
*/
void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer,
int start_row, int end_row, int offset);
/**
* Sets the given range of columns within the given row to the given
* character.
*/
void guac_terminal_buffer_set_columns(guac_terminal_buffer* buffer, int row,
int start_column, int end_column, guac_terminal_char* character);
#endif

View File

@ -0,0 +1,63 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _GUAC_SSH_CHAR_MAPPINGS_H
#define _GUAC_SSH_CHAR_MAPPINGS_H
/**
* VT100 graphics mapping. Each entry is the corresponding Unicode codepoint
* for the character N+32, where N is the index of the element in the array.
* All characters less than 32 are universally mapped to themselves.
*/
extern const int vt100_map[];
/**
* Null graphics mapping. Each entry is the corresponding Unicode codepoint
* for the character N+32, where N is the index of the element in the array.
* All characters less than 32 are universally mapped to themselves.
*/
extern const int null_map[];
/**
* User graphics mapping. Each entry is the corresponding Unicode codepoint
* for the character N+32, where N is the index of the element in the array.
* All characters less than 32 are universally mapped to themselves.
*/
extern const int user_map[];
#endif

View File

@ -0,0 +1,146 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* James Muehlner <dagger10k@users.sourceforge.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_CLIENT_H
#define _SSH_GUAC_CLIENT_H
#include <pthread.h>
#include <libssh/libssh.h>
#include "terminal.h"
#include "cursor.h"
/**
* SSH-specific client data.
*/
typedef struct ssh_guac_client_data {
/**
* The hostname of the SSH server to connect to.
*/
char hostname[1024];
/**
* The port of the SSH server to connect to.
*/
int port;
/**
* The name of the user to login as.
*/
char username[1024];
/**
* The password to give when authenticating.
*/
char password[1024];
/**
* The name of the font to use for display rendering.
*/
char font_name[1024];
/**
* The size of the font to use, in points.
*/
int font_size;
/**
* The SSH client thread.
*/
pthread_t client_thread;
/**
* SSH session, used by the SSH client thread.
*/
ssh_session session;
/**
* SSH terminal channel, used by the SSH client thread.
*/
ssh_channel term_channel;
/**
* The terminal which will render all output from the SSH client.
*/
guac_terminal* term;
/**
* The current contents of the clipboard.
*/
char* clipboard_data;
/**
* Whether the alt key is currently being held down.
*/
int mod_alt;
/**
* Whether the control key is currently being held down.
*/
int mod_ctrl;
/**
* Whether the shift key is currently being held down.
*/
int mod_shift;
/**
* The current mouse button state.
*/
int mouse_mask;
/**
* The cached I-bar cursor.
*/
guac_ssh_cursor* ibar_cursor;
/**
* The cached invisible (blank) cursor.
*/
guac_ssh_cursor* blank_cursor;
/**
* The current cursor, used to avoid re-setting the cursor.
*/
guac_ssh_cursor* current_cursor;
} ssh_guac_client_data;
#endif

View File

@ -0,0 +1,68 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_COMMON_H
#define _SSH_GUAC_COMMON_H
#include <stdbool.h>
/**
* Returns the closest value to the value given that is also
* within the given range.
*/
int guac_terminal_fit_to_range(int value, int min, int max);
/**
* Encodes the given codepoint as UTF-8, storing the result within the
* provided buffer, and returning the number of bytes stored.
*/
int guac_terminal_encode_utf8(int codepoint, char* utf8);
/**
* Returns whether a codepoint has a corresponding glyph, or is rendered
* as a blank space.
*/
bool guac_terminal_has_glyph(int codepoint);
/**
* Similar to write, but automatically retries the write operation until
* an error occurs.
*/
int guac_terminal_write_all(int fd, const char* buffer, int size);
#endif

View File

@ -0,0 +1,89 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _GUAC_SSH_CURSOR_H
#define _GUAC_SSH_CURSOR_H
#include <guacamole/client.h>
typedef struct guac_ssh_cursor {
/**
* A buffer allocated with guac_client_alloc_buffer() that contains the
* cursor image.
*/
guac_layer* buffer;
/**
* The width of the cursor in pixels.
*/
int width;
/**
* The height of the cursor in pixels.
*/
int height;
/**
* The X coordinate of the cursor hotspot.
*/
int hotspot_x;
/**
* The Y coordinate of the cursor hotspot.
*/
int hotspot_y;
} guac_ssh_cursor;
/**
* Allocates a new cursor, pre-populating the cursor with a newly-allocated
* buffer.
*/
guac_ssh_cursor* guac_ssh_cursor_alloc(guac_client* client);
/**
* Frees the buffer associated with this cursor as well as the cursor itself.
*/
void guac_ssh_cursor_free(guac_client* client, guac_ssh_cursor* cursor);
/**
* Set the remote cursor.
*/
void guac_ssh_set_cursor(guac_client* client, guac_ssh_cursor* cursor);
#endif

View File

@ -0,0 +1,293 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_DISPLAY_H
#define _SSH_GUAC_DISPLAY_H
#include <pango/pangocairo.h>
#include <guacamole/client.h>
#include "types.h"
/**
* The available color palette. All integer colors within structures
* here are indices into this palette.
*/
extern const guac_terminal_color guac_terminal_palette[16];
/**
* All available terminal operations which affect character cells.
*/
typedef enum guac_terminal_operation_type {
/**
* Operation which does nothing.
*/
GUAC_CHAR_NOP,
/**
* Operation which copies a character from a given row/column coordinate.
*/
GUAC_CHAR_COPY,
/**
* Operation which sets the character and attributes.
*/
GUAC_CHAR_SET
} guac_terminal_operation_type;
/**
* A pairing of a guac_terminal_operation_type and all parameters required by
* that operation type.
*/
typedef struct guac_terminal_operation {
/**
* The type of operation to perform.
*/
guac_terminal_operation_type type;
/**
* The character (and attributes) to set the current location to. This is
* only applicable to GUAC_CHAR_SET.
*/
guac_terminal_char character;
/**
* The row to copy a character from. This is only applicable to
* GUAC_CHAR_COPY.
*/
int row;
/**
* The column to copy a character from. This is only applicable to
* GUAC_CHAR_COPY.
*/
int column;
} guac_terminal_operation;
/**
* A cached glyph.
*/
typedef struct guac_terminal_glyph {
/**
* The location within the glyph layer that this glyph can be found.
*/
int location;
/**
* The codepoint currently stored at that location.
*/
int codepoint;
} guac_terminal_glyph;
/**
* Set of all pending operations for the currently-visible screen area.
*/
typedef struct guac_terminal_display {
/**
* The Guacamole client this display will use for rendering.
*/
guac_client* client;
/**
* Array of all operations pending for the visible screen area.
*/
guac_terminal_operation* operations;
/**
* The width of the screen, in characters.
*/
int width;
/**
* The height of the screen, in characters.
*/
int height;
/**
* The description of the font to use for rendering.
*/
PangoFontDescription* font_desc;
/**
* The width of each character, in pixels.
*/
int char_width;
/**
* The height of each character, in pixels.
*/
int char_height;
/**
* Index of next glyph to create
*/
int next_glyph;
/**
* Index of locations for each glyph in the stroke and fill layers.
*/
guac_terminal_glyph glyphs[512];
/**
* Color of glyphs in copy buffer
*/
int glyph_foreground;
/**
* Color of glyphs in copy buffer
*/
int glyph_background;
/**
* Layer above default layer which highlights selected text.
*/
guac_layer* select_layer;
/**
* A single wide layer holding each glyph, with each glyph only
* colored with foreground color (background remains transparent).
*/
guac_layer* glyph_stroke;
/**
* A single wide layer holding each glyph, with each glyph properly
* colored with foreground and background color (no transparency at all).
*/
guac_layer* filled_glyphs;
/**
* Whether text is being selected.
*/
bool text_selected;
/**
* Whether the selection is finished, and will no longer be modified. A
* committed selection remains highlighted for reference, but the
* highlight will be removed when the display changes.
*/
bool selection_committed;
/**
* The row that the selection starts at.
*/
int selection_start_row;
/**
* The column that the selection starts at.
*/
int selection_start_column;
/**
* The row that the selection ends at.
*/
int selection_end_row;
/**
* The column that the selection ends at.
*/
int selection_end_column;
} guac_terminal_display;
/**
* Allocates a new display having the given default foreground and background
* colors.
*/
guac_terminal_display* guac_terminal_display_alloc(guac_client* client,
const char* font_name, int font_size,
int foreground, int background);
/**
* Frees the given display.
*/
void guac_terminal_display_free(guac_terminal_display* display);
/**
* Copies the given range of columns to a new location, offset from
* the original by the given number of columns.
*/
void guac_terminal_display_copy_columns(guac_terminal_display* display, int row,
int start_column, int end_column, int offset);
/**
* Copies the given range of rows to a new location, offset from the
* original by the given number of rows.
*/
void guac_terminal_display_copy_rows(guac_terminal_display* display,
int start_row, int end_row, int offset);
/**
* Sets the given range of columns within the given row to the given
* character.
*/
void guac_terminal_display_set_columns(guac_terminal_display* display, int row,
int start_column, int end_column, guac_terminal_char* character);
/**
* Resize the terminal to the given dimensions.
*/
void guac_terminal_display_resize(guac_terminal_display* display, int width, int height);
/**
* Flushes all pending operations within the given guac_terminal_display.
*/
void guac_terminal_display_flush(guac_terminal_display* display);
/**
* Draws the text selection rectangle from the given coordinates to the given end coordinates.
*/
void guac_terminal_display_select(guac_terminal_display* display,
int start_row, int start_col, int end_row, int end_col);
/**
* Commits the select rectangle, allowing the display to clear it when
* necessary.
*/
void guac_terminal_display_commit_select(guac_terminal_display* display);
#endif

View File

@ -0,0 +1,52 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* James Muehlner <dagger10k@users.sourceforge.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_HANDLERS_H
#define _SSH_GUAC_HANDLERS_H
#include <guacamole/client.h>
int ssh_guac_client_handle_messages(guac_client* client);
int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed);
int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask);
int ssh_guac_client_clipboard_handler(guac_client* client, char* data);
int ssh_guac_client_size_handler(guac_client* client, int width, int height);
int ssh_guac_client_free_handler(guac_client* client);
#endif

View File

@ -0,0 +1,77 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _GUAC_SSH_IBAR_H
#define _GUAC_SSH_IBAR_H
#include <cairo/cairo.h>
#include <guacamole/client.h>
/**
* Width of the embedded mouse cursor graphic.
*/
extern const int guac_ssh_ibar_width;
/**
* Height of the embedded mouse cursor graphic.
*/
extern const int guac_ssh_ibar_height;
/**
* Number of bytes in each row of the embedded mouse cursor graphic.
*/
extern const int guac_ssh_ibar_stride;
/**
* The Cairo grapic format of the mouse cursor graphic.
*/
extern const cairo_format_t guac_ssh_ibar_format;
/**
* Embedded mouse cursor graphic.
*/
extern unsigned char guac_ssh_ibar[];
/**
* Creates a new I-bar cursor, returning the corresponding cursor object.
*
* @param client The guac_client to send the cursor to.
* @return A new cursor which must be free'd via guac_ssh_cursor_free()/
*/
guac_ssh_cursor* guac_ssh_create_ibar(guac_client* client);
#endif

View File

@ -0,0 +1,57 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_LIBSSH_COMPAT_H
#define _SSH_GUAC_LIBSSH_COMPAT_H
/* Define ssh_channel_close() if undefined */
#ifndef HAVE_SSH_CHANNEL_CLOSE
#define ssh_channel_close channel_close
#endif
/* Define ssh_channel_send_eof() if undefined */
#ifndef HAVE_SSH_CHANNEL_SEND_EOF
#define ssh_channel_send_eof channel_send_eof
#endif
/* Define ssh_channel_free() if undefined */
#ifndef HAVE_SSH_CHANNEL_FREE
#define ssh_channel_free channel_free
#endif
#endif

View File

@ -0,0 +1,49 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __SSH_CLIENT_H
#define __SSH_CLIENT_H
#include <guacamole/client.h>
/**
* Main SSH client thread, handling transfer of SSH output to STDOUT.
*/
void* ssh_client_thread(void* data);
#endif

View File

@ -0,0 +1,419 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_TERMINAL_H
#define _SSH_GUAC_TERMINAL_H
#include <stdbool.h>
#include <pthread.h>
#include <guacamole/client.h>
#include "types.h"
#include "display.h"
#include "buffer.h"
/**
* The maximum number of custom tab stops.
*/
#define GUAC_TERMINAL_MAX_TABS 16
/**
* The number of rows to scroll per scroll wheel event.
*/
#define GUAC_SSH_WHEEL_SCROLL_AMOUNT 3
typedef struct guac_terminal guac_terminal;
/**
* Handler for characters printed to the terminal. When a character is printed,
* the current char handler for the terminal is called and given that
* character.
*/
typedef int guac_terminal_char_handler(guac_terminal* term, char c);
/**
* Represents a terminal emulator which uses a given Guacamole client to
* render itself.
*/
struct guac_terminal {
/**
* The Guacamole client this terminal emulator will use for rendering.
*/
guac_client* client;
/**
* Lock which restricts simultaneous access to this terminal via the root
* guac_terminal_* functions.
*/
pthread_mutex_t lock;
/**
* Pipe which should be written to (and read from) to provide output to
* this terminal. Another thread should read from this pipe when writing
* data to the terminal. It would make sense for the terminal to provide
* this thread, but for simplicity, that logic is left to the guac
* message handler (to give the message handler something to block with).
*/
int stdout_pipe_fd[2];
/**
* Pipe which will be the source of user input. When a terminal code
* generates synthesized user input, that data will be written to
* this pipe.
*/
int stdin_pipe_fd[2];
/**
* The relative offset of the display. A positive value indicates that
* many rows have been scrolled into view, zero indicates that no
* scrolling has occurred. Negative values are illegal.
*/
int scroll_offset;
/**
* The width of the terminal, in characters.
*/
int term_width;
/**
* The height of the terminal, in characters.
*/
int term_height;
/**
* The index of the first row in the scrolling region.
*/
int scroll_start;
/**
* The index of the last row in the scrolling region.
*/
int scroll_end;
/**
* The current row location of the cursor.
*/
int cursor_row;
/**
* The current column location of the cursor.
*/
int cursor_col;
/**
* The row of the rendered cursor.
*/
int visible_cursor_row;
/**
* The column of the rendered cursor.
*/
int visible_cursor_col;
/**
* The row of the saved cursor (ESC 7).
*/
int saved_cursor_row;
/**
* The column of the saved cursor (ESC 7).
*/
int saved_cursor_col;
/**
* The attributes which will be applied to future characters.
*/
guac_terminal_attributes current_attributes;
/**
* The character whose attributes dictate the default attributes
* of all characters. When new screen space is allocated, this
* character fills the gaps.
*/
guac_terminal_char default_char;
/**
* Handler which will receive all printed characters, updating the terminal
* accordingly.
*/
guac_terminal_char_handler* char_handler;
/**
* The difference between the currently-rendered screen and the current
* state of the terminal.
*/
guac_terminal_display* display;
/**
* Current terminal display state. All characters present on the screen
* are within this buffer. This has nothing to do with the display, which
* facilitates transfer of a set of changes to the remote display.
*/
guac_terminal_buffer* buffer;
/**
* Automatically place a tabstop every N characters. If zero, then no
* tabstops exist automatically.
*/
int tab_interval;
/**
* Array of all tabs set. Each entry is the column number of a tab + 1,
* or 0 if that tab cell is unset.
*/
int custom_tabs[GUAC_TERMINAL_MAX_TABS];
/**
* Array of arrays of mapped characters, where the character N is located at the N-32
* position within the array. Each element in a contained array is the corresponding Unicode
* codepoint. If NULL, a direct mapping from Unicode is used. The entries of the main array
* correspond to the character set in use (G0, G1, etc.)
*/
const int* char_mapping[2];
/**
* The active character set. For example, 0 for G0, 1 for G1, etc.
*/
int active_char_set;
/**
* Whether text is being selected.
*/
bool text_selected;
/**
* The row that the selection starts at.
*/
int selection_start_row;
/**
* The column that the selection starts at.
*/
int selection_start_column;
/**
* The row that the selection ends at.
*/
int selection_end_row;
/**
* The column that the selection ends at.
*/
int selection_end_column;
/**
* Whether the cursor (arrow) keys should send cursor sequences
* or application sequences (DECCKM).
*/
bool application_cursor_keys;
/**
* Whether a CR should automatically follow a LF, VT, or FF.
*/
bool automatic_carriage_return;
/**
* Whether insert mode is enabled (DECIM).
*/
bool insert_mode;
};
/**
* Creates a new guac_terminal, having the given width and height, and
* rendering to the given client.
*/
guac_terminal* guac_terminal_create(guac_client* client,
const char* font_name, int font_size,
int width, int height);
/**
* Resets the state of the given terminal, as if it were just allocated.
*/
void guac_terminal_reset(guac_terminal* term);
/**
* Frees all resources associated with the given terminal.
*/
void guac_terminal_free(guac_terminal* term);
/**
* Writes the given string of characters to the terminal.
*/
int guac_terminal_write(guac_terminal* term, const char* c, int size);
/**
* Sets the character at the given row and column to the specified value.
*/
int guac_terminal_set(guac_terminal* term, int row, int col, int codepoint);
/**
* Clears the given region within a single row.
*/
int guac_terminal_clear_columns(guac_terminal* term,
int row, int start_col, int end_col);
/**
* Clears the given region from right-to-left, top-to-bottom, replacing
* all characters with the current background color and attributes.
*/
int guac_terminal_clear_range(guac_terminal* term,
int start_row, int start_col,
int end_row, int end_col);
/**
* Scrolls the terminal's current scroll region up by one row.
*/
int guac_terminal_scroll_up(guac_terminal* term,
int start_row, int end_row, int amount);
/**
* Scrolls the terminal's current scroll region down by one row.
*/
int guac_terminal_scroll_down(guac_terminal* term,
int start_row, int end_row, int amount);
/**
* Commits the current cursor location, updating the visible cursor
* on the screen.
*/
void guac_terminal_commit_cursor(guac_terminal* term);
/**
* Scroll down the display by the given amount, replacing the new space with
* data from the buffer. If not enough data is available, the maximum
* amount will be scrolled.
*/
void guac_terminal_scroll_display_down(guac_terminal* terminal, int amount);
/**
* Scroll up the display by the given amount, replacing the new space with data
* from either the buffer or the terminal buffer. If not enough data is
* available, the maximum amount will be scrolled.
*/
void guac_terminal_scroll_display_up(guac_terminal* terminal, int amount);
/**
* Marks the start of text selection at the given row and column.
*/
void guac_terminal_select_start(guac_terminal* terminal, int row, int column);
/**
* Updates the end of text selection at the given row and column.
*/
void guac_terminal_select_update(guac_terminal* terminal, int row, int column);
/**
* Ends text selection, removing any highlight. Character data is stored in the
* string buffer provided.
*/
void guac_terminal_select_end(guac_terminal* terminal, char* string);
/* LOW-LEVEL TERMINAL OPERATIONS */
/**
* Copies the given range of columns to a new location, offset from
* the original by the given number of columns.
*/
void guac_terminal_copy_columns(guac_terminal* terminal, int row,
int start_column, int end_column, int offset);
/**
* Copies the given range of rows to a new location, offset from the
* original by the given number of rows.
*/
void guac_terminal_copy_rows(guac_terminal* terminal,
int start_row, int end_row, int offset);
/**
* Sets the given range of columns within the given row to the given
* character.
*/
void guac_terminal_set_columns(guac_terminal* terminal, int row,
int start_column, int end_column, guac_terminal_char* character);
/**
* Resize the terminal to the given dimensions.
*/
void guac_terminal_resize(guac_terminal* term, int width, int height);
/**
* Flushes all pending operations within the given guac_terminal.
*/
void guac_terminal_flush(guac_terminal* terminal);
/**
* Sends the given string as if typed by the user.
*/
int guac_terminal_send_data(guac_terminal* term, const char* data, int length);
/**
* Sends the given string as if typed by the user.
*/
int guac_terminal_send_string(guac_terminal* term, const char* data);
/**
* Sends data through STDIN as if typed by the user, using the format
* string given and any args (similar to printf).
*/
int guac_terminal_sendf(guac_terminal* term, const char* format, ...);
/**
* Sets a tabstop in the given column.
*/
void guac_terminal_set_tab(guac_terminal* term, int column);
/**
* Removes the tabstop at the given column.
*/
void guac_terminal_unset_tab(guac_terminal* term, int column);
/**
* Removes all tabstops.
*/
void guac_terminal_clear_tabs(guac_terminal* term);
/**
* Given a column within the given terminal, returns the location of the
* next tabstop (or the rightmost character, if no more tabstops exist).
*/
int guac_terminal_next_tab(guac_terminal* term, int column);
#endif

View File

@ -0,0 +1,54 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_TERMINAL_HANDLERS
#define _SSH_GUAC_TERMINAL_HANDLERS
#include "terminal.h"
int guac_terminal_echo(guac_terminal* term, char c);
int guac_terminal_escape(guac_terminal* term, char c);
int guac_terminal_g0_charset(guac_terminal* term, char c);
int guac_terminal_g1_charset(guac_terminal* term, char c);
int guac_terminal_g2_charset(guac_terminal* term, char c);
int guac_terminal_g3_charset(guac_terminal* term, char c);
int guac_terminal_csi(guac_terminal* term, char c);
int guac_terminal_osc(guac_terminal* term, char c);
int guac_terminal_ctrl_func(guac_terminal* term, char c);
#endif

View File

@ -0,0 +1,122 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_TYPES_H
#define _SSH_GUAC_TYPES_H
#include <stdbool.h>
/**
* An RGB color, where each component ranges from 0 to 255.
*/
typedef struct guac_terminal_color {
/**
* The red component of this color.
*/
int red;
/**
* The green component of this color.
*/
int green;
/**
* The blue component of this color.
*/
int blue;
} guac_terminal_color;
/**
* Terminal attributes, as can be applied to a single character.
*/
typedef struct guac_terminal_attributes {
/**
* Whether the character should be rendered bold.
*/
bool bold;
/**
* Whether the character should be rendered with reversed colors
* (background becomes foreground and vice-versa).
*/
bool reverse;
/**
* Whether the associated character is highlighted by the cursor.
*/
bool cursor;
/**
* Whether to render the character with underscore.
*/
bool underscore;
/**
* The foreground color of this character, as a palette index.
*/
int foreground;
/**
* The background color of this character, as a palette index.
*/
int background;
} guac_terminal_attributes;
/**
* Represents a single character for display in a terminal, including actual
* character value, foreground color, and background color.
*/
typedef struct guac_terminal_char {
/**
* The Unicode codepoint of the character to display.
*/
int value;
/**
* The attributes of the character to display.
*/
guac_terminal_attributes attributes;
} guac_terminal_char;
#endif

12
protocols/ssh/m4/README Normal file
View File

@ -0,0 +1,12 @@
This file exists such that the m4/ directory will be created when cloning the
git repository.
The m4/ directory is not directly used by this project, but libtoolize
populates this directory with files, recommending that the directory be
included in the macro search path for aclocal.
Because autoreconf runs aclocal before libtoolize, this directory will not
exist when autoreconf is run, triggering an error from aclocal.
Creating this directory (and keeping this file in it as a placeholder)
prevents this error.

64
protocols/ssh/src/blank.c Normal file
View File

@ -0,0 +1,64 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include "cursor.h"
guac_ssh_cursor* guac_ssh_create_blank(guac_client* client) {
guac_socket* socket = client->socket;
guac_ssh_cursor* cursor = guac_ssh_cursor_alloc(client);
/* Set buffer to a single 1x1 transparent rectangle */
guac_protocol_send_rect(socket, cursor->buffer, 0, 0, 1, 1);
guac_protocol_send_cfill(socket, GUAC_COMP_SRC, cursor->buffer,
0x00, 0x00, 0x00, 0x00);
/* Initialize cursor properties */
cursor->width = 1;
cursor->height = 1;
cursor->hotspot_x = 0;
cursor->hotspot_y = 0;
return cursor;
}

214
protocols/ssh/src/buffer.c Normal file
View File

@ -0,0 +1,214 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdlib.h>
#include <string.h>
#include "buffer.h"
#include "common.h"
guac_terminal_buffer* guac_terminal_buffer_alloc(int rows, guac_terminal_char* default_character) {
/* Allocate scrollback */
guac_terminal_buffer* buffer =
malloc(sizeof(guac_terminal_buffer));
int i;
guac_terminal_buffer_row* row;
/* Init scrollback data */
buffer->default_character = *default_character;
buffer->available = rows;
buffer->top = 0;
buffer->length = 0;
buffer->rows = malloc(sizeof(guac_terminal_buffer_row) *
buffer->available);
/* Init scrollback rows */
row = buffer->rows;
for (i=0; i<rows; i++) {
/* Allocate row */
row->available = 256;
row->length = 0;
row->characters = malloc(sizeof(guac_terminal_char) * row->available);
/* Next row */
row++;
}
return buffer;
}
void guac_terminal_buffer_free(guac_terminal_buffer* buffer) {
int i;
guac_terminal_buffer_row* row = buffer->rows;
/* Free all rows */
for (i=0; i<buffer->available; i++) {
free(row->characters);
row++;
}
/* Free actual buffer */
free(buffer->rows);
free(buffer);
}
guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row, int width) {
int i;
guac_terminal_char* first;
guac_terminal_buffer_row* buffer_row;
/* Calculate scrollback row index */
int index = buffer->top + row;
if (index < 0)
index += buffer->available;
else if (index >= buffer->available)
index -= buffer->available;
/* Get row */
buffer_row = &(buffer->rows[index]);
/* If resizing is needed */
if (width >= buffer_row->length) {
/* Expand if necessary */
if (width > buffer_row->available) {
buffer_row->available = width*2;
buffer_row->characters = realloc(buffer_row->characters, sizeof(guac_terminal_char) * buffer_row->available);
}
/* Initialize new part of row */
first = &(buffer_row->characters[buffer_row->length]);
for (i=buffer_row->length; i<width; i++)
*(first++) = buffer->default_character;
buffer_row->length = width;
}
/* Return found row */
return buffer_row;
}
void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row,
int start_column, int end_column, int offset) {
guac_terminal_char* src;
guac_terminal_char* dst;
/* Get row */
guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(buffer, row, end_column + offset + 1);
/* Fit range within bounds */
start_column = guac_terminal_fit_to_range(start_column, 0, buffer_row->length - 1);
end_column = guac_terminal_fit_to_range(end_column, 0, buffer_row->length - 1);
start_column = guac_terminal_fit_to_range(start_column + offset, 0, buffer_row->length - 1) - offset;
end_column = guac_terminal_fit_to_range(end_column + offset, 0, buffer_row->length - 1) - offset;
/* Determine source and destination locations */
src = &(buffer_row->characters[start_column]);
dst = &(buffer_row->characters[start_column + offset]);
/* Copy data */
memmove(dst, src, sizeof(guac_terminal_char) * (end_column - start_column + 1));
}
void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer,
int start_row, int end_row, int offset) {
int i, current_row;
int step;
/* If shifting down, copy in reverse */
if (offset > 0) {
current_row = end_row;
step = -1;
}
/* Otherwise, copy forwards */
else {
current_row = start_row;
step = 1;
}
/* Copy each current_row individually */
for (i = start_row; i <= end_row; i++) {
/* Get source and destination rows */
guac_terminal_buffer_row* src_row = guac_terminal_buffer_get_row(buffer, current_row, 0);
guac_terminal_buffer_row* dst_row = guac_terminal_buffer_get_row(buffer, current_row + offset, src_row->length);
/* Copy data */
memcpy(dst_row->characters, src_row->characters, sizeof(guac_terminal_char) * src_row->length);
dst_row->length = src_row->length;
/* Next current_row */
current_row += step;
}
}
void guac_terminal_buffer_set_columns(guac_terminal_buffer* buffer, int row,
int start_column, int end_column, guac_terminal_char* character) {
int i;
guac_terminal_char* current;
/* Get and expand row */
guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(buffer, row, end_column+1);
/* Set values */
current = &(buffer_row->characters[start_column]);
for (i=start_column; i<=end_column; i++)
*(current++) = *character;
/* Update length depending on row written */
if (row >= buffer->length)
buffer->length = row+1;
}

View File

@ -0,0 +1,88 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const int vt100_map[] = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x00B0, 0x00B1, 0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA,
0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00B7, 0x007F,
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
};
const int null_map[] = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x007F,
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x000A
};
const int user_map[] = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
0x00A7, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x00C4, 0x00D6, 0x00DC, '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0x00E4, 0x00F6, 0x00FC, 0x00DF, 0x007F,
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
};

186
protocols/ssh/src/client.c Normal file
View File

@ -0,0 +1,186 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* James Muehlner <dagger10k@users.sourceforge.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <guacamole/socket.h>
#include <guacamole/protocol.h>
#include <guacamole/client.h>
#include <guacamole/error.h>
#include "client.h"
#include "guac_handlers.h"
#include "terminal.h"
#include "blank.h"
#include "ibar.h"
#include "ssh_client.h"
#define GUAC_SSH_DEFAULT_FONT_NAME "monospace"
#define GUAC_SSH_DEFAULT_FONT_SIZE 12
#define GUAC_SSH_DEFAULT_PORT 22
/* Client plugin arguments */
const char* GUAC_CLIENT_ARGS[] = {
"hostname",
"port",
"username",
"password",
"font-name",
"font-size",
NULL
};
enum __SSH_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 password to use when logging in. Optional.
*/
IDX_PASSWORD,
/**
* 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,
SSH_ARGS_COUNT
};
int guac_client_init(guac_client* client, int argc, char** argv) {
guac_socket* socket = client->socket;
ssh_guac_client_data* client_data = malloc(sizeof(ssh_guac_client_data));
/* Init client data */
client->data = client_data;
client_data->mod_alt =
client_data->mod_ctrl =
client_data->mod_shift = 0;
client_data->clipboard_data = NULL;
client_data->term_channel = NULL;
if (argc != SSH_ARGS_COUNT) {
guac_client_log_error(client, "Wrong number of arguments");
return -1;
}
/* Read parameters */
strcpy(client_data->hostname, argv[IDX_HOSTNAME]);
strcpy(client_data->username, argv[IDX_USERNAME]);
strcpy(client_data->password, argv[IDX_PASSWORD]);
/* Read font name */
if (argv[IDX_FONT_NAME][0] != 0)
strcpy(client_data->font_name, argv[IDX_FONT_NAME]);
else
strcpy(client_data->font_name, GUAC_SSH_DEFAULT_FONT_NAME );
/* Read font size */
if (argv[IDX_FONT_SIZE][0] != 0)
client_data->font_size = atoi(argv[IDX_FONT_SIZE]);
else
client_data->font_size = GUAC_SSH_DEFAULT_FONT_SIZE;
/* Read port */
if (argv[IDX_PORT][0] != 0)
client_data->port = atoi(argv[IDX_PORT]);
else
client_data->port = GUAC_SSH_DEFAULT_PORT;
/* Create terminal */
client_data->term = guac_terminal_create(client,
client_data->font_name, client_data->font_size,
client->info.optimal_width, client->info.optimal_height);
/* Set up I-bar pointer */
client_data->ibar_cursor = guac_ssh_create_ibar(client);
/* Set up blank pointer */
client_data->blank_cursor = guac_ssh_create_blank(client);
/* Send initial name */
guac_protocol_send_name(socket, client_data->hostname);
/* Initialize pointer */
client_data->current_cursor = client_data->blank_cursor;
guac_ssh_set_cursor(client, client_data->current_cursor);
guac_socket_flush(socket);
/* Set basic handlers */
client->handle_messages = ssh_guac_client_handle_messages;
client->clipboard_handler = ssh_guac_client_clipboard_handler;
client->key_handler = ssh_guac_client_key_handler;
client->mouse_handler = ssh_guac_client_mouse_handler;
client->size_handler = ssh_guac_client_size_handler;
client->free_handler = ssh_guac_client_free_handler;
/* Start client thread */
if (pthread_create(&(client_data->client_thread), NULL, ssh_client_thread, (void*) client)) {
guac_client_log_error(client, "Unable to SSH client thread");
return -1;
}
/* Success */
return 0;
}

121
protocols/ssh/src/common.c Normal file
View File

@ -0,0 +1,121 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdbool.h>
#include <unistd.h>
int guac_terminal_fit_to_range(int value, int min, int max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
int guac_terminal_encode_utf8(int codepoint, char* utf8) {
int i;
int mask, bytes;
/* Determine size and initial byte mask */
if (codepoint <= 0x007F) {
mask = 0x00;
bytes = 1;
}
else if (codepoint <= 0x7FF) {
mask = 0xC0;
bytes = 2;
}
else if (codepoint <= 0xFFFF) {
mask = 0xE0;
bytes = 3;
}
else if (codepoint <= 0x1FFFFF) {
mask = 0xF0;
bytes = 4;
}
/* Otherwise, invalid codepoint */
else {
*(utf8++) = '?';
return 1;
}
/* Offset buffer by size */
utf8 += bytes - 1;
/* Add trailing bytes, if any */
for (i=1; i<bytes; i++) {
*(utf8--) = 0x80 | (codepoint & 0x3F);
codepoint >>= 6;
}
/* Set initial byte */
*utf8 = mask | codepoint;
/* Done */
return bytes;
}
bool guac_terminal_has_glyph(int codepoint) {
return
codepoint != 0
&& codepoint != ' ';
}
int guac_terminal_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;
}

View File

@ -0,0 +1,74 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdlib.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include "cursor.h"
guac_ssh_cursor* guac_ssh_cursor_alloc(guac_client* client) {
/* Alloc new cursor, initialize buffer */
guac_ssh_cursor* cursor = malloc(sizeof(guac_ssh_cursor));
cursor->buffer = guac_client_alloc_buffer(client);
return cursor;
}
void guac_ssh_cursor_free(guac_client* client, guac_ssh_cursor* cursor) {
/* Free buffer */
guac_client_free_buffer(client, cursor->buffer);
/* Free cursor */
free(cursor);
}
void guac_ssh_set_cursor(guac_client* client, guac_ssh_cursor* cursor) {
/* Set cursor */
guac_protocol_send_cursor(client->socket,
cursor->hotspot_x, cursor->hotspot_y,
cursor->buffer,
0, 0, cursor->width, cursor->height);
}

1010
protocols/ssh/src/display.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,445 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* James Muehlner <dagger10k@users.sourceforge.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <sys/select.h>
#include <stdlib.h>
#include <string.h>
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <guacamole/socket.h>
#include <guacamole/protocol.h>
#include <guacamole/client.h>
#include <guacamole/error.h>
#include <libssh/libssh.h>
#include "libssh_compat.h"
#include "guac_handlers.h"
#include "client.h"
#include "common.h"
#include "cursor.h"
int ssh_guac_client_handle_messages(guac_client* client) {
guac_socket* socket = client->socket;
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
char buffer[8192];
int ret_val;
int fd = client_data->term->stdout_pipe_fd[0];
struct timeval timeout;
fd_set fds;
/* Build fd_set */
FD_ZERO(&fds);
FD_SET(fd, &fds);
/* Time to wait */
timeout.tv_sec = 1;
timeout.tv_usec = 0;
/* Wait for data to be available */
ret_val = select(fd+1, &fds, NULL, NULL, &timeout);
if (ret_val > 0) {
int bytes_read = 0;
/* Lock terminal access */
pthread_mutex_lock(&(client_data->term->lock));
/* Read data, write to terminal */
if ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
if (guac_terminal_write(client_data->term, buffer, bytes_read))
return 1;
}
/* Notify on error */
if (bytes_read < 0) {
guac_protocol_send_error(socket, "Error reading data.");
guac_socket_flush(socket);
return 1;
}
/* Update cursor */
guac_terminal_commit_cursor(client_data->term);
/* Flush terminal display */
guac_terminal_display_flush(client_data->term->display);
/* Unlock terminal access */
pthread_mutex_unlock(&(client_data->term->lock));
}
else if (ret_val < 0) {
guac_error_message = "Error waiting for pipe";
guac_error = GUAC_STATUS_SEE_ERRNO;
return 1;
}
return 0;
}
int ssh_guac_client_clipboard_handler(guac_client* client, char* data) {
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
free(client_data->clipboard_data);
client_data->clipboard_data = strdup(data);
return 0;
}
int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
guac_terminal* term = client_data->term;
/* Determine which buttons were just released and pressed */
int released_mask = client_data->mouse_mask & ~mask;
int pressed_mask = ~client_data->mouse_mask & mask;
client_data->mouse_mask = mask;
/* Show mouse cursor if not already shown */
if (client_data->current_cursor != client_data->ibar_cursor) {
pthread_mutex_lock(&(term->lock));
client_data->current_cursor = client_data->ibar_cursor;
guac_ssh_set_cursor(client, client_data->ibar_cursor);
guac_socket_flush(client->socket);
pthread_mutex_unlock(&(term->lock));
}
/* Paste contents of clipboard on right or middle mouse button up */
if ((released_mask & GUAC_CLIENT_MOUSE_RIGHT) || (released_mask & GUAC_CLIENT_MOUSE_MIDDLE)) {
if (client_data->clipboard_data != NULL)
return guac_terminal_send_string(term, client_data->clipboard_data);
else
return 0;
}
/* If text selected, change state based on left mouse mouse button */
if (term->text_selected) {
pthread_mutex_lock(&(term->lock));
/* If mouse button released, stop selection */
if (released_mask & GUAC_CLIENT_MOUSE_LEFT) {
/* End selection and get selected text */
char* string = malloc(term->term_width * term->term_height * sizeof(char));
guac_terminal_select_end(term, string);
/* Store new data */
free(client_data->clipboard_data);
client_data->clipboard_data = string;
/* Send data */
guac_protocol_send_clipboard(client->socket, string);
guac_socket_flush(client->socket);
}
/* Otherwise, just update */
else
guac_terminal_select_update(term,
y / term->display->char_height - term->scroll_offset,
x / term->display->char_width);
pthread_mutex_unlock(&(term->lock));
}
/* Otherwise, if mouse button pressed AND moved, start selection */
else if (!(pressed_mask & GUAC_CLIENT_MOUSE_LEFT) &&
mask & GUAC_CLIENT_MOUSE_LEFT) {
pthread_mutex_lock(&(term->lock));
guac_terminal_select_start(term,
y / term->display->char_height - term->scroll_offset,
x / term->display->char_width);
pthread_mutex_unlock(&(term->lock));
}
/* Scroll up if wheel moved up */
if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) {
pthread_mutex_lock(&(term->lock));
guac_terminal_scroll_display_up(term, GUAC_SSH_WHEEL_SCROLL_AMOUNT);
pthread_mutex_unlock(&(term->lock));
}
/* Scroll down if wheel moved down */
if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN) {
pthread_mutex_lock(&(term->lock));
guac_terminal_scroll_display_down(term, GUAC_SSH_WHEEL_SCROLL_AMOUNT);
pthread_mutex_unlock(&(term->lock));
}
return 0;
}
int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
guac_terminal* term = client_data->term;
/* Hide mouse cursor if not already hidden */
if (client_data->current_cursor != client_data->blank_cursor) {
pthread_mutex_lock(&(term->lock));
client_data->current_cursor = client_data->blank_cursor;
guac_ssh_set_cursor(client, client_data->blank_cursor);
guac_socket_flush(client->socket);
pthread_mutex_unlock(&(term->lock));
}
/* Track modifiers */
if (keysym == 0xFFE3)
client_data->mod_ctrl = pressed;
else if (keysym == 0xFFE9)
client_data->mod_alt = pressed;
else if (keysym == 0xFFE1)
client_data->mod_shift = pressed;
/* If key pressed */
else if (pressed) {
/* Ctrl+Shift+V shortcut for paste */
if (keysym == 'V' && client_data->mod_ctrl) {
if (client_data->clipboard_data != NULL)
return guac_terminal_send_string(term, client_data->clipboard_data);
else
return 0;
}
/* Shift+PgUp / Shift+PgDown shortcuts for scrolling */
if (client_data->mod_shift) {
/* Page up */
if (keysym == 0xFF55) {
pthread_mutex_lock(&(term->lock));
guac_terminal_scroll_display_up(term, term->term_height);
pthread_mutex_unlock(&(term->lock));
return 0;
}
/* Page down */
if (keysym == 0xFF56) {
pthread_mutex_lock(&(term->lock));
guac_terminal_scroll_display_down(term, term->term_height);
pthread_mutex_unlock(&(term->lock));
return 0;
}
}
/* Reset scroll */
if (term->scroll_offset != 0) {
pthread_mutex_lock(&(term->lock));
guac_terminal_scroll_display_down(term, term->scroll_offset);
pthread_mutex_unlock(&(term->lock));
}
/* If alt being held, also send escape character */
if (client_data->mod_alt)
return guac_terminal_send_string(term, "\x1B");
/* Translate Ctrl+letter to control code */
if (client_data->mod_ctrl) {
char data;
/* If valid control code, send it */
if (keysym >= 'A' && keysym <= 'Z')
data = (char) (keysym - 'A' + 1);
else if (keysym >= 'a' && keysym <= 'z')
data = (char) (keysym - 'a' + 1);
/* Otherwise ignore */
else
return 0;
return guac_terminal_send_data(term, &data, 1);
}
/* Translate Unicode to UTF-8 */
else if ((keysym >= 0x00 && keysym <= 0xFF) || ((keysym & 0xFFFF0000) == 0x01000000)) {
int length;
char data[5];
length = guac_terminal_encode_utf8(keysym & 0xFFFF, data);
return guac_terminal_send_data(term, data, length);
}
/* Non-printable keys */
else {
if (keysym == 0xFF08) return guac_terminal_send_string(term, "\x7F"); /* Backspace */
if (keysym == 0xFF09) return guac_terminal_send_string(term, "\x09"); /* Tab */
if (keysym == 0xFF0D) return guac_terminal_send_string(term, "\x0D"); /* Enter */
if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */
if (keysym == 0xFF50) return guac_terminal_send_string(term, "\x1B[1~"); /* Home */
/* Arrow keys w/ application cursor */
if (term->application_cursor_keys) {
if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1BOD"); /* Left */
if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1BOA"); /* Up */
if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1BOC"); /* Right */
if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1BOB"); /* Down */
}
else {
if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1B[D"); /* Left */
if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1B[A"); /* Up */
if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1B[C"); /* Right */
if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1B[B"); /* Down */
}
if (keysym == 0xFF55) return guac_terminal_send_string(term, "\x1B[5~"); /* Page up */
if (keysym == 0xFF56) return guac_terminal_send_string(term, "\x1B[6~"); /* Page down */
if (keysym == 0xFF57) return guac_terminal_send_string(term, "\x1B[4~"); /* End */
if (keysym == 0xFF63) return guac_terminal_send_string(term, "\x1B[2~"); /* Insert */
if (keysym == 0xFFBE) return guac_terminal_send_string(term, "\x1B[[A"); /* F1 */
if (keysym == 0xFFBF) return guac_terminal_send_string(term, "\x1B[[B"); /* F2 */
if (keysym == 0xFFC0) return guac_terminal_send_string(term, "\x1B[[C"); /* F3 */
if (keysym == 0xFFC1) return guac_terminal_send_string(term, "\x1B[[D"); /* F4 */
if (keysym == 0xFFC2) return guac_terminal_send_string(term, "\x1B[[E"); /* F5 */
if (keysym == 0xFFC3) return guac_terminal_send_string(term, "\x1B[17~"); /* F6 */
if (keysym == 0xFFC4) return guac_terminal_send_string(term, "\x1B[18~"); /* F7 */
if (keysym == 0xFFC5) return guac_terminal_send_string(term, "\x1B[19~"); /* F8 */
if (keysym == 0xFFC6) return guac_terminal_send_string(term, "\x1B[20~"); /* F9 */
if (keysym == 0xFFC7) return guac_terminal_send_string(term, "\x1B[21~"); /* F10 */
if (keysym == 0xFFC8) return guac_terminal_send_string(term, "\x1B[22~"); /* F11 */
if (keysym == 0xFFC9) return guac_terminal_send_string(term, "\x1B[23~"); /* F12 */
if (keysym == 0xFFFF) return guac_terminal_send_string(term, "\x1B[3~"); /* Delete */
/* Ignore unknown keys */
}
}
return 0;
}
int ssh_guac_client_size_handler(guac_client* client, int width, int height) {
/* Get terminal */
ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data;
guac_terminal* terminal = guac_client_data->term;
/* Calculate dimensions */
int rows = height / terminal->display->char_height;
int columns = width / terminal->display->char_width;
pthread_mutex_lock(&(terminal->lock));
/* If size has changed */
if (columns != terminal->term_width || rows != terminal->term_height) {
/* Resize terminal */
guac_terminal_resize(terminal, columns, rows);
/* Update SSH pty size if connected */
if (guac_client_data->term_channel != NULL)
channel_change_pty_size(guac_client_data->term_channel,
terminal->term_width, terminal->term_height);
/* Reset scroll region */
terminal->scroll_end = rows - 1;
guac_terminal_display_flush(terminal->display);
guac_socket_flush(terminal->client->socket);
}
pthread_mutex_unlock(&(terminal->lock));
return 0;
}
int ssh_guac_client_free_handler(guac_client* client) {
ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data;
/* Close SSH channel */
if (guac_client_data->term_channel != NULL) {
ssh_channel_close(guac_client_data->term_channel);
ssh_channel_send_eof(guac_client_data->term_channel);
}
/* Free terminal */
guac_terminal_free(guac_client_data->term);
pthread_join(guac_client_data->client_thread, NULL);
/* Free channels */
ssh_channel_free(guac_client_data->term_channel);
/* Free session */
ssh_free(guac_client_data->session);
/* Free clipboard data */
free(guac_client_data->clipboard_data);
/* Free cursors */
guac_ssh_cursor_free(client, guac_client_data->ibar_cursor);
guac_ssh_cursor_free(client, guac_client_data->blank_cursor);
/* Free generic data struct */
free(client->data);
return 0;
}

108
protocols/ssh/src/ibar.c Normal file
View File

@ -0,0 +1,108 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include "cursor.h"
/* Macros for prettying up the embedded image. */
#define X 0x00,0x00,0x00,0xFF
#define U 0x80,0x80,0x80,0xFF
#define O 0xFF,0xFF,0xFF,0xFF
#define _ 0x00,0x00,0x00,0x00
/* Dimensions */
const int guac_ssh_ibar_width = 7;
const int guac_ssh_ibar_height = 16;
/* Format */
const cairo_format_t guac_ssh_ibar_format = CAIRO_FORMAT_ARGB32;
const int guac_ssh_ibar_stride = 28;
/* Embedded pointer graphic */
unsigned char guac_ssh_ibar[] = {
X,X,X,X,X,X,X,
X,O,O,U,O,O,X,
X,X,X,O,X,X,X,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
X,X,X,O,X,X,X,
X,O,O,U,O,O,X,
X,X,X,X,X,X,X
};
guac_ssh_cursor* guac_ssh_create_ibar(guac_client* client) {
guac_socket* socket = client->socket;
guac_ssh_cursor* cursor = guac_ssh_cursor_alloc(client);
/* Draw to buffer */
cairo_surface_t* graphic = cairo_image_surface_create_for_data(
guac_ssh_ibar,
guac_ssh_ibar_format,
guac_ssh_ibar_width,
guac_ssh_ibar_height,
guac_ssh_ibar_stride);
guac_protocol_send_png(socket, GUAC_COMP_SRC, cursor->buffer,
0, 0, graphic);
cairo_surface_destroy(graphic);
/* Initialize cursor properties */
cursor->width = guac_ssh_ibar_width;
cursor->height = guac_ssh_ibar_height;
cursor->hotspot_x = guac_ssh_ibar_width / 2;
cursor->hotspot_y = guac_ssh_ibar_height / 2;
return cursor;
}

View File

@ -0,0 +1,252 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* James Muehlner <dagger10k@users.sourceforge.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <libssh/libssh.h>
#include "client.h"
#include "common.h"
/**
* Reads a single line from STDIN.
*/
static char* prompt(guac_client* client, const char* title, char* str, int size, bool echo) {
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
int pos;
char in_byte;
/* Get STDIN and STDOUT */
int stdin_fd = client_data->term->stdin_pipe_fd[0];
int stdout_fd = client_data->term->stdout_pipe_fd[1];
/* Print title */
guac_terminal_write_all(stdout_fd, title, strlen(title));
/* Make room for null terminator */
size--;
/* Read bytes until newline */
pos = 0;
while (pos < size && read(stdin_fd, &in_byte, 1) == 1) {
/* Backspace */
if (in_byte == 0x7F) {
if (pos > 0) {
guac_terminal_write_all(stdout_fd, "\b \b", 3);
pos--;
}
}
/* CR (end of input */
else if (in_byte == 0x0D) {
guac_terminal_write_all(stdout_fd, "\r\n", 2);
break;
}
else {
/* Store character, update buffers */
str[pos++] = in_byte;
/* Print character if echoing */
if (echo)
guac_terminal_write_all(stdout_fd, &in_byte, 1);
else
guac_terminal_write_all(stdout_fd, "*", 1);
}
}
str[pos] = 0;
return str;
}
void* ssh_input_thread(void* data) {
guac_client* client = (guac_client*) data;
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
char buffer[8192];
int bytes_read;
int stdin_fd = client_data->term->stdin_pipe_fd[0];
/* Write all data read */
while ((bytes_read = read(stdin_fd, buffer, sizeof(buffer))) > 0)
channel_write(client_data->term_channel, buffer, bytes_read);
return NULL;
}
void* ssh_client_thread(void* data) {
guac_client* client = (guac_client*) data;
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
char name[1024];
guac_socket* socket = client->socket;
char buffer[8192];
int bytes_read = -1234;
int stdout_fd = client_data->term->stdout_pipe_fd[1];
pthread_t input_thread;
/* Get username */
if (client_data->username[0] == 0 &&
prompt(client, "Login as: ", client_data->username, sizeof(client_data->username), true) == NULL)
return NULL;
/* Send new name */
snprintf(name, sizeof(name)-1, "%s@%s", client_data->username, client_data->hostname);
guac_protocol_send_name(socket, name);
/* Get password */
if (client_data->password[0] == 0 &&
prompt(client, "Password: ", client_data->password, sizeof(client_data->password), false) == NULL)
return NULL;
/* Clear screen */
guac_terminal_write_all(stdout_fd, "\x1B[H\x1B[J", 6);
/* Open SSH session */
client_data->session = ssh_new();
if (client_data->session == NULL) {
guac_protocol_send_error(socket, "Unable to create SSH session.");
guac_socket_flush(socket);
return NULL;
}
/* Set session options */
ssh_options_set(client_data->session, SSH_OPTIONS_HOST, client_data->hostname);
ssh_options_set(client_data->session, SSH_OPTIONS_PORT, &(client_data->port));
ssh_options_set(client_data->session, SSH_OPTIONS_USER, client_data->username);
/* Connect */
if (ssh_connect(client_data->session) != SSH_OK) {
guac_protocol_send_error(socket, "Unable to connect via SSH.");
guac_socket_flush(socket);
return NULL;
}
/* Authenticate */
if (ssh_userauth_password(client_data->session, NULL, client_data->password) != SSH_AUTH_SUCCESS) {
guac_protocol_send_error(socket, "SSH auth failed.");
guac_socket_flush(socket);
return NULL;
}
/* Open channel for terminal */
client_data->term_channel = channel_new(client_data->session);
if (client_data->term_channel == NULL) {
guac_protocol_send_error(socket, "Unable to open channel.");
guac_socket_flush(socket);
return NULL;
}
/* Open session for channel */
if (channel_open_session(client_data->term_channel) != SSH_OK) {
guac_protocol_send_error(socket, "Unable to open channel session.");
guac_socket_flush(socket);
return NULL;
}
/* Request PTY */
if (channel_request_pty_size(client_data->term_channel, "linux",
client_data->term->term_width, client_data->term->term_height) != SSH_OK) {
guac_protocol_send_error(socket, "Unable to allocate PTY for channel.");
guac_socket_flush(socket);
return NULL;
}
/* Request shell */
if (channel_request_shell(client_data->term_channel) != SSH_OK) {
guac_protocol_send_error(socket, "Unable to associate shell with PTY.");
guac_socket_flush(socket);
return NULL;
}
/* Logged in */
guac_client_log_info(client, "SSH connection successful.");
/* Start input thread */
if (pthread_create(&(input_thread), NULL, ssh_input_thread, (void*) client)) {
guac_client_log_error(client, "Unable to start SSH input thread");
return NULL;
}
/* While data available, write to terminal */
while (channel_is_open(client_data->term_channel)
&& !channel_is_eof(client_data->term_channel)) {
/* Repeat read if necessary */
if ((bytes_read = channel_read(client_data->term_channel, buffer, sizeof(buffer), 0)) == SSH_AGAIN)
continue;
/* Attempt to write data received. Exit on failure. */
if (bytes_read > 0) {
int written = guac_terminal_write_all(stdout_fd, buffer, bytes_read);
if (written < 0)
break;
}
}
/* Wait for input thread to die */
pthread_join(input_thread, NULL);
guac_client_log_info(client, "SSH connection ended.");
return NULL;
}

View File

@ -0,0 +1,856 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* James Muehlner <dagger10k@users.sourceforge.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <pthread.h>
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <guacamole/socket.h>
#include <guacamole/protocol.h>
#include <guacamole/client.h>
#include <guacamole/error.h>
#include "types.h"
#include "buffer.h"
#include "common.h"
#include "display.h"
#include "terminal.h"
#include "terminal_handlers.h"
void guac_terminal_reset(guac_terminal* term) {
int row;
/* Set current state */
term->char_handler = guac_terminal_echo;
term->active_char_set = 0;
term->char_mapping[0] =
term->char_mapping[1] = NULL;
/* Reset cursor location */
term->cursor_row = term->visible_cursor_row = term->saved_cursor_row = 0;
term->cursor_col = term->visible_cursor_col = term->saved_cursor_col = 0;
/* Clear scrollback, buffer, and scoll region */
term->buffer->top = 0;
term->buffer->length = 0;
term->scroll_start = 0;
term->scroll_end = term->term_height - 1;
term->scroll_offset = 0;
/* Reset flags */
term->text_selected = false;
term->application_cursor_keys = false;
term->automatic_carriage_return = false;
term->insert_mode = false;
/* Reset tabs */
term->tab_interval = 8;
memset(term->custom_tabs, 0, sizeof(term->custom_tabs));
/* Clear terminal */
for (row=0; row<term->term_height; row++)
guac_terminal_set_columns(term, row, 0, term->term_width, &(term->default_char));
}
guac_terminal* guac_terminal_create(guac_client* client,
const char* font_name, int font_size,
int width, int height) {
guac_terminal_char default_char = {
.value = 0,
.attributes = {
.foreground = 7,
.background = 0,
.bold = false,
.reverse = false,
.underscore = false
}};
guac_terminal* term = malloc(sizeof(guac_terminal));
term->client = client;
/* Init buffer */
term->buffer = guac_terminal_buffer_alloc(1000, &default_char);
/* Init display */
term->display = guac_terminal_display_alloc(client,
font_name, font_size,
default_char.attributes.foreground,
default_char.attributes.background);
/* Init terminal state */
term->current_attributes = default_char.attributes;
term->default_char = default_char;
term->term_width = width / term->display->char_width;
term->term_height = height / term->display->char_height;
/* Open STDOUT pipe */
if (pipe(term->stdout_pipe_fd)) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Unable to open pipe for STDOUT";
free(term);
return NULL;
}
/* Open STDIN pipe */
if (pipe(term->stdin_pipe_fd)) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Unable to open pipe for STDIN";
free(term);
return NULL;
}
/* Init terminal lock */
pthread_mutex_init(&(term->lock), NULL);
/* Size display */
guac_terminal_display_resize(term->display,
term->term_width, term->term_height);
/* Init terminal */
guac_terminal_reset(term);
return term;
}
void guac_terminal_free(guac_terminal* term) {
/* Close terminal output pipe */
close(term->stdout_pipe_fd[1]);
close(term->stdout_pipe_fd[0]);
/* Close user input pipe */
close(term->stdin_pipe_fd[1]);
close(term->stdin_pipe_fd[0]);
/* Free display */
guac_terminal_display_free(term->display);
/* Free buffer */
guac_terminal_buffer_free(term->buffer);
}
int guac_terminal_set(guac_terminal* term, int row, int col, int codepoint) {
/* Build character with current attributes */
guac_terminal_char guac_char;
guac_char.value = codepoint;
guac_char.attributes = term->current_attributes;
guac_terminal_set_columns(term, row, col, col, &guac_char);
return 0;
}
void guac_terminal_commit_cursor(guac_terminal* term) {
guac_terminal_char* guac_char;
guac_terminal_buffer_row* old_row;
guac_terminal_buffer_row* new_row;
/* If no change, done */
if (term->visible_cursor_row == term->cursor_row && term->visible_cursor_col == term->cursor_col)
return;
/* Get old and new rows with cursor */
new_row = guac_terminal_buffer_get_row(term->buffer, term->cursor_row, term->cursor_col+1);
old_row = guac_terminal_buffer_get_row(term->buffer, term->visible_cursor_row, term->visible_cursor_col+1);
/* Clear cursor */
guac_char = &(old_row->characters[term->visible_cursor_col]);
guac_char->attributes.cursor = false;
guac_terminal_display_set_columns(term->display, term->visible_cursor_row + term->scroll_offset,
term->visible_cursor_col, term->visible_cursor_col, guac_char);
/* Set cursor */
guac_char = &(new_row->characters[term->cursor_col]);
guac_char->attributes.cursor = true;
guac_terminal_display_set_columns(term->display, term->cursor_row + term->scroll_offset,
term->cursor_col, term->cursor_col, guac_char);
term->visible_cursor_row = term->cursor_row;
term->visible_cursor_col = term->cursor_col;
return;
}
int guac_terminal_write(guac_terminal* term, const char* c, int size) {
while (size > 0) {
term->char_handler(term, *(c++));
size--;
}
return 0;
}
int guac_terminal_scroll_up(guac_terminal* term,
int start_row, int end_row, int amount) {
/* If scrolling entire display, update scroll offset */
if (start_row == 0 && end_row == term->term_height - 1) {
/* Scroll up visibly */
guac_terminal_display_copy_rows(term->display, start_row + amount, end_row, -amount);
/* Advance by scroll amount */
term->buffer->top += amount;
if (term->buffer->top >= term->buffer->available)
term->buffer->top -= term->buffer->available;
term->buffer->length += amount;
if (term->buffer->length > term->buffer->available)
term->buffer->length = term->buffer->available;
/* Update cursor location if within region */
if (term->visible_cursor_row >= start_row &&
term->visible_cursor_row <= end_row)
term->visible_cursor_row -= amount;
}
/* Otherwise, just copy row data upwards */
else
guac_terminal_copy_rows(term, start_row + amount, end_row, -amount);
/* Clear new area */
guac_terminal_clear_range(term,
end_row - amount + 1, 0,
end_row, term->term_width - 1);
return 0;
}
int guac_terminal_scroll_down(guac_terminal* term,
int start_row, int end_row, int amount) {
guac_terminal_copy_rows(term, start_row, end_row - amount, amount);
/* Clear new area */
guac_terminal_clear_range(term,
start_row, 0,
start_row + amount - 1, term->term_width - 1);
return 0;
}
int guac_terminal_clear_columns(guac_terminal* term,
int row, int start_col, int end_col) {
/* Build space */
guac_terminal_char blank;
blank.value = 0;
blank.attributes = term->current_attributes;
/* Clear */
guac_terminal_set_columns(term,
row, start_col, end_col, &blank);
return 0;
}
int guac_terminal_clear_range(guac_terminal* term,
int start_row, int start_col,
int end_row, int end_col) {
/* If not at far left, must clear sub-region to far right */
if (start_col > 0) {
/* Clear from start_col to far right */
guac_terminal_clear_columns(term,
start_row, start_col, term->term_width - 1);
/* One less row to clear */
start_row++;
}
/* If not at far right, must clear sub-region to far left */
if (end_col < term->term_width - 1) {
/* Clear from far left to end_col */
guac_terminal_clear_columns(term, end_row, 0, end_col);
/* One less row to clear */
end_row--;
}
/* Remaining region now guaranteed rectangular. Clear, if possible */
if (start_row <= end_row) {
int row;
for (row=start_row; row<=end_row; row++) {
/* Clear entire row */
guac_terminal_clear_columns(term, row, 0, term->term_width - 1);
}
}
return 0;
}
void guac_terminal_scroll_display_down(guac_terminal* terminal,
int scroll_amount) {
int start_row, end_row;
int dest_row;
int row, column;
/* Limit scroll amount by size of scrollback buffer */
if (scroll_amount > terminal->scroll_offset)
scroll_amount = terminal->scroll_offset;
/* If not scrolling at all, don't bother trying */
if (scroll_amount <= 0)
return;
/* Shift screen up */
if (terminal->term_height > scroll_amount)
guac_terminal_display_copy_rows(terminal->display,
scroll_amount, terminal->term_height - 1,
-scroll_amount);
/* Advance by scroll amount */
terminal->scroll_offset -= scroll_amount;
/* Get row range */
end_row = terminal->term_height - terminal->scroll_offset - 1;
start_row = end_row - scroll_amount + 1;
dest_row = terminal->term_height - scroll_amount;
/* Draw new rows from scrollback */
for (row=start_row; row<=end_row; row++) {
/* Get row from scrollback */
guac_terminal_buffer_row* buffer_row =
guac_terminal_buffer_get_row(terminal->buffer, row, 0);
/* Clear row */
guac_terminal_display_set_columns(terminal->display,
dest_row, 0, terminal->display->width, &(terminal->default_char));
/* Draw row */
guac_terminal_char* current = buffer_row->characters;
for (column=0; column<buffer_row->length; column++)
guac_terminal_display_set_columns(terminal->display,
dest_row, column, column, current++);
/* Next row */
dest_row++;
}
guac_terminal_display_flush(terminal->display);
guac_socket_flush(terminal->client->socket);
}
void guac_terminal_scroll_display_up(guac_terminal* terminal,
int scroll_amount) {
int start_row, end_row;
int dest_row;
int row, column;
/* Limit scroll amount by size of scrollback buffer */
if (terminal->scroll_offset + scroll_amount > terminal->buffer->length - terminal->term_height)
scroll_amount = terminal->buffer->length - terminal->scroll_offset - terminal->term_height;
/* If not scrolling at all, don't bother trying */
if (scroll_amount <= 0)
return;
/* Shift screen down */
if (terminal->term_height > scroll_amount)
guac_terminal_display_copy_rows(terminal->display,
0, terminal->term_height - scroll_amount - 1,
scroll_amount);
/* Advance by scroll amount */
terminal->scroll_offset += scroll_amount;
/* Get row range */
start_row = -terminal->scroll_offset;
end_row = start_row + scroll_amount - 1;
dest_row = 0;
/* Draw new rows from scrollback */
for (row=start_row; row<=end_row; row++) {
/* Get row from scrollback */
guac_terminal_buffer_row* buffer_row =
guac_terminal_buffer_get_row(terminal->buffer, row, 0);
/* Clear row */
guac_terminal_display_set_columns(terminal->display,
dest_row, 0, terminal->display->width, &(terminal->default_char));
/* Draw row */
guac_terminal_char* current = buffer_row->characters;
for (column=0; column<buffer_row->length; column++)
guac_terminal_display_set_columns(terminal->display,
dest_row, column, column, current++);
/* Next row */
dest_row++;
}
guac_terminal_display_flush(terminal->display);
guac_socket_flush(terminal->client->socket);
}
void guac_terminal_select_redraw(guac_terminal* terminal) {
guac_terminal_display_select(terminal->display,
terminal->selection_start_row + terminal->scroll_offset,
terminal->selection_start_column,
terminal->selection_end_row + terminal->scroll_offset,
terminal->selection_end_column);
}
void guac_terminal_select_start(guac_terminal* terminal, int row, int column) {
terminal->selection_start_row =
terminal->selection_end_row = row;
terminal->selection_start_column =
terminal->selection_end_column = column;
terminal->text_selected = true;
guac_terminal_select_redraw(terminal);
}
void guac_terminal_select_update(guac_terminal* terminal, int row, int column) {
if (row != terminal->selection_end_row || column != terminal->selection_end_column) {
terminal->selection_end_row = row;
terminal->selection_end_column = column;
guac_terminal_select_redraw(terminal);
}
}
int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int end, char* string) {
int length = 0;
int i;
for (i=start; i<=end; i++) {
int codepoint = row->characters[i].value;
/* If not null (blank), add to string */
if (codepoint != 0) {
int bytes = guac_terminal_encode_utf8(codepoint, string);
string += bytes;
length += bytes;
}
}
return length;
}
void guac_terminal_select_end(guac_terminal* terminal, char* string) {
/* Deselect */
terminal->text_selected = false;
guac_terminal_display_commit_select(terminal->display);
guac_terminal_buffer_row* buffer_row;
int row;
int start_row, start_col;
int end_row, end_col;
/* Ensure proper ordering of start and end coords */
if (terminal->selection_start_row <= terminal->selection_end_row) {
start_row = terminal->selection_start_row;
start_col = terminal->selection_start_column;
end_row = terminal->selection_end_row;
end_col = terminal->selection_end_column;
}
else {
end_row = terminal->selection_start_row;
end_col = terminal->selection_start_column;
start_row = terminal->selection_end_row;
start_col = terminal->selection_end_column;
}
/* If only one row, simply copy */
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, start_row, 0);
if (end_row == start_row) {
if (buffer_row->length - 1 < end_col)
end_col = buffer_row->length - 1;
string += __guac_terminal_buffer_string(buffer_row, start_col, end_col, string);
}
/* Otherwise, copy multiple rows */
else {
/* Store first row */
string += __guac_terminal_buffer_string(buffer_row, start_col, buffer_row->length - 1, string);
/* Store all middle rows */
for (row=start_row+1; row<end_row; row++) {
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
*(string++) = '\n';
string += __guac_terminal_buffer_string(buffer_row, 0, buffer_row->length - 1, string);
}
/* Store last row */
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, end_row, 0);
if (buffer_row->length - 1 < end_col)
end_col = buffer_row->length - 1;
*(string++) = '\n';
string += __guac_terminal_buffer_string(buffer_row, 0, end_col, string);
}
/* Null terminator */
*string = 0;
}
void guac_terminal_copy_columns(guac_terminal* terminal, int row,
int start_column, int end_column, int offset) {
guac_terminal_display_copy_columns(terminal->display, row + terminal->scroll_offset,
start_column, end_column, offset);
guac_terminal_buffer_copy_columns(terminal->buffer, row,
start_column, end_column, offset);
/* Update cursor location if within region */
if (row == terminal->visible_cursor_row &&
terminal->visible_cursor_col >= start_column &&
terminal->visible_cursor_col <= end_column)
terminal->visible_cursor_col += offset;
}
void guac_terminal_copy_rows(guac_terminal* terminal,
int start_row, int end_row, int offset) {
guac_terminal_display_copy_rows(terminal->display,
start_row + terminal->scroll_offset, end_row + terminal->scroll_offset, offset);
guac_terminal_buffer_copy_rows(terminal->buffer,
start_row, end_row, offset);
/* Update cursor location if within region */
if (terminal->visible_cursor_row >= start_row &&
terminal->visible_cursor_row <= end_row)
terminal->visible_cursor_row += offset;
}
void guac_terminal_set_columns(guac_terminal* terminal, int row,
int start_column, int end_column, guac_terminal_char* character) {
guac_terminal_display_set_columns(terminal->display, row + terminal->scroll_offset,
start_column, end_column, character);
guac_terminal_buffer_set_columns(terminal->buffer, row,
start_column, end_column, character);
/* If visible cursor in current row, preserve state */
if (row == terminal->visible_cursor_row
&& terminal->visible_cursor_col >= start_column
&& terminal->visible_cursor_col <= end_column) {
/* Create copy of character with cursor attribute set */
guac_terminal_char cursor_character = *character;
cursor_character.attributes.cursor = true;
guac_terminal_display_set_columns(terminal->display, row + terminal->scroll_offset,
terminal->visible_cursor_col, terminal->visible_cursor_col, &cursor_character);
guac_terminal_buffer_set_columns(terminal->buffer, row,
terminal->visible_cursor_col, terminal->visible_cursor_col, &cursor_character);
}
}
static void __guac_terminal_redraw_rect(guac_terminal* term, int start_row, int start_col, int end_row, int end_col) {
int row, col;
/* Redraw region */
for (row=start_row; row<=end_row; row++) {
guac_terminal_buffer_row* buffer_row =
guac_terminal_buffer_get_row(term->buffer, row - term->scroll_offset, 0);
/* Clear row */
guac_terminal_display_set_columns(term->display,
row, start_col, end_col, &(term->default_char));
/* Copy characters */
for (col=start_col; col <= end_col && col < buffer_row->length; col++)
guac_terminal_display_set_columns(term->display, row, col, col,
&(buffer_row->characters[col]));
}
}
void guac_terminal_resize(guac_terminal* term, int width, int height) {
/* If height is decreasing, shift display up */
if (height < term->term_height) {
int shift_amount;
/* Get number of rows actually occupying terminal space */
int used_height = term->buffer->length;
if (used_height > term->term_height)
used_height = term->term_height;
shift_amount = used_height - height;
/* If the new terminal bottom covers N rows, shift up N rows */
if (shift_amount > 0) {
guac_terminal_display_copy_rows(term->display,
shift_amount, term->display->height - 1, -shift_amount);
/* Update buffer top and cursor row based on shift */
term->buffer->top += shift_amount;
term->cursor_row -= shift_amount;
term->visible_cursor_row -= shift_amount;
/* Redraw characters within old region */
__guac_terminal_redraw_rect(term, height - shift_amount, 0, height-1, width-1);
}
}
/* Resize display */
guac_terminal_display_flush(term->display);
guac_terminal_display_resize(term->display, width, height);
/* Reraw any characters on right if widening */
if (width > term->term_width)
__guac_terminal_redraw_rect(term, 0, term->term_width-1, height-1, width-1);
/* If height is increasing, shift display down */
if (height > term->term_height) {
/* If undisplayed rows exist in the buffer, shift them into view */
if (term->term_height < term->buffer->length) {
/* If the new terminal bottom reveals N rows, shift down N rows */
int shift_amount = height - term->term_height;
/* The maximum amount we can shift is the number of undisplayed rows */
int max_shift = term->buffer->length - term->term_height;
if (shift_amount > max_shift)
shift_amount = max_shift;
/* Update buffer top and cursor row based on shift */
term->buffer->top -= shift_amount;
term->cursor_row += shift_amount;
term->visible_cursor_row += shift_amount;
/* If scrolled enough, use scroll to fulfill entire resize */
if (term->scroll_offset >= shift_amount) {
term->scroll_offset -= shift_amount;
/* Draw characters from scroll at bottom */
__guac_terminal_redraw_rect(term, term->term_height, 0, term->term_height + shift_amount - 1, width-1);
}
/* Otherwise, fulfill with as much scroll as possible */
else {
/* Draw characters from scroll at bottom */
__guac_terminal_redraw_rect(term, term->term_height, 0, term->term_height + term->scroll_offset - 1, width-1);
/* Update shift_amount and scroll based on new rows */
shift_amount -= term->scroll_offset;
term->scroll_offset = 0;
/* If anything remains, move screen as necessary */
if (shift_amount > 0) {
guac_terminal_display_copy_rows(term->display,
0, term->display->height - shift_amount - 1, shift_amount);
/* Draw characters at top from scroll */
__guac_terminal_redraw_rect(term, 0, 0, shift_amount - 1, width-1);
}
}
} /* end if undisplayed rows exist */
}
/* Commit new dimensions */
term->term_width = width;
term->term_height = height;
}
int guac_terminal_send_data(guac_terminal* term, const char* data, int length) {
return guac_terminal_write_all(term->stdin_pipe_fd[1], data, length);
}
int guac_terminal_send_string(guac_terminal* term, const char* data) {
return guac_terminal_write_all(term->stdin_pipe_fd[1], data, strlen(data));
}
int guac_terminal_sendf(guac_terminal* term, const char* format, ...) {
int written;
va_list ap;
char buffer[1024];
/* Print to buffer */
va_start(ap, format);
written = vsnprintf(buffer, sizeof(buffer)-1, format, ap);
va_end(ap);
if (written < 0)
return written;
/* Write to STDIN */
return guac_terminal_write_all(term->stdin_pipe_fd[1], buffer, written);
}
void guac_terminal_set_tab(guac_terminal* term, int column) {
int i;
/* Search for available space, set if available */
for (i=0; i<GUAC_TERMINAL_MAX_TABS; i++) {
/* Set tab if space free */
if (term->custom_tabs[i] == 0) {
term->custom_tabs[i] = column+1;
break;
}
}
}
void guac_terminal_unset_tab(guac_terminal* term, int column) {
int i;
/* Search for given tab, unset if found */
for (i=0; i<GUAC_TERMINAL_MAX_TABS; i++) {
/* Unset tab if found */
if (term->custom_tabs[i] == column+1) {
term->custom_tabs[i] = 0;
break;
}
}
}
void guac_terminal_clear_tabs(guac_terminal* term) {
term->tab_interval = 0;
memset(term->custom_tabs, 0, sizeof(term->custom_tabs));
}
int guac_terminal_next_tab(guac_terminal* term, int column) {
int i;
/* Determine tab stop from interval */
int tabstop;
if (term->tab_interval != 0)
tabstop = (column / term->tab_interval + 1) * term->tab_interval;
else
tabstop = term->term_width - 1;
/* Walk custom tabs, trying to find an earlier occurrence */
for (i=0; i<GUAC_TERMINAL_MAX_TABS; i++) {
int custom_tabstop = term->custom_tabs[i] - 1;
if (custom_tabstop != -1 && custom_tabstop > column && custom_tabstop < tabstop)
tabstop = custom_tabstop;
}
return tabstop;
}

View File

@ -0,0 +1,926 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdlib.h>
#include "common.h"
#include "terminal.h"
#include "terminal_handlers.h"
#include "char_mappings.h"
/**
* Response string sent when identification is requested.
*/
#define GUAC_TERMINAL_VT102_ID "\x1B[?6c"
/**
* Arbitrary response to ENQ control character.
*/
#define GUAC_TERMINAL_ANSWERBACK "GUACAMOLE"
/**
* Response which indicates the terminal is alive.
*/
#define GUAC_TERMINAL_OK "\x1B[0n"
int guac_terminal_echo(guac_terminal* term, char c) {
static int bytes_remaining = 0;
static int codepoint = 0;
const int* char_mapping = term->char_mapping[term->active_char_set];
/* If using non-Unicode mapping, just map straight bytes */
if (char_mapping != NULL) {
codepoint = c;
bytes_remaining = 0;
}
/* 1-byte UTF-8 codepoint */
else if ((c & 0x80) == 0x00) { /* 0xxxxxxx */
codepoint = c & 0x7F;
bytes_remaining = 0;
}
/* 2-byte UTF-8 codepoint */
else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */
codepoint = c & 0x1F;
bytes_remaining = 1;
}
/* 3-byte UTF-8 codepoint */
else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */
codepoint = c & 0x0F;
bytes_remaining = 2;
}
/* 4-byte UTF-8 codepoint */
else if ((c & 0xF8) == 0xF0) { /* 11110xxx */
codepoint = c & 0x07;
bytes_remaining = 3;
}
/* Continuation of UTF-8 codepoint */
else if ((c & 0xC0) == 0x80) { /* 10xxxxxx */
codepoint = (codepoint << 6) | (c & 0x3F);
bytes_remaining--;
}
/* Unrecognized prefix */
else {
codepoint = '?';
bytes_remaining = 0;
}
/* If we need more bytes, wait for more bytes */
if (bytes_remaining != 0)
return 0;
switch (codepoint) {
/* Enquiry */
case 0x05:
guac_terminal_send_string(term, GUAC_TERMINAL_ANSWERBACK);
break;
/* Bell */
case 0x07:
break;
/* Backspace */
case 0x08:
if (term->cursor_col >= 1)
term->cursor_col--;
break;
/* Tab */
case 0x09:
term->cursor_col = guac_terminal_next_tab(term, term->cursor_col);
break;
/* Line feed / VT / FF */
case '\n':
case 0x0B: /* VT */
case 0x0C: /* FF */
term->cursor_row++;
/* Scroll up if necessary */
if (term->cursor_row > term->scroll_end) {
term->cursor_row = term->scroll_end;
/* Scroll up by one row */
guac_terminal_scroll_up(term, term->scroll_start,
term->scroll_end, 1);
}
/* If automatic carriage return, fall through to CR handler */
if (!term->automatic_carriage_return)
break;
/* Carriage return */
case '\r':
term->cursor_col = 0;
break;
/* SO (activates character set G1) */
case 0x0E:
term->active_char_set = 1;
break;
/* SI (activates character set G0) */
case 0x0F:
term->active_char_set = 0;
break;
/* ESC */
case 0x1B:
term->char_handler = guac_terminal_escape;
break;
/* CSI */
case 0x9B:
term->char_handler = guac_terminal_csi;
break;
/* DEL (ignored) */
case 0x7F:
break;
/* Displayable chars */
default:
/* Don't bother handling control chars if unknown */
if (codepoint < 0x20)
break;
/* Translate mappable codepoints to whatever codepoint is mapped */
if (codepoint >= 0x20 && codepoint <= 0xFF && char_mapping != NULL)
codepoint = char_mapping[codepoint - 0x20];
/* Wrap if necessary */
if (term->cursor_col >= term->term_width) {
term->cursor_col = 0;
term->cursor_row++;
}
/* Scroll up if necessary */
if (term->cursor_row > term->scroll_end) {
term->cursor_row = term->scroll_end;
/* Scroll up by one row */
guac_terminal_scroll_up(term, term->scroll_start,
term->scroll_end, 1);
}
/* If insert mode, shift other characters right by 1 */
if (term->insert_mode)
guac_terminal_copy_columns(term, term->cursor_row,
term->cursor_col, term->term_width-2, 1);
/* Write character */
guac_terminal_set(term,
term->cursor_row,
term->cursor_col,
codepoint);
/* Advance cursor */
term->cursor_col++;
}
return 0;
}
int guac_terminal_escape(guac_terminal* term, char c) {
switch (c) {
case '(':
term->char_handler = guac_terminal_g0_charset;
break;
case ')':
term->char_handler = guac_terminal_g1_charset;
break;
case ']':
term->char_handler = guac_terminal_osc;
break;
case '[':
term->char_handler = guac_terminal_csi;
break;
case '#':
term->char_handler = guac_terminal_ctrl_func;
break;
/* Save Cursor (DECSC) */
case '7':
term->saved_cursor_row = term->cursor_row;
term->saved_cursor_col = term->cursor_col;
term->char_handler = guac_terminal_echo;
break;
/* Restore Cursor (DECRC) */
case '8':
term->cursor_row = term->saved_cursor_row;
if (term->cursor_row >= term->term_height)
term->cursor_row = term->term_height - 1;
term->cursor_col = term->saved_cursor_col;
if (term->cursor_col >= term->term_width)
term->cursor_col = term->term_width - 1;
term->char_handler = guac_terminal_echo;
break;
/* Index (IND) */
case 'D':
term->cursor_row++;
/* Scroll up if necessary */
if (term->cursor_row > term->scroll_end) {
term->cursor_row = term->scroll_end;
/* Scroll up by one row */
guac_terminal_scroll_up(term, term->scroll_start,
term->scroll_end, 1);
}
term->char_handler = guac_terminal_echo;
break;
/* Next Line (NEL) */
case 'E':
term->cursor_col = 0;
term->cursor_row++;
/* Scroll up if necessary */
if (term->cursor_row > term->scroll_end) {
term->cursor_row = term->scroll_end;
/* Scroll up by one row */
guac_terminal_scroll_up(term, term->scroll_start,
term->scroll_end, 1);
}
term->char_handler = guac_terminal_echo;
break;
/* Set Tab (HTS) */
case 'H':
guac_terminal_set_tab(term, term->cursor_col);
break;
/* Reverse Linefeed */
case 'M':
term->cursor_row--;
/* Scroll down if necessary */
if (term->cursor_row < term->scroll_start) {
term->cursor_row = term->scroll_start;
/* Scroll down by one row */
guac_terminal_scroll_down(term, term->scroll_start,
term->scroll_end, 1);
}
term->char_handler = guac_terminal_echo;
break;
/* DEC Identify */
case 'Z':
guac_terminal_send_string(term, GUAC_TERMINAL_VT102_ID);
term->char_handler = guac_terminal_echo;
break;
/* Reset */
case 'c':
guac_terminal_reset(term);
break;
default:
guac_client_log_info(term->client, "Unhandled ESC sequence: %c", c);
term->char_handler = guac_terminal_echo;
}
return 0;
}
/**
* Given a character mapping specifier (such as B, 0, U, or K),
* returns the corresponding character mapping.
*/
static const int* __guac_terminal_get_char_mapping(char c) {
/* Translate character specifier to actual mapping */
switch (c) {
case 'B': return NULL;
case '0': return vt100_map;
case 'U': return null_map;
case 'K': return user_map;
}
/* Default to Unicode */
return NULL;
}
int guac_terminal_g0_charset(guac_terminal* term, char c) {
term->char_mapping[0] = __guac_terminal_get_char_mapping(c);
term->char_handler = guac_terminal_echo;
return 0;
}
int guac_terminal_g1_charset(guac_terminal* term, char c) {
term->char_mapping[1] = __guac_terminal_get_char_mapping(c);
term->char_handler = guac_terminal_echo;
return 0;
}
/**
* Looks up the flag specified by the given number and mode. Used by the Set/Reset Mode
* functions of the terminal.
*/
static bool* __guac_terminal_get_flag(guac_terminal* term, int num, char private_mode) {
if (private_mode == '?') {
switch (num) {
case 1: return &(term->application_cursor_keys); /* DECCKM */
}
}
else if (private_mode == 0) {
switch (num) {
case 4: return &(term->insert_mode); /* DECIM */
case 20: return &(term->automatic_carriage_return); /* LF/NL */
}
}
/* Unknown flag */
return NULL;
}
int guac_terminal_csi(guac_terminal* term, char c) {
/* CSI function arguments */
static int argc = 0;
static int argv[16] = {0};
/* Sequence prefix, if any */
static char private_mode_character = 0;
/* Argument building counter and buffer */
static int argv_length = 0;
static char argv_buffer[256];
/* Digits get concatenated into argv */
if (c >= '0' && c <= '9') {
/* Concatenate digit if there is space in buffer */
if (argv_length < sizeof(argv_buffer)-1)
argv_buffer[argv_length++] = c;
}
/* Specific non-digits stop the parameter, and possibly the sequence */
else if ((c >= 0x40 && c <= 0x7E) || c == ';') {
int i, row, col, amount;
bool* flag;
/* At most 16 parameters */
if (argc < 16) {
/* Finish parameter */
argv_buffer[argv_length] = 0;
argv[argc++] = atoi(argv_buffer);
/* Prepare for next parameter */
argv_length = 0;
}
/* Handle CSI functions */
switch (c) {
/* @: Insert characters (scroll right) */
case '@':
amount = argv[0];
if (amount == 0) amount = 1;
/* Scroll right by amount */
if (term->cursor_col + amount < term->term_width)
guac_terminal_copy_columns(term, term->cursor_row,
term->cursor_col, term->term_width - amount - 1,
amount);
/* Clear left */
guac_terminal_clear_columns(term, term->cursor_row,
term->cursor_col, term->cursor_col + amount - 1);
break;
/* A: Move up */
case 'A':
/* Get move amount */
amount = argv[0];
if (amount == 0) amount = 1;
/* Move cursor */
term->cursor_row -= amount;
if (term->cursor_row < 0)
term->cursor_row = 0;
break;
/* B: Move down */
case 'e':
case 'B':
/* Get move amount */
amount = argv[0];
if (amount == 0) amount = 1;
/* Move cursor */
term->cursor_row += amount;
if (term->cursor_row >= term->term_height)
term->cursor_row = term->term_height - 1;
break;
/* C: Move right */
case 'a':
case 'C':
/* Get move amount */
amount = argv[0];
if (amount == 0) amount = 1;
/* Move cursor */
term->cursor_col += amount;
if (term->cursor_col >= term->term_width)
term->cursor_col = term->term_width - 1;
break;
/* D: Move left */
case 'D':
/* Get move amount */
amount = argv[0];
if (amount == 0) amount = 1;
/* Move cursor */
term->cursor_col -= amount;
if (term->cursor_col < 0)
term->cursor_col = 0;
break;
/* E: Move cursor down given number rows, column 1 */
case 'E':
/* Get move amount */
amount = argv[0];
if (amount == 0) amount = 1;
/* Move cursor */
term->cursor_row += amount;
if (term->cursor_row >= term->term_height)
term->cursor_row = term->term_height - 1;
/* Reset to column 1 */
term->cursor_col = 0;
break;
/* F: Move cursor up given number rows, column 1 */
case 'F':
/* Get move amount */
amount = argv[0];
if (amount == 0) amount = 1;
/* Move cursor */
term->cursor_row -= amount;
if (term->cursor_row < 0)
term->cursor_row = 0;
/* Reset to column 1 */
term->cursor_col = 0;
break;
/* G: Move cursor, current row */
case '`':
case 'G':
col = argv[0]; if (col != 0) col--;
term->cursor_col = col;
break;
/* H: Move cursor */
case 'f':
case 'H':
row = argv[0]; if (row != 0) row--;
col = argv[1]; if (col != 0) col--;
term->cursor_row = row;
term->cursor_col = col;
break;
/* J: Erase display */
case 'J':
/* Erase from cursor to end of display */
if (argv[0] == 0)
guac_terminal_clear_range(term,
term->cursor_row, term->cursor_col,
term->term_height-1, term->term_width-1);
/* Erase from start to cursor */
else if (argv[0] == 1)
guac_terminal_clear_range(term,
0, 0,
term->cursor_row, term->cursor_col);
/* Entire screen */
else if (argv[0] == 2 || argv[0] == 3)
guac_terminal_clear_range(term,
0, 0, term->term_height - 1, term->term_width - 1);
break;
/* K: Erase line */
case 'K':
/* Erase from cursor to end of line */
if (argv[0] == 0)
guac_terminal_clear_columns(term, term->cursor_row,
term->cursor_col, term->term_width - 1);
/* Erase from start to cursor */
else if (argv[0] == 1)
guac_terminal_clear_columns(term, term->cursor_row,
0, term->cursor_col);
/* Erase line */
else if (argv[0] == 2)
guac_terminal_clear_columns(term, term->cursor_row,
0, term->term_width - 1);
break;
/* L: Insert blank lines (scroll down) */
case 'L':
amount = argv[0];
if (amount == 0) amount = 1;
guac_terminal_scroll_down(term,
term->cursor_row, term->scroll_end, amount);
break;
/* M: Delete lines (scroll up) */
case 'M':
amount = argv[0];
if (amount == 0) amount = 1;
guac_terminal_scroll_up(term,
term->cursor_row, term->scroll_end, amount);
break;
/* P: Delete characters (scroll left) */
case 'P':
amount = argv[0];
if (amount == 0) amount = 1;
/* Scroll left by amount */
if (term->cursor_col + amount < term->term_width)
guac_terminal_copy_columns(term, term->cursor_row,
term->cursor_col + amount, term->term_width - 1,
-amount);
/* Clear right */
guac_terminal_clear_columns(term, term->cursor_row,
term->term_width - amount, term->term_width - 1);
break;
/* X: Erase characters (no scroll) */
case 'X':
amount = argv[0];
if (amount == 0) amount = 1;
/* Clear characters */
guac_terminal_clear_columns(term, term->cursor_row,
term->cursor_col, term->cursor_col + amount - 1);
break;
/* ]: Linux Private CSI */
case ']':
/* Explicitly ignored */
break;
/* c: Identify */
case 'c':
if (argv[0] == 0 && private_mode_character == 0)
guac_terminal_send_string(term, GUAC_TERMINAL_VT102_ID);
break;
/* d: Move cursor, current col */
case 'd':
row = argv[0]; if (row != 0) row--;
term->cursor_row = row;
break;
/* g: Clear tab */
case 'g':
/* Clear tab at current location */
if (argv[0] == 0)
guac_terminal_unset_tab(term, term->cursor_col);
/* Clear all tabs */
else if (argv[0] == 3)
guac_terminal_clear_tabs(term);
break;
/* h: Set Mode */
case 'h':
/* Look up flag and set */
flag = __guac_terminal_get_flag(term, argv[0], private_mode_character);
if (flag != NULL)
*flag = true;
break;
/* l: Reset Mode */
case 'l':
/* Look up flag and clear */
flag = __guac_terminal_get_flag(term, argv[0], private_mode_character);
if (flag != NULL)
*flag = false;
break;
/* m: Set graphics rendition */
case 'm':
for (i=0; i<argc; i++) {
int value = argv[i];
/* Reset attributes */
if (value == 0)
term->current_attributes = term->default_char.attributes;
/* Bold */
else if (value == 1)
term->current_attributes.bold = true;
/* Underscore on */
else if (value == 4)
term->current_attributes.underscore = true;
/* Reverse video */
else if (value == 7)
term->current_attributes.reverse = true;
/* Normal intensity (not bold) */
else if (value == 21 || value == 22)
term->current_attributes.bold = false;
/* Reset underscore */
else if (value == 24)
term->current_attributes.underscore = false;
/* Reset reverse video */
else if (value == 27)
term->current_attributes.reverse = false;
/* Foreground */
else if (value >= 30 && value <= 37)
term->current_attributes.foreground = value - 30;
/* Underscore on, default foreground */
else if (value == 38) {
term->current_attributes.underscore = true;
term->current_attributes.foreground =
term->default_char.attributes.foreground;
}
/* Underscore off, default foreground */
else if (value == 39) {
term->current_attributes.underscore = false;
term->current_attributes.foreground =
term->default_char.attributes.foreground;
}
/* Background */
else if (value >= 40 && value <= 47)
term->current_attributes.background = value - 40;
/* Reset background */
else if (value == 49)
term->current_attributes.background =
term->default_char.attributes.background;
}
break;
/* n: Status report */
case 'n':
/* Device status report */
if (argv[0] == 5 && private_mode_character == 0)
guac_terminal_send_string(term, GUAC_TERMINAL_OK);
/* Cursor position report */
else if (argv[0] == 6 && private_mode_character == 0)
guac_terminal_sendf(term, "\x1B[%i;%iR", term->cursor_row+1, term->cursor_col+1);
break;
/* q: Set keyboard LEDs */
case 'q':
/* Explicitly ignored */
break;
/* r: Set scrolling region */
case 'r':
/* If parameters given, set region */
if (argc == 2) {
term->scroll_start = argv[0]-1;
term->scroll_end = argv[1]-1;
}
/* Otherwise, reset scrolling region */
else {
term->scroll_start = 0;
term->scroll_end = term->term_height - 1;
}
break;
/* Save Cursor */
case 's':
term->saved_cursor_row = term->cursor_row;
term->saved_cursor_col = term->cursor_col;
break;
/* Restore Cursor */
case 'u':
term->cursor_row = term->saved_cursor_row;
if (term->cursor_row >= term->term_height)
term->cursor_row = term->term_height - 1;
term->cursor_col = term->saved_cursor_col;
if (term->cursor_col >= term->term_width)
term->cursor_col = term->term_width - 1;
break;
/* Warn of unhandled codes */
default:
if (c != ';') {
guac_client_log_info(term->client,
"Unhandled CSI sequence: %c", c);
for (i=0; i<argc; i++)
guac_client_log_info(term->client,
" -> argv[%i] = %i", i, argv[i]);
}
}
/* If not a semicolon, end of CSI sequence */
if (c != ';') {
term->char_handler = guac_terminal_echo;
/* Reset parameters */
for (i=0; i<argc; i++)
argv[i] = 0;
/* Reset private mode character */
private_mode_character = 0;
/* Reset argument counters */
argc = 0;
argv_length = 0;
}
}
/* Set private mode character if given and unset */
else if (c >= 0x3A && c <= 0x3F && private_mode_character == 0)
private_mode_character = c;
return 0;
}
int guac_terminal_osc(guac_terminal* term, char c) {
/* TODO: Implement OSC */
if (c == 0x9C || c == 0x5C || c == 0x07) /* ECMA-48 ST (String Terminator */
term->char_handler = guac_terminal_echo;
return 0;
}
int guac_terminal_ctrl_func(guac_terminal* term, char c) {
int row;
/* Build character with current attributes */
guac_terminal_char guac_char;
guac_char.value = 'E';
guac_char.attributes = term->current_attributes;
switch (c) {
/* Alignment test (fill screen with E's) */
case '8':
for (row=0; row<term->term_height; row++)
guac_terminal_set_columns(term, row, 0, term->term_width-1, &guac_char);
break;
}
term->char_handler = guac_terminal_echo;
return 0;
}