LCOV - code coverage report
Current view: top level - src/frontend - stmclient.cc (source / functions) Hit Total Coverage
Test: mosh-1.3.2 Code Coverage Lines: 166 278 59.7 %
Date: 2022-02-06 20:19:53 Functions: 8 9 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :     Mosh: the mobile shell
       3             :     Copyright 2012 Keith Winstein
       4             : 
       5             :     This program is free software: you can redistribute it and/or modify
       6             :     it under the terms of the GNU General Public License as published by
       7             :     the Free Software Foundation, either version 3 of the License, or
       8             :     (at your option) any later version.
       9             : 
      10             :     This program is distributed in the hope that it will be useful,
      11             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :     GNU General Public License for more details.
      14             : 
      15             :     You should have received a copy of the GNU General Public License
      16             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      17             : 
      18             :     In addition, as a special exception, the copyright holders give
      19             :     permission to link the code of portions of this program with the
      20             :     OpenSSL library under certain conditions as described in each
      21             :     individual source file, and distribute linked combinations including
      22             :     the two.
      23             : 
      24             :     You must obey the GNU General Public License in all respects for all
      25             :     of the code used other than OpenSSL. If you modify file(s) with this
      26             :     exception, you may extend this exception to your version of the
      27             :     file(s), but you are not obligated to do so. If you do not wish to do
      28             :     so, delete this exception statement from your version. If you delete
      29             :     this exception statement from all source files in the program, then
      30             :     also delete it here.
      31             : */
      32             : 
      33             : #include "config.h"
      34             : 
      35             : #include <err.h>
      36             : #include <errno.h>
      37             : #include <locale.h>
      38             : #include <string.h>
      39             : #include <unistd.h>
      40             : #include <stdio.h>
      41             : #include <stdlib.h>
      42             : #include <sys/ioctl.h>
      43             : #include <sys/types.h>
      44             : #include <pwd.h>
      45             : #include <signal.h>
      46             : #include <time.h>
      47             : 
      48             : #if HAVE_PTY_H
      49             : #include <pty.h>
      50             : #elif HAVE_UTIL_H
      51             : #include <util.h>
      52             : #endif
      53             : 
      54             : #include "stmclient.h"
      55             : #include "swrite.h"
      56             : #include "completeterminal.h"
      57             : #include "user.h"
      58             : #include "fatal_assert.h"
      59             : #include "locale_utils.h"
      60             : #include "pty_compat.h"
      61             : #include "select.h"
      62             : #include "timestamp.h"
      63             : 
      64             : #include "networktransport-impl.h"
      65             : 
      66             : using std::wstring;
      67             : 
      68           0 : void STMClient::resume( void )
      69             : {
      70             :   /* Restore termios state */
      71           0 :   if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) {
      72           0 :       perror( "tcsetattr" );
      73           0 :       exit( 1 );
      74             :   }
      75             : 
      76             :   /* Put terminal in application-cursor-key mode */
      77           0 :   swrite( STDOUT_FILENO, display.open().c_str() );
      78             : 
      79             :   /* Flag that outer terminal state is unknown */
      80           0 :   repaint_requested = true;
      81           0 : }
      82             : 
      83         448 : void STMClient::init( void )
      84             : {
      85         448 :   if ( !is_utf8_locale() ) {
      86           0 :     LocaleVar native_ctype = get_ctype();
      87           0 :     string native_charset( locale_charset() );
      88             : 
      89           0 :     fprintf( stderr, "mosh-client needs a UTF-8 native locale to run.\n\n"
      90             :              "Unfortunately, the client's environment (%s) specifies\n"
      91             :              "the character set \"%s\".\n\n",
      92           0 :              native_ctype.str().c_str(), native_charset.c_str() );
      93           0 :     int unused __attribute((unused)) = system( "locale" );
      94           0 :     exit( 1 );
      95           0 :   }
      96             : 
      97             :   /* Verify terminal configuration */
      98         448 :   if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
      99           0 :     perror( "tcgetattr" );
     100           0 :     exit( 1 );
     101             :   }
     102             : 
     103             :   /* Put terminal driver in raw mode */
     104         448 :   raw_termios = saved_termios;
     105             : 
     106             : #ifdef HAVE_IUTF8
     107         448 :   if ( !(raw_termios.c_iflag & IUTF8) ) {
     108             :     //    fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" );
     109             :     /* Probably not really necessary since we are putting terminal driver into raw mode anyway. */
     110           2 :     raw_termios.c_iflag |= IUTF8;
     111             :   }
     112             : #endif /* HAVE_IUTF8 */
     113             : 
     114         448 :   cfmakeraw( &raw_termios );
     115             : 
     116         448 :   if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) {
     117           0 :       perror( "tcsetattr" );
     118           0 :       exit( 1 );
     119             :   }
     120             : 
     121             :   /* Put terminal in application-cursor-key mode */
     122         448 :   swrite( STDOUT_FILENO, display.open().c_str() );
     123             : 
     124             :   /* Add our name to window title */
     125         448 :   if ( !getenv( "MOSH_TITLE_NOPREFIX" ) ) {
     126        1344 :     overlays.set_title_prefix( wstring( L"[mosh] " ) );
     127             :   }
     128             : 
     129             :   /* Set terminal escape key. */
     130         448 :   const char *escape_key_env;
     131         448 :   if ( (escape_key_env = getenv( "MOSH_ESCAPE_KEY" )) != NULL ) {
     132           0 :     if ( strlen( escape_key_env ) == 1 ) {
     133           0 :       escape_key = (int)escape_key_env[0];
     134           0 :       if ( escape_key > 0 && escape_key < 128 ) {
     135           0 :         if ( escape_key < 32 ) {
     136             :           /* If escape is ctrl-something, pass it with repeating the key without ctrl. */
     137           0 :           escape_pass_key = escape_key + (int)'@';
     138             :         } else {
     139             :           /* If escape is something else, pass it with repeating the key itself. */
     140           0 :           escape_pass_key = escape_key;
     141             :         }
     142           0 :         if ( escape_pass_key >= 'A' && escape_pass_key <= 'Z' ) {
     143             :           /* If escape pass is an upper case character, define optional version
     144             :              as lower case of the same. */
     145           0 :           escape_pass_key2 = escape_pass_key + (int)'a' - (int)'A';
     146             :         } else {
     147           0 :           escape_pass_key2 = escape_pass_key;
     148             :         }
     149             :       } else {
     150           0 :         escape_key = 0x1E;
     151           0 :         escape_pass_key = '^';
     152           0 :         escape_pass_key2 = '^';
     153             :       }
     154           0 :     } else if ( strlen( escape_key_env ) == 0 ) {
     155           0 :       escape_key = -1;
     156             :     } else {
     157           0 :       escape_key = 0x1E;
     158           0 :       escape_pass_key = '^';
     159           0 :       escape_pass_key2 = '^';
     160             :     }
     161             :   } else {
     162         448 :     escape_key = 0x1E;
     163         448 :     escape_pass_key = '^';
     164         448 :     escape_pass_key2 = '^';
     165             :   }
     166             : 
     167             :   /* There are so many better ways to shoot oneself into leg than
     168             :      setting escape key to Ctrl-C, Ctrl-D, NewLine, Ctrl-L or CarriageReturn
     169             :      that we just won't allow that. */
     170         448 :   if ( escape_key == 0x03 || escape_key == 0x04 || escape_key == 0x0A || escape_key == 0x0C || escape_key == 0x0D ) {
     171           0 :     escape_key = 0x1E;
     172           0 :     escape_pass_key = '^';
     173           0 :     escape_pass_key2 = '^';
     174             :   }
     175             : 
     176             :   /* Adjust escape help differently if escape is a control character. */
     177         448 :   if ( escape_key > 0 ) {
     178         448 :     char escape_pass_name_buf[16];
     179         448 :     char escape_key_name_buf[16];
     180         448 :     snprintf(escape_pass_name_buf, sizeof escape_pass_name_buf, "\"%c\"", escape_pass_key);
     181         448 :     if (escape_key < 32) {
     182         448 :       snprintf(escape_key_name_buf, sizeof escape_key_name_buf, "Ctrl-%c", escape_pass_key);
     183         448 :       escape_requires_lf = false;
     184             :     } else {
     185           0 :       snprintf(escape_key_name_buf, sizeof escape_key_name_buf, "\"%c\"", escape_key);
     186           0 :       escape_requires_lf = true;
     187             :     }
     188         448 :     string tmp;
     189         448 :     tmp = string( escape_pass_name_buf );
     190         448 :     wstring escape_pass_name = std::wstring(tmp.begin(), tmp.end());
     191         448 :     tmp = string( escape_key_name_buf );
     192         448 :     wstring escape_key_name = std::wstring(tmp.begin(), tmp.end());
     193         896 :     escape_key_help = L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name;
     194         448 :     overlays.get_notification_engine().set_escape_key_string( tmp );
     195         448 :   }
     196         448 :   wchar_t tmp[ 128 ];
     197         448 :   swprintf( tmp, 128, L"Nothing received from server on UDP port %s.", port.c_str() );
     198         448 :   connecting_notification = wstring( tmp );
     199         448 : }
     200             : 
     201         448 : void STMClient::shutdown( void )
     202             : {
     203             :   /* Restore screen state */
     204         448 :   overlays.get_notification_engine().set_notification_string( wstring( L"" ) );
     205         448 :   overlays.get_notification_engine().server_heard( timestamp() );
     206         896 :   overlays.set_title_prefix( wstring( L"" ) );
     207         448 :   output_new_frame();
     208             : 
     209             :   /* Restore terminal and terminal-driver state */
     210         448 :   swrite( STDOUT_FILENO, display.close().c_str() );
     211             :   
     212         448 :   if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) {
     213           0 :     perror( "tcsetattr" );
     214           0 :     exit( 1 );
     215             :   }
     216             : 
     217         448 :   if ( still_connecting() ) {
     218           0 :     fprintf( stderr, "\nmosh did not make a successful connection to %s:%s.\n"
     219             :              "Please verify that UDP port %s is not firewalled and can reach the server.\n\n"
     220             :              "(By default, mosh uses a UDP port between 60000 and 61000. The -p option\n"
     221             :              "selects a specific UDP port number.)\n", ip.c_str(), port.c_str(), port.c_str() );
     222         448 :   } else if ( network && !clean_shutdown ) {
     223           0 :     fputs( "\n\nmosh did not shut down cleanly. Please note that the\n"
     224             :            "mosh-server process may still be running on the server.\n", stderr );
     225             :   }
     226         448 : }
     227             : 
     228         448 : void STMClient::main_init( void )
     229             : {
     230         448 :   Select &sel = Select::get_instance();
     231         448 :   sel.add_signal( SIGWINCH );
     232         448 :   sel.add_signal( SIGTERM );
     233         448 :   sel.add_signal( SIGINT );
     234         448 :   sel.add_signal( SIGHUP );
     235         448 :   sel.add_signal( SIGPIPE );
     236         448 :   sel.add_signal( SIGCONT );
     237             : 
     238             :   /* get initial window size */
     239         448 :   if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) {
     240           0 :     perror( "ioctl TIOCGWINSZ" );
     241           0 :     return;
     242             :   }  
     243             : 
     244             :   /* local state */
     245         448 :   local_framebuffer = Terminal::Framebuffer( window_size.ws_col, window_size.ws_row );
     246         448 :   new_state = Terminal::Framebuffer( 1, 1 );
     247             : 
     248             :   /* initialize screen */
     249         448 :   string init = display.new_frame( false, local_framebuffer, local_framebuffer );
     250         448 :   swrite( STDOUT_FILENO, init.data(), init.size() );
     251             : 
     252             :   /* open network */
     253         448 :   Network::UserStream blank;
     254         448 :   Terminal::Complete local_terminal( window_size.ws_col, window_size.ws_row );
     255         896 :   network = NetworkPointer( new NetworkType( blank, local_terminal, key.c_str(), ip.c_str(), port.c_str() ) );
     256             : 
     257         448 :   network->set_send_delay( 1 ); /* minimal delay on outgoing keystrokes */
     258             : 
     259             :   /* tell server the size of the terminal */
     260         448 :   network->get_current_state().push_back( Parser::Resize( window_size.ws_col, window_size.ws_row ) );
     261             : 
     262             :   /* be noisy as necessary */
     263         448 :   network->set_verbose( verbose );
     264         448 :   Select::set_verbose( verbose );
     265         896 : }
     266             : 
     267        6536 : void STMClient::output_new_frame( void )
     268             : {
     269        6536 :   if ( !network ) { /* clean shutdown even when not initialized */
     270           0 :     return;
     271             :   }
     272             : 
     273             :   /* fetch target state */
     274        6536 :   new_state = network->get_latest_remote_state().state.get_fb();
     275             : 
     276             :   /* apply local overlays */
     277        6536 :   overlays.apply( new_state );
     278             : 
     279             :   /* calculate minimal difference from where we are */
     280        6536 :   const string diff( display.new_frame( !repaint_requested,
     281        6536 :                                         local_framebuffer,
     282        6536 :                                         new_state ) );
     283        6536 :   swrite( STDOUT_FILENO, diff.data(), diff.size() );
     284             : 
     285        6536 :   repaint_requested = false;
     286             : 
     287        6536 :   local_framebuffer = new_state;
     288        6536 : }
     289             : 
     290        3333 : void STMClient::process_network_input( void )
     291             : {
     292        3333 :   network->recv();
     293             :   
     294             :   /* Now give hints to the overlays */
     295        3333 :   overlays.get_notification_engine().server_heard( network->get_latest_remote_state().timestamp );
     296        3333 :   overlays.get_notification_engine().server_acked( network->get_sent_state_acked_timestamp() );
     297             : 
     298        3333 :   overlays.get_prediction_engine().set_local_frame_acked( network->get_sent_state_acked() );
     299        3333 :   overlays.get_prediction_engine().set_send_interval( network->send_interval() );
     300        3333 :   overlays.get_prediction_engine().set_local_frame_late_acked( network->get_latest_remote_state().state.get_echo_ack() );
     301        3333 : }
     302             : 
     303         659 : bool STMClient::process_user_input( int fd )
     304             : {
     305         659 :   const int buf_size = 16384;
     306         659 :   char buf[ buf_size ];
     307             : 
     308             :   /* fill buffer if possible */
     309         659 :   ssize_t bytes_read = read( fd, buf, buf_size );
     310         659 :   if ( bytes_read == 0 ) { /* EOF */
     311             :     return false;
     312         659 :   } else if ( bytes_read < 0 ) {
     313           0 :     perror( "read" );
     314           0 :     return false;
     315             :   }
     316             : 
     317         659 :   NetworkType &net = *network;
     318             : 
     319         659 :   if ( net.shutdown_in_progress() ) {
     320             :     return true;
     321             :   }
     322         659 :   overlays.get_prediction_engine().set_local_frame_sent( net.get_sent_state_last() );
     323             : 
     324             :   /* Don't predict for bulk data. */
     325         659 :   bool paste = bytes_read > 100;
     326         659 :   if ( paste ) {
     327           0 :     overlays.get_prediction_engine().reset();
     328             :   }
     329             : 
     330        1842 :   for ( int i = 0; i < bytes_read; i++ ) {
     331        1183 :     char the_byte = buf[ i ];
     332             : 
     333        1183 :     if ( !paste ) {
     334        1183 :       overlays.get_prediction_engine().new_user_byte( the_byte, local_framebuffer );
     335             :     }
     336             : 
     337        1183 :     if ( quit_sequence_started ) {
     338           0 :       if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */
     339           0 :         if ( net.has_remote_addr() && (!net.shutdown_in_progress()) ) {
     340           0 :           overlays.get_notification_engine().set_notification_string( wstring( L"Exiting on user request..." ), true );
     341           0 :           net.start_shutdown();
     342           0 :           return true;
     343             :         }
     344             :         return false;
     345           0 :       } else if ( the_byte == 0x1a ) { /* Suspend sequence is escape_key Ctrl-Z */
     346             :         /* Restore terminal and terminal-driver state */
     347           0 :         swrite( STDOUT_FILENO, display.close().c_str() );
     348             : 
     349           0 :         if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) {
     350           0 :           perror( "tcsetattr" );
     351           0 :           exit( 1 );
     352             :         }
     353             : 
     354           0 :         fputs( "\n\033[37;44m[mosh is suspended.]\033[m\n", stdout );
     355             : 
     356           0 :         fflush( NULL );
     357             : 
     358             :         /* actually suspend */
     359           0 :         kill( 0, SIGSTOP );
     360             : 
     361           0 :         resume();
     362           0 :       } else if ( (the_byte == escape_pass_key) || (the_byte == escape_pass_key2) ) {
     363             :         /* Emulation sequence to type escape_key is escape_key +
     364             :            escape_pass_key (that is escape key without Ctrl) */
     365           0 :         net.get_current_state().push_back( Parser::UserByte( escape_key ) );
     366             :       } else {
     367             :         /* Escape key followed by anything other than . and ^ gets sent literally */
     368           0 :         net.get_current_state().push_back( Parser::UserByte( escape_key ) );
     369           0 :         net.get_current_state().push_back( Parser::UserByte( the_byte ) );        
     370             :       }
     371             : 
     372           0 :       quit_sequence_started = false;
     373             : 
     374           0 :       if ( overlays.get_notification_engine().get_notification_string() == escape_key_help ) {
     375           0 :         overlays.get_notification_engine().set_notification_string( L"" );
     376             :       }
     377             : 
     378           0 :       continue;
     379             :     }
     380             : 
     381        1183 :     quit_sequence_started = (escape_key > 0) && (the_byte == escape_key) && (lf_entered || (! escape_requires_lf));
     382        1183 :     if ( quit_sequence_started ) {
     383           0 :       lf_entered = false;
     384           0 :       overlays.get_notification_engine().set_notification_string( escape_key_help, true, false );
     385           0 :       continue;
     386             :     }
     387             : 
     388        1183 :     lf_entered = ( (the_byte == 0x0A) || (the_byte == 0x0D) ); /* LineFeed, Ctrl-J, '\n' or CarriageReturn, Ctrl-M, '\r' */
     389             : 
     390        1183 :     if ( the_byte == 0x0C ) { /* Ctrl-L */
     391           0 :       repaint_requested = true;
     392             :     }
     393             : 
     394        1183 :     net.get_current_state().push_back( Parser::UserByte( the_byte ) );          
     395             :   }
     396             : 
     397             :   return true;
     398             : }
     399             : 
     400          44 : bool STMClient::process_resize( void )
     401             : {
     402             :   /* get new size */
     403          44 :   if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) {
     404           0 :     perror( "ioctl TIOCGWINSZ" );
     405           0 :     return false;
     406             :   }
     407             :   
     408             :   /* tell remote emulator */
     409          44 :   Parser::Resize res( window_size.ws_col, window_size.ws_row );
     410             :   
     411          44 :   if ( !network->shutdown_in_progress() ) {
     412          44 :     network->get_current_state().push_back( res );
     413             :   }
     414             : 
     415             :   /* note remote emulator will probably reply with its own Resize to adjust our state */
     416             :   
     417             :   /* tell prediction engine */
     418          44 :   overlays.get_prediction_engine().reset();
     419             : 
     420          44 :   return true;
     421          44 : }
     422             : 
     423         448 : bool STMClient::main( void )
     424             : {
     425             :   /* initialize signal handling and structures */
     426         448 :   main_init();
     427             : 
     428             :   /* Drop unnecessary privileges */
     429             : #ifdef HAVE_PLEDGE
     430             :   /* OpenBSD pledge() syscall */
     431             :   if ( pledge( "stdio inet tty", NULL )) {
     432             :     perror( "pledge() failed" );
     433             :     exit( 1 );
     434             :   }
     435             : #endif
     436             : 
     437             :   /* prepare to poll for events */
     438         448 :   Select &sel = Select::get_instance();
     439             : 
     440        6088 :   while ( 1 ) {
     441        6088 :     try {
     442        6088 :       output_new_frame();
     443             : 
     444        6088 :       int wait_time = std::min( network->wait_time(), overlays.wait_time() );
     445             : 
     446             :       /* Handle startup "Connecting..." message */
     447      109584 :       if ( still_connecting() ) {
     448        1424 :         wait_time = std::min( 250, wait_time );
     449             :       }
     450             : 
     451             :       /* poll for events */
     452             :       /* network->fd() can in theory change over time */
     453        6088 :       sel.clear_fds();
     454        6088 :       std::vector< int > fd_list( network->fds() );
     455       12176 :       for ( std::vector< int >::const_iterator it = fd_list.begin();
     456       12176 :             it != fd_list.end();
     457       12176 :             it++ ) {
     458        6088 :         sel.add_fd( *it );
     459             :       }
     460        6088 :       sel.add_fd( STDIN_FILENO );
     461             : 
     462        6088 :       int active_fds = sel.select( wait_time );
     463        6088 :       if ( active_fds < 0 ) {
     464           0 :         perror( "select" );
     465             :         break;
     466             :       }
     467             : 
     468        6088 :       bool network_ready_to_read = false;
     469             : 
     470       12176 :       for ( std::vector< int >::const_iterator it = fd_list.begin();
     471       12176 :             it != fd_list.end();
     472       12176 :             it++ ) {
     473        6088 :         if ( sel.read( *it ) ) {
     474             :           /* packet received from the network */
     475             :           /* we only read one socket each run */
     476        3333 :           network_ready_to_read = true;
     477             :         }
     478             :       }
     479             : 
     480        6088 :       if ( network_ready_to_read ) {
     481        3333 :         process_network_input();
     482             :       }
     483             :     
     484        6088 :       if ( sel.read( STDIN_FILENO ) && !process_user_input( STDIN_FILENO ) ) { /* input from the user needs to be fed to the network */
     485           0 :         if ( !network->has_remote_addr() ) {
     486             :           break;
     487           0 :         } else if ( !network->shutdown_in_progress() ) {
     488           0 :           overlays.get_notification_engine().set_notification_string( wstring( L"Exiting..." ), true );
     489           0 :           network->start_shutdown();
     490             :         }
     491             :       }
     492             : 
     493        6088 :       if ( sel.signal( SIGWINCH ) && !process_resize() ) { /* resize */
     494           0 :         return false;
     495             :       }
     496             : 
     497        6088 :       if ( sel.signal( SIGCONT ) ) {
     498           0 :         resume();
     499             :       }
     500             : 
     501        6088 :       if ( sel.signal( SIGTERM )
     502        6088 :            || sel.signal( SIGINT )
     503        6088 :            || sel.signal( SIGHUP )
     504       12176 :            || sel.signal( SIGPIPE ) ) {
     505             :         /* shutdown signal */
     506           0 :         if ( !network->has_remote_addr() ) {
     507             :           break;
     508           0 :         } else if ( !network->shutdown_in_progress() ) {
     509           0 :           overlays.get_notification_engine().set_notification_string( wstring( L"Signal received, shutting down..." ), true );
     510           0 :           network->start_shutdown();
     511             :         }
     512             :       }
     513             : 
     514             :       /* quit if our shutdown has been acknowledged */
     515        6088 :       if ( network->shutdown_in_progress() && network->shutdown_acknowledged() ) {
     516           0 :         clean_shutdown = true;
     517           0 :         break;
     518             :       }
     519             : 
     520             :       /* quit after shutdown acknowledgement timeout */
     521        6088 :       if ( network->shutdown_in_progress() && network->shutdown_ack_timed_out() ) {
     522             :         break;
     523             :       }
     524             : 
     525             :       /* quit if we received and acknowledged a shutdown request */
     526        6088 :       if ( network->counterparty_shutdown_ack_sent() ) {
     527         448 :         clean_shutdown = true;
     528         448 :         break;
     529             :       }
     530             : 
     531             :       /* write diagnostic message if can't reach server */
     532        5640 :       if ( still_connecting()
     533         486 :            && (!network->shutdown_in_progress())
     534         486 :            && (timestamp() - network->get_latest_remote_state().timestamp > 250) ) {
     535           0 :         if ( timestamp() - network->get_latest_remote_state().timestamp > 15000 ) {
     536           0 :           if ( !network->shutdown_in_progress() ) {
     537           0 :             overlays.get_notification_engine().set_notification_string( wstring( L"Timed out waiting for server..." ), true );
     538           0 :             network->start_shutdown();
     539             :           }
     540             :         } else {
     541           0 :           overlays.get_notification_engine().set_notification_string( connecting_notification );
     542             :         }
     543        5640 :       } else if ( (network->get_remote_state_num() != 0)
     544        5640 :                   && (overlays.get_notification_engine().get_notification_string()
     545        5154 :                       == connecting_notification) ) {
     546           0 :         overlays.get_notification_engine().set_notification_string( L"" );
     547             :       }
     548             : 
     549        5640 :       network->tick();
     550             : 
     551        5640 :       string & send_error = network->get_send_error();
     552        5640 :       if ( !send_error.empty() ) {
     553           0 :         overlays.get_notification_engine().set_network_error( send_error );
     554        5640 :         send_error.clear();
     555             :       } else {
     556        5640 :         overlays.get_notification_engine().clear_network_error();
     557             :       }
     558        5640 :     } catch ( const Network::NetworkException &e ) {
     559           0 :       if ( !network->shutdown_in_progress() ) {
     560           0 :         overlays.get_notification_engine().set_network_error( e.what() );
     561             :       }
     562             : 
     563           0 :       struct timespec req;
     564           0 :       req.tv_sec = 0;
     565           0 :       req.tv_nsec = 200000000; /* 0.2 sec */
     566           0 :       nanosleep( &req, NULL );
     567           0 :       freeze_timestamp();
     568           0 :     } catch ( const Crypto::CryptoException &e ) {
     569           0 :       if ( e.fatal ) {
     570           0 :         throw;
     571             :       } else {
     572           0 :         wchar_t tmp[ 128 ];
     573           0 :         swprintf( tmp, 128, L"Crypto exception: %s", e.what() );
     574           0 :         overlays.get_notification_engine().set_notification_string( wstring( tmp ) );
     575             :       }
     576           0 :     }
     577             :   }
     578         448 :   return clean_shutdown;
     579             : }
     580             : 

Generated by: LCOV version 1.14