~ubuntu-branches/debian/sid/kmess/sid

« back to all changes in this revision

Viewing changes to src/network/msnsocketnull.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Mark Purcell
  • Date: 2009-05-26 22:58:41 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20090526225841-yx7029u4euscd0f5
Tags: 2.0~beta2-1
* New upstream release 
  - Fixes "the '">' characters appears" (Closes: #530358)
* Add Build-Depends: libgif-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/***************************************************************************
2
 
                          msnsocketnull.cpp  -  description
3
 
                             -------------------
4
 
    begin                : Sat May 10 2008
5
 
    copyright            : (C) 2008 by Valerio Pilo
6
 
    email                : valerio@kmess.org
7
 
 ***************************************************************************/
8
 
 
9
 
/***************************************************************************
10
 
 *                                                                         *
11
 
 *   This program is free software; you can redistribute it and/or modify  *
12
 
 *   it under the terms of the GNU General Public License as published by  *
13
 
 *   the Free Software Foundation; either version 2 of the License, or     *
14
 
 *   (at your option) any later version.                                   *
15
 
 *                                                                         *
16
 
 ***************************************************************************/
17
 
 
18
 
#include "msnsocketnull.h"
19
 
 
20
 
#include <QDir>
21
 
#include <QFile>
22
 
#include <QHostAddress>
23
 
#include <QTimer>
24
 
#include <QUrl>
25
 
 
26
 
#include <KInputDialog>
27
 
#include <KLocale>
28
 
 
29
 
#include <config-kmess.h>
30
 
#include "../kmessdebug.h"
31
 
#include "../contact/contact.h"
32
 
#include "../currentaccount.h"
33
 
 
34
 
 
35
 
// Initialize static class members
36
 
QMap<QString,MsnSocketNull::FakeContact>   MsnSocketNull::contactList_;
37
 
QMap<QString,QString>                      MsnSocketNull::groupList_;
38
 
QMap<QString,MsnSocketNull::OnlineDetails> MsnSocketNull::onlineList_;
39
 
 
40
 
 
41
 
#ifdef KMESSDEBUG_CONNECTION // Area-specific debug statements
42
 
  #define KMESSDEBUG_CONNECTION_SOCKET_NULL
43
 
#endif
44
 
 
45
 
 
46
 
// Change this to the position where you want KMess to search for a debug log
47
 
// to replicate :)
48
 
#define DEBUG_LOG_FILE    QDir::homePath() + "/kmessdebug.log"
49
 
 
50
 
 
51
 
 
52
 
/**
53
 
 * @brief The constructor
54
 
 *
55
 
 * Does nothing.
56
 
 */
57
 
MsnSocketNull::MsnSocketNull( ServerType serverType )
58
 
: MsnSocketBase( serverType )
59
 
, sendPings_( false )
60
 
