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( " " ); // 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( "<" ) );
2237
2238 last_semicolon_index = text_formatted.size();
2239 text_formatted += QLatin1Char( '<' );
2240 }
2241 else
2242 text_formatted += QLatin1String( "<" );
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( ">" );
2254 }
2255 else if( c == QLatin1Char( '"' ) )
2256 {
2257 if( last_semicolon_index >= 0 )
2258 text_formatted += QLatin1Char( '"' );
2259 else
2260 text_formatted += QLatin1String( """ );
2261 }
2262 else if( c == QLatin1Char( '&' ) )
2263 {
2264 text_formatted += QLatin1Char( '&' ); // not & 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( "<" ) );
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'> <i>" ) );
2283 text_formatted.replace( QLatin1String( "[/quote]" ), "</i> </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