1 //////////////////////////////////////////////////////////////////////
2 //
3 // BeeBEEP Copyright (C) 2010-2021 Marco Mastroddi
4 //
5 // BeeBEEP is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published
7 // by the Free Software Foundation, either version 3 of the License,
8 // or (at your option) any later version.
9 //
10 // BeeBEEP 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 BeeBEEP. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // Author: Marco Mastroddi <marco.mastroddi(AT)gmail.com>
19 //
20 // $Id: Protocol.cpp 1461 2020-12-31 11:33:22Z mastroddi $
21 //
22 //////////////////////////////////////////////////////////////////////
23 
24 
25 #include "BeeUtils.h"
26 #include "ChatManager.h"
27 #include "ColorManager.h"
28 
29 #include "EmoticonManager.h"
30 #include "PluginManager.h"
31 #include "Protocol.h"
32 #include "Random.h"
33 #include "Rijndael.h"
34 #include "Settings.h"
35 #include "UserManager.h"
36 
37 Protocol* Protocol::mp_instance = Q_NULLPTR;
38 const QChar PROTOCOL_FIELD_SEPARATOR = QChar::ParagraphSeparator;  // 0x2029
39 const QChar DATA_FIELD_SEPARATOR = QChar::LineSeparator; // 0x2028
40 
Protocol()41 Protocol::Protocol()
42   : m_id( ID_START ), m_fileShareListMessage( Message::Share, ID_SHARE_MESSAGE, "" )
43 {
44   m_id += static_cast<VNumber>(Random::d100());
45 #if QT_VERSION == 0x050603 && defined Q_OS_MAC
46   // Fixed a protocol bug in OsX Legacy version
47   m_datastreamMaxVersion = 12;
48 #else
49   QDataStream ds;
50   m_datastreamMaxVersion = ds.version();
51 #endif
52   qDebug() << "Protocol has detected latest datastream version:" << m_datastreamMaxVersion;
53 }
54 
messageHeader(Message::Type mt) const55 QString Protocol::messageHeader( Message::Type mt ) const
56 {
57   switch( mt )
58   {
59   case Message::Beep:     return "BEE-BEEP";
60   case Message::Ping:     return "BEE-PING";
61   case Message::Pong:     return "BEE-PONG";
62   case Message::Chat:     return "BEE-CHAT";
63   case Message::Buzz:     return "BEE-BUZZ";
64   case Message::Hello:    return "BEE-CIAO";
65   case Message::System:   return "BEE-SYST";
66   case Message::User:     return "BEE-USER";
67   case Message::File:     return "BEE-FILE";
68   case Message::Share:    return "BEE-FSHR";
69   case Message::Group:    return "BEE-GROU";
70   case Message::Folder:   return "BEE-FOLD";
71   case Message::Read:     return "BEE-READ";
72   case Message::Hive:     return "BEE-HIVE";
73   case Message::ShareBox: return "BEE-SBOX";
74   case Message::ShareDesktop : return "BEE-DESK";
75   case Message::Test:     return "BEE-TEST";
76   default:                return "BEE-BOOH";
77   }
78 }
79 
messageType(const QString & msg_type) const80 Message::Type Protocol::messageType( const QString& msg_type ) const
81 {
82   if( msg_type == "BEE-BEEP" )
83     return Message::Beep;
84   else if( msg_type == "BEE-PING" )
85     return Message::Ping;
86   else if( msg_type == "BEE-PONG" )
87     return Message::Pong;
88   else if( msg_type == "BEE-USER")
89     return Message::User;
90   else if( msg_type == "BEE-CHAT")
91     return Message::Chat;
92   else if( msg_type == "BEE-READ" )
93     return Message::Read;
94   else if( msg_type == "BEE-BUZZ" )
95     return Message::Buzz;
96   else if( msg_type == "BEE-CIAO")
97     return Message::Hello;
98   else if( msg_type == "BEE-SYST")
99     return Message::System;
100   else if( msg_type == "BEE-FILE" )
101     return Message::File;
102   else if( msg_type == "BEE-FSHR" )
103     return Message::Share;
104   else if( msg_type == "BEE-GROU" )
105     return Message::Group;
106   else if( msg_type == "BEE-FOLD" )
107     return Message::Folder;
108   else if( msg_type == "BEE-SBOX" )
109     return Message::ShareBox;
110   else if( msg_type == "BEE-HIVE" )
111     return Message::Hive;
112   else if( msg_type == "BEE-DESK" )
113     return Message::ShareDesktop;
114   else if( msg_type == "BEE-TEST" )
115     return Message::Test;
116   else
117     return Message::Undefined;
118 }
119 
fromMessage(const Message & m,int proto_version) const120 QByteArray Protocol::fromMessage( const Message& m, int proto_version ) const
121 {
122   if( !m.isValid() )
123     return "";
124   QStringList sl;
125   sl << messageHeader( m.type() );
126   sl << QString::number( m.id() );
127   sl << QString::number( m.text().size() );
128   sl << QString::number( m.flags() );
129   sl << m.data();
130   if( proto_version < UTC_TIMESTAMP_PROTO_VERSION )
131     sl << m.timestamp().toString( Qt::ISODate );
132   else
133     sl << m.timestamp().toUTC().toString( Qt::ISODate );
134   sl << m.text();
135   QByteArray byte_array = sl.join( PROTOCOL_FIELD_SEPARATOR ).toUtf8();
136   while( byte_array.size() % ENCRYPTED_DATA_BLOCK_SIZE )
137     byte_array.append( ' ' );
138   return byte_array;
139 }
140 
toMessage(const QByteArray & byte_array_data,int proto_version) const141 Message Protocol::toMessage( const QByteArray& byte_array_data, int proto_version ) const
142 {
143   QString message_data = QString::fromUtf8( byte_array_data );
144   Message m;
145   QStringList sl = message_data.split( PROTOCOL_FIELD_SEPARATOR, QString::KeepEmptyParts );
146   if( sl.size() < 7 )
147   {
148     if( !Settings::instance().disableConnectionSocketEncryption() )
149       qWarning() << "Invalid number of fields in message:" << message_data.simplified();
150     return m;
151   }
152 
153   m.setType( messageType( sl.takeFirst() ) );
154   if( !m.isValid() )
155   {
156     qWarning() << "Invalid message type:" << message_data.simplified();
157     return m;
158   }
159 
160   VNumber msg_id = Bee::qVariantToVNumber( sl.takeFirst() );
161   if( msg_id == ID_INVALID )
162   {
163     qWarning() << "Invalid message id:" << message_data.simplified();
164     m.setType( Message::Undefined );
165     return m;
166   }
167   m.setId( msg_id );
168 
169   bool ok = false;
170   int msg_size = sl.takeFirst().toInt( &ok );
171   if( !ok )
172   {
173     qWarning() << "Invalid message size:" << message_data.simplified();
174     m.setType( Message::Undefined );
175     return m;
176   }
177 
178   int msg_flags = sl.takeFirst().toInt( &ok );
179   if( !ok )
180   {
181     qWarning() << "Invalid message flags:" << message_data.simplified();
182     m.setType( Message::Undefined );
183     return m;
184   }
185   m.setFlags( msg_flags );
186 
187   m.setData( sl.takeFirst() );
188 
189   QDateTime dt_timestamp = QDateTime::fromString( sl.takeFirst(), Qt::ISODate );
190   if( !dt_timestamp.isValid() )
191   {
192     qWarning() << "Invalid message timestamp:" << message_data.simplified();
193     m.setType( Message::Undefined );
194     return m;
195   }
196   else
197   {
198     if( proto_version < UTC_TIMESTAMP_PROTO_VERSION )
199     {
200       m.setTimestamp( dt_timestamp.toLocalTime() );
201     }
202     else
203     {
204       dt_timestamp.setTimeSpec( Qt::UTC );
205       m.setTimestamp( dt_timestamp.toLocalTime() );
206     }
207   }
208 
209   QString msg_txt;
210   if( sl.size() > 1 )
211     msg_txt = sl.join( PROTOCOL_FIELD_SEPARATOR );
212   else if( sl.size() == 1 )
213     msg_txt = sl.first();
214   else
215     msg_txt = "";
216 
217   if( msg_txt.size() > msg_size )
218     msg_txt.resize( msg_size ); // to prevent spaces added for encryption
219   m.setText( msg_txt );
220 
221   return m;
222 }
223 
testQuestionMessage(const NetworkAddress & na) const224 QByteArray Protocol::testQuestionMessage( const NetworkAddress& na ) const
225 {
226   Message m( Message::Test, ID_TEST_MESSAGE, "?" );
227   m.addFlag( Message::Private );
228   m.addFlag( Message::Request );
229   QStringList sl_data;
230   sl_data << na.toString();
231   m.setData( sl_data.join( DATA_FIELD_SEPARATOR ) );
232   return fromMessage( m, 1 );
233 }
234 
isTestQuestionMessage(const Message & m) const235 bool Protocol::isTestQuestionMessage( const Message& m ) const
236 {
237   return m.type() == Message::Test && m.hasFlag( Message::Private ) && m.hasFlag( Message::Request );
238 }
239 
testAnswerMessage(const NetworkAddress & na,bool test_is_accepted,const QString & answer_msg) const240 QByteArray Protocol::testAnswerMessage( const NetworkAddress& na, bool test_is_accepted, const QString& answer_msg ) const
241 {
242   Message m( Message::Test, ID_TEST_MESSAGE, answer_msg );
243   m.addFlag( Message::Private );
244   m.addFlag( Message::Auto );
245   if( !test_is_accepted )
246     m.addFlag( Message::Refused );
247   QStringList sl_data;
248   sl_data << na.toString();
249   m.setData( sl_data.join( DATA_FIELD_SEPARATOR ) );
250   return fromMessage( m, 1 );
251 }
252 
isTestAnswerMessage(const Message & m) const253 bool Protocol::isTestAnswerMessage( const Message& m ) const
254 {
255   return m.type() == Message::Test && m.hasFlag( Message::Private ) && m.hasFlag( Message::Auto );
256 }
257 
networkAddressFromTestMessage(const Message & m) const258 NetworkAddress Protocol::networkAddressFromTestMessage( const Message& m ) const
259 {
260   NetworkAddress na;
261   if( m.data().isEmpty() )
262     return na;
263 
264   QStringList sl_data = m.data().split( DATA_FIELD_SEPARATOR );
265   if( sl_data.isEmpty() )
266     return na;
267   return NetworkAddress::fromString( sl_data.at( 0 ) );
268 }
269 
pingMessage() const270 QByteArray Protocol::pingMessage() const
271 {
272   Message m( Message::Ping, ID_PING_MESSAGE, "*" );
273   return fromMessage( m, 1 );
274 }
275 
pongMessage() const276 QByteArray Protocol::pongMessage() const
277 {
278   Message m( Message::Pong, ID_PONG_MESSAGE, "*" );
279   return fromMessage( m, 1 );
280 }
281 
broadcastMessage(const QHostAddress & to_host_address) const282 QByteArray Protocol::broadcastMessage( const QHostAddress& to_host_address ) const
283 {
284   Message m( Message::Beep, ID_BEEP_MESSAGE, QString::number( Settings::instance().localUser().networkAddress().hostPort() ) );
285   QStringList sl;
286   sl << to_host_address.toString();
287   m.setData( sl.join( DATA_FIELD_SEPARATOR ) );
288   return fromMessage( m, 1 );
289 }
290 
hostAddressFromBroadcastMessage(const Message & m) const291 QHostAddress Protocol::hostAddressFromBroadcastMessage( const Message& m ) const
292 {
293   QHostAddress host_address;
294 
295   if( m.data().isEmpty() )
296     return host_address;
297 
298   QStringList sl = m.data().split( DATA_FIELD_SEPARATOR );
299 
300   if( !sl.isEmpty() )
301     host_address = QHostAddress( sl.takeFirst() );
302 
303   return host_address;
304 }
305 
protocolVersion(const Message & m) const306 int Protocol::protocolVersion( const Message& m ) const
307 {
308   int proto_version = static_cast<int>( m.id() <= ID_HELLO_MESSAGE ? 1 : m.id() );
309   return proto_version;
310 }
311 
publicKey(const Message & m) const312 QString Protocol::publicKey( const Message& m ) const
313 {
314   QStringList data_list = m.text().split( DATA_FIELD_SEPARATOR );
315   return data_list.size() >= 6 ? data_list.at( 5 ) : QString();
316 }
317 
datastreamVersion(const Message & m) const318 int Protocol::datastreamVersion( const Message& m ) const
319 {
320   QStringList data_list = m.text().split( DATA_FIELD_SEPARATOR );
321 
322   int datastream_version = 0;
323   if( data_list.size() >= 12 )
324   {
325     bool ok = false;
326     datastream_version = data_list.at( 11 ).toInt( &ok );
327     if( !ok )
328       datastream_version = 0;
329   }
330 
331   return datastream_version;
332 }
333 
helloMessage(const QString & public_key,bool encrypted_connection,bool data_compressed) const334 QByteArray Protocol::helloMessage( const QString& public_key, bool encrypted_connection, bool data_compressed ) const
335 {
336   QStringList data_list;
337   data_list << QString::number( Settings::instance().localUser().networkAddress().hostPort() );
338   data_list << Settings::instance().localUser().name();
339   data_list << QString::number( Settings::instance().localUser().status() );
340   data_list << Settings::instance().localUser().statusDescription();
341   data_list << Settings::instance().localUser().accountName();
342   data_list << public_key;
343   data_list << Settings::instance().version( false, false, false );
344   data_list << Settings::instance().localUser().hash();
345   data_list << Settings::instance().localUser().color();
346   if( Settings::instance().localUser().workgroups().isEmpty() )
347     data_list << QString( "" );
348   else
349     data_list << Settings::instance().localUser().workgroups().join( ", " );
350   data_list << Settings::instance().localUser().qtVersion();
351   data_list << QString::number( m_datastreamMaxVersion );
352   if( Settings::instance().localUser().statusChangedIn().isValid() )
353     data_list << Settings::instance().localUser().statusChangedIn().toString( Qt::ISODate );
354   else
355     data_list << QString( "" );
356   data_list << Settings::instance().localUser().domainName();
357   data_list << Settings::instance().localUser().localHostName();
358   Message m( Message::Hello, static_cast<VNumber>(Settings::instance().protocolVersion()), data_list.join( DATA_FIELD_SEPARATOR ) );
359   if( !encrypted_connection )
360     m.addFlag( Message::EncryptionDisabled );
361   if( data_compressed )
362     m.addFlag( Message::Compressed );
363   m.setData( Settings::instance().currentHash() );
364   return fromMessage( m, 1 );
365 }
366 
writingMessage(const QString & chat_private_id) const367 Message Protocol::writingMessage( const QString& chat_private_id ) const
368 {
369   Message writing_message( Message::User, ID_WRITING_MESSAGE, "*" );
370   writing_message.addFlag( Message::Private );
371   writing_message.addFlag( Message::UserWriting );
372   if( !chat_private_id.isEmpty() )
373     writing_message.setData( chat_private_id );
374   return writing_message;
375 }
376 
userStatusMessage(int user_status,const QString & user_status_description) const377 Message Protocol::userStatusMessage( int user_status, const QString& user_status_description ) const
378 {
379   Message m( Message::User, ID_USER_MESSAGE, user_status_description );
380   m.addFlag( Message::UserStatus );
381   m.setData( QString::number( user_status ) );
382   return m;
383 }
384 
changeUserStatusFromMessage(User * u,const Message & m) const385 bool Protocol::changeUserStatusFromMessage( User* u, const Message& m ) const
386 {
387   int user_status = m.data().toInt();
388   QString user_status_description = m.text();
389   bool status_changed = false;
390   if( u->status() != user_status  )
391   {
392     status_changed = true;
393     u->setStatus( user_status );
394     u->setStatusChangedIn( QDateTime::currentDateTime() );
395   }
396 
397   if( u->statusDescription() != user_status_description )
398   {
399     status_changed = true;
400     u->setStatusDescription( user_status_description );
401   }
402 
403   return status_changed;
404 }
405 
pixmapToString(const QPixmap & pix) const406 QString Protocol::pixmapToString( const QPixmap& pix ) const
407 {
408   if( pix.isNull() )
409     return "";
410   QByteArray byte_array;
411   QBuffer buf( &byte_array );
412   buf.open( QIODevice::WriteOnly );
413   pix.save( &buf, "PNG" );
414   return QString( byte_array.toBase64() );
415 }
416 
stringToPixmap(const QString & s) const417 QPixmap Protocol::stringToPixmap( const QString& s ) const
418 {
419   QPixmap pix;
420   if( s.isEmpty() )
421     return pix;
422   QByteArray byte_array = QByteArray::fromBase64( s.toLatin1() ); // base64 uses latin1 chars
423   pix.loadFromData( byte_array, "PNG" );
424   return pix;
425 }
426 
localVCardMessage() const427 Message Protocol::localVCardMessage() const
428 {
429   const VCard& vc = Settings::instance().localUser().vCard();
430   Message m( Message::User, ID_USER_MESSAGE, pixmapToString( vc.photo() ) );
431   m.addFlag( Message::UserVCard );
432   QStringList data_list;
433   data_list << vc.nickName();
434   data_list << vc.firstName();
435   data_list << vc.lastName();
436   data_list << vc.birthday().toString( Qt::ISODate );
437   data_list << vc.email();
438   data_list << Settings::instance().localUser().color();
439   data_list << vc.phoneNumber();
440   data_list << vc.info();
441   m.setData( data_list.join( DATA_FIELD_SEPARATOR ) );
442   return m;
443 }
444 
changeVCardFromMessage(User * u,const Message & m) const445 bool Protocol::changeVCardFromMessage( User* u, const Message& m ) const
446 {
447   QStringList sl = m.data().split( DATA_FIELD_SEPARATOR );
448   if( sl.size() < 5 )
449     return false;
450 
451   VCard vc;
452   vc.setNickName( sl.takeFirst() );
453   vc.setFirstName( sl.takeFirst() );
454   vc.setLastName( sl.takeFirst() );
455   vc.setBirthday( QDate::fromString( sl.takeFirst(), Qt::ISODate ) );
456   vc.setEmail( sl.takeFirst() );
457   vc.setPhoto( stringToPixmap( m.text() ) );
458 
459   if( !sl.isEmpty() )
460   {
461     QString user_color = sl.takeFirst();
462     if( ColorManager::instance().isValidColor( user_color ) )
463     {
464       u->setColor( user_color );
465       ColorManager::instance().setColorSelected( user_color );
466     }
467   }
468 
469   if( !sl.isEmpty() )
470     vc.setPhoneNumber( sl.takeFirst() );
471 
472   if( !sl.isEmpty()  )
473     vc.setInfo( sl.takeFirst() );
474 
475   u->setVCard( vc );
476 
477   return true;
478 }
479 
workgroupsFromHelloMessage(const Message & hello_message) const480 QStringList Protocol::workgroupsFromHelloMessage( const Message& hello_message ) const
481 {
482   QStringList sl = hello_message.text().split( DATA_FIELD_SEPARATOR );
483   if( sl.size() < 10 )
484     return QStringList();
485 
486   QString workgroups = sl.at( 9 );
487   if( workgroups.isEmpty() )
488     return QStringList();
489   else
490     return workgroups.split( ", ", QString::SkipEmptyParts );
491 }
492 
acceptConnectionFromWorkgroup(const Message & hello_message) const493 bool Protocol::acceptConnectionFromWorkgroup( const Message& hello_message ) const
494 {
495   if( Settings::instance().localUser().workgroups().isEmpty() )
496     return true;
497 
498   QStringList workgroups = workgroupsFromHelloMessage( hello_message );
499   if( workgroups.isEmpty() )
500     return Settings::instance().localUser().workgroups().isEmpty();
501 
502   foreach( QString workgroup, workgroups )
503   {
504     if( Settings::instance().localUser().workgroups().contains( workgroup, Qt::CaseInsensitive ) )
505       return true;
506   }
507   return false;
508 }
509 
recognizeUser(const User & u,int user_recognition_method) const510 User Protocol::recognizeUser( const User& u, int user_recognition_method ) const
511 {
512   User user_found;
513   if( user_recognition_method == Settings::RecognizeByAccountAndDomain )
514   {
515     user_found = UserManager::instance().findUserByAccountNameAndDomainName( u.accountName(), u.domainName() );
516     if( !user_found.isValid() )
517       qDebug() << "User not found in list with account path" << qPrintable( u.accountPath() );
518   }
519   else if( user_recognition_method == Settings::RecognizeByAccount )
520   {
521     user_found = UserManager::instance().findUserByAccountName( u.accountName() );
522     if( !user_found.isValid() )
523       qDebug() << "User not found in list with account name" << qPrintable( u.accountName() );
524   }
525   else if( user_recognition_method == Settings::RecognizeByNickname )
526   {
527     user_found = UserManager::instance().findUserByNickname( u.name() );
528     if( !user_found.isValid() )
529     {
530       user_found = UserManager::instance().findUserByHash( u.hash() );
531       if( !user_found.isValid() )
532         qDebug() << "User not found in list with nickname" << qPrintable( u.name() ) << "and hash" << qPrintable( u.hash() );
533     }
534   }
535   else
536     qWarning() << "Invalid user recognition method found" << user_recognition_method;
537 
538   return user_found;
539 }
540 
recognizeUser(const UserRecord & ur,int user_recognition_method) const541 User Protocol::recognizeUser( const UserRecord& ur, int user_recognition_method ) const
542 {
543   User user_tmp( ID_INVALID, ur );
544   return recognizeUser( user_tmp, user_recognition_method );
545 }
546 
createUser(const Message & hello_message,const QHostAddress & peer_address)547 User Protocol::createUser( const Message& hello_message, const QHostAddress& peer_address )
548 {
549   /* Read User Field Data */
550   QStringList sl = hello_message.text().split( DATA_FIELD_SEPARATOR );
551   bool ok = false;
552 
553   if( sl.size() < 4 )
554   {
555     qWarning() << "HELLO message has not 4 (at least) field data but" << sl.size();
556     qWarning() << "Skip this HELLO:" << sl.join( DATA_FIELD_SEPARATOR );
557     return User();
558   }
559 
560   int listener_port = sl.takeFirst().toInt( &ok );
561   if( !ok )
562   {
563     qWarning() << "HELLO has an invalid Listener port";
564     return User();
565   }
566 
567   QString user_name = sl.takeFirst();
568   /* Auth */
569   if( hello_message.data().toUtf8() != Settings::instance().hash( user_name ) )
570   {
571     qWarning() << "HELLO message has an invalid password";
572     return User();
573   }
574 
575   int user_status = sl.takeFirst().toInt( &ok );
576   if( !ok )
577     user_status = User::Online;
578 
579   if( user_status == User::Offline )
580   {
581     qWarning() << "HELLO message has an user in offline status but it will be changed";
582     user_status = User::Online;
583   }
584 
585   QString user_status_description = sl.takeFirst();
586 
587   QString user_account_name = "";
588   if( !sl.isEmpty() )
589     user_account_name = sl.takeFirst();
590 
591   // skip public_key at 5
592   if( !sl.isEmpty() )
593     sl.takeFirst();
594 
595   QString user_version = "";
596   if( !sl.isEmpty() )
597     user_version = sl.takeFirst();
598 
599   QString user_hash = "";
600   if( !sl.isEmpty() )
601     user_hash = sl.takeFirst();
602 
603   QString user_color( "#000000" );
604   if( !sl.isEmpty() )
605     user_color = sl.takeFirst();
606 
607   QStringList user_workgroups;
608   if( !sl.isEmpty() )
609   {
610     QString s_workgroups = sl.takeFirst();
611     if( !s_workgroups.isEmpty()  )
612     {
613       QStringList user_workgroups_tmp = s_workgroups.split( ", ", QString::SkipEmptyParts );
614       if( Settings::instance().acceptConnectionsOnlyFromWorkgroups() && !Settings::instance().localUser().workgroups().isEmpty() )
615       {
616         foreach( QString user_workgroup, user_workgroups_tmp )
617         {
618           // User can see only his/her workgroups
619           if( Settings::instance().localUser().workgroups().contains( user_workgroup, Qt::CaseInsensitive ) )
620             user_workgroups.append( user_workgroup );
621         }
622       }
623       else
624         user_workgroups = s_workgroups.split( ", ", QString::SkipEmptyParts );
625     }
626   }
627 
628   QString user_qt_version = Settings::instance().localUser().qtVersion();
629   if( !sl.isEmpty() )
630     user_qt_version = sl.takeFirst();
631 
632   /* Skip datastream version */
633   if( !sl.isEmpty() )
634     sl.takeFirst();
635 
636   QDateTime status_changed_in = QDateTime::currentDateTime();
637   if( !sl.isEmpty() )
638   {
639     /* User status changed in datetime */
640     QString s_datetime = sl.takeFirst();
641     if( !s_datetime.isEmpty() )
642       status_changed_in = QDateTime::fromString( s_datetime, Qt::ISODate );
643   }
644 
645   QString user_domain_name = "";
646   if( !sl.isEmpty() )
647     user_domain_name = sl.takeFirst();
648 
649   QString user_local_host_name = "";
650   if( !sl.isEmpty() )
651     user_local_host_name = sl.takeFirst();
652 
653   /* Create User */
654   User u( newId() );
655   u.setName( user_name );
656   u.setNetworkAddress( NetworkAddress( peer_address, static_cast<quint16>(listener_port) ) );
657   u.setStatus( user_status );
658   u.setStatusChangedIn( status_changed_in );
659   u.setStatusDescription( user_status_description );
660   u.setAccountName( user_account_name.isEmpty() ? user_name : user_account_name );
661   u.setDomainName( user_domain_name );
662   u.setVersion( user_version );
663   u.setHash( user_hash.isEmpty() ? newMd5Id() : user_hash );
664   u.setColor( user_color );
665   u.setQtVersion( user_qt_version );
666   u.setWorkgroups( user_workgroups );
667   u.setLocalHostName( user_local_host_name );
668   return u;
669 }
670 
createTemporaryUser(const UserRecord & ur)671 User Protocol::createTemporaryUser( const UserRecord& ur )
672 {
673   User u( newId(), ur );
674   if( !ur.networkAddressIsValid() )
675     u.setNetworkAddress( NetworkAddress( QHostAddress::LocalHost, DEFAULT_LISTENER_PORT ) );
676   if( ur.account().isEmpty() )
677     u.setAccountName( u.name().toLower() );
678   if( ur.hash().isEmpty() )
679     u.setHash( newMd5Id() );
680   u.setStatus( User::Offline );
681   u.setIsFavorite( ur.isFavorite() );
682   VCard vc = u.vCard();
683   vc.setBirthday( ur.birthday() );
684   vc.setFirstName( ur.firstName() );
685   vc.setLastName( ur.lastName() );
686   vc.setEmail( ur.email() );
687   vc.setPhoneNumber( ur.phoneNumber() );
688   u.setVCard( vc );
689 #ifdef BEEBEEP_DEBUG
690   qDebug() << "Temporary user" << u.id() << qPrintable( u.path() ) << "created with account" << qPrintable( u.accountName() ) << "and hash" << qPrintable( u.hash() );
691 #endif
692   return u;
693 }
694 
saveUserRecord(const UserRecord & ur,bool add_extras) const695 QString Protocol::saveUserRecord( const UserRecord& ur, bool add_extras ) const
696 {
697   QStringList sl;
698   sl << ur.networkAddress().hostAddress().toString();
699   sl << QString::number( ur.networkAddress().hostPort() );
700   if( add_extras )
701   {
702     sl << ur.networkAddress().info();
703     sl << ur.name();
704     sl << ur.account();
705     if( ur.isFavorite() )
706       sl << QString( "*" );
707     else
708       sl << QString( "" );
709     sl << ur.color();
710     sl << ur.hash();
711     sl << ur.domainName();
712     sl << ur.lastConnection().toString( Qt::ISODate );
713     sl << ur.birthday().toString( Qt::ISODate );
714     sl << ur.firstName();
715     sl << ur.lastName();
716     sl << ur.email();
717     sl << ur.phoneNumber();
718     sl << ur.localHostName();
719   }
720   return sl.join( DATA_FIELD_SEPARATOR );
721 }
722 
loadUserRecord(const QString & s) const723 UserRecord Protocol::loadUserRecord( const QString& s ) const
724 {
725   QStringList sl = s.split( DATA_FIELD_SEPARATOR );
726   if( sl.size() < 2 )
727   {
728     qWarning() << "Invalid user record found in data:" << s << "(size error)";
729     return UserRecord();
730   }
731 
732   UserRecord ur;
733 
734   QHostAddress user_host_address = QHostAddress( sl.takeFirst() );
735   if( user_host_address.isNull() )
736   {
737     qWarning() << "Invalid user record found in data:" << s << "(host address error)";
738     return UserRecord();
739   }
740   bool ok = false;
741   int host_port = sl.takeFirst().toInt( &ok, 10 );
742   if( !ok || host_port < 1 || host_port > MAX_SOCKET_PORT )
743   {
744     qWarning() << "Invalid user record found in data:" << s << "(host port error)";
745     return UserRecord();
746   }
747 
748   NetworkAddress na( user_host_address, static_cast<quint16>(host_port) );
749 
750   if( !sl.isEmpty() )
751     na.setInfo( sl.takeFirst() );
752 
753   ur.setNetworkAddress( na );
754 
755   if( !sl.isEmpty() )
756     ur.setName( sl.takeFirst() );
757 
758   if( !sl.isEmpty() )
759     ur.setAccount( sl.takeFirst() );
760 
761   if( !sl.isEmpty() )
762   {
763     QString favorite_txt = sl.takeFirst();
764     if( favorite_txt == QString( "*" ) )
765     {
766  #ifdef BEEBEEP_DEBUG
767       qDebug() << "User" << qPrintable( ur.name() ) << "is in favorite list";
768  #endif
769       ur.setFavorite( true );
770     }
771   }
772 
773   if( !sl.isEmpty() )
774   {
775     ur.setColor( sl.takeFirst() );
776  #ifdef BEEBEEP_DEBUG
777     qDebug() << "User" << qPrintable( ur.name() ) << "has color saved:" << qPrintable( ur.color() );
778  #endif
779   }
780 
781   if( !sl.isEmpty() )
782   {
783     ur.setHash( sl.takeFirst() );
784  #ifdef BEEBEEP_DEBUG
785     qDebug() << "User" << qPrintable( ur.name() ) << "has hash saved:" << qPrintable( ur.hash() );
786  #endif
787   }
788 
789   if( !sl.isEmpty() )
790   {
791     ur.setDomainName( sl.takeFirst() );
792  #ifdef BEEBEEP_DEBUG
793     qDebug() << "User" << qPrintable( ur.name() ) << "has domain name saved:" << qPrintable( ur.domainName() );
794  #endif
795   }
796 
797   if( !sl.isEmpty() )
798   {
799     ur.setLastConnection( QDateTime::fromString( sl.takeFirst(), Qt::ISODate ) );
800     if( ur.lastConnection().isValid() )
801     {
802 #ifdef BEEBEEP_DEBUG
803       qDebug() << "User" << qPrintable( ur.name() ) << "has last connection date saved:" << qPrintable( ur.lastConnection().toString( Qt::ISODate ) );
804 #endif
805     }
806   }
807 
808   if( !sl.isEmpty() )
809   {
810     ur.setBirthday( QDate::fromString( sl.takeFirst(), Qt::ISODate ) );
811     if( ur.birthday().isValid() )
812     {
813 #ifdef BEEBEEP_DEBUG
814       qDebug() << "User" << qPrintable( ur.name() ) << "has birthday saved:" << qPrintable( ur.birthday().toString( Qt::ISODate ) );
815 #endif
816     }
817   }
818 
819   if( !sl.isEmpty() )
820   {
821     ur.setFirstName( sl.takeFirst() );
822 #ifdef BEEBEEP_DEBUG
823     qDebug() << "User" << qPrintable( ur.name() ) << "has first name saved:" << qPrintable( ur.firstName() );
824 #endif
825   }
826 
827   if( !sl.isEmpty() )
828   {
829     ur.setLastName( sl.takeFirst() );
830 #ifdef BEEBEEP_DEBUG
831     qDebug() << "User" << qPrintable( ur.name() ) << "has last name saved:" << qPrintable( ur.lastName() );
832 #endif
833   }
834 
835   if( !sl.isEmpty() )
836   {
837     ur.setEmail( sl.takeFirst() );
838 #ifdef BEEBEEP_DEBUG
839     qDebug() << "User" << qPrintable( ur.name() ) << "has e-mail saved:" << qPrintable( ur.email() );
840 #endif
841   }
842 
843   if( !sl.isEmpty() )
844   {
845     ur.setPhoneNumber( sl.takeFirst() );
846 #ifdef BEEBEEP_DEBUG
847     qDebug() << "User" << qPrintable( ur.name() ) << "has phone number saved:" << qPrintable( ur.phoneNumber() );
848 #endif
849   }
850 
851   if( !sl.isEmpty() )
852   {
853     ur.setLocalHostName( sl.takeFirst() );
854 #ifdef BEEBEEP_DEBUG
855     qDebug() << "User" << qPrintable( ur.name() ) << "has local host name saved:" << qPrintable( ur.localHostName() );
856 #endif
857   }
858 
859   return ur;
860 }
861 
saveMessageRecord(const MessageRecord & mr) const862 QString Protocol::saveMessageRecord( const MessageRecord& mr ) const
863 {
864   QStringList sl_root;
865   User u = UserManager::instance().findUser( mr.toUserId() );
866   if( !u.isValid() )
867   {
868     qWarning() << "Unable to save unsent messages for invalid user id" << mr.toUserId();
869     return QString();
870   }
871   Chat c = ChatManager::instance().chat( mr.chatId() );
872   if( !c.isValid() )
873   {
874     qWarning() << "Unable to save unsent messages for invalid chat id" << mr.chatId();
875     return QString();
876   }
877 
878   QStringList sl_user;
879   sl_user << u.path();
880   sl_user << u.accountName();
881   sl_user << u.domainName();
882   sl_root.append( Settings::instance().simpleEncrypt( sl_user.join( DATA_FIELD_SEPARATOR ) ) );
883   QStringList sl_chat;
884   sl_chat << c.name();
885   sl_chat << c.privateId();
886   sl_root.append( Settings::instance().simpleEncrypt( sl_chat.join( DATA_FIELD_SEPARATOR ) ) );
887   QByteArray ba = fromMessage( mr.message(), Settings::instance().protocolVersion() );
888   sl_root.append( QString::fromLatin1( ba.toBase64() ) );
889   return sl_root.join( PROTOCOL_FIELD_SEPARATOR );
890 }
891 
loadMessageRecord(const QString & s) const892 MessageRecord Protocol::loadMessageRecord( const QString& s ) const
893 {
894   QStringList sl_root = s.split( PROTOCOL_FIELD_SEPARATOR );
895   if( sl_root.size() < 3 )
896   {
897     qWarning() << sl_root.size() << "is invalid message record data size";
898     return MessageRecord();
899   }
900 
901   QStringList sl_user = Settings::instance().simpleDecrypt( sl_root.at( 0 ) ).split( DATA_FIELD_SEPARATOR );
902   if( sl_user.isEmpty() )
903   {
904     qWarning() << "User data not found in message record";
905     return MessageRecord();
906   }
907 
908   if( sl_user.size() < 3 )
909   {
910     qWarning() << sl_user.size() << "is invalid user data found in message record";
911     return MessageRecord();
912   }
913 
914   User u;
915   if( Settings::instance().userRecognitionMethod() == Settings::RecognizeByAccountAndDomain )
916     u = UserManager::instance().findUserByAccountNameAndDomainName( sl_user.at( 1 ), sl_user.at( 2 ) );
917   else if( Settings::instance().userRecognitionMethod() == Settings::RecognizeByAccount )
918     u = UserManager::instance().findUserByAccountName( sl_user.at( 1 ) );
919   else
920     u = UserManager::instance().findUserByPath( sl_user.at( 0 ) );
921   if( !u.isValid() )
922   {
923     qWarning() << "User found in message record is not in your user list";
924     return MessageRecord();
925   }
926 
927   QStringList sl_chat = Settings::instance().simpleDecrypt( sl_root.at( 1 ) ).split( DATA_FIELD_SEPARATOR );
928   if( sl_chat.isEmpty() )
929   {
930     qWarning() << "Chat data not found in message record";
931     return MessageRecord();
932   }
933 
934   if( sl_chat.size() < 2 )
935   {
936     qWarning() << sl_chat.size() << "is invalid chat data found in message record";
937     return MessageRecord();
938   }
939 
940   Chat c = ChatManager::instance().findChatByPrivateId( sl_chat.at( 1 ), false, u.id() );
941   if( !c.isValid() )
942     c = ChatManager::instance().findChatByName( sl_chat.at( 0 ) );
943 
944   if( !c.isValid() )
945   {
946     qWarning() << "Chat found in message record is not in your chat list";
947     return MessageRecord();
948   }
949 
950   Message m = toMessage( QByteArray::fromBase64( sl_root.at( 2 ).toLatin1() ), Settings::instance().protocolVersion() );
951   if( !m.isValid() )
952   {
953     qWarning() << "Invalid message data found in message record";
954     return MessageRecord();
955   }
956 
957   return MessageRecord( u.id(), c.id(), m );
958 }
959 
saveUser(const User & u) const960 QString Protocol::saveUser( const User& u ) const
961 {
962   UserRecord ur;
963   ur.setName( u.name() );
964   ur.setAccount( u.accountName() );
965   ur.setNetworkAddress( u.networkAddress() );
966   ur.setFavorite( u.isFavorite() );
967   ur.setColor( u.color() );
968   ur.setHash( u.hash() );
969   ur.setDomainName( u.domainName() );
970   if( u.lastConnection().isValid() )
971     ur.setLastConnection( u.lastConnection() );
972   else
973     ur.setLastConnection( QDateTime::currentDateTime() );
974   ur.setBirthday( u.vCard().birthday() );
975   ur.setFirstName( u.vCard().firstName() );
976   ur.setLastName( u.vCard().lastName() );
977   ur.setEmail( u.vCard().email() );
978   ur.setPhoneNumber( u.vCard().phoneNumber() );
979   ur.setLocalHostName( u.localHostName() );
980   return saveUserRecord( ur, true );
981 }
982 
loadUser(const QString & s)983 User Protocol::loadUser( const QString& s )
984 {
985   UserRecord ur = loadUserRecord( s );
986   if( ur.name().isEmpty() || !ur.networkAddressIsValid() )
987     return User();
988 
989   User u = createTemporaryUser( ur );
990   if( !u.lastConnection().isValid() )
991     u.setLastConnection( QDateTime::currentDateTime() );
992   if( ColorManager::instance().isValidColor( ur.color() ) )
993     ColorManager::instance().setColorSelected( ur.color() );
994   return u;
995 }
996 
saveNetworkAddress(const NetworkAddress & na) const997 QString Protocol::saveNetworkAddress( const NetworkAddress& na ) const
998 {
999   QStringList sl;
1000   sl << na.hostAddress().toString();
1001   sl << QString::number( na.hostPort() );
1002   sl << na.info();
1003   return sl.join( DATA_FIELD_SEPARATOR );
1004 }
1005 
loadNetworkAddress(const QString & s) const1006 NetworkAddress Protocol::loadNetworkAddress( const QString& s ) const
1007 {
1008   QStringList sl = s.split( DATA_FIELD_SEPARATOR );
1009   if( sl.size() < 2 )
1010   {
1011     qWarning() << "Invalid network address found in data:" << s << "(size error)";
1012     return NetworkAddress();
1013   }
1014 
1015   QHostAddress user_host_address = QHostAddress( sl.takeFirst() );
1016   if( user_host_address.isNull() )
1017   {
1018     qWarning() << "Invalid network address found in data:" << s << "(host address error)";
1019     return NetworkAddress();
1020   }
1021 
1022   bool ok = false;
1023   int host_port = sl.takeFirst().toInt( &ok, 10 );
1024   if( !ok || host_port < 1 || host_port > MAX_SOCKET_PORT )
1025   {
1026     qWarning() << "Invalid network address found in data:" << s << "(host port error)";
1027     return NetworkAddress();
1028   }
1029 
1030   NetworkAddress na( user_host_address, static_cast<quint16>(host_port) );
1031   if( !sl.isEmpty() )
1032     na.setInfo( sl.takeFirst() );
1033 
1034   return na;
1035 }
1036 
saveUserStatusRecord(const UserStatusRecord & usr) const1037 QString Protocol::saveUserStatusRecord( const UserStatusRecord& usr ) const
1038 {
1039   QStringList sl;
1040   sl << QString::number( usr.status() );
1041   sl << usr.statusDescription();
1042   return sl.join( DATA_FIELD_SEPARATOR );
1043 }
1044 
loadUserStatusRecord(const QString & s) const1045 UserStatusRecord Protocol::loadUserStatusRecord( const QString& s ) const
1046 {
1047   QStringList sl = s.split( DATA_FIELD_SEPARATOR );
1048   if( sl.size() < 2 )
1049   {
1050     qWarning() << "Invalid user status record found in data:" << s << "(size error)";
1051     return UserStatusRecord();
1052   }
1053 
1054   bool ok = false;
1055   int user_status = 0;
1056   QString user_status_desc = "";
1057 
1058   user_status = sl.takeFirst().toInt( &ok );
1059   if( !ok || user_status < 0 || user_status >= User::NumStatus )
1060   {
1061     qWarning() << "Invalid user status record found in data:" << s << "(status error)";
1062     return UserStatusRecord();
1063   }
1064 
1065   user_status_desc = sl.takeFirst();
1066 
1067   UserStatusRecord usr;
1068   usr.setStatus( user_status );
1069   usr.setStatusDescription( user_status_desc );
1070   return usr;
1071 }
1072 
createDefaultChat()1073 Chat Protocol::createDefaultChat()
1074 {
1075   Group g;
1076   g.setId( ID_DEFAULT_CHAT );
1077   g.setName( Settings::instance().defaultChatName() );
1078   g.setPrivateId( Settings::instance().defaultChatPrivateId() );
1079   return createChat( g, Group::DefaultChat );
1080 }
1081 
createPrivateChat(const User & u)1082 Chat Protocol::createPrivateChat( const User& u )
1083 {
1084   Group g;
1085   g.setName( u.name() );
1086   g.addUser( u.id() );
1087   return createChat( g, Group::PrivateChat );
1088 }
1089 
createChat(const Group & g,Group::ChatType chat_type)1090 Chat Protocol::createChat( const Group& g, Group::ChatType chat_type )
1091 {
1092   Group g_to_check = g;
1093   if( g_to_check.chatType() != chat_type )
1094     g_to_check.setChatType( chat_type );
1095   if( !g_to_check.isValid() )
1096     g_to_check.setId( newId() );
1097   if( g_to_check.chatType() == Group::GroupChat && g_to_check.privateId().isEmpty() )
1098     g_to_check.setPrivateId( newMd5Id() );
1099   g_to_check.addUser( ID_LOCAL_USER );
1100   Chat c;
1101   c.setGroup( g_to_check );
1102   return c;
1103 }
1104 
saveGroup(const Group & g) const1105 QString Protocol::saveGroup( const Group& g ) const
1106 {
1107   QStringList sl;
1108   sl << g.name();
1109   sl << g.privateId();
1110 
1111   UserList ul = UserManager::instance().userList().fromUsersId( g.usersId() );
1112   ul.remove( Settings::instance().localUser() );
1113   sl << QString::number( ul.toList().size() );
1114 
1115   foreach( User u, ul.toList() )
1116   {
1117     sl << u.name();
1118     sl << u.accountName();
1119     sl << u.hash();
1120     sl << u.domainName();
1121     sl << ""; // for future use
1122   }
1123 
1124   sl << g.lastModified().toString( Qt::ISODate );
1125 
1126   return sl.join( DATA_FIELD_SEPARATOR );
1127 }
1128 
loadGroup(const QString & group_data_saved)1129 Group Protocol::loadGroup( const QString& group_data_saved )
1130 {
1131   QStringList sl = group_data_saved.split( DATA_FIELD_SEPARATOR, QString::KeepEmptyParts );
1132   if( sl.size() < 5 )
1133     return Group();
1134 
1135   Group g;
1136   g.setId( newId() );
1137   g.setName( sl.takeFirst() );
1138   g.setPrivateId( sl.takeFirst() );
1139   bool ok = false;
1140   int members = sl.takeFirst().toInt( &ok );
1141   if( !ok )
1142     return Group();
1143 
1144   UserList member_list;
1145   QString user_nickname = "";
1146   QString user_account_name = "";
1147   QString user_hash = "";
1148   QString user_domain = "";
1149 
1150   member_list.set( Settings::instance().localUser() );
1151 
1152   bool read_user_extra_fields = sl.size() > (members*2);
1153 
1154   for( int i = 0; i < members; i++ )
1155   {
1156     if( sl.size() >= 2 )
1157     {
1158       user_nickname = User::nameFromPath( sl.takeFirst() );
1159       user_account_name = sl.takeFirst();
1160       user_hash = "";
1161       user_domain = "";
1162 
1163       if( read_user_extra_fields )
1164       {
1165         if( !sl.isEmpty() )
1166           user_hash = sl.takeFirst();
1167 
1168         if( !sl.isEmpty() )
1169           user_domain = sl.takeFirst();
1170 
1171         if( !sl.isEmpty() )
1172           sl.takeFirst(); // for future use
1173       }
1174 
1175       UserRecord ur( user_nickname, user_account_name, user_hash, user_domain );
1176       ur.setDomainName( user_domain );
1177       User u = recognizeUser( ur, Settings::instance().userRecognitionMethod() );
1178 
1179       if( !u.isValid() )
1180       {
1181         u = createTemporaryUser( ur );
1182         qDebug() << "Group chat" << qPrintable( g.name() ) << "has created temporary user" << u.id() << qPrintable( u.name() );
1183         UserManager::instance().setUser( u );
1184       }
1185 
1186       member_list.set( u );
1187     }
1188   }
1189 
1190   g.setUsers( member_list.toUsersId() );
1191 
1192   if( !sl.isEmpty() )
1193   {
1194     g.setLastModified( QDateTime::fromString( sl.takeFirst(), Qt::ISODate ) );
1195 #ifdef BEEBEEP_DEBUG
1196     qDebug() << "Group chat" << qPrintable( g.name() ) << "has last modified field" << qPrintable( g.lastModified().toString( Qt::ISODate ) );
1197 #endif
1198   }
1199 
1200   g.setChatType( Group::GroupChat );
1201   return g;
1202 }
1203 
groupChatRefuseMessage(const Chat & c)1204 Message Protocol::groupChatRefuseMessage( const Chat& c )
1205 {
1206   ChatMessageData cmd;
1207   cmd.setGroupId( c.privateId() );
1208   cmd.setGroupName( c.name() );
1209   return groupChatRefuseMessage( cmd );
1210 }
1211 
groupChatRefuseMessage(const ChatMessageData & cmd)1212 Message Protocol::groupChatRefuseMessage( const ChatMessageData& cmd )
1213 {
1214   Message m( Message::Group, newId(), "" );
1215   m.addFlag( Message::Refused );
1216   m.setData( chatMessageDataToString( cmd ) );
1217   return m;
1218 }
1219 
groupChatRequestMessage(const Chat & c,const User & to_user)1220 Message Protocol::groupChatRequestMessage( const Chat& c, const User& to_user )
1221 {
1222   Message m( Message::Group, newId(), "" );
1223   m.addFlag( Message::Request );
1224   UserList ul = UserManager::instance().userList().fromUsersId( c.usersId() );
1225   ul.remove( Settings::instance().localUser() );
1226   ul.remove( to_user );
1227 
1228   QStringList sl;
1229   sl << QString::number( ul.size() );
1230   foreach( User u, ul.toList() )
1231   {
1232     sl << u.name();
1233     sl << u.accountName();
1234     sl << u.hash();
1235     sl << u.domainName();
1236   }
1237 
1238   if( ul.size() >= 1 )
1239     m.setText( sl.join( PROTOCOL_FIELD_SEPARATOR ) );
1240 
1241   ChatMessageData cmd;
1242   cmd.setGroupId( c.privateId() );
1243   cmd.setGroupName( c.name() );
1244   cmd.setGroupLastModified( c.lastModified() );
1245   m.setData( chatMessageDataToString( cmd ) );
1246   return m;
1247 }
1248 
groupChatRemoveUserMessage(const Chat & c)1249 Message Protocol::groupChatRemoveUserMessage( const Chat& c )
1250 {
1251   Message m( Message::Group, newId(), "" );
1252   m.addFlag( Message::Delete );
1253   ChatMessageData cmd;
1254   cmd.setGroupId( c.privateId() );
1255   cmd.setGroupName( c.name() );
1256   cmd.setGroupLastModified( c.lastModified() );
1257   m.setData( chatMessageDataToString( cmd ) );
1258   return m;
1259 }
1260 
userRecordsFromGroupRequestMessage(const Message & m) const1261 QList<UserRecord> Protocol::userRecordsFromGroupRequestMessage( const Message& m ) const
1262 {
1263   QList<UserRecord> user_records;
1264   if( m.text().isEmpty() )
1265     return user_records;
1266   QStringList sl = m.text().split( PROTOCOL_FIELD_SEPARATOR );
1267   if( sl.isEmpty() )
1268     return user_records;
1269 
1270   bool ok = false;
1271   int members = sl.takeFirst().toInt( &ok );
1272   if( !ok )
1273     return user_records;
1274 
1275   for( int i = 0; i < members; i++ )
1276   {
1277     if( sl.size() >= 4 )
1278     {
1279       UserRecord ur;
1280       ur.setName( sl.takeFirst() );
1281       ur.setAccount( sl.takeFirst() );
1282       ur.setHash( sl.takeFirst() );
1283       ur.setDomainName( sl.takeFirst() );
1284       user_records.append( ur );
1285     }
1286     else
1287     {
1288       user_records.clear();
1289       return user_records;
1290     }
1291   }
1292 
1293   return user_records;
1294 }
1295 
groupChatRequestMessage_obsolete(const Chat & c,const User & to_user)1296 Message Protocol::groupChatRequestMessage_obsolete( const Chat& c, const User& to_user )
1297 {
1298   Message m( Message::Group, newId(), "" );
1299   m.addFlag( Message::Request );
1300   UserList ul = UserManager::instance().userList().fromUsersId( c.usersId() );
1301   ul.remove( Settings::instance().localUser() );
1302   ul.remove( to_user );
1303   QStringList sl;
1304   foreach( User u, ul.toList() )
1305     sl << u.path();
1306   if( !sl.isEmpty() )
1307     m.setText( sl.join( PROTOCOL_FIELD_SEPARATOR ) );
1308   ChatMessageData cmd;
1309   cmd.setGroupId( c.privateId() );
1310   cmd.setGroupName( c.name() );
1311   m.setData( chatMessageDataToString( cmd ) );
1312   return m;
1313 }
1314 
userPathsFromGroupRequestMessage_obsolete(const Message & m) const1315 QStringList Protocol::userPathsFromGroupRequestMessage_obsolete( const Message& m ) const
1316 {
1317   return m.text().isEmpty() ? QStringList() : m.text().split( PROTOCOL_FIELD_SEPARATOR );
1318 }
1319 
fileInfoRefusedToMessage(const FileInfo & fi,int proto_version)1320 Message Protocol::fileInfoRefusedToMessage( const FileInfo& fi, int proto_version )
1321 {
1322   Message m = fileInfoToMessage( fi, proto_version );
1323   m.addFlag( Message::Refused );
1324   m.addFlag( Message::Private );
1325   return m;
1326 }
1327 
folderRefusedToMessage(const QString & folder_name,const QString & chat_private_id)1328 Message Protocol::folderRefusedToMessage( const QString& folder_name, const QString& chat_private_id )
1329 {
1330   Message m( Message::Folder, newId(), folder_name );
1331   m.addFlag( Message::Refused );
1332   m.addFlag( Message::Private );
1333   m.setData( chat_private_id );
1334   return m;
1335 }
1336 
fileInfoToMessage(const FileInfo & fi,int proto_version)1337 Message Protocol::fileInfoToMessage( const FileInfo& fi, int proto_version )
1338 {
1339   Message m( Message::File, newId(), fi.name() );
1340   QStringList sl;
1341   sl << QString::number( fi.networkAddress().hostPort() );
1342   sl << QString::number( fi.size() );
1343   sl << QString::number( fi.id() );
1344   sl << QString::fromUtf8( fi.password() );
1345   sl << fi.fileHash();
1346   sl << fi.shareFolder();
1347   if( fi.isInShareBox() )
1348     sl << QString( "1" );
1349   else
1350     sl << QString( "0" );
1351   sl << fi.chatPrivateId();
1352   if( fi.lastModified().isValid() )
1353   {
1354     if( proto_version >= FILE_TRANSFER_UTC_MODIFIED_DATE_PROTO_VERSION )
1355       sl << fi.lastModified().toUTC().toString( Qt::ISODate );
1356     else
1357       sl << fi.lastModified().toString( Qt::ISODate );
1358   }
1359   else
1360     sl << QString( "" );
1361   sl << fi.mimeType();
1362   sl << QString::number( fi.contentType() );
1363   sl << QString::number( fi.startingPosition() );
1364   sl << QString::number( fi.duration() );
1365   m.setData( sl.join( DATA_FIELD_SEPARATOR ) );
1366   m.addFlag( Message::Private );
1367   if( fi.contentType() == FileInfo::VoiceMessage )
1368     m.addFlag( Message::VoiceMessage );
1369   return m;
1370 }
1371 
fileInfoFromMessage(const Message & m,int proto_version)1372 FileInfo Protocol::fileInfoFromMessage( const Message& m, int proto_version )
1373 {
1374   FileInfo fi( 0, FileInfo::Download );
1375   fi.setNameAndSuffix( m.text() );
1376   QStringList sl = m.data().split( DATA_FIELD_SEPARATOR );
1377   if( sl.size() < 4 )
1378     return fi;
1379   bool ok = false;
1380   quint16 host_port = sl.takeFirst().toUInt( &ok );
1381   if( ok )
1382     fi.setHostPort( host_port );
1383   fi.setSize( Bee::qVariantToFileSizeType( sl.takeFirst() ) );
1384   fi.setId( Bee::qVariantToVNumber( sl.takeFirst() ) );
1385   QString password = sl.takeFirst();
1386   fi.setPassword( password.toUtf8() );
1387   if( !sl.isEmpty() )
1388     fi.setFileHash( sl.takeFirst() );
1389   else
1390     fi.setFileHash( fileInfoHashTmp( fi.id(), fi.name(), fi.size() ) );
1391 
1392   if( !sl.isEmpty() )
1393     fi.setShareFolder( sl.takeFirst() );
1394 
1395   if( !sl.isEmpty() )
1396     fi.setIsInShareBox( sl.takeFirst() == QString( "1" ) );
1397 
1398   if( !sl.isEmpty() )
1399     fi.setChatPrivateId( sl.takeFirst() );
1400 
1401   if( !sl.isEmpty() )
1402   {
1403     if( !sl.first().isEmpty() )
1404     {
1405       QDateTime dt_last_modified = QDateTime::fromString( sl.takeFirst(), Qt::ISODate );
1406       if( dt_last_modified.isValid() )
1407       {
1408         if( proto_version >= FILE_TRANSFER_UTC_MODIFIED_DATE_PROTO_VERSION )
1409           fi.setLastModified( dt_last_modified.toLocalTime() );
1410         else
1411           fi.setLastModified( dt_last_modified );
1412       }
1413     }
1414   }
1415 
1416   if( !sl.isEmpty() )
1417     fi.setMimeType( sl.takeFirst() );
1418 
1419   if( !sl.isEmpty() )
1420   {
1421     int content_type = sl.takeFirst().toInt( &ok );
1422     if( ok && content_type >= 0 && content_type < FileInfo::NumContentTypes )
1423       fi.setContentType( static_cast<FileInfo::ContentType>( content_type ) );
1424   }
1425 
1426   if( !sl.isEmpty() )
1427   {
1428     FileSizeType file_position = Bee::qVariantToFileSizeType( sl.takeFirst(), &ok );
1429     if( !ok )
1430       fi.setStartingPosition( 0 );
1431     else if( file_position <  fi.size() )
1432       fi.setStartingPosition( file_position );
1433     else
1434       fi.setStartingPosition( fi.size() );
1435   }
1436 
1437   if( !sl.isEmpty() )
1438   {
1439     qint64 file_duration = Bee::qVariantToFileSizeType( sl.takeFirst(), &ok );
1440     if( !ok || file_duration <= 0 )
1441       fi.setDuration( -1 );
1442     else
1443       fi.setDuration( file_duration );
1444   }
1445 
1446   return fi;
1447 }
1448 
fileInfo(const QFileInfo & fi,const QString & share_folder,bool to_share_box,const QString & chat_private_id,FileInfo::ContentType content_type)1449 FileInfo Protocol::fileInfo( const QFileInfo& fi, const QString& share_folder, bool to_share_box, const QString& chat_private_id, FileInfo::ContentType content_type )
1450 {
1451   FileInfo file_info = FileInfo( newId(), FileInfo::Upload );
1452   file_info.setName( fi.fileName() );
1453   file_info.setPath( Bee::convertToNativeFolderSeparator( fi.absoluteFilePath() ) );
1454   file_info.setShareFolder( share_folder );
1455   file_info.setIsInShareBox( to_share_box );
1456 
1457   if( fi.isFile() )
1458   {
1459     file_info.setSuffix( fi.suffix() );
1460     file_info.setSize( static_cast<FileSizeType>( fi.size() ) );
1461   }
1462   else
1463     file_info.setIsFolder( true );
1464 
1465   if( to_share_box )
1466   {
1467     file_info.setPassword( Settings::instance().hash( file_info.path() ) );
1468     file_info.setFileHash( QString::fromLatin1( file_info.password() ) );
1469   }
1470   else
1471   {
1472     file_info.setFileHash( fileInfoHash( fi ) );
1473     QString password_key = QString( "%1%2%3%4%5%6" )
1474                             .arg( Random::number32( 111111, 999999 ) )
1475                             .arg( file_info.id() )
1476                             .arg( Random::number32( 111111, 999999 ) )
1477                             .arg( file_info.path() )
1478                             .arg( Random::number32( 111111, 999999 ) )
1479                             .arg( file_info.size() );
1480     file_info.setPassword( Settings::instance().hash( password_key ) );
1481   }
1482 
1483   file_info.setLastModified( fi.lastModified() );
1484   file_info.setChatPrivateId( chat_private_id );
1485 
1486 #if QT_VERSION >= 0x050000
1487   QMimeDatabase mime_db;
1488   file_info.setMimeType( mime_db.mimeTypeForFile( fi ).name() );
1489 #endif
1490 
1491   file_info.setContentType( content_type );
1492 
1493   return file_info;
1494 }
1495 
fileCanBeShared(const QFileInfo & file_info)1496 bool Protocol::fileCanBeShared( const QFileInfo& file_info )
1497 {
1498   if( !file_info.exists() )
1499   {
1500     qWarning() << "Path" << qPrintable( file_info.absoluteFilePath() ) << "not exists and cannot be shared";
1501     return false;
1502   }
1503 
1504   if( file_info.isDir() )
1505   {
1506     if( file_info.fileName().endsWith( "." ) )
1507     {
1508       // skip folder . and folder ..
1509       return false;
1510     }
1511   }
1512   else
1513   {
1514     if( !Settings::instance().isFileExtensionAllowedInFileTransfer( file_info.suffix() ) )
1515     {
1516       qDebug() << "Path" << qPrintable( file_info.absoluteFilePath() ) << "has file extension not allowed and cannot be shared";
1517       return false;
1518     }
1519   }
1520 
1521   if( !file_info.isReadable() )
1522   {
1523     qWarning() << "Path" << qPrintable( file_info.absoluteFilePath() ) << "is not readable and cannot be shared";
1524     return false;
1525   }
1526 
1527   if( file_info.isSymLink() )
1528   {
1529     qDebug() << "Path" << qPrintable( file_info.absoluteFilePath() ) << "is a symbolic link and cannot be shared";
1530     return false;
1531   }
1532 
1533   if( file_info.isHidden() )
1534   {
1535     qDebug() << "Path" << qPrintable( file_info.absoluteFilePath() ) << "is hidden and cannot be shared";
1536     return false;
1537   }
1538 
1539   return true;
1540 }
1541 
countFilesCanBeSharedInPath(const QString & file_path)1542 int Protocol::countFilesCanBeSharedInPath( const QString& file_path )
1543 {
1544 #ifdef BEEBEEP_DEBUG
1545   qDebug() << "Protocol checks file path:" << file_path;
1546 #endif
1547   int num_files = 0;
1548   QFileInfo file_info( file_path );
1549   if( fileCanBeShared( file_info ) )
1550   {
1551     if( file_info.isDir() )
1552     {
1553       QDir dir( file_path );
1554       QStringList dir_entries = dir.entryList();
1555       foreach( QString dir_entry, dir_entries )
1556       {
1557         if( num_files > Settings::instance().maxQueuedDownloads() )
1558           break;
1559         dir_entry = Bee::convertToNativeFolderSeparator( QString( "%1/%2" ).arg( dir.absolutePath() ).arg( dir_entry ) );
1560         num_files += countFilesCanBeSharedInPath( dir_entry );
1561       }
1562     }
1563     else
1564       num_files = 1;
1565   }
1566 #ifdef BEEBEEP_DEBUG
1567   qDebug() << "Protocol counts" << num_files << "files which can be shared";
1568 #endif
1569   return num_files;
1570 }
1571 
createFolderMessage(const QString & folder_name,const QList<FileInfo> & file_info_list,int server_port)1572 Message Protocol::createFolderMessage( const QString& folder_name, const QList<FileInfo>& file_info_list, int server_port )
1573 {
1574   QStringList msg_list;
1575   foreach( FileInfo fi, file_info_list )
1576   {
1577     QStringList sl;
1578     sl << fi.name();
1579     sl << fi.suffix();
1580     sl << QString::number( fi.size() );
1581     sl << QString::number( fi.id() );
1582     sl << QString::fromUtf8( fi.password() );
1583     sl << fi.fileHash();
1584     sl << fi.shareFolder();
1585     sl.append( fi.chatPrivateId().isEmpty() ? QString( "" ) : fi.chatPrivateId() );
1586     msg_list.append( sl.join( DATA_FIELD_SEPARATOR ) );
1587   }
1588 
1589   Message m( Message::Folder, newId(), msg_list.join( PROTOCOL_FIELD_SEPARATOR ) );
1590   msg_list.clear();
1591   msg_list << QString::number( server_port );
1592   msg_list << folder_name;
1593   m.setData( msg_list.join( DATA_FIELD_SEPARATOR ) );
1594   m.addFlag( Message::Request );
1595   m.addFlag( Message::Private );
1596 
1597   return m;
1598 }
1599 
messageFolderToInfoList(const Message & m,const QHostAddress & server_address,QString * pFolderName) const1600 QList<FileInfo> Protocol::messageFolderToInfoList( const Message& m, const QHostAddress& server_address, QString* pFolderName ) const
1601 {
1602   QList<FileInfo> file_info_list;
1603   if( m.type() != Message::Folder )
1604     return file_info_list;
1605 
1606   QStringList sl = m.data().split( DATA_FIELD_SEPARATOR );
1607   if( sl.isEmpty() )
1608   {
1609     qWarning() << "Folder message received has invalid data";
1610     return file_info_list;
1611   }
1612 
1613   int server_port = sl.takeFirst().toInt();
1614   QString folder_name = sl.takeFirst();
1615   if( pFolderName )
1616     *pFolderName = folder_name;
1617 
1618   sl = m.text().split( PROTOCOL_FIELD_SEPARATOR, QString::SkipEmptyParts );
1619 
1620   QStringList::const_iterator it = sl.begin();
1621   while( it != sl.end() )
1622   {
1623     QStringList sl_tmp = (*it).split( DATA_FIELD_SEPARATOR );
1624     if( sl_tmp.size() >= 7 )
1625     {
1626       FileInfo fi;
1627       fi.setTransferType( FileInfo::Download );
1628       fi.setHostAddress( server_address );
1629       fi.setHostPort( static_cast<quint16>(server_port) );
1630       fi.setNameAndSuffix( sl_tmp.takeFirst() );
1631       sl_tmp.takeFirst(); // suffix obsolete since 5.8.3
1632       fi.setSize( Bee::qVariantToVNumber( sl_tmp.takeFirst() ) );
1633       fi.setId( Bee::qVariantToVNumber( sl_tmp.takeFirst() ) );
1634       fi.setPassword( sl_tmp.takeFirst().toUtf8() );
1635       fi.setFileHash( sl_tmp.takeFirst() );
1636       fi.setShareFolder( Bee::convertToNativeFolderSeparator( sl_tmp.takeFirst() ) );
1637       if( !sl_tmp.isEmpty() )
1638         fi.setChatPrivateId( sl_tmp.takeFirst() );
1639       file_info_list.append( fi );
1640     }
1641     ++it;
1642   }
1643 
1644   return file_info_list;
1645 }
1646 
fileShareRequestMessage() const1647 Message Protocol::fileShareRequestMessage() const
1648 {
1649   Message file_share_request_message( Message::Share, ID_SHARE_MESSAGE, "" );
1650   file_share_request_message.addFlag( Message::Request );
1651   return file_share_request_message;
1652 }
1653 
createFileShareListMessage(const QMultiMap<QString,FileInfo> & file_info_list,int server_port)1654 void Protocol::createFileShareListMessage( const QMultiMap<QString, FileInfo>& file_info_list, int server_port )
1655 {
1656   QStringList msg_list;
1657 
1658   if( server_port > 0 )
1659   {
1660     QMultiMap<QString, FileInfo>::const_iterator it = file_info_list.begin();
1661     while( it != file_info_list.end() )
1662     {
1663       QStringList sl;
1664       sl << it.value().name();
1665       sl << it.value().suffix();
1666       sl << QString::number( it.value().size() );
1667       sl << QString::number( it.value().id() );
1668       sl << QString::fromUtf8( it.value().password() );
1669       sl << it.value().fileHash();
1670       sl << it.value().shareFolder();
1671       msg_list.append( sl.join( DATA_FIELD_SEPARATOR ) );
1672       ++it;
1673     }
1674   }
1675 
1676   Message m( Message::Share, ID_SHARE_MESSAGE, msg_list.isEmpty() ? QString( "" ) : msg_list.join( PROTOCOL_FIELD_SEPARATOR ) );
1677   m.setData( msg_list.isEmpty() ? QString( "0" ) : QString::number( server_port ) );
1678   m.addFlag( Message::List );
1679 
1680   m_fileShareListMessage = m;
1681 }
1682 
messageToFileShare(const Message & m,const QHostAddress & server_address) const1683 QList<FileInfo> Protocol::messageToFileShare( const Message& m, const QHostAddress& server_address ) const
1684 {
1685   QList<FileInfo> file_info_list;
1686   if( m.type() != Message::Share )
1687     return file_info_list;
1688 
1689   QStringList sl = m.data().split( DATA_FIELD_SEPARATOR );
1690   if( sl.isEmpty() )
1691     return file_info_list;
1692 
1693   int server_port = sl.takeFirst().toInt();
1694 
1695   sl = m.text().split( PROTOCOL_FIELD_SEPARATOR, QString::SkipEmptyParts );
1696   QStringList::const_iterator it = sl.begin();
1697   while( it != sl.end() )
1698   {
1699     QStringList sl_tmp = (*it).split( DATA_FIELD_SEPARATOR );
1700     if( sl_tmp.size() >= 5 )
1701     {
1702       FileInfo fi;
1703       fi.setTransferType( FileInfo::Download );
1704       fi.setHostAddress( server_address );
1705       fi.setHostPort( static_cast<quint16>(server_port) );
1706       fi.setNameAndSuffix( sl_tmp.takeFirst() );
1707       if( Settings::instance().isFileExtensionAllowedInFileTransfer( fi.suffix() ) )
1708       {
1709         sl_tmp.takeFirst(); // suffix obsolete since 5.8.3
1710         fi.setSize( Bee::qVariantToVNumber( sl_tmp.takeFirst() ) );
1711         fi.setId( Bee::qVariantToVNumber( sl_tmp.takeFirst() ) );
1712         fi.setPassword( sl_tmp.takeFirst().toUtf8() );
1713         if( !sl_tmp.isEmpty() )
1714           fi.setFileHash( sl_tmp.takeFirst() );
1715         else
1716           fi.setFileHash( fileInfoHashTmp( fi.id(), fi.name(), fi.size() ) );
1717         if( !sl_tmp.isEmpty() )
1718           fi.setShareFolder( Bee::convertToNativeFolderSeparator( sl_tmp.takeFirst() ) );
1719         file_info_list.append( fi );
1720       }
1721     }
1722     ++it;
1723   }
1724   return file_info_list;
1725 }
1726 
shareBoxRequestPathList(const QString & folder_name,bool set_create_flag)1727 Message Protocol::shareBoxRequestPathList( const QString& folder_name, bool set_create_flag )
1728 {
1729   Message m( Message::ShareBox, ID_SHAREBOX_MESSAGE, "" );
1730   QStringList msg_data;
1731   msg_data << QString::number( 0 );
1732   msg_data << folder_name;
1733   m.setData( msg_data.join( DATA_FIELD_SEPARATOR ) );
1734   if( set_create_flag )
1735     m.addFlag( Message::Create );
1736   else
1737     m.addFlag( Message::Request );
1738   return m;
1739 }
1740 
refuseToShareBoxPath(const QString & folder_name,bool set_create_flag)1741 Message Protocol::refuseToShareBoxPath( const QString& folder_name, bool set_create_flag )
1742 {
1743   Message m( Message::ShareBox, ID_SHAREBOX_MESSAGE, "" );
1744   QStringList msg_data;
1745   msg_data << QString::number( 0 );
1746   msg_data << folder_name;
1747   m.setData( msg_data.join( DATA_FIELD_SEPARATOR ) );
1748   m.addFlag( Message::Refused );
1749   if( set_create_flag )
1750     m.addFlag( Message::Create );
1751   else
1752     m.addFlag( Message::Request );
1753   return m;
1754 }
1755 
acceptToShareBoxPath(const QString & folder_name,const QList<FileInfo> & file_info_list,int server_port)1756 Message Protocol::acceptToShareBoxPath( const QString& folder_name, const QList<FileInfo>& file_info_list, int server_port )
1757 {
1758   QStringList msg_list;
1759   foreach( FileInfo fi, file_info_list )
1760   {
1761     QStringList sl;
1762     sl << fi.name();
1763     sl << fi.suffix();
1764     sl << QString::number( fi.size() );
1765     sl << QString::number( fi.id() );
1766     sl << QString::fromUtf8( fi.password() );
1767     sl << fi.fileHash();
1768     sl << QString( "" ); // shareFolder;
1769     sl << fi.lastModified().toString( Qt::ISODate );
1770     if( fi.isFolder() )
1771       sl << QString( "1" );
1772     else
1773       sl << QString( "" );
1774     msg_list.append( sl.join( DATA_FIELD_SEPARATOR ) );
1775   }
1776 
1777   Message m( Message::ShareBox, ID_SHAREBOX_MESSAGE, msg_list.join( PROTOCOL_FIELD_SEPARATOR ) );
1778   msg_list.clear();
1779   msg_list << QString::number( server_port );
1780   msg_list << folder_name;
1781   m.setData( msg_list.join( DATA_FIELD_SEPARATOR ) );
1782   m.addFlag( Message::List );
1783   return m;
1784 }
1785 
folderNameFromShareBoxMessage(const Message & m) const1786 QString Protocol::folderNameFromShareBoxMessage( const Message& m ) const
1787 {
1788   QStringList sl = m.data().split( DATA_FIELD_SEPARATOR );
1789   if( sl.size() < 2 )
1790     return "";
1791   else
1792     return Bee::convertToNativeFolderSeparator( sl.at( 1 ) );
1793 }
1794 
messageToShareBoxFileList(const Message & m,const QHostAddress & server_address) const1795 QList<FileInfo> Protocol::messageToShareBoxFileList( const Message& m, const QHostAddress& server_address ) const
1796 {
1797   QList<FileInfo> file_info_list;
1798   if( m.type() != Message::ShareBox )
1799     return file_info_list;
1800 
1801   QStringList sl = m.data().split( DATA_FIELD_SEPARATOR );
1802   if( sl.size() < 2 )
1803     return file_info_list;
1804 
1805   int server_port = sl.takeFirst().toInt();
1806   QString folder_name = Bee::convertToNativeFolderSeparator( sl.takeFirst() );
1807   QString s_tmp;
1808 
1809   sl = m.text().split( PROTOCOL_FIELD_SEPARATOR, QString::SkipEmptyParts );
1810   QStringList::const_iterator it = sl.begin();
1811   while( it != sl.end() )
1812   {
1813     QStringList sl_tmp = (*it).split( DATA_FIELD_SEPARATOR );
1814     if( sl_tmp.size() >= 9 )
1815     {
1816       FileInfo fi;
1817       fi.setTransferType( FileInfo::Download );
1818       fi.setHostAddress( server_address );
1819       fi.setHostPort( static_cast<quint16>(server_port) );
1820       QString file_name_tmp = sl_tmp.takeFirst();
1821       sl_tmp.takeFirst(); // suffix obsolete since 5.8.3
1822       fi.setSize( Bee::qVariantToVNumber( sl_tmp.takeFirst() ) );
1823       fi.setId( Bee::qVariantToVNumber( sl_tmp.takeFirst() ) );
1824       fi.setPassword( sl_tmp.takeFirst().toUtf8() );
1825       fi.setFileHash( sl_tmp.takeFirst() );
1826       s_tmp = sl_tmp.takeFirst();
1827       if( s_tmp.isEmpty() )
1828         fi.setShareFolder( folder_name );
1829       else
1830         fi.setShareFolder( Bee::convertToNativeFolderSeparator( s_tmp ) );
1831       fi.setLastModified( QDateTime::fromString( sl_tmp.takeFirst(), Qt::ISODate ) );
1832       s_tmp = sl_tmp.takeFirst();
1833       fi.setIsFolder( !s_tmp.isEmpty() );
1834       fi.setIsInShareBox( true );
1835       if( fi.isFolder() )
1836       {
1837         fi.setName( file_name_tmp );
1838         file_info_list.append( fi );
1839       }
1840       else
1841       {
1842         fi.setNameAndSuffix( file_name_tmp );
1843         if( Settings::instance().isFileExtensionAllowedInFileTransfer( fi.suffix() ) )
1844           file_info_list.append( fi );
1845       }
1846     }
1847     ++it;
1848   }
1849 
1850   return file_info_list;
1851 }
1852 
1853 #ifdef BEEBEEP_USE_SHAREDESKTOP
refuseToViewDesktopShared() const1854   Message Protocol::refuseToViewDesktopShared() const
1855   {
1856     Message m( Message::ShareDesktop, ID_SHAREDESKTOP_MESSAGE, "" );
1857     m.addFlag( Message::Refused );
1858     return m;
1859   }
1860 
readImageFromDesktopShared() const1861   Message Protocol::readImageFromDesktopShared() const
1862   {
1863     Message m( Message::ShareDesktop, ID_SHAREDESKTOP_MESSAGE, "" );
1864     m.addFlag( Message::Request );
1865     return m;
1866   }
1867 
1868 
shareDesktopImageDataToMessage(const ShareDesktopData & sdd) const1869   Message Protocol::shareDesktopImageDataToMessage( const ShareDesktopData& sdd ) const
1870   {
1871     Message m( Message::ShareDesktop, ID_SHAREDESKTOP_MESSAGE, QString::fromLatin1( sdd.imageData().toBase64() ) );
1872     m.addFlag( Message::Private );
1873     QStringList sl_data;
1874     sl_data << sdd.imageType();
1875     if( sdd.isCompressed() )
1876       sl_data << QString( "9" );
1877     else
1878       sl_data << QString( "" );
1879     sl_data << QString::number( sdd.diffColor() );
1880     sl_data << QLatin1String( "base64" );
1881     m.setData( sl_data.join( DATA_FIELD_SEPARATOR ) );
1882     return m;
1883   }
1884 
imageDataFromShareDesktopMessage(const Message & m) const1885   ShareDesktopData Protocol::imageDataFromShareDesktopMessage( const Message& m ) const
1886   {
1887     ShareDesktopData sdd;
1888     if( m.text().isEmpty() )
1889     {
1890 #ifdef BEEBEEP_DEBUG
1891       qDebug() << "Share desktop message is arrived with empty image";
1892 #endif
1893       return sdd;
1894     }
1895 
1896     QString img_type = Settings::instance().shareDesktopImageType();
1897     QRgb diff_color = qRgba( 0, 0, 0, 0 );
1898     bool use_compression = true;
1899     QString image_codec = "base64";
1900     if( !m.data().isEmpty() )
1901     {
1902       QStringList sl_data = m.data().split( DATA_FIELD_SEPARATOR, QString::KeepEmptyParts );
1903       if( sl_data.size() >= 4 )
1904       {
1905         img_type = sl_data.takeFirst();
1906         use_compression = sl_data.takeFirst().isEmpty() ? false : true;
1907         bool ok = false;
1908         unsigned int diff_color_tmp = sl_data.takeFirst().toUInt( &ok );
1909         if( ok )
1910           diff_color = diff_color_tmp;
1911         image_codec = sl_data.takeFirst();
1912       }
1913       else
1914         qWarning() << "Invalid image data found in share desktop message (default values used)";
1915     }
1916     else
1917       qWarning() << "Empty image data found in share desktop message (default values used)";
1918 
1919     sdd.setImageType( img_type );
1920     sdd.setDiffColor( diff_color );
1921     sdd.setIsCompressed( use_compression );
1922     sdd.setImageData( image_codec == "base64" ? QByteArray::fromBase64( m.text().toLatin1() ) : m.text().toLatin1() );
1923     return sdd;
1924   }
1925 #endif
1926 
dataFromChatMessage(const Message & m) const1927 ChatMessageData Protocol::dataFromChatMessage( const Message& m ) const
1928 {
1929   ChatMessageData cmd;
1930   if( m.data().isEmpty() )
1931     return cmd;
1932   QStringList sl = m.data().split( DATA_FIELD_SEPARATOR );
1933   if( sl.isEmpty() )
1934     return cmd;
1935 
1936   if( !sl.first().isEmpty() )
1937   {
1938     QColor c( sl.first() );
1939     if( !c.isValid() )
1940     {
1941       qWarning() << "Invalid text color in Chat Message Data:" << qPrintable( m.data() );
1942       cmd.setTextColor( QColor( Qt::black ) );
1943     }
1944     else
1945       cmd.setTextColor( c );
1946   }
1947 
1948   sl.removeFirst();
1949   if( sl.isEmpty() )
1950     return cmd;
1951   else
1952     cmd.setGroupId( sl.takeFirst() );
1953 
1954   if( sl.isEmpty() )
1955     return cmd;
1956   else
1957     cmd.setGroupName( sl.takeFirst() );
1958 
1959   if( sl.isEmpty() )
1960     return cmd;
1961 
1962   QString s_group_last_modified = sl.takeFirst();
1963   if( !s_group_last_modified.isEmpty() )
1964     cmd.setGroupLastModified( QDateTime::fromString( s_group_last_modified, Qt::ISODate ) );
1965 
1966   return cmd;
1967 }
1968 
chatMessageDataToString(const ChatMessageData & cmd) const1969 QString Protocol::chatMessageDataToString( const ChatMessageData& cmd ) const
1970 {
1971   QStringList sl;
1972   sl << (cmd.textColor().isValid() ? cmd.textColor().name() : "");
1973   sl << (cmd.groupId().size() > 0 ? cmd.groupId() : "");
1974   sl << (cmd.groupName().size() > 0 ? cmd.groupName() : "");
1975   sl << (cmd.groupLastModified().isValid() ? cmd.groupLastModified().toString( Qt::ISODate ) : "");
1976   return sl.join( DATA_FIELD_SEPARATOR );
1977 }
1978 
chatMessage(const Chat & c,const QString & msg_txt)1979 Message Protocol::chatMessage( const Chat& c, const QString& msg_txt )
1980 {
1981   Message m( Message::Chat, newId(), msg_txt );
1982   ChatMessageData cmd;
1983   cmd.setTextColor( Settings::instance().chatFontColor() );
1984   if( c.isGroup() )
1985   {
1986     m.addFlag( Message::GroupChat );
1987     cmd.setGroupId( c.privateId() );
1988   }
1989   else
1990   {
1991     if( !c.isDefault() )
1992       m.addFlag( Message::Private );
1993   }
1994 
1995   m.setData( chatMessageDataToString( cmd ) );
1996   return m;
1997 }
1998 
chatReadMessage(const Chat & c)1999 Message Protocol::chatReadMessage( const Chat& c )
2000 {
2001   Message m = chatMessage( c, "" );
2002   m.setType( Message::Read );
2003   return m;
2004 }
2005 
fileInfoHash(const QFileInfo & file_info) const2006 QString Protocol::fileInfoHash( const QFileInfo& file_info ) const
2007 {
2008   QStringList sl;
2009   sl << file_info.fileName();
2010   sl << QString::number( file_info.size() );
2011   sl << file_info.lastModified().toString( "dd.MM.yyyy-hh:mm:ss" );
2012   return Settings::instance().simpleHash( sl.join( "-" ) );
2013 }
2014 
fileInfoHashTmp(VNumber file_info_id,const QString & file_info_name,FileSizeType file_info_size) const2015 QString Protocol::fileInfoHashTmp( VNumber file_info_id, const QString& file_info_name, FileSizeType file_info_size ) const
2016 {
2017   QStringList sl;
2018   sl << QString::number( file_info_id );
2019   sl << file_info_name;
2020   sl << QString::number( file_info_size );
2021   return Settings::instance().simpleHash( sl.join( "-" ) );
2022 }
2023 
newMd5Id()2024 QString Protocol::newMd5Id()
2025 {
2026   QStringList sl;
2027   sl << QString::number( newId() );
2028   sl << QString::number( Random::d100() );
2029   sl << Settings::instance().localUser().path();
2030   sl << QHostInfo::localHostName();
2031   sl << QString::number( Random::d100() );
2032   sl << QDateTime::currentDateTime().toString( "dd.MM.yyyy-hh:mm:ss.zzz" );
2033   sl << QString::number( Random::d100() );
2034   return Settings::instance().simpleHash( sl.join( QString::number( Random::d100() ) ) );
2035 }
2036 
fileTransferBytesArrivedConfirmation(int proto_version,FileSizeType bytes_arrived_size,FileSizeType total_bytes_arrived_size,bool pause_transfer) const2037 QByteArray Protocol::fileTransferBytesArrivedConfirmation( int proto_version, FileSizeType bytes_arrived_size, FileSizeType total_bytes_arrived_size, bool pause_transfer ) const
2038 {
2039   QByteArray byte_array;
2040   if( proto_version < FILE_TRANSFER_RESUME_PROTO_VERSION )
2041   {
2042     byte_array = QByteArray::number( bytes_arrived_size );
2043     while( byte_array.size() % ENCRYPTED_DATA_BLOCK_SIZE )
2044       byte_array.prepend( '0' );
2045   }
2046   else
2047   {
2048     QStringList sl;
2049     sl << QString::number( bytes_arrived_size );
2050     sl << QString::number( total_bytes_arrived_size );
2051     if( pause_transfer )
2052       sl << QLatin1String( "2" );
2053     else
2054       sl << QLatin1String( "1" );
2055     sl << QLatin1String( " " ); // end of message
2056     byte_array = sl.join( DATA_FIELD_SEPARATOR ).toUtf8();
2057     while( byte_array.size() % ENCRYPTED_DATA_BLOCK_SIZE )
2058       byte_array.append( '0' );
2059   }
2060   return byte_array;
2061 }
2062 
parseFileTransferBytesArrivedConfirmation(int proto_version,const QByteArray & bytes_arrived,FileSizeType * bytes_arrived_size,FileSizeType * total_bytes_arrived_size,bool * pause_transfer) const2063 bool Protocol::parseFileTransferBytesArrivedConfirmation( int proto_version, const QByteArray& bytes_arrived, FileSizeType* bytes_arrived_size, FileSizeType* total_bytes_arrived_size, bool* pause_transfer ) const
2064 {
2065   bool ok = false;
2066   if( proto_version < FILE_TRANSFER_RESUME_PROTO_VERSION )
2067   {
2068     *bytes_arrived_size = bytes_arrived.simplified().toInt( &ok );
2069     *total_bytes_arrived_size = -1;
2070     *pause_transfer = false;
2071     return ok;
2072   }
2073   else
2074   {
2075     QByteArray bytes_to_parse = bytes_arrived;
2076     while( bytes_to_parse.endsWith( '0' ) )
2077       bytes_to_parse.chop( 1 );
2078     QStringList sl_data = QString::fromUtf8( bytes_arrived.trimmed() ).split( DATA_FIELD_SEPARATOR );
2079     if( sl_data.size() < 3 )
2080       return false;
2081     *bytes_arrived_size = Bee::qVariantToFileSizeType( sl_data.takeFirst(), &ok );
2082     if( !ok )
2083       return false;
2084     *total_bytes_arrived_size = Bee::qVariantToFileSizeType( sl_data.takeFirst(), &ok );
2085     if( !ok )
2086       *total_bytes_arrived_size = -1;
2087     int cmd_id = sl_data.takeFirst().toInt( &ok );
2088     if( !ok )
2089       *pause_transfer = false;
2090     else
2091       *pause_transfer = cmd_id == 2;
2092     return true;
2093   }
2094 }
2095 
userRecordListToHiveMessage(const QList<UserRecord> & user_record_list)2096 Message Protocol::userRecordListToHiveMessage( const QList<UserRecord>& user_record_list )
2097 {
2098   QStringList msg_list;
2099   foreach( UserRecord ur, user_record_list )
2100     msg_list.append( saveUserRecord( ur, false ) );
2101 
2102   Message m( Message::Hive, newId(), msg_list.join( PROTOCOL_FIELD_SEPARATOR ) );
2103   m.addFlag( Message::List );
2104   return m;
2105 }
2106 
hiveMessageToUserRecordList(const Message & m) const2107 QList<UserRecord> Protocol::hiveMessageToUserRecordList( const Message& m ) const
2108 {
2109   QList<UserRecord> user_record_list;
2110   if( m.type() != Message::Hive && !m.hasFlag( Message::List ) )
2111     return user_record_list;
2112 
2113   QStringList sl = m.text().split( PROTOCOL_FIELD_SEPARATOR, QString::SkipEmptyParts );
2114   UserRecord ur;
2115   foreach( QString s, sl )
2116   {
2117     ur = loadUserRecord( s );
2118     if( ur.networkAddressIsValid() )
2119       user_record_list.append( ur );
2120   }
2121 
2122   return user_record_list;
2123 }
2124 
saveChatRecord(const ChatRecord & cr) const2125 QString Protocol::saveChatRecord( const ChatRecord& cr ) const
2126 {
2127   QStringList sl;
2128   sl << cr.name();
2129   sl << cr.privateId();
2130   return sl.join( DATA_FIELD_SEPARATOR );
2131 }
2132 
loadChatRecord(const QString & s) const2133 ChatRecord Protocol::loadChatRecord( const QString& s ) const
2134 {
2135   if( s.isEmpty() )
2136     return ChatRecord();
2137 
2138   ChatRecord cr;
2139   QStringList sl = s.split( DATA_FIELD_SEPARATOR );
2140   if( !sl.isEmpty() )
2141     cr.setName( sl.takeFirst() );
2142   if( !sl.isEmpty() )
2143     cr.setPrivateId( sl.takeFirst() );
2144   return cr;
2145 }
2146 
2147 
linkifyText(const QString & text)2148 QString Protocol::linkifyText( const QString& text )
2149 {
2150   // File and folder path must be a single message, url can be inside a message
2151   QString simplified_text = text.simplified();
2152   QString linkfied_text = text.simplified();
2153 
2154 #ifdef Q_OS_WIN
2155   // linkify windows network path
2156   if( simplified_text.contains( "\\\\" ) )
2157   {
2158 
2159     int index_backslash = linkfied_text.indexOf( "\\\\" );
2160     QString pre_text = "";
2161     if( index_backslash > 0 )
2162     {
2163       pre_text = linkfied_text.section( "\\\\", 0, 0 );
2164       if( !pre_text.isEmpty() )
2165         linkfied_text.remove( 0, pre_text.size() );
2166     }
2167 
2168     QUrl url_to_add = QUrl::fromLocalFile( simplified_text );
2169 #if QT_VERSION >= 0x050000
2170     linkfied_text = QString( "<a href=\"%1\">%2</a>" ).arg( url_to_add.url() ).arg( simplified_text );
2171 #else
2172     linkfied_text = QString( "<a href=\"%1\">%2</a>" ).arg( url_to_add.toString() ).arg( simplified_text );
2173 #endif
2174     if( !pre_text.isEmpty() )
2175       linkfied_text.prepend( pre_text );
2176 
2177 #ifdef BEEBEEP_DEBUG
2178     qDebug() << "Linkified windows network path:" << qPrintable( text );
2179 #endif
2180     return linkfied_text;
2181   }
2182 #endif
2183 
2184   if( !simplified_text.startsWith( ":" ) )
2185   {
2186     QUrl input_url = QUrl::fromUserInput( simplified_text );
2187     if( input_url.isLocalFile() )
2188     {
2189 #if QT_VERSION >= 0x050000
2190       linkfied_text = QString( "<a href=\"%1\">%2</a>" ).arg( input_url.url() ).arg( simplified_text );
2191 #else
2192       linkfied_text = QString( "<a href=\"%1\">%2</a>" ).arg( input_url.toString() ).arg( simplified_text );
2193 #endif
2194       return linkfied_text;
2195     }
2196   }
2197 
2198   if( !linkfied_text.contains( QLatin1Char( '.' ) ) )
2199     return linkfied_text;
2200   linkfied_text.prepend( " " ); // for matching www.miosito.it
2201   linkfied_text.replace( QRegExp( "(((f|ht){1}tp(s:|:){1}//)[-a-zA-Z0-9@:%_\\+.,~#?!&//=\\(\\)]+)" ), "<a href=\"\\1\">\\1</a>" );
2202   linkfied_text.replace( QRegExp( "([\\s()[{}])(www.[-a-zA-Z0-9@:%_\\+.,~#?!&//=\\(\\)]+)" ), "\\1<a href=\"http://\\2\">\\2</a>" );
2203   linkfied_text.replace( QRegExp( "([_\\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\\.)+[a-z]{2,3})" ), "<a href=\"mailto:\\1\">\\1</a>" );
2204   linkfied_text.remove( 0, 1 ); // remove the space added
2205 
2206   return linkfied_text;
2207 }
2208 
formatHtmlText(const QString & text)2209 QString Protocol::formatHtmlText( const QString& text )
2210 {
2211   QString text_formatted = "";
2212   int last_semicolon_index = -1;
2213   bool there_is_a_space_before_it = false;
2214   QChar c;
2215 
2216   for( int i = 0; i < text.length(); i++ )
2217   {
2218     c = text.at( i );
2219     if( c == QLatin1Char( ' ' ) )
2220     {
2221       if( there_is_a_space_before_it )
2222         text_formatted += QLatin1String( "&nbsp; " ); // space added for emoticons recognize
2223       else
2224         text_formatted += QLatin1Char( ' ' );
2225     }
2226     else if( c == QLatin1Char( '\n' ) )
2227     {
2228       // space added to match url after a \n
2229       text_formatted += QLatin1String( "<br> " );
2230     }
2231     else if( c == QLatin1Char( '<' ) )
2232     {
2233       if( Settings::instance().chatUseHtmlTags() )
2234       {
2235         if( last_semicolon_index >= 0 )
2236           text_formatted.replace( last_semicolon_index, 1, QLatin1String( "&lt;" ) );
2237 
2238         last_semicolon_index = text_formatted.size();
2239         text_formatted += QLatin1Char( '<' );
2240       }
2241       else
2242         text_formatted += QLatin1String( "&lt;" );
2243     }
2244     else if( c == QLatin1Char( '>' ) )
2245     {
2246       if( Settings::instance().chatUseHtmlTags() )
2247       {
2248         text_formatted += QLatin1Char( '>' );
2249         if( last_semicolon_index >= 0 )
2250           last_semicolon_index = -1;
2251       }
2252       else
2253         text_formatted += QLatin1String( "&gt;" );
2254     }
2255     else if( c == QLatin1Char( '"' ) )
2256     {
2257       if( last_semicolon_index >= 0 )
2258         text_formatted += QLatin1Char( '"' );
2259       else
2260         text_formatted += QLatin1String( "&quot;" );
2261     }
2262     else if( c == QLatin1Char( '&' ) )
2263     {
2264       text_formatted += QLatin1Char( '&' ); // not &amp; for Linkify
2265     }
2266     else if( c == QLatin1Char( '\r' ) )
2267     {
2268       // skip
2269     }
2270     else
2271       text_formatted += c;
2272 
2273     there_is_a_space_before_it = c == QLatin1Char( ' ' );
2274   }
2275 
2276   if( last_semicolon_index >= 0 )
2277     text_formatted.replace( last_semicolon_index, 1, QLatin1String( "&lt;" ) );
2278 
2279   text_formatted.replace( QRegExp("(^|\\s|>)_(\\S+)_(<|\\s|$)"), "\\1<u>\\2</u>\\3" );
2280   text_formatted.replace( QRegExp("(^|\\s|>)\\*(\\S+)\\*(<|\\s|$)"), "\\1<b>\\2</b>\\3" );
2281   text_formatted.replace( QRegExp("(^|\\s|>)\\/(\\S+)\\/(<|\\s|$)"), "\\1<i>\\2</i>\\3" );
2282   text_formatted.replace( QLatin1String( "[quote]" ), QString( "<br><span class='bee-quote'>&nbsp;&nbsp;<i>" ) );
2283   text_formatted.replace( QLatin1String( "[/quote]" ), "</i>&nbsp;&nbsp;</span> " );
2284 
2285   if( Settings::instance().chatUseClickableLinks() )
2286     text_formatted = linkifyText( text_formatted );
2287 
2288   if( Settings::instance().showEmoticons() )
2289     text_formatted = EmoticonManager::instance().parseEmoticons( text_formatted, Settings::instance().emoticonSizeInChat(), Settings::instance().useFontEmoticons() );
2290 
2291   PluginManager::instance().parseText( &text_formatted, false );
2292 
2293   return text_formatted.trimmed();
2294 }
2295 
2296 /* Encryption */
createCipherKey(const QByteArray & shared_key,int data_stream_version) const2297 QByteArray Protocol::createCipherKey( const QByteArray& shared_key, int data_stream_version ) const
2298 {
2299   if( shared_key.isEmpty() )
2300     return shared_key;
2301 #if QT_VERSION < 0x050000
2302   Q_UNUSED( data_stream_version )
2303   QCryptographicHash ch( QCryptographicHash::Sha1 );
2304 #else
2305   QCryptographicHash ch( data_stream_version < 13 ? QCryptographicHash::Sha1 : QCryptographicHash::Sha3_256 );
2306 #endif
2307   ch.addData( shared_key );
2308   return ch.result().toHex(); // must be in HEX
2309 }
2310 
createCipherKey(const QString & key_1,const QString & key_2,int data_stream_version) const2311 QByteArray Protocol::createCipherKey( const QString& key_1, const QString& key_2, int data_stream_version ) const
2312 {
2313   QString sum_keys = key_1 + key_2;
2314   return createCipherKey( sum_keys.toUtf8(), data_stream_version );
2315 }
2316 
splitByteArray(const QByteArray & byte_array,int num_chars) const2317 QList<QByteArray> Protocol::splitByteArray( const QByteArray& byte_array, int num_chars ) const
2318 {
2319   QList<QByteArray> array_list;
2320 
2321   if( byte_array.isEmpty() )
2322     return array_list;
2323 
2324   QByteArray tmp = "";
2325 
2326   for( int i = 0; i < byte_array.size(); i++ )
2327   {
2328     tmp += byte_array.at( i );
2329     if( tmp.size() == num_chars )
2330     {
2331       array_list.append( tmp );
2332       tmp = "";
2333     }
2334   }
2335 
2336   if( !tmp.isEmpty() )
2337   {
2338 #ifdef BEEBEEP_DEBUG
2339     qDebug() << "Protocol splits byte array in" << array_list.size() << "parts but some chars remains out:" << tmp;
2340 #endif
2341     array_list.append( tmp );
2342   }
2343 
2344   return array_list;
2345 }
2346 
hexToUnsignedChar(const QByteArray & hex_byte_array,unsigned char * out_string,unsigned int len_out_string) const2347 void Protocol::hexToUnsignedChar( const QByteArray& hex_byte_array, unsigned char* out_string, unsigned int len_out_string ) const
2348 {
2349   // Thanks to Christophe David
2350   const char* hex_string = hex_byte_array.data();
2351   unsigned int i = 0;
2352   unsigned int j = 0;
2353   bool msb = true;
2354   while( j < len_out_string )
2355   {
2356     if( hex_string[i] >= '0' && hex_string[i] <='9' )
2357     {
2358       if( msb )
2359       {
2360         out_string[j] = static_cast<unsigned char>(int(hex_string[i++] - '0')*16);
2361         msb = false;
2362       }
2363       else
2364       {
2365         out_string[j++] |= int(hex_string[i++] - '0');
2366         msb = true;
2367       }
2368     }
2369     else if( toupper(hex_string[i]) >= 'A' && toupper(hex_string[i]) <= 'F' )
2370     {
2371       if( msb )
2372       {
2373         out_string[j] = static_cast<unsigned char>((int(toupper(hex_string[i++]) - 'A') + 10) * 16);
2374         msb = false;
2375       }
2376       else
2377       {
2378         out_string[j++] |= (int(toupper(hex_string[i++]) - 'A')+10);
2379         msb = true;
2380       }
2381     }
2382     else
2383     {
2384       // whatever it is and aspecially a '\x00'
2385       if( i < strlen( hex_string ) )
2386         i++;
2387 
2388       if( msb ) // lsb can not be decoded, we keep it to 0 else we fill data with 0
2389         out_string[j++] = 0;
2390       else
2391         msb = true;
2392     }
2393   }
2394 }
2395 
encryptByteArray(const QByteArray & text_to_encrypt,const QByteArray & cipher_key,int proto_version) const2396 QByteArray Protocol::encryptByteArray( const QByteArray& text_to_encrypt, const QByteArray& cipher_key, int proto_version ) const
2397 {
2398   unsigned long rk[ RKLENGTH(ENCRYPTION_KEYBITS) ];
2399   unsigned char key[ KEYLENGTH(ENCRYPTION_KEYBITS) ];
2400   unsigned int i;
2401   int nrounds;
2402 
2403   if( text_to_encrypt.isEmpty() )
2404     return QByteArray();
2405 
2406   if( cipher_key.isEmpty() )
2407     return text_to_encrypt.toBase64();
2408 
2409   if( proto_version < SECURE_LEVEL_3_PROTO_VERSION )
2410   {
2411     // What the hell...
2412     for( unsigned int i = 0; i < sizeof( key ); i++ )
2413       key[ i ] = static_cast<unsigned int>(cipher_key.size()) < i ? static_cast<unsigned char>( cipher_key.at( static_cast<int>(i) ) ) : 0;
2414   }
2415   else
2416   {
2417     hexToUnsignedChar( cipher_key, key, KEYLENGTH(ENCRYPTION_KEYBITS) );
2418   }
2419 
2420   nrounds = rijndaelSetupEncrypt( rk, key, ENCRYPTION_KEYBITS );
2421 
2422   QList<QByteArray> byte_array_list = splitByteArray( text_to_encrypt, ENCRYPTED_DATA_BLOCK_SIZE );
2423 
2424   unsigned char plaintext[ ENCRYPTED_DATA_BLOCK_SIZE ];
2425   unsigned char ciphertext[ ENCRYPTED_DATA_BLOCK_SIZE ];
2426 
2427   memset( ciphertext, 0, sizeof( ciphertext ) );
2428   memset( plaintext, 0, sizeof( plaintext ) );
2429 
2430   QByteArray encrypted_byte_array;
2431 
2432   foreach( QByteArray ba, byte_array_list )
2433   {
2434     if( ba.size() == sizeof( plaintext ) )
2435     {
2436       for( i = 0; i < sizeof( plaintext ); i++ )
2437         plaintext[ i ] = static_cast<unsigned char>( ba[ i ] );
2438 
2439       rijndaelEncrypt( rk, nrounds, plaintext, ciphertext );
2440 
2441       for( i = 0; i < sizeof( ciphertext ); i++ )
2442         encrypted_byte_array.append( static_cast<char>( ciphertext[ i ] ) );
2443 
2444       memset( ciphertext, 0, sizeof( ciphertext ) );
2445       memset( plaintext, 0, sizeof( plaintext ) );
2446     }
2447     else
2448       encrypted_byte_array += ba;
2449 
2450   }
2451 
2452   return encrypted_byte_array;
2453 }
2454 
decryptByteArray(const QByteArray & text_to_decrypt,const QByteArray & cipher_key,int proto_version) const2455 QByteArray Protocol::decryptByteArray( const QByteArray& text_to_decrypt, const QByteArray& cipher_key, int proto_version ) const
2456 {
2457   unsigned long rk[RKLENGTH(ENCRYPTION_KEYBITS)];
2458   unsigned char key[KEYLENGTH(ENCRYPTION_KEYBITS)];
2459   unsigned int i;
2460   int nrounds;
2461 
2462   if( text_to_decrypt.isEmpty() )
2463     return QByteArray();
2464 
2465   if( cipher_key.isEmpty() )
2466     return QByteArray::fromBase64( text_to_decrypt );
2467 
2468   if( proto_version < SECURE_LEVEL_3_PROTO_VERSION )
2469   {
2470     // What the hell...
2471     for( unsigned int i = 0; i < sizeof( key ); i++ )
2472       key[ i ] = static_cast<unsigned int>(cipher_key.size()) < i ? static_cast<unsigned char>( cipher_key.at( static_cast<int>(i) ) ) : 0;
2473   }
2474   else
2475   {
2476     hexToUnsignedChar( cipher_key, key, KEYLENGTH(ENCRYPTION_KEYBITS) );
2477   }
2478 
2479   nrounds = rijndaelSetupDecrypt( rk, key, ENCRYPTION_KEYBITS );
2480 
2481   QList<QByteArray> byte_array_list = splitByteArray( text_to_decrypt, ENCRYPTED_DATA_BLOCK_SIZE );
2482 
2483   unsigned char plaintext[ ENCRYPTED_DATA_BLOCK_SIZE ];
2484   unsigned char ciphertext[ ENCRYPTED_DATA_BLOCK_SIZE ];
2485 
2486   memset( ciphertext, 0, sizeof( ciphertext ) );
2487   memset( plaintext, 0, sizeof( plaintext ) );
2488 
2489   QByteArray decrypted_byte_array;
2490 
2491   foreach( QByteArray ba, byte_array_list )
2492   {
2493     if( ba.size() == sizeof( ciphertext ) )
2494     {
2495       for( i = 0; i < sizeof( ciphertext ); i++ )
2496         ciphertext[ i ] = static_cast<unsigned char>( ba[ i ] );
2497 
2498       rijndaelDecrypt( rk, nrounds, ciphertext, plaintext );
2499 
2500       for( i = 0; i < sizeof( plaintext ); i++ )
2501         decrypted_byte_array.append( static_cast<char>( plaintext[ i ] ) );
2502 
2503       memset( ciphertext, 0, sizeof( ciphertext ) );
2504       memset( plaintext, 0, sizeof( plaintext ) );
2505     }
2506     else
2507       decrypted_byte_array += ba;
2508   }
2509 
2510   return decrypted_byte_array;
2511 }
2512