{
61
 
  setObjectName( "MsnSocketNull" );
62
 
 
63
 
  // Connect the server request timer
64
 
  connect( &responseTimer_, SIGNAL(        timeout() ),
65
 
           this,            SLOT  (   sendResponse() ) );
66
 
 
67
 
  // If the lists are already filled, we're done
68
 
  if( ! contactList_.isEmpty() || ! groupList_.isEmpty() )
69
 
  {
70
 
    return;
71
 
  }
72
 
 
73
 
  // If that file exists, parse it
74
 
  kDebug() << "File" << DEBUG_LOG_FILE << "file exists?" << QFile::exists( DEBUG_LOG_FILE );
75
 
  if( QFile::exists( DEBUG_LOG_FILE ) )
76
 
  {
77
 
    QFile file( DEBUG_LOG_FILE );
78
 
    file.open( QIODevice::ReadOnly );
79
 
    QStringList fileContents( QString( file.readAll() ).split( QString( "\n" ) ) );
80
 
    file.close();
81
 
 
82
 
    int index;
83
 
    QList<int> connections;
84
 
    QStringList fileCommands;
85
 
    QRegExp commandRegexp( "\"\\s+-\\s+<<<\\s+\\((.+)\\)$" );
86
 
 
87
 
    foreach( const QString &row, fileContents )
88
 
    {
89
 
      index = commandRegexp.indexIn( row );
90
 
      if( index == -1 )
91
 
        continue;
92
 
 
93
 
      QString command( commandRegexp.cap( 1 ) );
94
 
 
95
 
      // Every connection starts with a good SYN
96
 
      if( command.startsWith( "\"SYN\"" ) )
97
 
      {
98
 
        connections.append( fileCommands.size() );
99
 
        continue;
100
 
      }
101
 
 
102
 
      // The only messages we care of are the list retrieval ones
103
 
      if( ! command.startsWith( "\"LSG\"" ) &&
104
 
          ! command.startsWith( "\"LST\"" ) &&
105
 
          ! command.startsWith( "\"ILN\"" ) )
106
 
      {
107
 
        continue;
108
 
      }
109
 
 
110
 
      // Extract the QStringList of commands from the row
111
 
      fileCommands.append( command );
112
 
    }
113
 
 
114
 
/*
115
 
    kDebug() << "###########################################################################";
116
 
    kDebug() << connections;
117
 
    kDebug() << "###########################################################################";
118
 
    for( int i = 0; i < fileCommands.size(); i++ )
119
 
    {
120
 
      if( connections.contains( i ) )
121
 
      {
122
 
        kDebug() << "###########################################################################";
123
 
        kDebug() << "Connection" << connections.indexOf( i );
124
 
        kDebug() << "**************";
125
 
      }
126
 
 
127
 
      kDebug() << fileCommands[ i ];
128
 
    }
129
 
 
130
 
    kDebug() << "###########################################################################";
131
 
*/
132
 
 
133
 
    int number = 0;
134
 
    if( connections.size() > 1 )
135
 
    {
136
 
      bool ok;
137
 
      number = KInputDialog::getInteger( "KMess log mimicker", "Choose a connection from the log file \""
138
 
                                         + DEBUG_LOG_FILE + "\" to replicate:",
139
 
                                         1, 1,
140
 
                                         connections.size(),
141
 
                                         1, 10,
142
 
                                         &ok );
143
 
      if( ! ok )
144
 
      {
145
 
        number = 0;
146
 
      }
147
 
      else
148
 
      {
149
 
        number--; // reindex from 0
150
 
      }
151
 
    }
152
 
 
153
 
    int end = ( number == connections.size() - 1 ) ? fileCommands.size() - 1 : connections[ number ];
154
 
 
155
 
    for( int idx = connections[ number ]; idx < end; idx++ )
156
 
    {
157
 
      if( fileCommands[idx].isEmpty() )
158
 
        continue;
159
 
 
160
 
      QStringList parts( fileCommands[idx].split( ", " ) );
161
 
      for( int i=0; i < parts.size(); i++ )
162
 
      {
163
 
        parts[ i ] = parts[ i ].mid( 1, parts[i].size() - 2 );
164
 
      }
165
 
 
166
 
      if( parts[0] == "LSG" )
167
 
      {
168
 
        //("LSG", "KMess", "b07456fe-a6fd-4746-b63a-62f363a923ef")
169
 
        groupList_[ parts[2] ] = QUrl::fromPercentEncoding( parts[1].toUtf8() );
170
 
      }
171
 
      else if( parts[0] == "LST" )
172
 
      {
173
 
        // ("LST", "N=friend@site.dom", "F=[a=1][c=4][b]-%20Emet%20-[/b][/c][/a]", "C=aef50adb-434d-4e85-bcfd-0d14c3baf529", "11", "1", "7ed2567a-71f0-4bf1-8ab6-9549f616c093")
174
 
//          ("LST", "N=non-friend@site.dom", "10", "1")
175
 
        FakeContact newContact;
176
 
 
177
 
        if( parts.size() == 4 )
178
 
        {
179
 
          newContact.handle       = parts[1].mid(2);
180
 
          newContact.friendlyName = parts[1].mid(2);
181
 
          newContact.guId         = parts[1].mid(2);
182
 
          newContact.lists        = parts[2].toInt();
183
 
          newContact.groupIds     = QStringList();
184
 
        }
185
 
        else
186
 
        {
187
 
          newContact.handle       = parts[1].mid(2);
188
 
          newContact.friendlyName = QUrl::fromPercentEncoding( parts[2].mid(2).toUtf8() );
189
 
          newContact.guId         = parts[3].mid(2);
190
 
          newContact.lists        = parts[4].toInt();
191
 
          newContact.groupIds     = ( parts.size() > 6 ) ? parts[6].split( "," ) : QStringList();
192
 
        }
193
 
 
194
 
        contactList_[ newContact.handle ] = newContact;
195
 
      }
196
 
      else if( parts[0] == "ILN" )
197
 
      {
198
 
        // ("ILN", "10", "BSY", "contact@site.com", "Nickname%20encoded", "1342210100", "%3Cmsnobj%20Creator%3D%22contact%40site.com%22%20Size%3D%229333%22%20Type%3D%223%22%20Location%3D%22KMess.tmp%22%20Friendly%3D%22AA%3D%3D%22%20SHA1D%3D%22ysV4cHpd04lOhjHYB%2BTnggTwjdA%3D%22%20SHA1C%3D%226jkVICIHHIgJvSZAzgJ6FlNK10c%3D%22%2F%3E")
199
 
        contactList_[ parts[3] ].friendlyName = QUrl::fromPercentEncoding( parts[4].toUtf8() );
200
 
 
201
 
        OnlineDetails details;
202
 
        details.msnObject    = parts[6];
203
 
        details.capabilities = parts[5].toUInt();
204
 
        details.status       = parts[2];
205
 
        onlineList_[ parts[3] ] = details;
206
 
      }
207
 
    }
208
 
  }
209
 
  else // Generate a standard contact list
210
 
  {
211
 
    groupList_[ "0000-family" ] = "Family";
212
 
    groupList_[ "000-friends" ] = "Friends";
213
 
    groupList_[ "0-workmates" ] = "Workmates";
214
 
    groupList_[ "00000-empty" ] = "Empty Group";
215
 
 
216
 
    // lists 'templates'
217
 
    int isFriend  = Contact::MSN_LIST_ALLOWED | Contact::MSN_LIST_FRIEND | Contact::MSN_LIST_REVERSE;
218
 
    // NOTE: Add some invalid characters to the email addresses, so they will never resolve to possibly valid mailboxes.
219
 
    contactList_.insert( "mum@kmess.orgX", createFakeContact
220
 
      ( "mum@kmess.orgX",    "Mom", "c1mum", QStringList() << "0000-family", isFriend ) );
221
 
    contactList_.insert( "sister@kmess.orgX", createFakeContact
222
 
      ( "sister@kmess.orgX", "Sis", "c2sister", QStringList() << "0000-family", isFriend ) );
223
 
    contactList_.insert( "frank@kmess.com" , createFakeContact
224
 
      ( "frank@kmess.comX", "[u]Frankie[/u]", "c3frank", QStringList() << "000-friends", isFriend ) );
225
 
    contactList_.insert( "elaine@kmess.comX", createFakeContact
226
 
      ( "elaine@kmess.com", "[c=orange]Ely[/c=yellow]", "c4elaine", QStringList() << "0-workmates", isFriend ) );
227
 
    contactList_.insert( "badguy@bothering.comX", createFakeContact
228
 
      ( "badguy@bothering.comX", "[b]Bad Guy[/b]", "c5badguy", QStringList(), Contact::MSN_LIST_BLOCKED ) );
229
 
    contactList_.insert( "idiot@friend.comX", createFakeContact
230
 
      ( "jack@idiotry.comX", "Jackie - OLOLOLO", "c6jack", QStringList() << "000-friends", isFriend | Contact::MSN_LIST_BLOCKED ) );
231
 
    contactList_.insert( "sylvie@hotmail.comX", createFakeContact
232
 
      ( "sylvie@hotmail.comX", "(*)Sylvie(*) I'm working! (l)", "c7sylvie", QStringList() << "0-workmates", isFriend ) );
233
 
    contactList_.insert( "evan123@hotmail.usX", createFakeContact
234
 
      ( "evan123@hotmail.usX", "€v1³", "c8evan", QStringList() << "0-workmates", isFriend ) );
235
 
    contactList_.insert( "richard@multinational.comX", createFakeContact
236
 
      ( "richard@multinational.comX", "Richard ~ next week: vacation! :D", "c9richard", QStringList() << "0-workmates", isFriend ) );
237
 
    contactList_.insert( "awesome_girl222@hotmail.comX", createFakeContact
238
 
      ( "awesome_girl222@hotmail.comX", "[b][c=12]Katie (L)[/c=21] [c=21]I love you...[/c=12][/b]", "c10katie", QStringList() << "0000-family", isFriend ) );
239
 
    contactList_.insert( "unknown@unknown.wtfX", createFakeContact
240
 
      ( "unknown@unknown.wtfX", "Unknown Faceless Guy", "c11unknown", QStringList(), Contact::MSN_LIST_ALLOWED | Contact::MSN_LIST_REVERSE ) );
241
 
 
242
 
    // Same seed everytime, so we get the same results when starting multiple times KMess
243
 
    srand( 5 );
244
 
 
245
 
    QMap<QString,FakeContact>::ConstIterator cIt;
246
 
    for( cIt = contactList_.constBegin(); cIt != contactList_.constEnd(); ++cIt )
247
 
    {
248
 
      // Don't change to online all contacts
249
 
      if( ( rand() % 4 ) == 0 || ( rand() % 3 ) == 0 )
250
 
        continue;
251
 
 
252
 
        OnlineDetails details;
253
 
        details.msnObject    = ""; // no pic atm
254
 
        details.capabilities = 1985855532;
255
 
        details.status       = MsnStatus::getCode( (Status)(rand() % 8) );
256
 
        onlineList_[ cIt.key() ] = details;
257
 
    }
258
 
  }
259
 
}
260
 
 
261
 
 
262
 
 
263
 
