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 <assert.h>
34 #include <typeinfo>
35 
36 #include "user.h"
37 #include "fatal_assert.h"
38 #include "userinput.pb.h"
39 
40 using namespace Parser;
41 using namespace Network;
42 using namespace ClientBuffers;
43 
subtract(const UserStream * prefix)44 void UserStream::subtract( const UserStream *prefix )
45 {
46   // if we are subtracting ourself from ourself, just clear the deque
47   if ( this == prefix ) {
48     actions.clear();
49     return;
50   }
51   for ( deque<UserEvent>::const_iterator i = prefix->actions.begin();
52 	i != prefix->actions.end();
53 	i++ ) {
54     assert( this != prefix );
55     assert( !actions.empty() );
56     assert( *i == actions.front() );
57     actions.pop_front();
58   }
59 }
60 
diff_from(const UserStream & existing) const61 string UserStream::diff_from( const UserStream &existing ) const
62 {
63   deque<UserEvent>::const_iterator my_it = actions.begin();
64 
65   for ( deque<UserEvent>::const_iterator i = existing.actions.begin();
66 	i != existing.actions.end();
67 	i++ ) {
68     assert( my_it != actions.end() );
69     assert( *i == *my_it );
70     my_it++;
71   }
72 
73   ClientBuffers::UserMessage output;
74 
75   while ( my_it != actions.end() ) {
76     switch ( my_it->type ) {
77     case UserByteType:
78       {
79 	char the_byte = my_it->userbyte.c;
80 	/* can we combine this with a previous Keystroke? */
81 	if ( (output.instruction_size() > 0)
82 	     && (output.instruction( output.instruction_size() - 1 ).HasExtension( keystroke )) ) {
83 	  output.mutable_instruction( output.instruction_size() - 1 )->MutableExtension( keystroke )->mutable_keys()->append( string( &the_byte, 1 ) );
84 	} else {
85 	  Instruction *new_inst = output.add_instruction();
86 	  new_inst->MutableExtension( keystroke )->set_keys( &the_byte, 1 );
87 	}
88       }
89       break;
90     case ResizeType:
91       {
92 	Instruction *new_inst = output.add_instruction();
93 	new_inst->MutableExtension( resize )->set_width( my_it->resize.width );
94 	new_inst->MutableExtension( resize )->set_height( my_it->resize.height );
95       }
96       break;
97     default:
98       assert( false );
99       break;
100     }
101 
102     my_it++;
103   }
104 
105   return output.SerializeAsString();
106 }
107 
apply_string(const string & diff)108 void UserStream::apply_string( const string &diff )
109 {
110   ClientBuffers::UserMessage input;
111   fatal_assert( input.ParseFromString( diff ) );
112 
113   for ( int i = 0; i < input.instruction_size(); i++ ) {
114     if ( input.instruction( i ).HasExtension( keystroke ) ) {
115       string the_bytes = input.instruction( i ).GetExtension( keystroke ).keys();
116       for ( unsigned int loc = 0; loc < the_bytes.size(); loc++ ) {
117 	actions.push_back( UserEvent( UserByte( the_bytes.at( loc ) ) ) );
118       }
119     } else if ( input.instruction( i ).HasExtension( resize ) ) {
120       actions.push_back( UserEvent( Resize( input.instruction( i ).GetExtension( resize ).width(),
121 					    input.instruction( i ).GetExtension( resize ).height() ) ) );
122     }
123   }
124 }
125 
get_action(unsigned int i) const126 const Parser::Action *UserStream::get_action( unsigned int i ) const
127 {
128   switch( actions[ i ].type ) {
129   case UserByteType:
130     return &( actions[ i ].userbyte );
131   case ResizeType:
132     return &( actions[ i ].resize );
133   default:
134     assert( false );
135     return NULL;
136   }
137 }
138