/*
 * oscar.c
 *
 * Copyright (C) 2009-2011 by ipoque GmbH
 * Copyright (C) 2011-18 - ntop.org
 *
 * This file is part of nDPI, an open source deep packet inspection
 * library based on the OpenDPI and PACE technology by ipoque GmbH
 *
 * nDPI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * nDPI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with nDPI.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "ndpi_protocol_ids.h"

#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_OSCAR

#include "ndpi_api.h"

#define FLAPVERSION         0x00000001

/* Flap channels */
#define SIGNON              0x01
#define DATA                0x02
#define O_ERROR               0x03
#define SIGNOFF             0x04
#define KEEP_ALIVE          0x05

/* Signon tags */
#define SCREEN_NAME         0x0001
#define PASSWD              0x0002
#define CLIENT_NAME	    0x0003
#define BOS                 0x0005
#define LOGIN_COOKIE	    0x0006
#define MAJOR_VERSION	    0x0017
#define MINOR_VERSION	    0x0018
#define POINT_VERSION	    0x0019
#define BUILD_NUM	    0x001a
#define MULTICONN_FLAGS	    0x004a
#define CLIENT_LANG         0x00OF
#define CLIENT_CNTRY        0x00OE
#define CLIENT_RECONNECT    0x0094

/* Family */
#define GE_SE_CTL           0x0001
#define LOC_SRV             0x0002
#define BUDDY_LIST          0x0003
#define IM                  0x0004
#define IS                  0x0006
#define ACC_ADM             0x0007
#define POPUP               0x0008
#define PMS                 0x0009
#define USS                 0x000b
#define CHAT_ROOM_SETUP     0x000d
#define CHAT_ROOM_ACT       0x000e
#define USER_SRCH	    0x000f
#define BUDDY_ICON_SERVER   0x0010
#define SERVER_STORED_INFO  0x0013
#define ICQ                 0x0015
#define INIT_AUTH           0x0017
#define EMAIL               0x0018
#define IS_EXT              0x0085


static void ndpi_int_oscar_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
					  struct ndpi_flow_struct *flow)
{

  struct ndpi_packet_struct *packet = &flow->packet;
  struct ndpi_id_struct *src = flow->src;
  struct ndpi_id_struct *dst = flow->dst;

  ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OSCAR, NDPI_PROTOCOL_UNKNOWN);

  if (src != NULL) {
    src->oscar_last_safe_access_time = packet->tick_timestamp;
  }
  if (dst != NULL) {
    dst->oscar_last_safe_access_time = packet->tick_timestamp;
  }
}