/**
264
 
 * @brief The destructor
265
 
 *
266
 
 * Does nothing.
267
 
 */
268
 
MsnSocketNull::~MsnSocketNull()
269
 
{
270
 
  // Disconnect from the server on exit
271
 
  if( connected_ )
272
 
  {
273
 
    disconnectFromServer();
274
 
  }
275
 
 
276
 
  // Stop the timer
277
 
  responseTimer_.stop();
278
 
 
279
 
 
280
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
281
 
  kDebug() << "DESTROYED";
282
 
#endif
283
 
}
284
 
 
285
 
 
286
 
MsnSocketNull::FakeContact MsnSocketNull::createFakeContact( const QString &handle, const QString &friendlyName, const QString &guId, const QStringList &groupIds, int lists ) const
287
 
{
288
 
    MsnSocketNull::FakeContact c;
289
 
    c.handle = handle;
290
 
    c.friendlyName = friendlyName;
291
 
    c.guId = guId;
292
 
    c.groupIds = groupIds;
293
 
    c.lists = lists;
294
 
    return c;
295
 
}
296
 
 
297
 
 
298
 
/**
299
 
 * @brief Connect to the given server
300
 
 *
301
 
 * The initial connection is always made through the main Gateway.
302
 
 * The first server response usually carries the new gateway IP
303
 
 * which will be used for the rest of the connection.
304
 
 *
305
 
 * @param  server  The server hostname or IP address.
306
 
 * @param  port    The port to connect to. Not used - all of our
307
 
 *                 traffic goes through the default port.
308
 
 */
