From 16c20dc6e3720d40f5f6af3b9f0aa7560243d63f Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 17 Jan 2020 17:21:48 +0000 Subject: [PATCH 1/9] misc: Fix cppcheck 1.82 + 1.90 warnings --- tests/tcp_proxy/main.c | 2 - vrplayer/decoderthread.cpp | 266 ------------------------------------- vrplayer/decoderthread.h | 95 ------------- xrdpapi/xrdp-ssh-agent.c | 2 +- 4 files changed, 1 insertion(+), 364 deletions(-) delete mode 100644 vrplayer/decoderthread.cpp delete mode 100644 vrplayer/decoderthread.h diff --git a/tests/tcp_proxy/main.c b/tests/tcp_proxy/main.c index 09822f28..5c4e259a 100644 --- a/tests/tcp_proxy/main.c +++ b/tests/tcp_proxy/main.c @@ -37,8 +37,6 @@ int g_rem_io_count = 0; // bytes read from remote port static int g_terminated = 0; static char g_buf[1024 * 32]; -#define -#define typedef unsigned short tui16; diff --git a/vrplayer/decoderthread.cpp b/vrplayer/decoderthread.cpp deleted file mode 100644 index 7841a3b5..00000000 --- a/vrplayer/decoderthread.cpp +++ /dev/null @@ -1,266 +0,0 @@ - - -// not used - - - - - - - - - - - - - - - - - -#include "decoderthread.h" - -/* - * TODO: - * o need to maintain aspect ratio while resizing - * o clicking in the middle of the slider bar should move the slider to the middle - * o need to be able to rewind the move when it is done playing - * o need to be able to load another move and play it w/o restarting player - * o pause button needs to work - * o need images for btns - */ - -DecoderThread::DecoderThread() -{ - channel = NULL; - geometry.setX(0); - geometry.setY(0); - geometry.setWidth(0); - geometry.setHeight(0); - stream_id = 101; - elapsedTime = 0; - la_seekPos = -1; - videoTimer = NULL; - audioTimer = NULL; -} - -void DecoderThread::run() -{ - /* need a media file */ - if (filename.length() == 0) - { - emit on_decoderErrorMsg("No media file", - "Please select a media file to play"); - return; - } -} - -void DecoderThread::startMediaPlay() -{ - MediaPacket *mediaPkt; - int is_video_frame; - int rv; - - /* setup video timer; each time this timer fires, it sends */ - /* one video pkt to the client then resets the callback duration */ - videoTimer = new QTimer; - connect(videoTimer, SIGNAL(timeout()), this, SLOT(videoTimerCallback())); - //videoTimer->start(1500); - - /* setup audio timer; does the same as above, but with audio pkts */ - audioTimer = new QTimer; - connect(audioTimer, SIGNAL(timeout()), this, SLOT(audioTimerCallback())); - //audioTimer->start(500); - - /* setup pktTimer; each time this timer fires, it reads AVPackets */ - /* and puts them into audio/video Queues */ - pktTimer = new QTimer; - connect(pktTimer, SIGNAL(timeout()), this, SLOT(pktTimerCallback())); - - while (1) - { - /* fill the audio/video queues with initial data; thereafter */ - /* data will be filled by pktTimerCallback() */ - if ((audioQueue.count() >= 3000) || (videoQueue.count() >= 3000)) - { - //pktTimer->start(50); - - //videoTimer->start(1500); - //audioTimer->start(500); - - playVideo = new PlayVideo(NULL, &videoQueue, channel, 101); - playVideoThread = new QThread(this); - connect(playVideoThread, SIGNAL(started()), playVideo, SLOT(play())); - playVideo->moveToThread(playVideoThread); - playVideoThread->start(); - - playAudio = new PlayAudio(NULL, &audioQueue, channel, 101); - playAudioThread = new QThread(this); - connect(playAudioThread, SIGNAL(started()), playAudio, SLOT(play())); - playAudio->moveToThread(playAudioThread); - playAudioThread->start(); - - return; - } - - mediaPkt = new MediaPacket; - rv = xrdpvr_get_frame(&mediaPkt->av_pkt, - &is_video_frame, - &mediaPkt->delay_in_us); - if (rv < 0) - { - /* looks like we reached end of file */ - break; - } - - if (is_video_frame) - videoQueue.enqueue(mediaPkt); - else - audioQueue.enqueue(mediaPkt); - - } /* end while (1) */ -} - -void DecoderThread::on_mediaSeek(int value) -{ - mutex.lock(); - la_seekPos = value; - mutex.unlock(); - - qDebug() << "media seek value=" << value; - - /* pktTimer stops at end of media; need to restart it */ - if (!pktTimer->isActive()) - { - updateSlider(); - pktTimer->start(100); - } -} - -void DecoderThread::setFilename(QString filename) -{ - this->filename = filename; -} - -void DecoderThread::stopPlayer() -{ - pktTimer->stop(); - audioQueue.clear(); - videoQueue.clear(); -} - -void DecoderThread::pausePlayer() -{ - pktTimer->stop(); -} - -void DecoderThread::resumePlayer() -{ - pktTimer->start(100); -} - -void DecoderThread::close() -{ -} - -void DecoderThread::audioTimerCallback() -{ - MediaPacket *pkt; - int delayInMs; - - if (audioQueue.isEmpty()) - { - qDebug() << "audioTimerCallback: got empty"; - audioTimer->setInterval(100); - return; - } - - pkt = audioQueue.dequeue(); - delayInMs = (int) ((float) pkt->delay_in_us / 1000.0); - send_audio_pkt(channel, 101, pkt->av_pkt); - delete pkt; - - //qDebug() << "audioTimerCallback: delay :" << delayInMs; - - audioTimer->setInterval(delayInMs); -} - -void DecoderThread::videoTimerCallback() -{ - MediaPacket *pkt; - int delayInMs; - - if (videoQueue.isEmpty()) - { - qDebug() << "videoTimerCallback: GOT EMPTY"; - videoTimer->setInterval(100); - return; - } - - pkt = videoQueue.dequeue(); - delayInMs = (int) 10; // ((float) pkt->delay_in_us / 1000.0); - send_video_pkt(channel, 101, pkt->av_pkt); - delete pkt; - updateSlider(); - //qDebug() << "videoTimerCallback: delay :" << delayInMs; - videoTimer->setInterval(delayInMs); -} - -void DecoderThread::pktTimerCallback() -{ - MediaPacket *mediaPkt; - int is_video_frame; - int rv; - - while (1) - { - qDebug() << "pktTimerCallback: audioCount=" << audioQueue.count() << "videoCount=" << videoQueue.count(); -#if 1 - if ((audioQueue.count() >= 20) || (videoQueue.count() >= 20)) - return; -#else - if (videoQueue.count() >= 60) - return; -#endif - mediaPkt = new MediaPacket; - rv = xrdpvr_get_frame(&mediaPkt->av_pkt, - &is_video_frame, - &mediaPkt->delay_in_us); - if (rv < 0) - { - /* looks like we reached end of file */ - qDebug() << "###### looks like we reached EOF"; - pktTimer->stop(); - // LK_TODO set some flag so audio/video timer also stop when q is empty - return; - } - - if (is_video_frame) - videoQueue.enqueue(mediaPkt); - else - audioQueue.enqueue(mediaPkt); - } -} - -void DecoderThread::updateSlider() -{ - if (elapsedTime == 0) - elapsedTime = av_gettime(); - - /* time elapsed in 1/100th sec units since play started */ - emit on_elapsedtime((av_gettime() - elapsedTime) / 10000); - - mutex.lock(); - if (la_seekPos >= 0) - { - qDebug() << "seeking to" << la_seekPos; - //audioTimer->stop(); - //videoTimer->stop(); - xrdpvr_seek_media(la_seekPos, 0); - elapsedTime = av_gettime() - la_seekPos * 1000000; - //audioTimer->start(10); - //videoTimer->start(10); - la_seekPos = -1; - } - mutex.unlock(); -} diff --git a/vrplayer/decoderthread.h b/vrplayer/decoderthread.h deleted file mode 100644 index e7116fa4..00000000 --- a/vrplayer/decoderthread.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef DECODERTHREAD_H -#define DECODERTHREAD_H - -#ifdef __cplusplus -#define __STDC_CONSTANT_MACROS -#ifdef _STDINT_H -#undef _STDINT_H -#endif -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* ffmpeg related stuff */ -extern "C" -{ - #include - #include -} - -class DecoderThread : public QObject -{ - Q_OBJECT - -public: - /* public methods */ - DecoderThread(); - - void setFilename(QString filename); - void stopPlayer(); - void pausePlayer(); - void resumePlayer(); - void oneTimeDeinit(); - void close(); - void run(); - void startMediaPlay(); - -public slots: - void on_mediaSeek(int value); - -private: - /* private variables */ - QQueue audioQueue; - QQueue videoQueue; - - QTimer *videoTimer; - QTimer *audioTimer; - QTimer *pktTimer; - QString filename; - void *channel; - int stream_id; - QRect geometry; - int64_t elapsedTime; /* elapsed time in usecs since play started */ - QMutex mutex; - int64_t la_seekPos; /* locked access; must hold mutex */ - - PlayVideo *playVideo; - QThread *playVideoThread; - PlayAudio *playAudio; - QThread *playAudioThread; - - /* private functions */ - int sendMetadataFile(); - int sendAudioFormat(); - int sendVideoFormat(); - int sendGeometry(); - void updateSlider(); - -private slots: - /* private slots */ - void audioTimerCallback(); - void videoTimerCallback(); - void pktTimerCallback(); - -signals: - /* private signals */ - void on_progressUpdate(int percent); - void on_decoderErrorMsg(QString title, QString msg); - void on_mediaDurationInSeconds(int duration); - void on_elapsedtime(int val); /* in hundredth of a sec */ -}; - -#endif // DECODERTHREAD_H diff --git a/xrdpapi/xrdp-ssh-agent.c b/xrdpapi/xrdp-ssh-agent.c index 84587082..99094b9e 100644 --- a/xrdpapi/xrdp-ssh-agent.c +++ b/xrdpapi/xrdp-ssh-agent.c @@ -267,7 +267,7 @@ handle_connection(int client_fd) } if (retlen != sizeof(rdp_fd)) { - fprintf(stderr, "WTSVirtualChannelQuery() returned wrong length %d\n", + fprintf(stderr, "WTSVirtualChannelQuery() returned wrong length %u\n", retlen); } rdp_fd = *retdata; From 5aca3aa8476c9e5a9bf47129009ae2776fa8eda9 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 21 Jan 2020 10:19:41 +0000 Subject: [PATCH 2/9] Fix cppcheck warnings in vrplayer/ourinterface.cpp --- vrplayer/ourinterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/vrplayer/ourinterface.cpp b/vrplayer/ourinterface.cpp index 261ba04d..ba83107a 100644 --- a/vrplayer/ourinterface.cpp +++ b/vrplayer/ourinterface.cpp @@ -11,6 +11,7 @@ OurInterface::OurInterface(QObject *parent) : savedGeometry.setHeight(0); stream_id = 101; demuxMedia = 0; + demuxMediaThread = NULL; //elapsedTime = 0; } From 77686bf8321b24a25d7369acba1829c29b3eb54f Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 16 Jan 2020 11:49:13 +0000 Subject: [PATCH 3/9] Fix cppcheck 1.89+1.90 warnings --- sesman/chansrv/chansrv_fuse.c | 2 ++ sesman/chansrv/devredir.c | 24 +++--------------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/sesman/chansrv/chansrv_fuse.c b/sesman/chansrv/chansrv_fuse.c index 7d8d1a22..b57cc662 100644 --- a/sesman/chansrv/chansrv_fuse.c +++ b/sesman/chansrv/chansrv_fuse.c @@ -2537,6 +2537,8 @@ static char *get_name_for_entry_in_parent(fuse_ino_t parent, const char *name) strlen(result) + 1 + strlen(name) + 1); if (p == NULL) { + /* See cppcheck trac #9292 and #9437 */ + /* cppcheck-suppress doubleFree symbolName=result */ free(result); result = NULL; } diff --git a/sesman/chansrv/devredir.c b/sesman/chansrv/devredir.c index 49b7ce32..6455746b 100644 --- a/sesman/chansrv/devredir.c +++ b/sesman/chansrv/devredir.c @@ -185,28 +185,10 @@ devredir_init(void) { struct stream *s; int bytes; - int fd; - - union _u - { - tui32 clientID; - char buf[4]; - } u; + tui32 clientID; /* get a random number that will act as a unique clientID */ - if ((fd = open("/dev/urandom", O_RDONLY)) != -1) - { - if (read(fd, u.buf, 4) != 4) - { - } - close(fd); - } - else - { - /* /dev/urandom did not work - use address of struct s */ - tui64 u64 = (tui64) (tintptr) &s; - u.clientID = (tui32) u64; - } + g_random((char *) &clientID, sizeof(clientID)); /* setup stream */ xstream_new(s, 1024); @@ -216,7 +198,7 @@ devredir_init(void) xstream_wr_u16_le(s, PAKID_CORE_SERVER_ANNOUNCE); xstream_wr_u16_le(s, 0x0001); /* server major ver */ xstream_wr_u16_le(s, 0x000C); /* server minor ver - pretend 2 b Win 7 */ - xstream_wr_u32_le(s, u.clientID); /* unique ClientID */ + xstream_wr_u32_le(s, clientID); /* unique ClientID */ /* send data to client */ bytes = xstream_len(s); From 2faf98ca61d317b71a7f46ce02b9faceabdf1bc1 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 21 Jan 2020 14:31:35 +0000 Subject: [PATCH 4/9] Add cppcheck to travis-ci --- .travis.yml | 27 ++++++++++ scripts/install_cppcheck.sh | 105 ++++++++++++++++++++++++++++++++++++ scripts/run_cppcheck.sh | 58 ++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100755 scripts/install_cppcheck.sh create mode 100755 scripts/run_cppcheck.sh diff --git a/.travis.yml b/.travis.yml index 775f8b02..255612bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,12 @@ addons: packages: &common_deps - nasm +# This is required to use a version of cppcheck other than that +# suplied with the operating system +cppcheck_defs: &cppcheck_defs + - CPPCHECK_VER=1.90 + - CPPCHECK_REPO=https://github.com/danmar/cppcheck.git + min_amd64_deps: &min_amd64_deps - *common_deps - libpam0g-dev @@ -81,6 +87,22 @@ max_x86_conf: &max_x86_conf packages: - *max_x86_deps +# For cppcheck, we've got a custom script +cppcheck_conf: &cppcheck_conf + env: + - *cppcheck_defs + +# addons: +# apt: +# packages: +# - cppcheck + + script: + - ./bootstrap + - scripts/install_cppcheck.sh $CPPCHECK_REPO $CPPCHECK_VER + - scripts/run_cppcheck.sh -v $CPPCHECK_VER + + matrix: include: @@ -108,6 +130,11 @@ matrix: - compiler: clang << : *max_x86_conf + # cppcheck + - name: cppcheck + compiler: gcc + << : *cppcheck_conf + script: - ./bootstrap - ./configure $CONF_FLAGS diff --git a/scripts/install_cppcheck.sh b/scripts/install_cppcheck.sh new file mode 100755 index 00000000..87f436fb --- /dev/null +++ b/scripts/install_cppcheck.sh @@ -0,0 +1,105 @@ +#!/bin/sh + +# Script to install a version of cppcheck in ~/cppcheck.local/ +# +# Used by Travis-CI builds, until Travis supports cppcheck natively +# +# Currently only supports git repos as sources +# +# Usage: /path/to/install_cppcheck.sh + +INSTALL_ROOT=~/cppcheck.local + +# ---------------------------------------------------------------------------- +# U S A G E +# ---------------------------------------------------------------------------- +usage() +{ + echo "** Usage: $0 " + echo " e.g. $0 https://github.com/danmar/cppcheck.git 1.90" +} >&2 + +# ---------------------------------------------------------------------------- +# C A L L _ M A K E +# +# Calls make with the specified parameters, but only displays the error +# log if it fails +# ---------------------------------------------------------------------------- +call_make() +{ + # Disable set -e, if active + set_entry_opts=`set +o` + set +e + + status=1 + log=`mktemp /tmp/cppcheck-log.XXXXXXXXXX` + if [ -n "$log" ]; then + make "$@" >$log 2>&1 + status=$? + if [ $status -ne 0 ]; then + cat $log >&2 + fi + rm $log + fi + + # Re-enable `set -e` if active before + $set_entry_opts + + return $status +} + +# ---------------------------------------------------------------------------- +# M A I N +# ---------------------------------------------------------------------------- +if [ $# -ne 2 ]; then + usage + exit 1 +fi +REPO_URL="$1" +CPPCHECK_VER="$2" + +# Already installed? +exe=$INSTALL_ROOT/$CPPCHECK_VER/bin/cppcheck +if [ -x "$exe" ]; then + echo "cppcheck version $CPPCHECK_VER is already installed at $exe" >&2 + exit 0 +fi + +workdir=`mktemp -d /tmp/cppcheck.XXXXXXXXXX` +if [ -z "$workdir" ]; then + echo "** Unable to create temporary working directory" 2>&1 + exit 1 +fi + +# Use a sub-process for the next bit to restrict the scope of 'set -e' +( + set -e ; # Exit sub-process on first error + + # Put everything in this directory + FILESDIR=$INSTALL_ROOT/$CPPCHECK_VER + + # CFGDIR is needed for cppcheck before 1.86 + make_args="FILESDIR=$FILESDIR PREFIX=$FILESDIR CFGDIR=$FILESDIR" + + # See https://stackoverflow.com/questions/ + # 791959/download-a-specific-tag-with-git + git clone -b $CPPCHECK_VER --depth 1 $REPO_URL $workdir + + cd $workdir + echo "Making cppcheck..." + # CFGDIR is needed for cppcheck before 1.86 + call_make $make_args + + echo "Installing cppcheck..." + mkdir -p $FILESDIR + call_make install $make_args +) +status=$? + +if [ $status -eq 0 ]; then + rm -rf $workdir +else + "** Script failed. Work dir is $workdir" >&2 +fi + +exit $status diff --git a/scripts/run_cppcheck.sh b/scripts/run_cppcheck.sh new file mode 100755 index 00000000..2c152782 --- /dev/null +++ b/scripts/run_cppcheck.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +# Script to run cppcheck +# +# Usage: /path/to/run_cppcheck.sh [ -v CPPCHECK_VER] [] +# +# - If is missing, '.' is assumed +# - If -v CPPCHECK_VER is specified, that version of cppcheck is run from +# ~/cppcheck.local (whether or not it's there!). Use install_cppcheck.sh +# to install a new version. +# +# Environment (all optional):- +# +# CPPCHECK : Override the default cppcheck command ('cppcheck'). +# Ignored if -v is specified +# CPPCHECK_FLAGS : Override the default cppcheck flags + +INSTALL_ROOT=~/cppcheck.local + +# Figure out CPPCHECK setting, if any. Currently '-v' must be the first +# argument on the command line. +case "$1" in + -v) # Version is separate parameter + if [ $# -ge 2 ]; then + CPPCHECK="$INSTALL_ROOT/$2/bin/cppcheck" + shift 2 + else + echo "** ignoring '-v' with no arg" >&2 + shift 1 + fi + ;; + -v*) # Version is in same parameter + # ${parameter#word} is not supported by classic Bourne shell, + # but it is on bash, dash, etc. If it doesn't work on your shell, + # don't use this form! + CPPCHECK="$INSTALL_ROOT/${1#-v}/bin/cppcheck" + shift 1 +esac +if [ -z "$CPPCHECK" ]; then + CPPCHECK=cppcheck +fi + +# Supply default flags passed to cppcheck if necessary +if [ -z "$CPPCHECK_FLAGS" ]; then + CPPCHECK_FLAGS="--quiet --force --std=c11 --std=c++11 --inline-suppr \ + --enable=warning --error-exitcode=1" +fi + +# Any options/directories specified? +if [ $# -eq 0 ]; then + set -- . +fi + +# Display the cppcheck version and command for debugging +"$CPPCHECK" --version && { + echo Command: $CPPCHECK $CPPCHECK_FLAGS "$@" + "$CPPCHECK" $CPPCHECK_FLAGS "$@" +} From a3d429b4f7bc5d06236c48df7e9b1f770514f7c9 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 3 Mar 2020 10:02:32 +0000 Subject: [PATCH 5/9] Minor fixes to drive redirection --- common/ms-erref.h | 3 +- sesman/chansrv/chansrv_fuse.c | 54 ++++++++++++++------- sesman/chansrv/chansrv_fuse.h | 1 + sesman/chansrv/chansrv_xfs.c | 4 +- sesman/chansrv/devredir.c | 89 ++++++++++++++++++++--------------- sesman/chansrv/devredir.h | 9 ++-- 6 files changed, 100 insertions(+), 60 deletions(-) diff --git a/common/ms-erref.h b/common/ms-erref.h index 3b1b75e5..394766a1 100644 --- a/common/ms-erref.h +++ b/common/ms-erref.h @@ -36,7 +36,8 @@ enum NTSTATUS STATUS_ACCESS_DENIED = 0xc0000022, STATUS_OBJECT_NAME_INVALID = 0xc0000033, STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034, - STATUS_SHARING_VIOLATION = 0xc0000043 + STATUS_SHARING_VIOLATION = 0xc0000043, + STATUS_NOT_SUPPORTED = 0xc00000bb }; #endif /* MS_ERREF_H */ diff --git a/sesman/chansrv/chansrv_fuse.c b/sesman/chansrv/chansrv_fuse.c index b57cc662..178d1ded 100644 --- a/sesman/chansrv/chansrv_fuse.c +++ b/sesman/chansrv/chansrv_fuse.c @@ -78,6 +78,7 @@ void xfuse_devredir_cb_open_file(struct state_open *fip, tui32 DeviceId, tui32 FileId) {} void xfuse_devredir_cb_read_file(struct state_read *fip, + enum NTSTATUS IoStatus, const char *buf, size_t length) {} void xfuse_devredir_cb_write_file( @@ -1313,9 +1314,18 @@ void xfuse_devredir_cb_open_file(struct state_open *fip, } void xfuse_devredir_cb_read_file(struct state_read *fip, + enum NTSTATUS IoStatus, const char *buf, size_t length) { - fuse_reply_buf(fip->req, buf, length); + if (IoStatus != STATUS_SUCCESS) + { + log_error("Read NTSTATUS is %d", (int) IoStatus); + fuse_reply_err(fip->req, EIO); + } + else + { + fuse_reply_buf(fip->req, buf, length); + } free(fip); } @@ -1679,7 +1689,7 @@ static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent, //XFUSE_HANDLE *fh; log_debug("LK_TODO: this is still a TODO"); - fuse_reply_err(req, EINVAL); + fuse_reply_err(req, EROFS); } else { @@ -1744,26 +1754,26 @@ static void xfuse_cb_rename(fuse_req_t req, else if (!(new_parent_xinode = xfs_get(g_xfs, new_parent))) { log_error("inode %ld is not valid", new_parent); - fuse_reply_err(req, EINVAL); + fuse_reply_err(req, ENOENT); } else if (!xfs_check_move_entry(g_xfs, old_xinode->inum, new_parent, new_name)) { + /* Catchall -see rename(2). Fix when logging is improved */ fuse_reply_err(req, EINVAL); } else if (new_parent_xinode->device_id != old_xinode->device_id) { - log_error("rename across file systems not supported"); - fuse_reply_err(req, EINVAL); + fuse_reply_err(req, EXDEV); } else if (old_xinode->device_id == 0) { /* specified file is a local resource */ log_debug("LK_TODO: this is still a TODO"); - fuse_reply_err(req, EINVAL); + fuse_reply_err(req, EROFS); } else @@ -1853,20 +1863,25 @@ static void xfuse_create_dir_or_file(fuse_req_t req, fuse_ino_t parent, } /* is parent inode valid? */ - if (parent == FUSE_ROOT_ID || - (xinode = xfs_get(g_xfs, parent)) == NULL || - (xinode->mode & S_IFDIR) == 0) + if (parent == FUSE_ROOT_ID) + { + fuse_reply_err(req, EROFS); + } + else if ((xinode = xfs_get(g_xfs, parent)) == NULL) { - log_error("inode %ld is not valid", parent); fuse_reply_err(req, ENOENT); } + else if ((xinode->mode & S_IFDIR) == 0) + { + fuse_reply_err(req, ENOTDIR); + } else if (xinode->device_id == 0) { /* specified file is a local resource */ //XFUSE_HANDLE *fh; log_debug("LK_TODO: this is still a TODO"); - fuse_reply_err(req, EINVAL); + fuse_reply_err(req, EROFS); } else { @@ -1948,10 +1963,17 @@ static void xfuse_cb_open(fuse_req_t req, fuse_ino_t ino, else if (xinode->device_id == 0) { /* specified file is a local resource */ - XFUSE_HANDLE *fh = g_new0(XFUSE_HANDLE, 1); - fh->is_loc_resource = 1; - fi->fh = (tintptr) fh; - fuse_reply_open(req, fi); + if ((fi->flags & O_ACCMODE) != O_RDONLY) + { + fuse_reply_err(req, EROFS); + } + else + { + XFUSE_HANDLE *fh = g_new0(XFUSE_HANDLE, 1); + fh->is_loc_resource = 1; + fi->fh = (tintptr) fh; + fuse_reply_open(req, fi); + } } else { @@ -2153,7 +2175,7 @@ static void xfuse_cb_write(fuse_req_t req, fuse_ino_t ino, const char *buf, { /* target file is in .clipboard dir */ log_debug("THIS IS STILL A TODO!"); - fuse_reply_err(req, EINVAL); + fuse_reply_err(req, EROFS); } else { diff --git a/sesman/chansrv/chansrv_fuse.h b/sesman/chansrv/chansrv_fuse.h index a692dcdd..abd68343 100644 --- a/sesman/chansrv/chansrv_fuse.h +++ b/sesman/chansrv/chansrv_fuse.h @@ -98,6 +98,7 @@ void xfuse_devredir_cb_open_file(struct state_open *fip, tui32 DeviceId, tui32 FileId); void xfuse_devredir_cb_read_file(struct state_read *fip, + enum NTSTATUS IoStatus, const char *buf, size_t length); void xfuse_devredir_cb_write_file( struct state_write *fip, diff --git a/sesman/chansrv/chansrv_xfs.c b/sesman/chansrv/chansrv_xfs.c index feff7e2b..a5f009d2 100644 --- a/sesman/chansrv/chansrv_xfs.c +++ b/sesman/chansrv/chansrv_xfs.c @@ -565,7 +565,7 @@ xfs_get_full_path(struct xfs_fs *xfs, fuse_ino_t inum) */ size_t len = 0; XFS_INODE_ALL *p; - for (p = xino ; p->pub.inum != FUSE_ROOT_ID ; p = p->parent) + for (p = xino ; p && p->pub.inum != FUSE_ROOT_ID ; p = p->parent) { len += strlen(p->pub.name); ++len; /* Allow for '/' prefix */ @@ -578,7 +578,7 @@ xfs_get_full_path(struct xfs_fs *xfs, fuse_ino_t inum) char *end = result + len; *end = '\0'; - for (p = xino ; p->pub.inum != FUSE_ROOT_ID ; p = p->parent) + for (p = xino ; p && p->pub.inum != FUSE_ROOT_ID ; p = p->parent) { len = strlen(p->pub.name); end -= (len + 1); diff --git a/sesman/chansrv/devredir.c b/sesman/chansrv/devredir.c index 6455746b..806d9c06 100644 --- a/sesman/chansrv/devredir.c +++ b/sesman/chansrv/devredir.c @@ -560,7 +560,8 @@ devredir_send_server_user_logged_on(void) } static void -devredir_send_server_device_announce_resp(tui32 device_id) +devredir_send_server_device_announce_resp(tui32 device_id, + enum NTSTATUS result_code) { struct stream *s; int bytes; @@ -571,7 +572,7 @@ devredir_send_server_device_announce_resp(tui32 device_id) xstream_wr_u16_le(s, RDPDR_CTYP_CORE); xstream_wr_u16_le(s, PAKID_CORE_DEVICE_REPLY); xstream_wr_u32_le(s, device_id); - xstream_wr_u32_le(s, 0); /* ResultCode */ + xstream_wr_u32_le(s, (tui32)result_code); /* send to client */ bytes = xstream_len(s); @@ -811,6 +812,7 @@ devredir_proc_client_devlist_announce_req(struct stream *s) tui32 device_type; tui32 device_data_len; char preferred_dos_name[9]; + enum NTSTATUS response_status; /* get number of devices being announced */ xstream_rd_u32_le(s, device_count); @@ -821,19 +823,20 @@ devredir_proc_client_devlist_announce_req(struct stream *s) { xstream_rd_u32_le(s, device_type); xstream_rd_u32_le(s, g_device_id); + /* get preferred DOS name + * DOS names that are 8 chars long are not NULL terminated */ + for (j = 0; j < 8; j++) + { + preferred_dos_name[j] = *s->p++; + } + preferred_dos_name[8] = 0; + + /* Assume this device isn't supported by us */ + response_status = STATUS_NOT_SUPPORTED; switch (device_type) { case RDPDR_DTYP_FILESYSTEM: - /* get preferred DOS name */ - for (j = 0; j < 8; j++) - { - preferred_dos_name[j] = *s->p++; - } - - /* DOS names that are 8 chars long are not NULL terminated */ - preferred_dos_name[8] = 0; - /* get device data len */ xstream_rd_u32_le(s, device_data_len); if (device_data_len) @@ -847,7 +850,7 @@ devredir_proc_client_devlist_announce_req(struct stream *s) preferred_dos_name, device_data_len, g_full_name_for_filesystem); - devredir_send_server_device_announce_resp(g_device_id); + response_status = STATUS_SUCCESS; /* create share directory in xrdp file system; */ /* think of this as the mount point for this share */ @@ -855,31 +858,44 @@ devredir_proc_client_devlist_announce_req(struct stream *s) break; case RDPDR_DTYP_SMARTCARD: - /* get preferred DOS name */ - for (j = 0; j < 8; j++) - { - preferred_dos_name[j] = *s->p++; - } - - /* DOS names that are 8 chars long are not NULL terminated */ - preferred_dos_name[8] = 0; - /* for smart cards, device data len always 0 */ log_debug("device_type=SMARTCARD device_id=0x%x dosname=%s", g_device_id, preferred_dos_name); - devredir_send_server_device_announce_resp(g_device_id); + response_status = STATUS_SUCCESS; + scard_device_announce(g_device_id); break; - /* we don't yet support these devices */ case RDPDR_DTYP_SERIAL: + log_debug( + "device_type=SERIAL device_id=0x%x dosname=%s", + g_device_id, preferred_dos_name); + break; + case RDPDR_DTYP_PARALLEL: + log_debug( + "device_type=PARALLEL device_id=0x%x dosname=%s", + g_device_id, preferred_dos_name); + break; + case RDPDR_DTYP_PRINT: - log_debug("unsupported dev: 0x%x", device_type); + log_debug( + "device_type=PRINT device_id=0x%x dosname=%s", + g_device_id, preferred_dos_name); + break; + + default: + log_debug( + "device_type=UNKNOWN device_id=0x%x dosname=%s", + g_device_id, preferred_dos_name); break; } + + /* Tell the client wheth or not we're supporting this one */ + devredir_send_server_device_announce_resp(g_device_id, + response_status); } } @@ -993,6 +1009,7 @@ devredir_proc_device_iocompletion(struct stream *s) case CID_READ: xstream_rd_u32_le(s, Length); xfuse_devredir_cb_read_file((struct state_read *) irp->fuse_info, + IoStatus, s->p, Length); devredir_irp_delete(irp); break; @@ -1511,10 +1528,10 @@ devredir_rmdir_or_file(struct state_remove *fusep, tui32 device_id, /** * Read data from previously opened file * - * @return 0 on success, -1 on failure + * Errors are reported via xfuse_devredir_cb_read_file() *****************************************************************************/ -int +void devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId, tui32 Length, tui64 Offset) { @@ -1522,7 +1539,6 @@ devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId, IRP *irp; IRP *new_irp; int bytes; - int rval = -1; xstream_new(s, 1024); @@ -1530,14 +1546,14 @@ devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId, if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL) { log_error("no IRP found with FileId = %d", FileId); - xfuse_devredir_cb_read_file(fusep, NULL, 0); + xfuse_devredir_cb_read_file(fusep, STATUS_UNSUCCESSFUL, NULL, 0); xstream_free(s); } /* create a new IRP for this request */ else if ((new_irp = devredir_irp_new()) == NULL) { /* system out of memory */ - xfuse_devredir_cb_read_file(fusep, NULL, 0); + xfuse_devredir_cb_read_file(fusep, STATUS_UNSUCCESSFUL, NULL, 0); xstream_free(s); } else @@ -1563,13 +1579,16 @@ devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId, bytes = xstream_len(s); send_channel_data(g_rdpdr_chan_id, s->data, bytes); xstream_free(s); - rval = 0; } - - return rval; } -int +/** + * Read data from previously opened file + * + * Errors are reported via xfuse_devredir_cb_write_file() + *****************************************************************************/ + +void devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId, const char *buf, int Length, tui64 Offset) { @@ -1577,7 +1596,6 @@ devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId, IRP *irp; IRP *new_irp; int bytes; - int rval = -1; log_debug("DeviceId=%d FileId=%d Length=%d Offset=%lld", DeviceId, FileId, Length, (long long)Offset); @@ -1625,10 +1643,7 @@ devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId, bytes = xstream_len(s); send_channel_data(g_rdpdr_chan_id, s->data, bytes); xstream_free(s); - rval = 0; } - - return rval; } diff --git a/sesman/chansrv/devredir.h b/sesman/chansrv/devredir.h index 04483753..7ad13745 100644 --- a/sesman/chansrv/devredir.h +++ b/sesman/chansrv/devredir.h @@ -78,12 +78,13 @@ int devredir_file_open(struct state_open *fusep, tui32 device_id, int devredir_file_close(struct state_close *fusep, tui32 device_id, tui32 file_id); -int devredir_file_read(struct state_read *fusep, tui32 device_id, tui32 FileId, - tui32 Length, tui64 Offset); +void +devredir_file_read(struct state_read *fusep, tui32 device_id, tui32 FileId, + tui32 Length, tui64 Offset); -int +void devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId, - const char *buf, int Length, tui64 Offset); + const char *buf, int Length, tui64 Offset); int devredir_file_rename( struct state_rename *fusep, tui32 device_id, From a2266f23f61781d45b2d4a8dc6e19a3d6d00e6a9 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 3 Mar 2020 16:16:09 +0000 Subject: [PATCH 6/9] Allow a redirected drive device_id to be zero (Guacamole support) --- sesman/chansrv/chansrv_fuse.c | 22 ++++++++++-------- sesman/chansrv/chansrv_xfs.c | 43 +++++++++++++++++++---------------- sesman/chansrv/chansrv_xfs.h | 13 ++++------- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/sesman/chansrv/chansrv_fuse.c b/sesman/chansrv/chansrv_fuse.c index 178d1ded..62ee050a 100644 --- a/sesman/chansrv/chansrv_fuse.c +++ b/sesman/chansrv/chansrv_fuse.c @@ -638,6 +638,7 @@ int xfuse_create_share(tui32 device_id, const char *dirname) } else { + xinode->is_redirected = 1; xinode->device_id = device_id; result = 0; } @@ -654,7 +655,7 @@ int xfuse_create_share(tui32 device_id, const char *dirname) void xfuse_delete_share(tui32 device_id) { - xfs_delete_entries_with_device_id(g_xfs, device_id); + xfs_delete_redirected_entries_with_device_id(g_xfs, device_id); } /** @@ -1448,7 +1449,7 @@ static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) } else { - if (parent_xinode->device_id == 0) + if (!parent_xinode->is_redirected) { /* File cannot be remote - we either know about it or we don't */ if ((xinode = xfs_lookup_in_dir(g_xfs, parent, name)) != NULL) @@ -1683,7 +1684,7 @@ static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent, fuse_reply_err(req, ENOTEMPTY); } - else if (xinode->device_id == 0) + else if (!xinode->is_redirected) { /* specified file is a local resource */ //XFUSE_HANDLE *fh; @@ -1764,12 +1765,13 @@ static void xfuse_cb_rename(fuse_req_t req, fuse_reply_err(req, EINVAL); } - else if (new_parent_xinode->device_id != old_xinode->device_id) + else if (new_parent_xinode->is_redirected != old_xinode->is_redirected || + new_parent_xinode->device_id != old_xinode->device_id) { fuse_reply_err(req, EXDEV); } - else if (old_xinode->device_id == 0) + else if (!old_xinode->is_redirected) { /* specified file is a local resource */ log_debug("LK_TODO: this is still a TODO"); @@ -1875,7 +1877,7 @@ static void xfuse_create_dir_or_file(fuse_req_t req, fuse_ino_t parent, { fuse_reply_err(req, ENOTDIR); } - else if (xinode->device_id == 0) + else if (!xinode->is_redirected) { /* specified file is a local resource */ //XFUSE_HANDLE *fh; @@ -1960,7 +1962,7 @@ static void xfuse_cb_open(fuse_req_t req, fuse_ino_t ino, log_debug("Invalid access mode specified"); fuse_reply_err(req, EINVAL); } - else if (xinode->device_id == 0) + else if (!xinode->is_redirected) { /* specified file is a local resource */ if ((fi->flags & O_ACCMODE) != O_RDONLY) @@ -2039,7 +2041,7 @@ static void xfuse_cb_release(fuse_req_t req, fuse_ino_t ino, struct log_error("inode %ld is not valid", ino); fuse_reply_err(req, ENOENT); } - else if (xinode->device_id == 0) + else if (!xinode->is_redirected) { /* specified file is a local resource */ fuse_reply_err(req, 0); @@ -2305,7 +2307,7 @@ static void xfuse_cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, /* No changes have been made */ make_fuse_attr_reply(req, xinode); } - else if (xinode->device_id == 0) + else if (!xinode->is_redirected) { /* Update the local fs */ update_inode_file_attributes(&attrs, change_mask, xinode); @@ -2371,7 +2373,7 @@ static void xfuse_cb_opendir(fuse_req_t req, fuse_ino_t ino, log_error("inode %ld is not valid", ino); fuse_reply_err(req, ENOENT); } - else if (xinode->device_id == 0) + else if (!xinode->is_redirected) { if ((fi->fh = (tintptr) xfs_opendir(g_xfs, ino)) == 0) { diff --git a/sesman/chansrv/chansrv_xfs.c b/sesman/chansrv/chansrv_xfs.c index a5f009d2..5e75650c 100644 --- a/sesman/chansrv/chansrv_xfs.c +++ b/sesman/chansrv/chansrv_xfs.c @@ -344,6 +344,7 @@ xfs_create_xfs_fs(mode_t umask, uid_t uid, gid_t gid) xino1->pub.ctime = xino1->pub.atime; strcpy(xino1->pub.name, "."); xino1->pub.generation = xfs->generation; + xino1->pub.is_redirected = 0; xino1->pub.device_id = 0; /* @@ -365,6 +366,7 @@ xfs_create_xfs_fs(mode_t umask, uid_t uid, gid_t gid) xino2->pub.ctime = xino2->pub.atime; strcpy(xino2->pub.name, ".delete-pending"); xino2->pub.generation = xfs->generation; + xino2->pub.is_redirected = 0; xino2->pub.device_id = 0; xino2->parent = NULL; @@ -469,6 +471,7 @@ xfs_add_entry(struct xfs_fs *xfs, fuse_ino_t parent_inum, xino->pub.ctime = xino->pub.atime; strcpy(xino->pub.name, name); xino->pub.generation = xfs->generation; + xino->pub.is_redirected = parent->pub.is_redirected; xino->pub.device_id = parent->pub.device_id; xino->pub.lindex = 0; @@ -816,35 +819,35 @@ xfs_get_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum) /* ------------------------------------------------------------------------ */ void -xfs_delete_entries_with_device_id(struct xfs_fs *xfs, tui32 device_id) +xfs_delete_redirected_entries_with_device_id(struct xfs_fs *xfs, + tui32 device_id) { fuse_ino_t inum; XFS_INODE_ALL *xino; - if (device_id != 0) + /* Using xfs_remove_entry() is convenient, but it recurses + * in to directories. To make sure all entries are removed, set the + * open_count of all affected files to 0 first + */ + for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum) { - /* Using xfs_remove_entry() is convenient, but it recurses - * in to directories. To make sure all entries are removed, set the - * open_count of all affected files to 0 first - */ - for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum) + if ((xino = xfs->inode_table[inum]) != NULL && + xino->pub.is_redirected != 0 && + xino->pub.device_id == device_id && + (xino->pub.mode & S_IFREG) != 0) { - if ((xino = xfs->inode_table[inum]) != NULL && - xino->pub.device_id == device_id && - (xino->pub.mode & S_IFREG) != 0) - { - xino->open_count = 0; - } + xino->open_count = 0; } + } - /* Now we can be sure everything will be deleted correctly */ - for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum) + /* Now we can be sure everything will be deleted correctly */ + for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum) + { + if ((xino = xfs->inode_table[inum]) != NULL && + xino->pub.is_redirected != 0 && + xino->pub.device_id == device_id) { - if ((xino = xfs->inode_table[inum]) != NULL && - xino->pub.device_id == device_id) - { - xfs_remove_entry(xfs, xino->pub.inum); - } + xfs_remove_entry(xfs, xino->pub.inum); } } } diff --git a/sesman/chansrv/chansrv_xfs.h b/sesman/chansrv/chansrv_xfs.h index 17dc7b4f..76fa1682 100644 --- a/sesman/chansrv/chansrv_xfs.h +++ b/sesman/chansrv/chansrv_xfs.h @@ -49,9 +49,8 @@ typedef struct xfs_inode time_t ctime; /* Time of last status change. */ char name[XFS_MAXFILENAMELEN + 1]; /* Short name */ tui32 generation; /* Changes if inode is reused */ - tui32 device_id; /* for file system redirection - * Non-redirected devices are guaranteed - * to have a device_id or zero */ + char is_redirected; /* file is on redirected device */ + tui32 device_id; /* device ID of redirected device */ int lindex; /* used in clipboard operations */ } XFS_INODE; @@ -271,17 +270,15 @@ unsigned int xfs_get_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum); /* - * Deletes all entries with the matching device id + * Deletes all redirected entries with the matching device id * * Files are deleted even if they are open * - * The specified device_id must be non-zero so that the root - * filesystem is not deleted! - * * @param device_id Device ID */ void -xfs_delete_entries_with_device_id(struct xfs_fs *xfs, tui32 device_id); +xfs_delete_redirected_entries_with_device_id(struct xfs_fs *xfs, + tui32 device_id); /* * Check an entry move will be successful From 96cbde725d0c60bb8ba826ab4c8c47ed875193c7 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Wed, 11 Mar 2020 10:13:56 +0900 Subject: [PATCH 7/9] README: gitter badge refer to xrdp-questions room --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 183842ed..95cb6686 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Build Status](https://travis-ci.org/neutrinolabs/xrdp.svg?branch=devel)](https://travis-ci.org/neutrinolabs/xrdp) -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/neutrinolabs/xrdp) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/neutrinolabs/xrdp-questions) ![Apache-License](https://img.shields.io/badge/License-Apache%202.0-blue.svg) *Current Version:* 0.9.12 From 6e7a5399336007f97b402c6bbf7a340755a4fcb6 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Wed, 11 Mar 2020 12:58:01 +0900 Subject: [PATCH 8/9] Update NEWS for v0.9.13 --- NEWS.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/NEWS.md b/NEWS.md index 088735a9..6d949ba7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,30 @@ +# Release notes for xrdp v0.9.13 (2020/03/11) + +This release is an intermediate bugfix release. The previous version v0.9.12 has some regressions on drive redirection. + +## Bug fixes (drive redirection related) +* Fix chansrv crashes with segmentation fault (regression in #1449) #1487 +* Drive redirection now supports Guacamole client #1505 #1507 +* Prevent a coredump in the event of a corrupted file system #1507 +* Resolve double-free in `chansrv_fuse` #1469 + +## Bug fixes (other) +* Fix the issue `xrdp --version | less` will show empty output #1471 #1472 +* Fix some warnings found by cppcheck #1479 #1481 #1484 #1485 + +## Other changes +* Add FreeBSD CI test #1466 +* Move Microsoft-defined constants into separate includes #1470 +* Perform cppcheck during CI test #1493 +* Support mousex button 8/9 #1478 + +## Known issues +* FreeRDP 2.0.0-rc4 or later might not able to connect to xrdp due to + xrdp's bad-mannered behaviour, add `+glyph-cache` option to FreeRDP to connect #1266 +* Audio redirection by MP3 codec doesn't sound with some client, use AAC instead #965 + +----------------------- + # Release notes for xrdp v0.9.12 (2019/12/28) ## Bug fixes From b3b1fff833bab83d49154513b40f5ac6810a7ee2 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Wed, 11 Mar 2020 13:04:35 +0900 Subject: [PATCH 9/9] bump version to v0.9.13 --- README.md | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 95cb6686..84ba4615 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/neutrinolabs/xrdp-questions) ![Apache-License](https://img.shields.io/badge/License-Apache%202.0-blue.svg) -*Current Version:* 0.9.12 +*Current Version:* 0.9.13 # xrdp - an open source RDP server diff --git a/configure.ac b/configure.ac index edaf2941..c64867ad 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # Process this file with autoconf to produce a configure script AC_PREREQ(2.65) -AC_INIT([xrdp], [0.9.12], [xrdp-devel@googlegroups.com]) +AC_INIT([xrdp], [0.9.13], [xrdp-devel@googlegroups.com]) AC_CONFIG_HEADERS(config_ac.h:config_ac-h.in) AM_INIT_AUTOMAKE([1.7.2 foreign]) AC_CONFIG_MACRO_DIR([m4])