1#!/usr/bin/perl
2
3############################################################################
4#   Copyright (C) 2005-2020 by Oleksandr Shneyder                          #
5#   oleksandr.shneyder@obviously-nice.de                                   #
6#                                                                          #
7#   This program is free software; you can redistribute it and/or modify   #
8#   it under the terms of the GNU General Public License as published by   #
9#   the Free Software Foundation; either version 2 of the License, or      #
10#   (at your option) any later version.                                    #
11#                                                                          #
12#   This program is distributed in the hope that it will be useful,        #
13#   but WITHOUT ANY WARRANTY; without even the implied warranty of         #
14#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          #
15#   GNU General Public License for more details.                           #
16#                                                                          #
17#   You should have received a copy of the GNU General Public License      #
18#   along with this program; if not, write to the                          #
19#   Free Software Foundation, Inc.,                                        #
20#   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.              #
21############################################################################
22
23
24
25use File::Path;
26use Proc::Simple;
27use Term::ReadPassword;
28use Getopt::Long;
29use strict;
30
31my $user;
32my $server;
33my $geometry="fullscreen";
34my $link="lan";
35my $pack="16m-jpeg-9";
36my $type="unix-kde";
37my $stype="desktop";
38my $kbdlay="us";
39my $kbdtype="pc105/us";
40my $setkbd="0";
41my $accept=0;
42my $sound=1;
43my $cmd="startkde";
44my $ssh_key=0;
45my $port="22";
46
47system ("rm -rf ~/.x2go/ssh/askpass*");
48
49sub printpass
50{
51    my $prog=shift;
52    my $pass=shift;
53    open (F,">$prog") or die "Couldn't open $prog for writing";
54    print F '#!/usr/bin/perl
55                $param=shift;';
56    print F "   open (F,\">$prog.log\");";
57    print F '   print F $param;
58                close (F);
59                if($param =~ m/RSA key/)
60                {';
61    if($accept){
62       print F     'print "yes\n";';}
63    else{
64       print F     'print "no\n";';}
65    print F '   }
66                else
67                {';
68    print F "      print \"$pass\\n\";
69                }";
70    close(F);
71    chmod (0700, $prog);
72}
73
74sub checkstat
75{
76    my $prog=shift;
77    open (F,"<$prog.log") or return;
78    my @outp;
79    my $ln=<F>;
80    for(my $i=0;$ln;$i++)
81    {
82       @outp[$i]=$ln;
83       $ln=<F>;
84    }
85    close(F);
86    if(join(" ",@outp) =~ m/Are you sure you want to continue connecting/)
87    {
88         print "@outp[0]@outp[1]";
89         print "If you are sure you want to continue connecting, please launch this programm with --add-to-known-hosts yes\n";
90         exit;
91    }
92}
93
94sub hidepass
95{
96    my $prog=shift;
97    open (F,">$prog") or die "Couldn't open $prog for writing";
98    print F "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
99    close(F);
100}
101
102
103my $pack_methods=
104"nopack
1058
10664
107256
108512
1094k
11032k
11164k
112256k
1132m
11416m
115256-rdp
116256-rdp-compressed
11732k-rdp
11832k-rdp-compressed
11964k-rdp
12064k-rdp-compressed
12116m-rdp
12216m-rdp-compressed
123rfb-hextile
124rfb-tight
125rfb-tight-compressed
1268-tight
12764-tight
128256-tight
129512-tight
1304k-tight
13132k-tight
13264k-tight
133256k-tight
1342m-tight
13516m-tight
1368-jpeg-%
13764-jpeg
138256-jpeg
139512-jpeg
1404k-jpeg
14132k-jpeg
14264k-jpeg
143256k-jpeg
1442m-jpeg
14516m-jpeg-%
1468-png-jpeg-%
14764-png-jpeg
148256-png-jpeg
149512-png-jpeg
1504k-png-jpeg
15132k-png-jpeg
15264k-png-jpeg
153256k-png-jpeg
1542m-png-jpeg
15516m-png-jpeg-%
1568-png-%
15764-png
158256-png
159512-png
1604k-png
16132k-png
16264k-png
163256k-png
1642m-png
16516m-png-%
16616m-rgb-%
16716m-rle-%";
168
169
170
171sub printargs
172{
173    print "Usage: $0 --user <username> --server <hostname> [Options]\nOptions:\
174    --help                              Print this message\
175    --help-pack                         Print availabel pack methods
176    --user <username>                   Connect as user 'username'\
177    --server <hostname>                 Connect to 'hostname'\
178    --command <cmd>                     Run command 'cmd', default value 'startkde'
179    --port                              SSH port, default 22
180    --ssh-key <fname>                   Us 'fname' as a key for ssh connection
181    --add-to-known-hosts <yes|no>       Add RSA key fingerprint to .ssh/known_hosts if authenticity of server can't be established, default value 'no'
182    --use-sound <yes|no>                Start sound server and ssh tunel for sound connections, default value 'yes'
183
184    nxagent options:
185    --geometry <Width>x<Height>         Set window size, default value 'fullscreen'
186    --link <modem|isdn|adsl|wan|lan>    Set link type, default 'lan'
187    --pack <packmethod>                 Use pack method, default '16m-jpeg-9'
188    --session-type <type>               Session type, 'desktop' or 'application' default 'desktop'
189    --kbd-layout <layout>               Use keyboard layout, default 'us'
190    --kbd-type <type>                   Set Keyboard type, default 'pc105/us'\n";
191exit;
192}
193
194sub printpack
195{
196    my $m=$pack_methods;
197    $m=~s/%/[0-9]/g;
198    print "$m\n";
199    exit;
200}
201
202sub check_pack
203{
204    my $p=shift;
205    if($p =~ m/%/)
206    {
207        return 0;
208    }
209    my @arr=split("-","$p");
210    my $pm=$pack_methods;
211
212    if(@arr>1)
213    {
214       my $qa=@arr[@arr-1];
215       my @vals=unpack('cc',$qa);
216       if($qa ge '0' && $qa le '9' && @vals == 1)
217       {
218            $pm=~s/%/$qa/g;
219       }
220    }
221
222    my @met=split("\n","$pm");
223    for(my $i=0;$i<@met;$i++)
224    {
225        if(@met[$i] eq $p)
226        {
227             return 1;
228        }
229    }
230    return 0;
231}
232
233
234sub getindex
235{
236    my @sess=@_;
237    print("Number\tStatus\t\tCreation time\t\tDisplay\tClient IP\n");
238    for(my $i=1;$i<=@sess;$i++)
239    {
240         my @vals=split('\\|',"@sess[$i-1]");
241         my $status=@vals[4];
242         if(@vals[4] eq 'S')
243         {
244             $status='Suspended'
245         }
246         elsif(@vals[4] eq 'R')
247         {
248             $status='Running  '
249         }
250         print "$i)\t$status\t@vals[5]\t@vals[2]\t@vals[7]\n";
251    }
252    print "Enter numer of session to resume or 'n' to start new session: ";
253    my $answ=<STDIN>;
254    chomp($answ);
255    if($answ > 0 && $answ <= @sess)
256    {
257         my @vals=split('\\|',"@sess[$answ-1]");
258         if(@vals[4] eq 'R')
259         {
260              print("Session is already running on @vals[7], continue? (y/n):  ");
261              my $nansw=<STDIN>;
262              chomp($nansw);
263              if($nansw eq 'y')
264              {
265                  return $answ;
266              }
267              else
268              {
269                  return -1;
270              }
271         }
272         else
273         {
274              return $answ;
275         }
276    }
277    elsif ($answ eq "n")
278    {
279       return 0;
280    }
281    else
282    {
283       print "Input Error\n";
284       return -1;
285    }
286}
287
288
289
290
291GetOptions("user=s" => \$user,
292"server=s" => \$server,
293"command=s" => \$cmd,
294"port=s" => \$port,
295"ssh-key=s" => \$ssh_key,
296"add-to-known-hosts=s" => \$accept,
297"use-sound=s" => \$sound,
298"geometry=s" => \$geometry,
299"link=s" => \$link,
300"pack=s" => \$pack,
301"session-type=s" => \$stype,
302"kbd-layout=s" => \$kbdlay,
303"kbd-type=s" => \$kbdtype,
304'help-pack' => \&printpack,
305'help' => \&printargs) or printargs;
306printargs unless (($user and $server));
307
308
309   if($ssh_key)
310   {
311       if ( !  -e $ssh_key)
312       {
313              print "Error, $ssh_key not exists\n";
314              exit;
315       }
316   }
317   if($kbdlay or $kbdtype)
318   {
319       $setkbd=1;
320   }
321
322   if(($link ne "modem" )&&($link ne "isdn")&&($link ne "adsl")&&($link ne "wan")&&($link ne "lan"))
323   {
324           print "Error parsing command line, wrong \"link\" argument\n";
325           printargs;
326           exit;
327   }
328
329   if(! check_pack($pack) )
330   {
331       print "Error parsing command line, wrong \"pack\" argument\n";
332       printargs;
333       exit;
334   }
335
336   if($accept)
337   {
338        if($accept eq   "yes")
339        {
340           $accept=1;
341        }
342        elsif($accept eq "no")
343        {
344           $accept=0;
345        }
346        else
347        {
348           print "Error parsing command line, wrong \"add-to-known-hosts\" argument\n";
349           printargs;
350           exit;
351        }
352   }
353
354   if($sound != 1)
355   {
356        if($sound eq "yes")
357        {
358           $sound=1;
359        }
360        elsif($sound eq "no")
361        {
362           $sound=0;
363        }
364        else
365        {
366           print "Error parsing command line, wrong \"use-sound\" argument\n";
367           printargs;
368           exit;
369        }
370   }
371
372
373my $nxroot="$ENV{'HOME'}/.x2go";
374my $dirpath="$nxroot/ssh";
375eval
376{
377    mkpath($dirpath)
378};
379if ($@)
380{
381      print "Couldn't create $dirpath: $@";
382      exit;
383}
384
385my $askpass="$dirpath/askpass";
386my $pass;
387my $sessions;
388if(!$ssh_key)
389{
390  $pass=read_password('Password:');
391  printpass $askpass,$pass;
392  $sessions=`DISPLAY=:0 SSH_ASKPASS=$askpass setsid ssh -p $port $user\@$server "x2golistsessions"`;
393  hidepass $askpass;
394  checkstat $askpass;
395}
396else
397{
398  $sessions=`ssh -p $port -i $ssh_key $user\@$server "x2golistsessions"`;
399}
400
401my @sess=split("\n","$sessions");
402my $newses=0;
403
404
405my $snum=-1;#index of session+1,-1 - error, -0 - new session
406if (@sess == 0)
407{
408   $newses=1;
409}
410else
411{
412   my @lines=split('\\|',"@sess[0]");
413   my $status=@lines[4];
414   if($status eq 'S' && @sess == 1)
415   {
416       $snum=0;
417   }
418   else
419   {
420       while($snum==-1)
421       {
422            $snum=getindex(@sess);
423       }
424       if($snum == 0)
425       {
426            $newses=1;
427       }
428       else
429       {
430            $snum--;
431       }
432   }
433}
434
435my $disp;
436my $snd_port;
437my $gr_port;
438my $cookie;
439my $agentpid;
440my $sname;
441my $t="D";
442if( $stype eq "application" )
443{
444   $t="R";
445}
446if($newses)
447{
448   my $outp;
449   if(! $ssh_key)
450   {
451       printpass $askpass,$pass;
452       $outp=`DISPLAY=:0 SSH_ASKPASS=$askpass setsid ssh -p $port $user\@$server "x2gostartagent $geometry $link $pack $type $kbdlay $kbdtype $setkbd $t"`;
453       hidepass $askpass;
454       checkstat $askpass;
455   }
456   else
457   {
458       $outp=`ssh -p $port -i $ssh_key $user\@$server "x2gostartagent $geometry $link $pack $type $kbdlay $kbdtype $setkbd $t"`;
459   }
460   my @lines=split("\n","$outp");
461   $disp=@lines[0];
462   $cookie=@lines[1];
463   $agentpid=@lines[2];
464   $sname=@lines[3];
465   $gr_port=@lines[4];
466   $snd_port=@lines[5];
467#   print ":$disp $cookie $agentpid $sname $gr_port $snd_port\n";
468}
469else
470{
471   my @lines=split('\\|',"@sess[$snum]");
472   $agentpid=@lines[0];
473   $sname=@lines[1];
474   $disp=@lines[2];
475   my $status=@lines[4];
476   $cookie=@lines[6];
477   $gr_port=@lines[8];
478   $snd_port=@lines[9];
479
480   if($status eq 'R')
481   {
482      if(! $ssh_key)
483      {
484        printpass $askpass,$pass;
485        system("DISPLAY=:0 SSH_ASKPASS=$askpass setsid ssh -p $port $user\@$server \"setsid x2gosuspend-session $sname\"");
486        hidepass $askpass;
487        checkstat $askpass;
488      }
489      else
490      {
491        system("ssh -p $port -i $ssh_key $user\@$server \"setsid x2gosuspend-session $sname\"");
492      }
493      sleep (1);
494   }
495   if(! $ssh_key)
496   {
497     printpass $askpass,$pass;
498     system("DISPLAY=:0 SSH_ASKPASS=$askpass setsid ssh -p $port $user\@$server \"setsid x2goresume-session $sname $geometry $link $pack $kbdlay $kbdtype $setkbd\"");
499     hidepass $askpass;
500     checkstat $askpass;
501   }
502   else
503   {
504     system("ssh -p $port -i $ssh_key $user\@$server \"setsid x2goresume-session $sname $geometry $link $pack $kbdlay $kbdtype $setkbd\"");
505   }
506}
507my $tunnel = Proc::Simple->new();
508my $snd_tun;
509my $snd_server;
510if($sound)
511{
512   $snd_tun= Proc::Simple->new();
513   $snd_server= Proc::Simple->new();
514   $snd_server->start("artsd -u -N -p $snd_port");
515}
516if( !$ssh_key)
517{
518  printpass $askpass,$pass;
519  $tunnel->start("DISPLAY=:0 SSH_ASKPASS=$askpass setsid ssh -p $port -N -L $gr_port:localhost:$gr_port $user\@$server");
520  if($sound)
521  {
522      $snd_tun->start("DISPLAY=:0 SSH_ASKPASS=$askpass setsid ssh -p $port  -N -R $snd_port:localhost:$snd_port $user\@$server");
523  }
524}
525else
526{
527  $tunnel->start("ssh -p $port -i $ssh_key -N -L $gr_port:localhost:$gr_port $user\@$server");
528  if($sound)
529  {
530      $snd_tun->start("ssh -p $port -i $ssh_key -N -R $snd_port:localhost:$snd_port $user\@$server");
531  }
532}
533
534sleep 2;
535$dirpath="$nxroot/S-$sname";
536eval
537{
538    mkpath($dirpath)
539};
540if ($@)
541{
542      print "Couldn't create $dirpath: $@";
543      hidepass $askpass;
544      exit;
545}
546
547my $nx_host="nx/nx,composite=1,root=$nxroot,connect=localhost,cookie=$cookie,port=$gr_port,errors=$dirpath/session";
548
549open (FILE,">$dirpath/options")or die "Couldnt't open $dirpath/options for writing";
550print FILE "$nx_host:$disp";
551close (FILE);
552my $proxy = Proc::Simple->new();
553$proxy->start("LD_LIBRARY_PATH=\$X2GO_LIB nxproxy  -S nx/nx,options=$dirpath/options:$disp 2>>$dirpath/session");
554if($newses)
555{
556   if(! $ssh_key)
557   {
558    system("DISPLAY=:0 SSH_ASKPASS=$askpass setsid ssh -p $port $user\@$server \"setsid x2goruncommand $disp $agentpid $sname $snd_port $cmd arts $t >& /dev/null & exit \"");
559   }
560   else
561   {
562    system("ssh -p $port -i $ssh_key $user\@$server \"setsid x2goruncommand $disp $agentpid $sname $snd_port $cmd  arts $t>& /dev/null & exit \"");
563   }
564}
565
566#hidepass $askpass;
567#checkstat $askpass;
568while($proxy->poll())
569{
570  sleep(1);
571}
572$tunnel->kill();
573if($sound)
574{
575   $snd_server->kill();
576   $snd_tun->kill();
577}
578system ("rm -rf ~/.x2go/ssh/askpass*");
579