309
 
void MsnSocketNull::connectToServer( const QString& server, const quint16 port )
310
 
{
311
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
312
 
  if( server.isEmpty() && port == 0 )
313
 
  {
314
 
    kDebug() << "Faking connection to the default server.";
315
 
  }
316
 
  else
317
 
  {
318
 
    kDebug() << "Faking connection to server at " << server << ":" << port << ".";
319
 
  }
320
 
#endif
321
 
 
322
 
  // Set our internal state variables
323
 
  connected_ = true;
324
 
  setSendPings( true );
325
 
  responseTime_.start();
326
 
 
327
 
  // Start answering to the queued messages
328
 
  scheduleNext();
329
 
 
330
 
  // Signal we're ready
331
 
  emit connected();
332
 
  emit statusMessage( i18n( "Connected" ), false );
333
 
}
334
 
 
335
 
 
336
 
 
337
 
/**
338
 
 * @brief Send to the client the fake contact list
339
 
 *
340
 
 * This method sends the entire groups and contacts list back to KMess.
341
 
 */
342
 
void MsnSocketNull::deliverContactList()
343
 
{
344
 
  QMap<QString,QString>::ConstIterator gIt;
345
 
  for( gIt = groupList_.constBegin(); gIt != groupList_.constEnd(); ++gIt )
346
 
  {
347
 
    emit dataReceived( QStringList() << "LSG"
348
 
                                     << QUrl::toPercentEncoding( gIt.value() )
349
 
                                     << gIt.key(), QByteArray() );
350
 
  }
351
 
 
352
 
  QMap<QString,FakeContact>::ConstIterator cIt;
353
 
  for( cIt = contactList_.constBegin(); cIt != contactList_.constEnd(); ++cIt )
354
 
  {
355
 
    const FakeContact contact = cIt.value();
356
 
    emit dataReceived( QStringList() << "LST"
357
 
                                     << "N=" + contact.handle
358
 
                                     << "F=" + QUrl::toPercentEncoding( contact.friendlyName )
359
 
                                     << "C=" + contact.guId
360
 
                                     << QString::number( contact.lists )
361
 
                                     << "1" // Meaning unknown
362
 
                                     << contact.groupIds.join(","),
363
 
                       QByteArray() );
364
 
  }
365
 
 
366
 
  // Same seed everytime, so we get the same results when starting multiple times KMess
367
 
  srand( 5 );
368
 
 
369
 
  QMap<QString,OnlineDetails>::ConstIterator oIt;
370
 
  for( oIt = onlineList_.constBegin(); oIt != onlineList_.constEnd(); ++oIt )
371
 
  {
372
 
    const OnlineDetails details = oIt.value();
373
 
    emit dataReceived( QStringList() << "ILN"
374
 
                                     << "999" // TODO: what is this? the ack number of the initial CHG? We don't use it.
375
 
                                     << details.status
376
 
                                     << contactList_[ oIt.key() ].handle
377
 
                                     << contactList_[ oIt.key() ].friendlyName
378
 
                                     << QString::number( details.capabilities )
379
 
                                     << details.msnObject
380
 
                     , QByteArray() );
381
 
  }
382
 
}
383
 
 
384
 
 
385
 
 
386
 