/**
   Oscar connection work on FLAP protocol.

   FLAP is a low-level communications protocol that facilitates the development of higher-level, datagram-oriented, communications layers.
   It is used on the TCP connection between all clients and servers.
   Here is format of FLAP datagram
**/
static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct
					  *ndpi_struct, struct ndpi_flow_struct *flow)
{

  int excluded = 0;
//  u_int8_t channel;
  u_int16_t family;
  u_int16_t type;
  u_int16_t flag;
  u_int32_t req_ID;

  struct ndpi_packet_struct * packet = &flow->packet;

  struct ndpi_id_struct * src = flow->src;
  struct ndpi_id_struct * dst = flow->dst;

  /* FLAP__Header
   *
   * [ 6 byte FLAP header ]
   * +-----------+--------------+-------------+--------------+
   * | 0x2a (1B) | Channel (1B) | SeqNum (2B) | PyldLen (2B) |
   * +-----------+--------------+-------------+--------------+
   *
   * [ 4 byte of data ]
   *
   * */
  if (packet->payload_packet_len >= 6 && packet->payload[0] == 0x2a)
    {

      /* FLAP__FRAME_TYPE (Channel)*/
      u_int8_t channel = get_u_int8_t(packet->payload, 1);

      /*
	 Initialize the FLAP connection.

	 SIGNON -> FLAP__SIGNON_FRAME
	 +--------------------------------------------------+
	 + FLAP__Header | 6 byte                            +
	 + FlapVersion  | 4 byte  (Always 1 = 0x00000001)   +
	 + TLVs         | [Class: FLAP__SIGNON_TAGS] TLVs   +
	 +--------------------------------------------------+
      */
      if (channel == SIGNON &&
	  get_u_int16_t(packet->payload, 4) == htons(packet->payload_packet_len - 6) &&
	  get_u_int32_t(packet->payload, 6) == htonl(FLAPVERSION))
	{

	  /* No TLVs */
	  if(packet->payload_packet_len == 10)
	    {
	      NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Sign In \n");
	      ndpi_int_oscar_add_connection(ndpi_struct, flow);
	      return;
	    }
	  /* /\* SCREEN_NAME *\/ */
	  /* if (get_u_int16_t(packet->payload, 10) == htons(SCREEN_NAME)) /\* packet->payload[10] == 0x00 && packet->payload[11] == 0x01 *\/ */
	  /*   { */
	  /*     NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Screen Name \n"); */
	  /*     ndpi_int_oscar_add_connection(ndpi_struct, flow); */
	  /*     return; */
	  /*   } */
	  /* /\* PASSWD *\/ */
	  /* if (get_u_int16_t(packet->payload, 10) == htons(PASSWD)) /\* packet->payload[10] == 0x00 && packet->payload[11] == 0x02 *\/ */
	  /*   { */
	  /*     NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Password (roasted) \n"); */
	  /*     ndpi_int_oscar_add_connection(ndpi_struct, flow); */
	  /*     return; */
	  /*   } */
	  /* CLIENT_NAME */
	  if (get_u_int16_t(packet->payload, 10) == htons(CLIENT_NAME)) /* packet->payload[10] == 0x00 && packet->payload[11] == 0x03 */
	    {
	      NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Client Name \n");
	      ndpi_int_oscar_add_connection(ndpi_struct, flow);
	      return;
	    }
	  /* LOGIN_COOKIE */
	  if (get_u_int16_t(packet->payload, 10) == htons(LOGIN_COOKIE) &&
	      get_u_int16_t(packet->payload, 12) == htons(0x0100))
	    {
	      if(get_u_int16_t(packet->payload, packet->payload_packet_len - 5) == htons(MULTICONN_FLAGS)) /* MULTICONN_FLAGS */
		{
		  if(get_u_int16_t(packet->payload, packet->payload_packet_len - 3) == htons(0x0001))
		    if((get_u_int8_t(packet->payload, packet->payload_packet_len - 1) == 0x00) ||
		       (get_u_int8_t(packet->payload, packet->payload_packet_len - 1) == 0x01) ||
		       (get_u_int8_t(packet->payload, packet->payload_packet_len - 1) == 0x03))
		      {
			NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Login \n");
			ndpi_int_oscar_add_connection(ndpi_struct, flow);
			return;
		      }
		}
	    }
	  /* MAJOR_VERSION */
	  if (get_u_int16_t(packet->payload, 10) == htons(MAJOR_VERSION))
	    {
	      NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Major_Version \n");
	      ndpi_int_oscar_add_connection(ndpi_struct, flow);
	      return;
	    }
	  /* MINOR_VERSION */
	  if (get_u_int16_t(packet->payload, 10) == htons(MINOR_VERSION))
	    {
	      NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Minor_Version \n");
	      ndpi_int_oscar_add_connection(ndpi_struct, flow);
	      return;
	    }
	  /* POINT_VERSION */
	  if (get_u_int16_t(packet->payload, 10) == htons(POINT_VERSION))
	    {
	      NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Point_Version \n");
	      ndpi_int_oscar_add_connection(ndpi_struct, flow);
	      return;
	    }
	  /* BUILD_NUM */
	  if (get_u_int16_t(packet->payload, 10) == htons(BUILD_NUM))
	    {
	      NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Build_Num \n");
	      ndpi_int_oscar_add_connection(ndpi_struct, flow);
	      return;
	    }
	  /* CLIENT_RECONNECT */
	  if (get_u_int16_t(packet->payload, 10) == htons(CLIENT_RECONNECT))
	    {
	      NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Client_Reconnect \n");
	      ndpi_int_oscar_add_connection(ndpi_struct, flow);
	      return;
	    }
	}

      /*
	 Messages using the FLAP connection, usually a SNAC message.

	 DATA -> FLAP__DATA_FRAME
	 +-------------------------+
	 + FLAP__Header | 6 byte   +
	 + SNAC__Header | 10 byte  +
	 + snac         |          +
	 +-------------------------+

	 SNAC__Header
	 +----------------------------------------------+
	 + ID           | 4 byte (2 foodgroup + 2 type) +
	 + FLAGS        | 2 byte                        +
	 + requestId    | 4 byte                        +
	 +----------------------------------------------+
      */
      if (channel == DATA)
	{
	  if (packet->payload_packet_len >= 8)
	    family = get_u_int16_t(packet->payload, 6);
	  else
	    family = 0;
	  if (packet->payload_packet_len >= 10)
	    type = get_u_int16_t(packet->payload, 8);
	  else
	    type = 0;
	  if (family == 0 || type == 0)
	  {
	      NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
	      return;
	  }

	  /* Family 0x0001 */
	  if (family == htons(GE_SE_CTL))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x0008): break;
	      case  (0x0009): break;
	      case  (0x000a): break;
	      case  (0x000b): break;
	      case  (0x000c): break;
	      case  (0x000d): break;
	      case  (0x000e): break;
	      case  (0x000f): break;
	      case  (0x0010): break;
	      case  (0x0011): break;
	      case  (0x0012): break;
	      case  (0x0013): break;
	      case  (0x0014): break;
	      case  (0x0015): break;
	      case  (0x0016): break;
	      case  (0x0017): break;
	      case  (0x0018): break;
	      case  (0x001e): break;
	      case  (0x001f): break;
	      case  (0x0020): break;
	      case  (0x0021): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0002 */
	  if (family == htons(LOC_SRV))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x0008): break;
	      case  (0x0009): break;
	      case  (0x000a): break;
	      case  (0x000b): break;
	      case  (0x000c): break;
	      case  (0x000f): break;
	      case  (0x0010): break;
	      case  (0x0015): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0003 */
	  if (family == htons(BUDDY_LIST))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x0008): break;
	      case  (0x0009): break;
	      case  (0x000a): break;
	      case  (0x000b): break;
	      case  (0x000c): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0004 */
	  if (family == htons(IM))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x0008): break;
	      case  (0x0009): break;
	      case  (0x000a): break;
	      case  (0x000b): break;
	      case  (0x000c): break;
	      case  (0x0014): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0006 */
	  if (family == htons(IS))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0007 */
	  if (family == htons(ACC_ADM))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x0008): break;
	      case  (0x0009): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0008 */
	  if (family == htons(POPUP))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0009 */
	  if (family == htons(PMS))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x0008): break;
	      case  (0x0009): break;
	      case  (0x000a): break;
	      case  (0x000b): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x000b */
	  if (family == htons(USS))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x000d */
	  if (family == htons(CHAT_ROOM_SETUP))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x0008): break;
	      case  (0x0009): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x000e */
	  if (family == htons(CHAT_ROOM_ACT))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x0008): break;
	      case  (0x0009): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x000f */
	  if (family == htons(USER_SRCH))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0010 */
	  if (family == htons(BUDDY_ICON_SERVER))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0013 */
	  if (family == htons(SERVER_STORED_INFO))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x0008): break;
	      case  (0x0009): break;
	      case  (0x000a): break;
	      case  (0x000e): break;
	      case  (0x000f): break;
	      case  (0x0011): break;
	      case  (0x0012): break;
	      case  (0x0014): break;
	      case  (0x0015): break;
	      case  (0x0016): break;
	      case  (0x0018): break;
	      case  (0x001a): break;
	      case  (0x001b): break;
	      case  (0x001c): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0015 */
	  if (family == htons(ICQ))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0017 */
	  if (family == htons(INIT_AUTH))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      case  (0x0004): break;
	      case  (0x0005): break;
	      case  (0x0006): break;
	      case  (0x0007): break;
	      case  (0x000a): break;
	      case  (0x000b): break;
	      default: excluded = 1;
	      }
	    }
	  /* Family 0x0018 */
	  if (family == htons(EMAIL))
	    {
	      /* TODO */
	    }
	  /* Family 0x0085 */
	  if (family == htons(IS_EXT))
	    {
	      switch (type) {

	      case  (0x0001): break;
	      case  (0x0002): break;
	      case  (0x0003): break;
	      default: excluded = 1;
	      }
	    }

	  if(excluded == 1)
	    {
	      NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
	    }

	  /* flag */
	  if (packet->payload_packet_len >= 12)
	  {
	    flag = get_u_int16_t(packet->payload, 10);
	    if (flag == htons(0x0000)|| flag == htons(0x8000) || flag == htons(0x0001))
	      {
	        if (packet->payload_packet_len >= 16)
		{
		  /* request ID */
		  req_ID = get_u_int32_t(packet->payload, 12);
		  if((req_ID <= ((u_int32_t)-1)))
		    {
		      NDPI_LOG_INFO(ndpi_struct, "found OSCAR\n");
		      ndpi_int_oscar_add_connection(ndpi_struct, flow);
		      return;
		    }
		}
	      }
	  }
	}
      /*
	 ERROR -> FLAP__ERROR_CHANNEL_0x03
	 A FLAP error - rare
      */
      if (channel == O_ERROR)
	{
	  NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Error frame \n");
	  ndpi_int_oscar_add_connection(ndpi_struct, flow);
	  return;
	}
      /*
	 Close down the FLAP connection gracefully.
	 SIGNOFF: FLAP__SIGNOFF_CHANNEL_0x04
      */
      if (channel == SIGNOFF)
	{
	  NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Signoff frame \n");
	  ndpi_int_oscar_add_connection(ndpi_struct, flow);
	  return;
	}
      /*
	 Send a heartbeat to server to help keep connection open.
	 KEEP_ALIVE: FLAP__KEEP_ALIVE_CHANNEL_0x05
      */
      if (channel == KEEP_ALIVE)
	{
	  NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Keep Alive frame \n");
	  ndpi_int_oscar_add_connection(ndpi_struct, flow);
	  return;
	}
    }


  /* detect http connections */
  if (packet->payload_packet_len >= 18) {
    if ((packet->payload[0] == 'P') && (memcmp(packet->payload, "POST /photo/upload", 18) == 0)) {
      NDPI_PARSE_PACKET_LINE_INFO(ndpi_struct, flow, packet);
      if (packet->host_line.len >= 18 && packet->host_line.ptr != NULL) {
	if (memcmp(packet->host_line.ptr, "lifestream.aol.com", 18) == 0) {
	  NDPI_LOG_INFO(ndpi_struct,
		   "found OSCAR over HTTP, POST method\n");
	  ndpi_int_oscar_add_connection(ndpi_struct, flow);
	  return;
	}
      }
    }
  }
  if (packet->payload_packet_len > 40) {
    if ((packet->payload[0] == 'G') && (memcmp(packet->payload, "GET /", 5) == 0)) {
      if ((memcmp(&packet->payload[5], "aim/fetchEvents?aimsid=", 23) == 0) ||
	  (memcmp(&packet->payload[5], "aim/startSession?", 17) == 0) ||
	  (memcmp(&packet->payload[5], "aim/gromit/aim_express", 22) == 0) ||
	  (memcmp(&packet->payload[5], "b/ss/aolwpaim", 13) == 0) ||
	  (memcmp(&packet->payload[5], "hss/storage/aimtmpshare", 23) == 0)) {
	NDPI_LOG_INFO(ndpi_struct, "found OSCAR over HTTP, GET /aim/\n");
	ndpi_int_oscar_add_connection(ndpi_struct, flow);
	return;
      }

      if ((memcmp(&packet->payload[5], "aim", 3) == 0) || (memcmp(&packet->payload[5], "im", 2) == 0)) {
	NDPI_PARSE_PACKET_LINE_INFO(ndpi_struct, flow, packet);
	if (packet->user_agent_line.len > 15 && packet->user_agent_line.ptr != NULL &&
	    ((memcmp(packet->user_agent_line.ptr, "mobileAIM/", 10) == 0) ||
	     (memcmp(packet->user_agent_line.ptr, "ICQ/", 4) == 0) ||
	     (memcmp(packet->user_agent_line.ptr, "mobileICQ/", 10) == 0) ||
	     (memcmp(packet->user_agent_line.ptr, "AIM%20Free/", NDPI_STATICSTRING_LEN("AIM%20Free/")) == 0) ||
	     (memcmp(packet->user_agent_line.ptr, "AIM/", 4) == 0))) {
	  NDPI_LOG_INFO(ndpi_struct, "found OSCAR over HTTP\n");
	  ndpi_int_oscar_add_connection(ndpi_struct, flow);
	  return;
	}
      }
      NDPI_PARSE_PACKET_LINE_INFO(ndpi_struct, flow, packet);
      if (packet->referer_line.ptr != NULL && packet->referer_line.len >= 22) {

	if (memcmp(&packet->referer_line.ptr[packet->referer_line.len - NDPI_STATICSTRING_LEN("WidgetMain.swf")],
		   "WidgetMain.swf", NDPI_STATICSTRING_LEN("WidgetMain.swf")) == 0) {
	  u_int16_t i;
	  for (i = 0; i < (packet->referer_line.len - 22); i++) {
	    if (packet->referer_line.ptr[i] == 'a') {
	      if (memcmp(&packet->referer_line.ptr[i + 1], "im/gromit/aim_express", 21) == 0) {
		NDPI_LOG_INFO(ndpi_struct,
			 "found OSCAR over HTTP : aim/gromit/aim_express\n");
		ndpi_int_oscar_add_connection(ndpi_struct, flow);
		return;
	      }
	    }
	  }
	}
      }
    }
    if (memcmp(packet->payload, "CONNECT ", 8) == 0) {
      if (memcmp(packet->payload, "CONNECT login.icq.com:443 HTTP/1.", 33) == 0) {
	NDPI_LOG_INFO(ndpi_struct, "found OSCAR ICQ-HTTP\n");
	ndpi_int_oscar_add_connection(ndpi_struct, flow);
	return;
      }
      if (memcmp(packet->payload, "CONNECT login.oscar.aol.com:5190 HTTP/1.", 40) == 0) {
	NDPI_LOG_INFO(ndpi_struct, "found OSCAR AIM-HTTP\n");
	ndpi_int_oscar_add_connection(ndpi_struct, flow);
	return;
      }

    }
  }

  if (packet->payload_packet_len > 43
      && memcmp(packet->payload, "GET http://http.proxy.icq.com/hello HTTP/1.", 43) == 0) {
    NDPI_LOG_INFO(ndpi_struct, "found OSCAR ICQ-HTTP PROXY\n");
    ndpi_int_oscar_add_connection(ndpi_struct, flow);
    return;
  }

  if (packet->payload_packet_len > 46
      && memcmp(packet->payload, "GET http://aimhttp.oscar.aol.com/hello HTTP/1.", 46) == 0) {
    NDPI_LOG_INFO(ndpi_struct, "found OSCAR AIM-HTTP PROXY\n");
    ndpi_int_oscar_add_connection(ndpi_struct, flow);
    return;
  }

  if (packet->payload_packet_len > 5 && get_u_int32_t(packet->payload, 0) == htonl(0x05010003)) {
    NDPI_LOG_DBG2(ndpi_struct, "Maybe OSCAR Picturetransfer\n");
    return;
  }

  if (packet->payload_packet_len == 10 && get_u_int32_t(packet->payload, 0) == htonl(0x05000001) &&
      get_u_int32_t(packet->payload, 4) == 0) {
    NDPI_LOG_DBG2(ndpi_struct, "Maybe OSCAR Picturetransfer\n");
    return;
  }

  if (packet->payload_packet_len >= 70 &&
      memcmp(&packet->payload[packet->payload_packet_len - 26],
	     "\x67\x00\x65\x00\x74\x00\x43\x00\x61\x00\x74\x00\x61\x00\x6c\x00\x6f\x00\x67", 19) == 0) {
    NDPI_LOG_INFO(ndpi_struct, "found OSCAR PICTURE TRANSFER\n");
    ndpi_int_oscar_add_connection(ndpi_struct, flow);
    return;
  }

  if (NDPI_SRC_OR_DST_HAS_PROTOCOL(src, dst, NDPI_PROTOCOL_OSCAR) != 0) {

    if (flow->packet_counter == 1
	&&
	((packet->payload_packet_len == 9
	  && memcmp(packet->payload, "\x00\x09\x00\x00\x83\x01\xc0\x00\x00", 9) == 0)
	 || (packet->payload_packet_len == 13
	     && (memcmp(packet->payload, "\x00\x0d\x00\x87\x01\xc0", 6) == 0
		 || memcmp(packet->payload, "\x00\x0d\x00\x87\x01\xc1", 6) == 0)))) {
      flow->oscar_video_voice = 1;
    }
    if (flow->oscar_video_voice && ntohs(get_u_int16_t(packet->payload, 0)) == packet->payload_packet_len
	&& packet->payload[2] == 0x00 && packet->payload[3] == 0x00) {
    }

    if (packet->payload_packet_len >= 70 && ntohs(get_u_int16_t(packet->payload, 4)) == packet->payload_packet_len) {
      if (memcmp(packet->payload, "OFT", 3) == 0 &&
	  ((packet->payload[3] == '3' && ((memcmp(&packet->payload[4], "\x01\x00\x01\x01", 4) == 0)
					  || (memcmp(&packet->payload[6], "\x01\x01\x00", 3) == 0)))
	   || (packet->payload[3] == '2' && ((memcmp(&packet->payload[6], "\x01\x01", 2)
					      == 0)
					     )))) {
	// FILE TRANSFER PATTERN:: OFT3 or OFT2
	NDPI_LOG_INFO(ndpi_struct, "found OSCAR FILE TRANSFER\n");
	ndpi_int_oscar_add_connection(ndpi_struct, flow);
	return;
      }

      if (memcmp(packet->payload, "ODC2", 4) == 0 && memcmp(&packet->payload[6], "\x00\x01\x00\x06", 4) == 0) {
	//PICTURE TRANSFER PATTERN EXMAPLE::
	//4f 44 43 32 00 4c 00 01 00 06 00 00 00 00 00 00  ODC2.L..........
	NDPI_LOG_INFO(ndpi_struct, "found OSCAR PICTURE TRANSFER\n");
	ndpi_int_oscar_add_connection(ndpi_struct, flow);
	return;
      }
    }
    if (packet->payload_packet_len > 40 && (memcmp(&packet->payload[2], "\x04\x4a\x00", 3) == 0)
	&& (memcmp(&packet->payload[6], "\x00\x00", 2) == 0)
	&& packet->payload[packet->payload_packet_len - 15] == 'F'
	&& packet->payload[packet->payload_packet_len - 12] == 'L'
	&& (memcmp(&packet->payload[packet->payload_packet_len - 6], "DEST", 4) == 0)
	&& (memcmp(&packet->payload[packet->payload_packet_len - 2], "\x00\x00", 2) == 0)) {
      NDPI_LOG_INFO(ndpi_struct, "found OSCAR PICTURE TRANSFER\n");
      ndpi_int_oscar_add_connection(ndpi_struct, flow);
      if (ntohs(packet->tcp->dest) == 443 || ntohs(packet->tcp->source) == 443) {
	flow->oscar_ssl_voice_stage = 1;
      }
      return;

    }
  }
  if (flow->packet_counter < 3 && packet->payload_packet_len > 11 && (memcmp(packet->payload, "\x00\x37\x04\x4a", 4)
								      || memcmp(packet->payload, "\x00\x0a\x04\x4a",
										4))) {
    return;
  }


  if (packet->detected_protocol_stack[0] != NDPI_PROTOCOL_OSCAR) {
    NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
    return;
  }
}

void ndpi_search_oscar(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow)
{
  struct ndpi_packet_struct *packet = &flow->packet;
  NDPI_LOG_DBG(ndpi_struct, "search OSCAR\n");
  if (packet->tcp != NULL) {
    ndpi_search_oscar_tcp_connect(ndpi_struct, flow);
  }
}


void init_oscar_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask)
{
  ndpi_set_bitmask_protocol_detection("Oscar", ndpi_struct, detection_bitmask, *id,
				      NDPI_PROTOCOL_OSCAR,
				      ndpi_search_oscar,
				      NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
				      SAVE_DETECTION_BITMASK_AS_UNKNOWN,
				      ADD_TO_DETECTION_BITMASK);

  *id += 1;
}