/**
387
 
 * @brief Disconnect from the server
388
 
 *
389
 
 * This method closes the connection to the server and does some cleaning up.
390
 
 * It empties all buffers, and resets the internal state.
391
 
 * To avoid losing data, all pending requests are merged into a single huge
392
 
 * request and sent altogether, before closing the connection. We won't
393
 
 * receive responses for them; but at least we lose no outgoing data.
394
 
 *
395
 
 * @param  isTransfer  When set, a disconnected() signal won't be fired.
396
 
 *                     This is used to handle transfers to another server
397
 
 *                     without noticing a disconnect.
398
 
 */
399
 
void MsnSocketNull::disconnectFromServer( bool isTransfer )
400
 
{
401
 
  // Stop pinging
402
 
  setSendPings( false );
403
 
 
404
 
  // Just clean up if we are disconnected already
405
 
  if( ! connected_ )
406
 
  {
407
 
    return;
408
 
  }
409
 
 
410
 
  connected_ = false;
411
 
  messageQueue_.clear();
412
 
  responseTimer_.stop();
413
 
 
414
 
#ifdef KMESS_NETWORK_WINDOW
415
 
  KMESS_NET_CLOSE( this );
416
 
#endif
417
 
 
418
 
  // Do not signal a disconnection when we are transferring to another "server"
419
 
  if( isTransfer )
420
 
  {
421
 
    return;
422
 
  }
423
 
 
424
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
425
 
  kDebug() << "Emitting disconnection signal.";
426
 
#endif
427
 
 
428
 
  emit disconnected();
429
 
}
430
 
 
431
 
 
432
 
 
433
 
/**
434
 
 * @brief Return the local IP address
435
 
 *
436
 
 * Just returns "127.0.0.1". We're not going anywhere.
437
 
 *
438
 
 * @returns The IP address the socket uses at the system.
439
 
 */
440
 
QString MsnSocketNull::getLocalIp() const
441
 
{
442
 
  // We don't care about our own IP, return a generic one
443
 
  return QHostAddress( QHostAddress::LocalHost ).toString();
444
 
}
445
 
 
446
 
/**
447
 
 * @brief Schedule the next response
448
 
 *
449
 
 * Restarts the internal timer to determine when the next command
450
 
 * will be sent back to KMess
451
 
 */
452
 
void MsnSocketNull::scheduleNext()
453
 
{
454
 
  // Also send the ping if needed
455
 
  if( sendPings_ && responseTime_.restart() > 30000 )
456
 
  {
457
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
458
 
    kDebug() << "Emitting ping signal.";
459
 
#endif
460
 
 
461
 
    emit pingSent();
462
 
  }
463
 
 
464
 
  int nextResponseInterval = SOCKET_NULL_LAGGINESS_MIN + ( rand() % ( SOCKET_NULL_LAGGINESS_MAX + 1 ) );
465
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
466
 
  kDebug() << "Next response will happen in" << nextResponseInterval << "milliseconds.";
467
 
#endif
468
 
 
469
 
  responseTimer_.start( nextResponseInterval );
470
 
}
471
 
 
472
 
 
473
 
 
474
 
/**
475
 
 * @brief Answer a queued command
476
 
 *
477
 
 * This slot sends a response to a command sent by kmess
478
 
 */
479
 
void MsnSocketNull::sendResponse()
480
 
{
481
 
  // When the queue is empty, just try again every now and then
482
 
  if( messageQueue_.isEmpty() )
483
 
  {
484
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
485
 
    kDebug() << "Queue is empty, idling.";
486
 
#endif
487
 
    scheduleNext();
488
 
    return;
489
 
  }
490
 
 
491
 
  // Extract the oldest message from the queue
492
 
  bool success;
493
 
  QByteArray  lastMessage( messageQueue_.takeFirst() );
494
 
  QString     commandLine( QString::fromUtf8( lastMessage.data(), lastMessage.size() ) );
495
 
  QStringList     command( commandLine.trimmed().split(" ") );
496
 
 
497
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
498
 
  kDebug() << "Parsing oldest queue message:" << command;
499
 
#endif
500
 
 
501
 
  // Each server type has a different message set with different responses; so we implement them separately
502
 
  if( serverType_ == MsnSocketBase::SERVER_NOTIFICATION )
503
 
  {
504
 
    success = sendNotificationServerResponse( command );
505
 
  }
506
 
  else
507
 
  {
508
 
    success = sendSwitchboardServerResponse( command );
509
 
  }
510
 
 
511
 
  // Answer the next message
512
 
  if( success && ! messageQueue_.isEmpty() )
513
 
  {
514
 
    scheduleNext();
515
 
  }
516
 
  else
517
 
  {
518
 
    // Stop parsing stuff if there's an error or there's no messages to parse
519
 
    responseTimer_.stop();
520
 
  }
521
 
}
522
 
 
523
 
 
524
 
 
525
 
// Respond to a command when acting as a Notification Server
526
 
bool MsnSocketNull::sendNotificationServerResponse( const QStringList& command )
527
 
{
528
 
  QStringList replyCommand;
529
 
  QByteArray  payloadData;
530
 
 
531
 
  // Set the command for the reply to the name and ack id, so we can fill it with some other stuff
532
 
  for( int i = 0; i < 2; i++ )
533
 
  {
534
 
    if( command.size() <= i )
535
 
    {
536
 
      break;
537
 
    }
538
 
    replyCommand.append( command[ i ] );
539
 
  }
540
 
 
541
 
  // Reply to the message, if it contains a known command
542
 
 
543
 
  if( command[0] == "VER" )
544
 
  {
545
 
    // Initial version info
546
 
 
547
 
    // Only reply with "VER <ackID> <MSNP version>"
548
 
 
549
 
    emit dataReceived( replyCommand << command[2], payloadData );
550
 
  }
551
 
  else if( command[0] == "CVR" )
552
 
  {
553
 
    emit dataReceived( replyCommand << KMESS_VERSION << KMESS_VERSION << KMESS_VERSION
554
 
                                    << "http://www.kmess.org/download/" << "http://www.kmess.org/",
555
 
                       payloadData );
556
 
  }
557
 
  else if( command[0] == "USR" )
558
 
  {
559
 
    // Test once the server redirection capabilities of KMess
560
 
    if( ! isTransferDone_ )
561
 
    {
562
 
      isTransferDone_ = true;
563
 
 
564
 
      // TODO: WTF are the other two arguments? We don't use them..
565
 
      messageQueue_ << QString( "XFR %1 NS fakeServerRedirection::1234 0 fakeServerRedirection2::4321" )
566
 
                                .arg( command[1] ).toUtf8();
567
 
    }
568
 
    else
569
 
    {
570
 
      // We can't easily proceed with faking authentication, since it involves HTTP requests external to
571
 
      // the scope of this class. Therefore, we just 'confirm' authentication
572
 
      emit dataReceived( replyCommand << "OK"
573
 
                         << CurrentAccount::instance()->getHandle()
574
 
                         << SOCKET_NULL_IS_ACCOUNT_VERIFIED
575
 
                         << "0",
576
 
                         payloadData );
577
 
    }
578
 
  }
579
 
  else if( command[0] == "XFR" )
580
 
  {
581
 
    // Redirection request: we emit it as is
582
 
    emit dataReceived( command, payloadData );
583
 
  }
584
 
  else if( command[0] == "OUT" )
585
 
  {
586
 
    // Quit/disconnection request
587
 
 
588
 
    // If needed, fake having been disconnected from this server due to connection from another place/client
589
 
    if( SOCKET_NULL_FAKE_OTH )
590
 
    {
591
 
      emit dataReceived( replyCommand << "OTH", payloadData );
592
 
    }
593
 
 
594
 
    disconnectFromServer();
595
 
  }
596
 
  else if( command[0] == "SYN" )
597
 
  {
598
 
    // Contact list synchronization
599
 
    QDateTime now( QDateTime::currentDateTime() );
600
 
 
601
 
    // TODO: When we'll start caching contact list contents, this could need to be updated
602
 
    // to avoid sync issues with old data
603
 
    QString listDate( now.toString( Qt::ISODate ) + ".00-00:00" );
604
 
 
605
 
    emit dataReceived( replyCommand << listDate << listDate
606
 
                                    << QString::number( contactList_.count() )
607
 
                                    << QString::number( groupList_.count() ),
608
 
                       payloadData );
609
 
 
610
 
    // Then start sending out to the client our Original Fake(tm) of the contact list
611
 
    deliverContactList();
612
 
  }
613
 
  else if( command[0] == "GCF" )
614
 
  {
615
 
    // Do nothing.
616
 
    // TODO: It should send something back so check what and replicate here
617
 
  }
618
 
  else if( command[0] == "CHG" )
619
 
  {
620
 
    // Status change. Reply as is.
621
 
    emit dataReceived( command, payloadData );
622
 
  }
623
 
  else if( command[0] == "UUX" )
624
 
  {
625
 
    // Response to an UUX command: reply without the original payload (if any)
626
 
    replyCommand << "0";
627
 
    emit dataReceived( replyCommand, payloadData );
628
 
  }
629
 
  else if( command[0] == "URL" )
630
 
  {
631
 
    if( command[2] == "INBOX" )
632
 
    {
633
 
      replyCommand << "inbox";                 // TODO: Fix URL
634
 
      replyCommand << "http://mail.live.com/"; // TODO: Fix URL
635
 
      replyCommand << "wtf";                   // TODO: Fix URL
636
 
    }
637
 
    else if( command[2] == "COMPOSE" )
638
 
    {
639
 
      replyCommand << "compose";               // TODO: Fix URL
640
 
      replyCommand << "http://mail.live.com/"; // TODO: Fix URL
641
 
      replyCommand << "wtf";                   // TODO: Fix URL
642
 
    }
643
 
    else
644
 
    {
645
 
      replyCommand << "http://www.kmess.org/";
646
 
    }
647
 
 
648
 
    emit dataReceived( replyCommand, payloadData );
649
 
  }
650
 
  else if( command[0] == "ADC" )
651
 
  {
652
 
    QString arg( command[3] );
653
 
    QString str, handle;
654
 
 
655
 
    if( arg.startsWith("N=") )
656
 
    {
657
 
      handle = arg.mid(2).toLower();
658
 
    }
659
 
    else if( arg.startsWith("F=") )
660
 
    {
661
 
      str = QUrl::fromPercentEncoding( arg.mid(2).toUtf8() );
662
 
      foreach( const FakeContact &contact, contactList_ )
663
 
      {
664
 
        if( contact.friendlyName == str )
665
 
        {
666
 
          handle = contact.handle;
667
 
          break;
668
 
        }
669
 
      }
670
 
    }
671
 
    else if( arg.startsWith("C=") )
672
 
    {
673
 
      str = arg.mid(2);
674
 
      foreach( const FakeContact &contact, contactList_ )
675
 
      {
676
 
        if( contact.guId == str )
677
 
        {
678
 
          handle = contact.handle;
679
 
          break;
680
 
        }
681
 
      }
682
 
    }
683
 
 
684
 
    if( ! handle.isEmpty() && ! contactList_.contains( handle ) )
685
 
    {
686
 
      FakeContact item;
687
 
      item.handle = handle;
688
 
      item.guId = handle + "-guid";
689
 
      item.friendlyName = handle + "'s Friendly Name";
690
 
      item.groupIds = QStringList();
691
 
      item.lists = 3; // Allowed + Friend
692
 
 
693
 
      contactList_[ handle ] = item;
694
 
    }
695
 
 
696
 
    // Contact adding or list change, reply as is
697
 
    emit dataReceived( command, payloadData );
698
 
  }
699
 
  else if( command[0] == "REM" )
700
 
  {
701
 
    // Contact removal
702
 
 
703
 
    emit dataReceived( command, payloadData );
704
 
  }
705
 
  else if( command[0] == "ADG" )
706
 
  {
707
 
    // Group adding, append the group ID
708
 
 
709
 
    QString id( replyCommand.last().toLower() );
710
 
    id.replace( QRegExp( "[^a-z0-9]" ), "-" );
711
 
    id.append( "-" + QString::number( rand() ) );
712
 
 
713
 
    groupList_[ id ] = QUrl::fromPercentEncoding( replyCommand.last().toUtf8() );
714
 
 
715
 
    replyCommand = command;
716
 
    replyCommand << id;
717
 
    emit dataReceived( replyCommand, payloadData );
718
 
  }
719
 
  else if( command[0] == "REG" )
720
 
  {
721
 
    // Group rename
722
 
    groupList_[ command[2] ] = QUrl::fromPercentEncoding( command[3].toUtf8() );
723
 
 
724
 
    emit dataReceived( command, payloadData );
725
 
  }
726
 
  else if( command[0] == "RMG" )
727
 
  {
728
 
    // Group rename
729
 
    groupList_.remove( command[2] );
730
 
    emit dataReceived( command, payloadData );
731
 
  }
732
 
 
733
 
 
734
 
  else if( command[0] == "WTF" )
735
 
  {
736
 
    // Template for other commands :)
737
 
    replyCommand << QString();
738
 
    emit dataReceived( replyCommand, payloadData );
739
 
  }
740
 
  else
741
 
  {
742
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
743
 
    kDebug() << "Unknown notification message:" << command;
744
 
#endif
745
 
 
746
 
    disconnectFromServer();
747
 
    emit error( "Connection closed by peer", ERROR_DROP );
748
 
    return false;
749
 
  }
750
 
 
751
 
  return true;
752
 
}
753
 
 
754
 
 
755
 
 
756
 
// Respond to a command when acting as a Switchboard Server
757
 
bool MsnSocketNull::sendSwitchboardServerResponse( const QStringList& command )
758
 
{
759
 
  QStringList replyCommand;
760
 
  QByteArray  payloadData;
761
 
 
762
 
  // Set the command for the reply to the name and ack id, so we can fill it with some other stuff
763
 
  for( int i = 0; i < 2; i++ )
764
 
  {
765
 
    if( command.size() <= i )
766
 
    {
767
 
      break;
768
 
    }
769
 
    replyCommand.append( command[ i ] );
770
 
  }
771
 
 
772
 
  // Reply to the message, if it contains a known command
773
 
 
774
 
  // TODO: Everything here
775
 
  if( true )
776
 
  {
777
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
778
 
    kDebug() << "Unknown switchboard message:" << command;
779
 
#endif
780
 
 
781
 
    disconnectFromServer();
782
 
    emit error( "Connection closed by peer", ERROR_DROP );
783
 
    return false;
784
 
  }
785
 
 
786
 
  return true;
787
 
}
788
 
 
789
 
 
790
 
 
791
 
/**
792
 
 * @brief Set whether to send pings or not
793
 
 *
794
 
 * When sending pings, the application receives a periodic signal
795
 
 * which is used to perform timed updates (like receiving display pics).
796
 
 *
797
 
 * @param  sendPings  True to send pings.
798
 
 */
799
 
void MsnSocketNull::setSendPings( bool sendPings )
800
 
{
801
 
  sendPings_ = sendPings;
802
 
}
803
 
 
804
 
 
805
 
 
806
 
/**
807
 
 * @brief Write data to the gateway without any conversion
808
 
 *
809
 
 * This method creates a new request and queues it to be sent when possible.
810
 
 * See sendNextRequest() for more info on sending.
811
 
 *
812
 
 * @param  data  Contents of the message which will be sent
813
 
 * @return -1 on error, or else always the exact size of the sent data.
814
 
 */
815
 
qint64 MsnSocketNull::writeBinaryData( const QByteArray &data )
816
 
{
817
 
  if( ! connected_ )
818
 
  {
819
 
    kWarning() << "Attempted to write data to a disconnected interface, aborting.";
820
 
    return -1;
821
 
  }
822
 
 
823
 
#ifdef KMESSDEBUG_CONNECTION_SOCKET_NULL
824
 
  kDebug() << "Appending message:" << data;
825
 
  kDebug() << "To current list:" << messageQueue_;
826
 
#endif
827
 
 
828
 
  // Reactivate the timer if it's been stopped due to an empty queue
829
 
  if( ! responseTimer_.isActive() )
830
 
  {
831
 
    scheduleNext();
832
 
  }
833
 
 
834
 
  messageQueue_.append( data.trimmed() );
835
 
 
836
 
  return data.size();
837
 
}
838
 
 
839
 
 
840
 
 
841
 
/**
842
 
 * @brief Write a string to the gateway
843
 
 *
844
 
 * This is a convenience method for writeBinaryData, which can be used to directly send a QString.
845
 
 *
846
 
 * @param  data  The message which will be sent
847
 
 * @return -1 on error, or else always the exact size of the sent data.
848
 
 */
849
 
qint64 MsnSocketNull::writeData( const QString &data )
850
 
{
851
 
  return writeBinaryData( data.toUtf8() );
852
 
}
853
 
 
854
 
 
855
 
 
856
 
#include "msnsocketnull.moc"