+++ /dev/null
-=begin\r
-Raimbo, the Ruby AIM Bot Object.\r
-Copyright (C) 2003 Kurt M. Dresner (
[email protected])
\r
-\r
-This code is derived from code by Justin Bishop (
[email protected])
\r
-Please see http://filebox.vt.edu/users/jubishop/Raimo/ for the\r
-original version of this file.\r
-\r
---\r
-This code is derived from the one mention aboved.\r
---\r
-This program is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation.\r
-\r
-This program is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with this program; if not, write to the Free Software\r
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
-=end\r
-\r
-require 'oscar/types/flap'\r
-require 'oscar/types/tlv'\r
-require 'oscar/types/snac'\r
-\r
-require 'logger'\r
-require 'socket'\r
-require 'thread'\r
-\r
-module OSCAR\r
- Client_ID_String = "Ruby AIM Library";\r
- Client_ID = 0xBEEF;\r
- Client_Major_Version = 0x0001;\r
- Client_Minor_Version = 0x0000;\r
- Client_Lesser_Version = 0x0000;\r
- Client_Build_Version = 0x0000;\r
- Client_Distribution = 0x00000001;\r
- Client_Language = "en";\r
- Client_Country = "us";\r
- \r
- class OscarError < RuntimeError\r
- end\r
- \r
- class Client\r
- include OSCAR::Types::TLV\r
-\r
- Multithreaded = true; #set to false for more straightforward debugging\r
- \r
- $log = Logger.new(STDOUT);\r
- $log.level = Logger::Severity::INFO;\r
-\r
- Roasting_Data = [0xF3, 0x26, 0x81, 0xC4,\r
- 0x39, 0x86, 0xDB, 0x92,\r
- 0x71, 0xA3, 0xB9, 0xE6,\r
- 0x53, 0x7A, 0x95, 0x7C];\r
- \r
- Auth_Server_Address = "login.oscar.aol.com";\r
- Auth_Server_Port = 5190;\r
-\r
- def Client.logger()\r
- return $log\r
- end\r
-\r
- def initialize(username)\r
- @username = username\r
- @message_cb = Hash.new\r
- end\r
-\r
- def auth(password)\r
- begin\r
- Client.logger.info("Client::auth"){ "Connecting to auth server." };\r
- @conn = TCPSocket.new(Auth_Server_Address, Auth_Server_Port);\r
- if(!@conn)\r
- Client.logger.error("Client::auth"){ "Unable to create socket to authentication server." };\r
- raise OscarError, "Unable to create socket to authentication server."\r
- end\r
- \r
- Client.logger.info("Client::auth"){ "Creating FLAP service for authentication service." };\r
- #this one is local, since it won't get reused\r
- flapsrv = OSCAR::Types::FLAP.new(@conn, self);\r
- \r
- flapsrv.get_signon();\r
-\r
- formattedUsername, bosServer, cookie = flapsrv.get_authentication_details(@username, roast(password));\r
- \r
- Client.logger.info("Client::auth"){ "Done authenticating: #{formattedUsername}, #{bosServer}." };\r
- @username = formattedUsername;\r
- @loginServer = bosServer;\r
- @cookie = cookie;\r
- ensure\r
- if(@conn)\r
- @conn.close();\r
- end\r
- @conn = nil;\r
- end\r
-\r
- if(!@cookie)\r
- Client.logger.error("Client::login"){ "login() without authenticate()." };\r
- raise OscarError, "Not authenticated."\r
- end\r
- \r
- addr, port = @loginServer.split(":");\r
- Client.logger.info("Client::login"){ "Connecting to BOS server #{addr} on port #{port}." };\r
- @conn = TCPSocket.new(addr, port);\r
- if(!@conn)\r
- Client.logger.error("Client::login"){ "Unable to create socket to BOS server." };\r
- raise OscarError, "Unable to create socket to BOS server."\r
- end\r
- \r
- @flapsrv = OSCAR::Types::FLAP.new(@conn, self);\r
- \r
- @flapsrv.get_signon();\r
- @flapsrv.send_credentials(@cookie);\r
- @flapsrv.snac.negotiate_services();\r
- @flapsrv.snac.negotiate_rate_limits();\r
- @flapsrv.snac.negotiate_capabilities();\r
- @flapsrv.snac.send_ready();\r
- end\r
- \r
- def send(username, message)\r
- if(!@conn)\r
- raise OscarError, "Not connected."\r
- end\r
- Client.logger.info("Client::send"){ "Sending to #{username}, msg #{message}." };\r
- @flapsrv.snac.send_message(username, message);\r
- end\r
-\r
- def close()\r
- @conn.close\r
- @conn = nil\r
- end\r
-\r
- #Message CB's\r
- def handle_message(userid, message)\r
- @message_cb[:all_messages].each do |block|\r
- block.call(userid,message)\r
- end if @message_cb.has_key? :all_messages\r
-\r
- @message_cb[userid].each do |block|\r
- block.call(userid,message)\r
- end if @message_cb.has_key? userid\r
- end\r
-\r
- def add_message_callback(userid=:all_messages, &block)\r
- @message_cb[userid] = Array.new unless @message_cb.has_key? userid\r
- @message_cb[userid].push block\r
- end\r
-\r
- ###################################################\r
- # Private helper functions\r
- #\r
- \r
- # Roast a password per the AIM roasting standard.\r
- def roast(password)\r
- passnum = password.unpack('C*');\r
- roasted = [];\r
- passnum.each_index { |index|\r
- roasted << (passnum[index] ^ Roasting_Data[index % Roasting_Data.length()]);\r
- }\r
- return roasted.pack("c*");\r
- end\r
-\r
- def normalize(string)\r
- retstring = string.gsub(/[\\\}\{\(\)\[\]\$\"]/) { '\\'+$& }\r
- # This next bit fixes some special character stuff from HTML. The\r
- # HTML would work fine in the chat, but not in the IM where it is not\r
- # interpreted as HTML. Thus, we must fix the encoding, which seems\r
- # to precede characters with a special 195 character. So, we just make\r
- # the switch.\r
- retstring.gsub!(/#{195.chr}(.)/) { ($1[0] + 64).chr }\r
- return "\"#{retstring}\"";\r
- end\r
-\r
- def normalizechat(string)\r
- retstring = string.gsub(/[\\\}\{\(\)\[\]\$\"]/) { '\\'+$& }\r
- retstring = retstring.gsub(/\n/, '<br>')\r
- return "\"#{retstring}\"";\r
- end\r
- end #module Client\r
-end #module OSCAR \r
+++ /dev/null
-require 'socket'
-
-require 'oscar/client'
-require 'oscar/types/tlv'
-require 'oscar/types/threads/singlethread'
-require 'oscar/types/threads/multithread'
-
-module OSCAR
- module Types
- class FLAP
- include OSCAR::Types::TLV
-
- ###################################################
- # Constants
- #
- FLAP_Type_Signon = 1;
- FLAP_Type_Data = 2;
- FLAP_Type_Error = 3;
- FLAP_Type_Signoff = 4;
- FLAP_Type_Keepalive = 5;
-
- FLAP_Type_nil = 0;
-
- Sequence_Max = 65535;
- Sequence_Min = 0;
-
- ###################################################
- # Object functionality
- #
- def initialize(connection, parent)
- @conn = connection;
- @snacsrv = OSCAR::Types::SNAC.new(self);
- @parent = parent;
-
- initialize_threads();
- end
-
- def snac
- return @snacsrv;
- end
-
- ###################################################
- # Stubs
- #
- def initialize_threads()
- if Client::Multithreaded
- return mt_initialize_threads();
- else
- return st_initialize_threads();
- end
- end
- def wait_for_message(type)
- if Client::Multithreaded
- return mt_wait_for_message(type);
- else
- return st_wait_for_message(type);
- end
- end
-
- ###################################################
- # FLAP operations
- #
- def get_signon()
- Client.logger.info("FLAP::get_signon"){ "Looking for signon FLAP." };
- #enqueue ourselves to get the response
-
- wait_for_message(FLAP_Type_Signon);
- end
-
- def get_authentication_details(username, roastedPassword);
- Client.logger.info("FLAP::get_authentication_cookie"){ "Sending cli_ident FLAP." };
- send_flap(construct_cli_ident_flap(username, roastedPassword))
-
- Client.logger.info("FLAP::get_authentication_cookie"){ "Waiting for srv_cookie FLAP." };
- data = wait_for_message(FLAP_Type_Signoff)
-
- formattedUsername, bosServer, cookie = parse_srv_cookie_flap(data);
- end
-
- def send_credentials(cookie)
- Client.logger.info("FLAP::send_credentials"){ "Sending cli_cookie FLAP." };
- send_flap(construct_cli_cookie_flap(cookie));
- end
-
- def send_flap(flap)
- @conn.send(flap, 0);
- end
-
- ###################################################
- # FLAP constructors
- #
- def construct_flap(type, data)
- flap =
- [
- '*',
- type,
- sequence_next(),
- data.length(),
- data
- ].pack("aCnna*");
-
- Client.logger.info("FLAP::construct_flap"){ "Built FLAP of length #{flap.length}, as hex: " + flap.unpack("H*").join() + "." }
- return flap;
- end
-
- def construct_cli_ident_flap(username, roastedPassword)
- return construct_flap(FLAP_Type_Signon,
- [
- [1].pack("N"),
- construct_tlv_string(TLV::Type_Screen_Name, username),
- construct_tlv_raw(TLV::Type_Roasted_Password, roastedPassword),
- construct_tlv_string(TLV::Type_Client_ID_String, OSCAR::Client_ID_String),
- construct_tlv_short(TLV::Type_Client_ID, OSCAR::Client_ID),
- construct_tlv_short(TLV::Type_Client_Major_Version, OSCAR::Client_Major_Version),
- construct_tlv_short(TLV::Type_Client_Minor_Version, OSCAR::Client_Minor_Version),
- construct_tlv_short(TLV::Type_Client_Lesser_Version, OSCAR::Client_Lesser_Version),
- construct_tlv_short(TLV::Type_Client_Build_Version, OSCAR::Client_Build_Version),
- construct_tlv_int(TLV::Type_Client_Distribution, OSCAR::Client_Distribution),
- construct_tlv_string(TLV::Type_Client_Language, OSCAR::Client_Language),
- construct_tlv_string(TLV::Type_Client_Country, OSCAR::Client_Country)
- ].join()
- );
- end
-
- def construct_cli_cookie_flap(cookie)
- return construct_flap(FLAP_Type_Signon,
- [
- 1,
- construct_tlv_raw(TLV::Type_Connection_Cookie, cookie)
- ].pack("Na*")
- );
- end
-
- ###################################################
- # FLAP parsers
- #
- def receive_flap()
- Client.logger.info("FLAP::receive_flap"){ "Reading header." }
- begin
- header = @conn.recv(6);
- rescue
- Client.logger.error("FLAP::receive_flap"){ "Connection dropped." }
- raise Client.rror, "Connection error."
- end
-
- asterisk, channel, sequence, length = header.unpack("aCnn");
- Client.logger.info("FLAP::receive_flap"){ "Received header: #{asterisk} #{channel} #{sequence} #{length}." }
- Client.logger.info("FLAP::receive_flap"){ "Reading data..." }
-
- #TODO: maybe use the sequence number
-
- begin
- data = @conn.recv(length);
- rescue
- Client.logger.error("FLAP::receive_flap"){ "Connection dropped." }
- raise Client.rror, "Connection error."
- end
-
- Client.logger.info("FLAP::receive_flap"){ "...data received." }
-
- return channel, data;
- end
-
- def parse_srv_cookie_flap(data)
- sn = 0;
- addr = 0;
- cookie = 0;
- error_code = nil;
-
- while(data.length() > 0)
- type, current, data = parse_tlv_raw(data);
- case type
- #the data we're looking for
- when TLV::Type_Screen_Name
- sn = current;
- Client.logger.info("FLAP::parse_srv_cookie_flap"){ "received screenname #{sn}." }
-
- when TLV::Type_Connection_Address
- addr = current;
- Client.logger.info("FLAP::parse_srv_cookie_flap"){ "received connection address #{addr}." }
-
- when TLV::Type_Connection_Cookie
- cookie = current;
- Client.logger.info("FLAP::parse_srv_cookie_flap"){ "received connection cookie, length #{current.size()}." }
-
- #errors
- when TLV::Type_Error_Code
- error_code = current.unpack("n").first();
- Client.logger.error("FLAP::parse_srv_cookie_flap"){ "received error code #{error_code}, message = #{TLV::Error_Messages[error_code]}]." }
-
- when TLV::Type_Error_URL
- Client.logger.error("FLAP::parse_srv_cookie_flap"){ "received error URL #{current}." }
- raise Client.rror, "Server error."
-
- else
- Client.logger.warn("FLAP::parse_srv_cookie_flap"){ "received unhandled TLV type #{type}, value = #{current}]." }
- end
- end
-
- if(error_code != nil)
- raise Client.logger.error, "Server error."
- end
-
- return sn, addr, cookie;
- end
-
- ###################################################
- # FLAP helpers
- #
- def sequence_next()
- if(!@sequence)
- @sequence = rand(Sequence_Max);
- end
-
- @sequence = @sequence + 1;
-
- if(Sequence_Max == @sequence)
- @sequence = Sequence_Min;
- end
-
- return @sequence;
- end
-
- def handle_message(uin, message)
- @parent.handle_message(uin, message);
- end
- end
- end
-end
+++ /dev/null
-require 'oscar/types/flap'
-require 'oscar/types/snacdefs'
-require 'oscar/types/threads/singlethread'
-require 'oscar/types/threads/multithread'
-
-module OSCAR
- module Types
- class SNAC
- include OSCAR::Types::TLV
-
- ###################################################
- # Constants [also see snacdefs.rb]
- #
- Flags_None = 0;
-
- Requestid_Max = 2147483647;
- Requestid_Min = 0;
-
- #parameters index keys
- Message_of_the_Day_Type = 1;
- Message_of_the_Day = 2;
- Location_Service_Max_Profile_Length = 3;
- Location_Service_Max_Capabilities = 4;
- User_Class = 5;
- User_Status = 6;
- Member_Since = 7;
- Signon_Time = 8;
- Idle_Time = 9;
- External_IP = 10;
- Buddylist_Max_Size = 11;
- Buddylist_Max_Watchers = 12;
- Visible_List_Max_Size = 13;
- Invisible_List_Max_Size = 14;
-
- #Flags is a general SNAC properties.
- #There is not enough information about snac flags, but know
- #that if bit1 of flags=1 there are more SNACs for this
- #request-id was sent. Bit16=1 mean that SNAC contain some
- #unknown information at the beginning (first come a length of
- #additional data (word) and then data itself).
-
- ###################################################
- # Object functionality
- #
- def initialize(flapsrv)
- @flapsrv = flapsrv;
- @services = nil;
-
- @rate_classes = {};
- @rate_groups = {};
-
- @parameters = {};
-
- initialize_threads();
- end
-
- ###################################################
- # Stubs
- #
- def initialize_threads()
- if Client::Multithreaded
- return mt_initialize_threads();
- else
- return st_initialize_threads();
- end
- end
- def wait_for_requestid(requestid)
- if Client::Multithreaded
- return mt_wait_for_requestid(requestid);
- else
- return st_wait_for_requestid(requestid);
- end
- end
- def wait_for_type(family, subtype)
- if Client::Multithreaded
- return mt_wait_for_type(family, subtype);
- else
- return st_wait_for_type(family, subtype);
- end
- end
-
- def default_responder(family, subtype, flags, requestid, data)
- Client.logger.info("SNAC::default_responder"){ "Got a SNAC, #{sprintf('0x%04x', family)} #{sprintf('0x%04x', subtype)}, data length #{data.length}." };
-
- case type_key(family, subtype)
- when type_key(Family_Service_Control, Service_Control_Subtype_Message_of_the_Day)
- Client.logger.info("SNAC::default_responder"){ "Parsing MOTD data." };
- parse_motd_snac(data);
-
- when type_key(Family_Service_Control, Service_Control_Subtype_Online_Info)
- Client.logger.info("SNAC::default_responder"){ "Parsing online info." };
- parse_online_info_snac(data);
-
- when type_key(Family_Messages, Messages_Subtype_Receive)
- Client.logger.info("SNAC::default_responder"){ "Parsing incoming message." };
- parse_message_snac(data);
- end
- Client.logger.info("SNAC::default_responder"){ "Done." };
- end
-
- def type_key(family, subtype)
- return sprintf("0x%04x 0x%04x", family, subtype);
- end
-
- def set_snac_filter_list(services)
- @services = services;
- end
-
- def supports_service(family)
- return @services.include?(family)
- end
-
- def check_snac_versions(services)
- services.keys.each do |family|
- if(Family_Versions[family])
- if(Family_Versions[family] < services[family])
- Client.logger.warn("SNAC::check_snac_versions"){ "Local version of family #{sprintf('0x%04x', family)} is older than server. (#{sprintf('0x%04x', Family_Versions[family])} versus #{sprintf('0x%04x', services[family])})." }
- elsif(Family_Versions[family] > services[family])
- Client.logger.error("SNAC::check_snac_versions"){ "Local version of family #{sprintf('0x%04x', family)} is newer than server. (#{sprintf('0x%04x', Family_Versions[family])} versus #{sprintf('0x%04x', services[family])})." }
- end
- else
- Client.logger.warn("SNAC::check_snac_versions"){ "Client doesn't support service #{sprintf('0x%04x', family)}, ignoring." }
- end
- end
- end
-
- ###################################################
- # SNAC operations
- #
- def negotiate_services()
- Client.logger.info("SNAC::negotiate_services"){ "Waiting for services SNAC." }
- data = wait_for_type(Family_Service_Control, Service_Control_Subtype_Supported_Families);
- services = parse_supported_services_snac(data);
- set_snac_filter_list(services);
-
- requestid = requestid_next();
- Client.logger.info("SNAC::negotiate_services"){ "Requesting all versions of known families." };
- @flapsrv.send_flap(construct_family_version_snac(services, requestid));
-
- Client.logger.info("SNAC::negotiate_services"){ "Obtaining server versions of families." }
- data = wait_for_type(Family_Service_Control, Service_Control_Subtype_Server_Service_Versions);
- service_versions = parse_family_version_snac(data);
- check_snac_versions(service_versions);
- end
-
- def negotiate_rate_limits()
- Client.logger.info("SNAC::negotiate_rate_limits"){ "Sending rate limit request SNAC." }
- requestid = requestid_next();
- @flapsrv.send_flap(construct_snac(Family_Service_Control, Service_Control_Subtype_Request_Rate_Limits, Flags_None, requestid));
-
- Client.logger.info("SNAC::negotiate_rate_limits"){ "Waiting for server rate limit information." }
- data = wait_for_requestid(requestid);
- rate_limits = parse_rate_limit_snac(data);
-
- requestid = requestid_next();
- Client.logger.info("SNAC::negotiate_rate_limits"){ "Sending rate limit confirmation SNAC." }
- @flapsrv.send_flap(construct_rate_limit_confirm_snac(requestid));
- end
-
- def negotiate_capabilities()
- ###################################
- # Location services negotiation
- Client.logger.info("SNAC::negotiate_capabilities"){ "Sending location service parameter request SNAC." }
- requestid = requestid_next();
- @flapsrv.send_flap(construct_snac(Family_Location_Services, Location_Services_Subtype_Request_Parameters, Flags_None, requestid));
-
- Client.logger.info("SNAC::negotiate_capabilities"){ "Waiting for location service parameters." }
- data = wait_for_requestid(requestid);
- parse_location_service_parameter_snac(data);
-
- requestid = requestid_next();
- Client.logger.info("SNAC::negotiate_capabilities"){ "Sending client capabilities." }
- @flapsrv.send_flap(construct_client_capability_snac(requestid, Capabilities));
- #no response, server tends to send userinfo which is automagically parsed
-
- ###################################
- # Buddy list negotiations
- requestid = requestid_next();
- Client.logger.info("SNAC::negotiate_capabilities"){ "Sending buddy list parameter request." }
- @flapsrv.send_flap(construct_snac(Family_Buddy_List, Buddy_List_Subtype_Request_Parameters, Flags_None, requestid));
- Client.logger.info("SNAC::negotiate_capabilities"){ "Waiting for buddy list parameters." }
- data = wait_for_requestid(requestid);
- parse_buddy_list_parameter_snac(data);
-
- ###################################
- # Messages negotiation
- requestid = requestid_next();
- Client.logger.info("SNAC::negotiate_capabilities"){ "Sending messages parameter request." }
- @flapsrv.send_flap(construct_snac(Family_Messages, Messages_Subtype_Request_Parameters, Flags_None, requestid));
- Client.logger.info("SNAC::negotiate_capabilities"){ "Recieving messages parameter request." }
- data = wait_for_requestid(requestid);
- parse_messages_parameter_snac(data);
- #if we cared, we could send SNAC 4, 2 to change the parameters. but we don't, right now.
- #todo: care
-
- ###################################
- # Privacy management negotiation
- requestid = requestid_next();
- Client.logger.info("SNAC::negotiate_capabilities"){ "Sending privacy management parameter request." }
- @flapsrv.send_flap(construct_snac(Family_Privacy_Management, Privacy_Management_Subtype_Request_Parameters, Flags_None, requestid));
- Client.logger.info("SNAC::negotiate_capabilities"){ "Receiving privacy management parameter request." }
- data = wait_for_requestid(requestid);
- parse_privacy_parameter_snac(data);
-
- ###################################
- # Server-side contact list negotiation
- #we don't support Server-Side contact lists, so we don't parse any of the result SNACs. we should...
- #todo: support server contact lists
- requestid = requestid_next();
- Client.logger.info("SNAC::negotiate_capabilities"){ "Sending server side information parameter request." }
- @flapsrv.send_flap(construct_snac(Family_Server_Information, Server_Information_Subtype_Request_Parameters, Flags_None, requestid));
- Client.logger.info("SNAC::negotiate_capabilities"){ "Receiving server side information parameter request." }
- wait_for_requestid(requestid);
-
- requestid = requestid_next();
- Client.logger.info("SNAC::negotiate_capabilities"){ "Sending server side contact list query." }
- @flapsrv.send_flap(construct_snac(Family_Server_Information, Server_Information_Subtype_Server_Contact_List_Query, Flags_None, requestid, "000000"));
- Client.logger.info("SNAC::negotiate_capabilities"){ "Receiving server side contact list." }
- wait_for_requestid(requestid);
-
- requestid = requestid_next();
- Client.logger.info("SNAC::negotiate_capabilities"){ "Sending server side contact list activation request." }
- @flapsrv.send_flap(construct_snac(Family_Server_Information, Server_Information_Subtype_Server_Contact_List_Activate, Flags_None, requestid));
- end
-
- def send_ready()
- requestid = requestid_next();
- Client.logger.info("SNAC::send_ready"){ "Setting client status." }
- @flapsrv.send_flap(construct_set_status_snac(requestid, TLV::User_Status_Online | TLV::User_Status_Direct_Connection_Disabled))
-
- requestid = requestid_next();
- Client.logger.info("SNAC::send_ready"){ "Sending client ready." }
- @flapsrv.send_flap(construct_client_ready_snac(requestid));
- end
-
- def send_message(username, message)
- Client.logger.info("SNAC::send_message"){ "Constructing message to #{username}, msg #{message}." };
- requestid = requestid_next();
- @flapsrv.send_flap(construct_send_message_snac(requestid, username, message));
- # wait for the ack
- wait_for_requestid(requestid);
- end
-
- ###################################################
- # SNAC constructors
- #
- def construct_snac(family, subtype, flags, requestid, data=nil)
- if(@services)
- if(!supports_service(family))
- Client.logger.warn("SNAC::construct_snac"){ "The server doesn't support family #{sprintf('0x%04x', family)}." };
- end
- end
-
- if(data)
- snac = [
- family,
- subtype,
- flags,
- requestid,
- data
- ].pack("nnnNa*");
- else
- snac = [
- family,
- subtype,
- flags,
- requestid
- ].pack("nnnN");
- end
-
- Client.logger.info("SNAC::construct_snac"){ "Built SNAC of length #{snac.length}, as hex: " + snac.unpack("H*").join() + "." };
-
- return @flapsrv.construct_flap(FLAP::FLAP_Type_Data, snac);
- end
-
- def construct_family_version_snac(services, requestid)
- servicedata = "";
-
- services.each do |service|
- version = Family_Versions[service];
- if(version)
- servicedata = servicedata + [service, version].pack("nn");
- else
- Client.logger.warn("SNAC::construct_family_version_snac"){ "We don't support service #{sprintf('0x%04x', service)}, ignoring." }
- end
- end
-
- return construct_snac(Family_Service_Control, Service_Control_Subtype_Request_Service_Versions, Flags_None, requestid, servicedata);
- end
-
- def construct_rate_limit_confirm_snac(requestid)
- data = "";
-
- @rate_classes.keys.each do |id|
- data += [id].pack("n");
- end
- return construct_snac(Family_Service_Control, Service_Control_Subtype_Add_Rate_Limit_Group, Flags_None, requestid, data);
- end
-
- def construct_client_capability_snac(requestid, capabilities)
- captlv = construct_tlv_raw(Type_Client_Cabilities_List, Capabilities);
- return construct_snac(Family_Location_Services, Location_Services_Subtype_Client_Parameters, Flags_None, requestid, captlv)
- end
-
- def construct_set_status_snac(requestid, flags)
- statustlv = construct_tlv_int(TLV::Type_User_Status, flags);
- return construct_snac(Family_Service_Control, Service_Control_Subtype_Set_Status, Flags_None, requestid, statustlv);
- end
-
- def construct_client_ready_snac(requestid)
- data = "";
- Family_Versions.keys.each do |family|
- data = data + [family, Family_Versions[family], 0x0110, 0x047B].pack("nnnn");
- end
- return construct_snac(Family_Service_Control, Service_Control_Subtype_Client_Ready, Flags_None, requestid, data);
- end
-
- def construct_send_message_snac(requestid, username, message)
- #messagecookie is 8 bytes, we're just setting upper to 0
- headerdata = [0, messagecookie_next, Message_Channel_Plaintext, username.length(), username].pack("NNnca*");
-
- #first fragment, required capabilities
- # type subtp len text-mode
- frag1 = [0x05, 0x01, 0x0001, 0x01].pack("ccnc");
-
- #second fragment, message contents
- #first, the contents
- # charset language
- message = [0x0000, 0xFFFF, message].pack("nna*");
- frag2 = [0x01, 0x01, message.length(), message].pack("ccna*");
-
- #message TLV
- msgtlv = construct_tlv_raw(TLV::Type_Message_Data, frag1 + frag2)
-
- #request a server ACK
- acktlv = construct_tlv_empty(TLV::Type_Server_Ack_Request);
-
- #construct the message
- return construct_snac(Family_Messages, Messages_Subtype_Send, Flags_None, requestid, headerdata + msgtlv + acktlv)
- end
-
- ###################################################
- # SNAC parsers
- #
- def parse_snac(data)
- Client.logger.info("SNAC::parse_snac"){ "Parsing SNAC." }
- family, subtype, flags, requestid = data.slice!(0..9).unpack("nnnN");
-
- Client.logger.info("SNAC::parse_snac"){ "parsed SNAC: #{sprintf('0x%04x', family)}, #{sprintf('0x%04x', subtype)}, #{sprintf('0x%08x', flags)}, #{requestid}." }
- return family, subtype, flags, requestid, data
- end
-
- def parse_error_snac(data)
- errorcode = data.unpack("n").slice!(0..1).first();
- while(data.length() > 0)
- begin
- type, errorsubcode, data = parse_tlv_short(data);
- rescue
- end
- Client.logger.error("SNAC::parse_error_snac"){ "TLV type #{type} value #{sprintf('0x%04x', errorsubcode)}." }
- end
-
- Client.logger.error("SNAC::parse_error_snac"){ "Received error SNAC with type: #{sprintf('0x%04x', errorcode)}, meaning #{Error_Messages[errorcode]}."}
- end
-
- def parse_supported_services_snac(data)
- services = data.unpack("n*");
-
- services.each do |service|
- Client.logger.info("SNAC::receive_supported_services_snac"){ "Supports service #{service}." }
- end
-
- return services;
- end
-
- def parse_location_service_parameter_snac(data)
- while(data.length() > 0)
- type, current, data = parse_tlv_raw(data);
- case type
- #the data we're looking for
- when TLV::Type_Profile_Max_Length
- @parameters[Location_Service_Max_Profile_Length] = current.unpack("n");
- Client.logger.info("SNAC::parse_location_service_parameter_snac"){ "received profile max length #{@parameters[Location_Service_Max_Profile_Length]}." }
-
- when TLV::Type_Profile_Max_Capabilities
- @parameters[Location_Service_Max_Capabilities] = current.unpack("n");
- Client.logger.info("SNAC::parse_location_service_parameter_snac"){ "received max capabilities #{@parameters[Location_Service_Max_Capabilities]}." }
-
- else
- Client.logger.warn("SNAC::parse_location_service_parameter_snac"){ "received unhandled TLV type #{type}, value = #{current}]." }
- end
- end
- end
-
- def parse_motd_snac(data)
- # read the type
- motdtype = data.slice!(0..1).unpack("n").first();
-
- @parameters[Message_of_the_Day_Type] = motdtype;
- Client.logger.info("SNAC::parse_motd_snac"){ "Read MOTD type #{motdtype}." };
-
- while(data.length() > 0)
- tlvtype, msg, data = parse_tlv_raw(data);
-
- if(TLV::Type_Message_of_the_Day == tlvtype)
- @parameters[Message_of_the_Day] = msg;
-
- Client.logger.info("SNAC::parse_motd_snac"){ "Read MOTD message #{msg}." };
- end
- end
- end
-
- def parse_buddy_list_parameter_snac(data)
- while(data.length() > 0)
- type, current, data = parse_tlv_raw(data);
- case type
- #the data we're looking for
- when TLV::Type_Buddylist_Max_Size
- @parameters[Buddylist_Max_Size] = current.unpack("n");
- Client.logger.info("SNAC::parse_buddy_list_parameter_snac"){ "received max size #{@parameters[Buddylist_Max_Size]}." }
-
- when TLV::Type_Buddylist_Max_Watchers
- @parameters[Buddylist_Max_Watchers] = current.unpack("n");
- Client.logger.info("SNAC::parse_buddy_list_parameter_snac"){ "received max watchers #{@parameters[Buddylist_Max_Watchers]}." }
-
- else
- Client.logger.warn("SNAC::parse_buddy_list_parameter_snac"){ "received unhandled TLV type #{type}, value = #{current}]." }
- end
- end
- end
-
- def parse_messages_parameter_snac(data)
- channel, flags, maxsnacsize, maxsenderwarninglevel,
- maxreceiverwarninglevel, minimummessageinterval, unknown =
- data.unpack("nNnnnnn");
-
- Client.logger.info("SNAC::parse_messages_parameter_snac"){ "For channel #{channel}:" };
- Client.logger.info("SNAC::parse_messages_parameter_snac"){ "-- flags are #{sprintf('0x%08x', flags)}" };
- Client.logger.info("SNAC::parse_messages_parameter_snac"){ "-- max SNAC size = #{maxsnacsize}" };
- Client.logger.info("SNAC::parse_messages_parameter_snac"){ "-- max sender/receiver warning level = #{maxsenderwarninglevel}/#{maxreceiverwarninglevel}" };
- Client.logger.info("SNAC::parse_messages_parameter_snac"){ "-- min message interval = #{unknown}" };
- end
-
- def parse_privacy_parameter_snac(data)
- while(data.length() > 0)
- type, current, data = parse_tlv_raw(data);
- case type
- #the data we're looking for
- when TLV::Type_Visible_List_Max_Size
- @parameters[Visible_List_Max_Size] = current.unpack("n");
- Client.logger.info("SNAC::parse_privacy_parameter_snac"){ "received visible max size #{@parameters[Visible_List_Max_Size]}." }
-
- when TLV::Type_Invisible_List_Max_Size
- @parameters[Invisible_List_Max_Size] = current.unpack("n");
- Client.logger.info("SNAC::parse_privacy_parameter_snac"){ "received invisible max size #{@parameters[Invisible_List_Max_Size]}." }
-
- else
- Client.logger.warn("SNAC::parse_privacy_parameter_snac"){ "received unhandled TLV type #{type}, value = #{current}]." }
- end
- end
- end
-
- def parse_server_information_parameter_snac(data)
- while(data.length() > 0)
- type, current, data = parse_tlv_raw(data);
- case type
- #the data we're looking for
- when TLV::Type_Visible_List_Max_Size
- @parameters[Visible_List_Max_Size] = current.unpack("n");
- Client.logger.info("SNAC::parse_server_information_parameter_snac"){ "received visible max size #{@parameters[Visible_List_Max_Size]}." }
-
- when TLV::Type_Invisible_List_Max_Size
- @parameters[Invisible_List_Max_Size] = current.unpack("n");
- Client.logger.info("SNAC::parse_server_information_parameter_snac"){ "received invisible max size #{@parameters[Invisible_List_Max_Size]}." }
-
- else
- Client.logger.warn("SNAC::parse_server_information_parameter_snac"){ "received unhandled TLV type #{type}, value = #{current}]." }
- end
- end
- end
-
- def parse_online_info_snac(data)
- uinlen = data.slice!(0);
- uin = data.slice!(0..(uinlen-1));
-
- warnlevel, n_tlvs = data.slice!(0..3).unpack("nn");
- Client.logger.info("SNAC::parse_online_info_snac"){ "Received online info SNAC, with UIN #{uin}, and #{n_tlvs} TLVs. Warnlevel is #{warnlevel}." }
-
- if n_tlvs.nil?
- Client.logger.warn("SNAC::parse_online_info_snac"){ "Received bad online info SNAC with with UIN #{uin}, and #{n_tlvs} TLVs. Warnlevel is #{warnlevel}. .. break" }
- return
- end
-
- n_tlvs.times do
- type, contents, data = parse_tlv_raw(data);
- case type
- when TLV::Type_User_Class
- @parameters[User_Class] = contents.unpack("N").first();
- Client.logger.info("SNAC::parse_online_info_snac"){ "User class TLV, data: #{sprintf('0x%08x', @parameters[User_Class])}." }
-
- when TLV::Type_User_Status
- @parameters[User_Status] = contents.unpack("N").first();
- Client.logger.info("SNAC::parse_online_info_snac"){ "User status TLV, data: #{sprintf('0x%08x', @parameters[User_Status])}." }
-
- when TLV::Type_Signon_Time
- @parameters[Signon_Time] = contents.unpack("N").first();
- #todo: parse -- this is in unix time_t format
- Client.logger.info("SNAC::parse_online_info_snac"){ "Signon time TLV, data: #{sprintf('0x%08x', @parameters[Signon_Time])}." }
-
- when TLV::Type_Member_Since
- @parameters[Member_Since] = contents.unpack("N").first();
- Client.logger.info("SNAC::parse_online_info_snac"){ "Memeber since TLV, data: #{sprintf('0x%08x', @parameters[Signon_Time])}." }
-
- when TLV::Type_External_IP
- @parameters[External_IP] = contents.unpack("CCCC").collect{|foo| foo.to_s()}.join(".");
- Client.logger.info("SNAC::parse_online_info_snac"){ "External IP TLV, data: #{@parameters[External_IP]}." }
-
- when TLV::Type_Direct_Connection_Info
- #ignore this one. too much work!
- #todo: implement this
-
- when TLV::Type_Client_Idle_Time
- @parameters[Idle_Time] = contents.unpack("N").first();
- #todo: parse -- this is in seconds
- Client.logger.info("SNAC::parse_online_info_snac"){ "Idle time TLV, data: #{@parameters[Idle_Time]}." }
- end
- end
- end
-
- def parse_message_snac(data)
- cookie1, cookie2, channel, uinlen = data.slice!(0..10).unpack("NNnc");
-
- if(Message_Channel_Plaintext != channel)
- Client.logger.warn("SNAC::parse_message_snac"){ "Got a non-plaintext message. ignoring." }
- return;
- end
-
- uin = data.slice!(0..(uinlen-1));
-
- warnlevel, n_tlvs = data.slice!(0..3).unpack("nn");
-
- Client.logger.info("SNAC::parse_message_snac"){ "Received message from #{uin} with #{n_tlvs} TLVs. Warnlevel is #{warnlevel}." }
-
- n_tlvs.times do
- type, contents, data = parse_tlv_raw(data);
- case type
- when TLV::Type_User_Class
- Client.logger.info("SNAC::parse_message_snac"){ "-- user class = #{sprintf('0x%08x', @parameters[User_Class])}" }
- when TLV::Type_User_Status
- Client.logger.info("SNAC::parse_message_snac"){ "-- user status = #{sprintf('0x%08x', @parameters[User_Status])}" }
- when TLV::Type_Signon_Time
- Client.logger.info("SNAC::parse_message_snac"){ "-- signon time = #{sprintf('0x%08x', @parameters[Signon_Time])}" }
- when TLV::Type_Member_Since
- Client.logger.info("SNAC::parse_message_snac"){ "-- member since = #{sprintf('0x%08x', @parameters[Signon_Time])}" }
- when TLV::Type_Client_Idle_Time
- Client.logger.info("SNAC::parse_message_snac"){ "-- idle time = #{@parameters[Idle_Time]}" }
- when TLV::Type_Automated_Response
- Client.logger.info("SNAC::parse_message_snac"){ "-- automated response" }
- end
- end
-
- #the rest of data is the message
- while(data.length() > 0)
- fragidentifier, fraglength = data.slice!(0..3).unpack("nn");
- case fragidentifier
- when 0x0501 #capabilities
- data.slice!(0..(fraglength-1));
- when 0x0101 #message
- charset, charsubset, text = data.slice!(0..(fraglength-1)).unpack("nna*");
- Client.logger.info("SNAC::parse_message_snac"){ "-- message: #{text}" }
- message = text;
- end
- end
-
- @flapsrv.handle_message(uin, message)
- end
-
- def parse_family_version_snac(data)
- parse = data.unpack("n*");
- services = {};
- while(parse.length() > 0)
- family = parse.delete_at(0);
- version = parse.delete_at(0);
- services[family] = version;
- Client.logger.info("SNAC::receive_family_version_snac"){ "Supports service #{sprintf('0x%04x', family)} version #{sprintf('0x%04x', version)}." }
- end
-
- return services;
- end
-
- def parse_rate_limit_snac(data)
- num_classes = data.slice!(0..1).unpack("n").first();
- Client.logger.info("SNAC::parse_rate_limit_snac"){ "Reading data for #{num_classes} classes." }
-
- #first, read in the classes
- num_classes.times do
- #TODO: in version 2, lasttime and state don't exist. handle this.
- rtcls = RateClass.from_data(data.slice!(0..34));
- @rate_classes[rtcls.id] = rtcls;
- end
-
- #now, read in the family/subtypes
- num_classes.times do
- groupid, count = data.slice!(0..3).unpack("nn");
- Client.logger.info("SNAC::parse_rate_limit_snac"){ "Reading #{count} mappings for #{groupid}."}
-
- count.times do
- family, type = data.slice!(0..3).unpack("nn");
- @rate_groups[type_key(family, type)] = groupid;
- end
- end
- end
-
- ###################################################
- # SNAC helpers
- #
- def requestid_next()
- if(!@requestid)
- @requestid = rand(Requestid_Max);
- end
-
- @requestid = @requestid + 1;
-
- if(Requestid_Max == @requestid)
- @requestid = Requestid_Min;
- end
-
- return @requestid;
- end
-
- def messagecookie_next()
- #pick a number
- return rand(10000000);
- end
-
- class RateClass
- attr_reader :id, :window, :level_clear, :level_alert, :level_limit, :level_disconnect, :level_current, :level_max, :lasttime, :state;
- attr_writer :id, :window, :level_clear, :level_alert, :level_limit, :level_disconnect, :level_current, :level_max, :lasttime, :state;
-
- def RateClass.from_data(data)
- id, window, level_clear, level_alert, level_limit, level_disconnect, level_current, level_max, lasttime, state = data.unpack("nNNNNNNNNc");
- Client.logger.info("RateClass::from_data"){
- "Class #{id}: window: #{window} clrlvl: #{level_clear} alrtlvl: " +
- "#{level_alert} limlvl: #{level_limit} dislvl: #{level_disconnect} " +
- "curlvl: #{level_current} maxlvl: #{level_max} lasttime: #{lasttime} state: #{state}." }
-
- return RateClass.new(id, window, level_clear, level_alert, level_limit, level_disconnect, level_current, level_max, lasttime, state);
- end
-
- def initialize(id, window, level_clear, level_alert, level_limit, level_disconnect, level_current, level_max, lasttime, state)
- @id = id;
- @window = window;
- @level_clear = level_clear;
- @level_alert = level_alert;
- @level_limit = level_limit;
- @level_disconnect = level_disconnect;
- @level_current = level_current;
- @level_max = level_max;
- @lasttime = lasttime;
- @state = state;
- end
- end
-
- end #class SNAC
- end
-end
+++ /dev/null
-module OSCAR
- module Types
- class SNAC
-
- # this is identical for all families
- Subtype_Error = 0x0001;
-
- ###################################################
- # Service Control Family
- #
- Family_Service_Control = 0x0001;
- Service_Control_Subtype_Client_Ready = 0x0002;
- Service_Control_Subtype_Supported_Families = 0x0003;
- Service_Control_Subtype_Request_Rate_Limits = 0x0006;
- Service_Control_Subtype_Rate_Limits = 0x0007;
- Service_Control_Subtype_Add_Rate_Limit_Group = 0x0008;
- Service_Control_Subtype_Remove_Rate_Limit_Group = 0x0009;
- Service_Control_Subtype_Request_Online_Info = 0x000E;
- Service_Control_Subtype_Online_Info = 0x000F;
- Service_Control_Subtype_Message_of_the_Day = 0x0013;
- Service_Control_Subtype_Request_Service_Versions = 0x0017;
- Service_Control_Subtype_Server_Service_Versions = 0x0018;
- Service_Control_Subtype_Set_Status = 0x001E;
-
- ###################################################
- # Location Services Family
- #
- Family_Location_Services = 0x0002;
- Location_Services_Subtype_Request_Parameters = 0x0002;
- Location_Services_Subtype_Server_Parameters = 0x0003;
- Location_Services_Subtype_Client_Parameters = 0x0004;
-
- ###################################################
- # Buddy List Family
- #
- Family_Buddy_List = 0x0003;
- Buddy_List_Subtype_Request_Parameters = 0x0002;
- Buddy_List_Subtype_Server_Parameters = 0x0003;
-
- ###################################################
- # Messages Family
- #
- Family_Messages = 0x0004;
- Messages_Subtype_Set_Parameters = 0x0002;
- Messages_Subtype_Request_Parameters = 0x0004;
- Messages_Subtype_Server_Parameters = 0x0005;
- Messages_Subtype_Send = 0x0006;
- Messages_Subtype_Receive = 0x0007;
- #used by all parameter fields:
- Message_Channel_Plaintext = 0x0001;
- Message_Channel_RTF = 0x0002;
- Channel_Flag_Enabled = 0x00000001;
- Channel_Missed_Calls_Notifications_Enabled = 0x00000002;
- Channel_Typing_Notifications_Enabled = 0x00000008;
-
-
- ###################################################
- # Advertisements Family
- #
- Family_Advertisements = 0x0005;
-
- ###################################################
- # Invitation Family
- #
- Family_Invitation = 0x0006;
-
- ###################################################
- # Administration Family
- #
- Family_Administrative = 0x0007;
-
- ###################################################
- # Popup Notices Family
- #
- Family_Popup_Notices = 0x0008;
-
- ###################################################
- # Privacy Management Family
- #
- Family_Privacy_Management = 0x0009;
- Privacy_Management_Subtype_Request_Parameters = 0x0002;
- Privacy_Management_Subtype_Server_Parameters = 0x0003;
-
- ###################################################
- # User Lookup Family
- #
- Family_User_Lookup = 0x000a;
-
- ###################################################
- # Usage Statistics Family
- #
- Family_Usage_Statistics = 0x000b;
-
- ###################################################
- # Translation Service Family
- #
- Family_Translation_Service = 0x000c;
-
- ###################################################
- # Chat Navigation Family
- #
- Family_Chat_Navigation = 0x000d;
-
- ###################################################
- # Chat Service Family
- #
- Family_Chat_Service = 0x000e;
-
- ###################################################
- # User Directory Family
- #
- Family_User_Directory = 0x000f;
-
- ###################################################
- # Server Buddy Icons Family
- #
- Family_Server_Buddy_Icons = 0x0010;
-
- ###################################################
- # Server Information Family
- #
- Family_Server_Information = 0x0013;
- Server_Information_Subtype_Request_Parameters = 0x0002;
- Server_Information_Subtype_Server_Parameters = 0x0003;
- Server_Information_Subtype_Server_Contact_List_Request = 0x0004;
- Server_Information_Subtype_Server_Contact_List_Query = 0x0005;
- Server_Information_Subtype_Server_Contact_List = 0x0006;
- Server_Information_Subtype_Server_Contact_List_Activate = 0x0007;
- Server_Information_Subtype_Server_Contact_List_Latest = 0x000F;
-
- ###################################################
- # ICQ-Specific Family
- #
- Family_ICQ_Specific = 0x0015;
-
- ###################################################
- # Authorization Family
- #
- Family_Authorization = 0x0017;
-
- ###################################################
- # Broadcast Family
- #
- Family_Broadcast = 0x0085;
-
- ###################################################
- # Client Family Versions Table
- #
- Family_Versions = {
- Family_Service_Control => 3,
- Family_Location_Services => 1,
- Family_Buddy_List => 1,
- Family_Messages => 1,
- Family_Invitation => 1,
- Family_Privacy_Management => 1,
- Family_User_Lookup => 1,
- Family_Usage_Statistics => 1,
- Family_Server_Information => 2,
- Family_ICQ_Specific => 1
- }
-
- ###################################################
- # Client Capabilities
- #
- Capability_Voice_Chat = "{09461341-4C7F-11D1-8222-444553540000}";
- Capability_Direct_Play = "{09461342-4C7F-11D1-8222-444553540000}";
- Capability_File_Transfer_Send = "{09461343-4C7F-11D1-8222-444553540000}";
- Capability_DirectIM = "{09461345-4C7F-11D1-8222-444553540000}";
- Capability_Avatar = "{09461346-4C7F-11D1-8222-444553540000}";
- Capability_Stocks = "{09461347-4C7F-11D1-8222-444553540000}";
- Capability_File_Transfer_Receive = "{09461348-4C7F-11D1-8222-444553540000}";
- #note - these two mean the same thing. use one or the other, not both
- Capability_Games = "{0946134A-4C7F-11D1-8222-444553540000}";
- Capability_Games2 = "{0946134A-4C7F-11D1-2282-444553540000}";
- Capability_Buddy_List_Transfer = "{0946134B-4C7F-11D1-8222-444553540000}";
- Capability_ICQ_Interaction = "{0946134D-4C7F-11D1-8222-444553540000}";
- Capability_UTF8 = "{0946134E-4C7F-11D1-8222-444553540000}";
- Capability_Chat = "{748F2420-6287-11D1-8222-444553540000}";
-
- def SNAC.data_for_capability(capability)
- #strip the brackets
- capability = capability.slice(1..(capability.length()-2));
-
- #strip the dashes
- capability = capability.split('-').join();
-
- #convert to integer, then to binary, then output
- return [capability.slice(0..7).hex(), capability.slice(8..15).hex(),
- capability.slice(16..23).hex(), capability.slice(24..31).hex()].pack("NNNN");
- end
-
- Capabilities = SNAC.data_for_capability(Capability_Buddy_List_Transfer) +
- SNAC.data_for_capability(Capability_UTF8) +
- SNAC.data_for_capability(Capability_Chat);
- end
-
- Error_Messages = {
- 0x01 => "Invalid SNAC header.",
- 0x02 => "Server rate limit exceeded.",
- 0x03 => "Client rate limit exceeded.",
- 0x04 => "Recipient is not logged in.",
- 0x05 => "Requested service unavailable.",
- 0x06 => "Requested service not defined.",
- 0x07 => "You sent obsolete SNAC.",
- 0x08 => "Not supported by server.",
- 0x09 => "Not supported by client.",
- 0x0A => "Refused by client.",
- 0x0B => "Reply too big.",
- 0x0C => "Responses lost.",
- 0x0D => "Request denied.",
- 0x0E => "Incorrect SNAC format.",
- 0x0F => "Insufficient rights.",
- 0x10 => "In local permit/deny (recipient blocked).",
- 0x11 => "Sender too evil.",
- 0x12 => "Receiver too evil.",
- 0x13 => "User temporarily unavailable.",
- 0x14 => "No match.",
- 0x15 => "List overflow.",
- 0x16 => "Request ambiguous.",
- 0x17 => "Server queue full.",
- 0x18 => "Not while on AOL."
- }
-
- ###################################################
- # Message of the Day types
- #
- Message_Mandatory_Upgrade = 0x0001;
- Message_Advised_Upgrade = 0x0002;
- Message_System_Announcement = 0x0003;
- Message_Standard_Notice = 0x0004;
- Message_News = 0x0006;
- end
-end
+++ /dev/null
-module OSCAR
- module Types
- class FLAP
- def mt_initialize_threads()
- @proc_thread = nil;
- @recv_lock = Mutex.new();
- @message_type = nil;
- @message_data = nil;
- @responders = {
- FLAP_Type_Signon => ConditionVariable.new(),
- FLAP_Type_Data => ConditionVariable.new(),
- FLAP_Type_Error => ConditionVariable.new(),
- FLAP_Type_Signoff => ConditionVariable.new(),
- FLAP_Type_Keepalive => ConditionVariable.new()
- }
-
- start_processor();
- end
-
- def start_processor()
- Client.logger.info("FLAP::start_processor"){ "Starting FLAP processor." }
-
- @proc_thread = Thread.new {
- while(true)
- Client.logger.info("FLAP::start_processor"){ "Trying to read FLAP." }
- type, data = receive_flap();
-
- @recv_lock.lock();
- if(@message_data != nil)
- Client.logger.warn("FLAP::start_processor"){ "Unclaimed FLAP of type #{@message_type} being discarded." }
- end
- @message_type = type;
- @message_data = data;
- @responders[type].broadcast();
- @recv_lock.unlock();
- end
- }
- end
-
- def stop_processor()
- @proc_thread.exit();
- end
-
- def mt_wait_for_message(type)
- Client.logger.info("FLAP::wait_for_message"){ "Trying to read FLAP type #{type}." }
-
- begin
- @recv_lock.lock();
-
- while((!@message_data) || (@message_type != type))
- @responders[type].wait(@recv_lock);
- end
- data = @message_data;
- @message_type = nil;
- @message_data = nil;
- Client.logger.info("FLAP::wait_for_message"){ "Received message we wanted: #{type}." }
- ensure
- @recv_lock.unlock();
- end
-
- return data;
- end
- end #FLAP
-
- class SNAC
- def mt_initialize_threads()
- @proc_thread = nil;
-
- @recv_lock = Mutex.new();
- @message_requestid = nil;
- @message_family = nil;
- @message_subtype = nil;
- @message_data = nil;
-
- @requestid_responders = {};
- @type_responders = {};
-
- start_processor();
- end
-
- def start_processor()
- Client.logger.info("SNAC::start_processor"){ "Starting FLAP processor for SNACs." }
-
- @snac_thread = Thread.new {
- while(true)
- Client.logger.info("SNAC::start_processor"){ "Waiting for SNAC Data." }
- #enqueue ourselves to get the response
- data = @flapsrv.wait_for_message(FLAP::FLAP_Type_Data)
-
- Client.logger.info("SNAC::start_processor"){ "Received SNAC data, forwarding to process_snac." }
- process_snac(data);
- end
- }
- end
-
- def process_snac(data)
- Client.logger.info("SNAC::process_snac"){ "Processing SNAC." }
-
- family, subtype, flags, requestid, data = parse_snac(data);
-
- begin
- @recv_lock.lock();
-
- if(@message_data != nil)
- Client.logger.warn("SNAC::process_snac"){ "Unclaimed SNAC of family #{sprintf('0x%04x', @message_family)}, subtype #{sprintf('0x%04x', @message_subtype)}, requestID #{@message_requestid} being discarded." }
- end
- @message_requestid = requestid;
- @message_family = family;
- @message_subtype = subtype;
- @message_data = data;
- key = type_key(@message_family, @message_subtype);
- ensure
- @recv_lock.unlock();
- end
-
- Client.logger.info("SNAC::process_snac"){ "Looking for SNAC waiters." }
- if(@requestid_responders[requestid])
- Client.logger.info("SNAC::process_snac"){ "Request ID waiters exist for ID #{@message_requestid}" }
- @requestid_responders[requestid].broadcast();
- elsif(@type_responders[key])
- Client.logger.info("SNAC::process_snac"){ "Request ID waiters exist for type #{key}." }
- @type_responders[key].broadcast();
- else
- Client.logger.warn("SNAC::process_snac"){ "Couldn't find a destination for this SNAC." }
- begin
- @recv_lock.lock();
- @message_requestid = nil;
- @message_family = nil;
- @message_subtype = nil;
- @message_data = nil;
- ensure
- @recv_lock.unlock();
- end
- default_responder(family, subtype, flags, requestid, data)
- end
- end
-
- def mt_wait_for_requestid(requestid)
- Client.logger.info("SNAC::wait_for_requestid"){ "Waiting for Request ID #{requestid}" }
- error = 0;
-
- begin
- @recv_lock.lock();
-
- if(!@requestid_responders[requestid])
- @requestid_responders[requestid] = ConditionVariable.new();
- else
- Client.logger.warn("SNAC::wait_for_requestid"){ "Hash entry already exists!" }
- end
-
- while(!(@message_data && (@message_requestid == requestid)))
- @requestid_responders[requestid].wait(@recv_lock);
- end
-
- if(Subtype_Error == @message_subtype)
- error = 1;
- end
-
- data = @message_data;
- @message_family = nil;
- @message_subtype = nil;
- @message_requestid = nil;
- @message_data = nil;
- Client.logger.info("SNAC::wait_for_requestid"){ "Received request ID we wanted: #{requestid}." }
-
- @requestid_responders.delete(requestid);
- ensure
- @recv_lock.unlock();
- end
-
- if(error == 1)
- parse_error_snac(data);
- raise OscarError, "Client/server error."
- end
-
- return data;
-
- end
-
- def mt_wait_for_type(family, subtype)
- key = type_key(family, subtype);
- Client.logger.info("SNAC::wait_for_type"){ "Waiting for type #{key}" }
-
- begin
- @recv_lock.lock();
-
- if(!@type_responders[key])
- @type_responders[key] = ConditionVariable.new();
- else
- Client.logger.warn("SNAC::wait_for_type"){ "Hash entry already exists!" }
- end
-
- while(!(@message_data && (@message_family == family) && (@message_subtype == subtype)))
- @type_responders[key].wait(@recv_lock);
- end
-
- data = @message_data;
- @message_family = nil;
- @message_subtype = nil;
- @message_requestid = nil;
- @message_data = nil;
- Client.logger.info("SNAC::wait_for_type"){ "Received type we wanted: #{key}." }
-
- @type_responders.delete(key);
- ensure
- @recv_lock.unlock();
- end
-
- return data;
- end
- end #SNAC
- end #Types
-end #OSCAR
+++ /dev/null
-module OSCAR
- module Types
- class FLAP
- def st_initialize_threads()
- end
-
- def st_wait_for_message(seektype)
- Client.logger.info("FLAP::wait_for_message"){ "Trying to read FLAP type #{seektype}." }
-
- while(1)
- begin
- type, data = receive_flap();
- rescue
- Client.logger.info("FLAP::wait_for_message"){ "Connection dropped." }
- raise OscarError, "Connection error."
- end
- Client.logger.info("FLAP::wait_for_message"){ "Got FLAP: type #{type}, wanted #{seektype}." }
- if(seektype == type)
- break;
- else
- Client.logger.info("FLAP::wait_for_message"){ "FLAP isn't the one we expected, ignoring." }
- end
- end
-
- return data;
- end
- end #FLAP
-
- class SNAC
- def st_initialize_threads()
- end
-
- def st_wait_for_requestid(seekrequestid)
- Client.logger.info("SNAC::wait_for_requestid"){ "Waiting for Request ID #{seekrequestid}" }
-
- while(1)
- begin
- family, subtype, flags, requestid, data = parse_snac(@flapsrv.wait_for_message(FLAP::FLAP_Type_Data));
- rescue
- Client.logger.info("SNAC::wait_for_requestid"){ "Connection dropped." }
- raise OscarError, "Connection error."
- end
- Client.logger.info("SNAC::wait_for_requestid"){ "Got SNAC: requestid #{requestid}, wanted #{seekrequestid}." }
- if(requestid == seekrequestid)
- break;
- else
- Client.logger.warn("SNAC::wait_for_type"){ "Sending SNAC type #{type_key(family, subtype)} to default responder." }
- default_responder(family, subtype, flags, requestid, data);
- end
- end
-
- if(Subtype_Error == subtype)
- parse_error_snac(data);
- Client.logger.info("SNAC::wait_for_requestid"){ "It's an error subtype. Stealing." }
- raise OscarError, "Client/server error.";
- end
-
- return data;
- end
-
- def st_wait_for_type(seekfamily, seeksubtype)
- key = type_key(seekfamily, seeksubtype);
- Client.logger.info("SNAC::wait_for_type"){ "Waiting for type #{key}" }
-
- while(1)
- begin
- family, subtype, flags, requestid, data = parse_snac(@flapsrv.wait_for_message(FLAP::FLAP_Type_Data));
- rescue
- Client.logger.info("SNAC::wait_for_type"){ "Connection dropped." }
- raise OscarError, "Connection error."
- end
- Client.logger.info("SNAC::wait_for_type"){ "Got SNAC: type #{type_key(family, subtype)}, wanted #{key}." }
-
- if((family == seekfamily) && (subtype == seeksubtype))
- break;
- else
- Client.logger.warn("SNAC::wait_for_type"){ "Sending SNAC type #{type_key(family, subtype)} to default responder." }
- default_responder(family, subtype, flags, requestid, data);
- end
- end
-
- return data;
- end
- end #SNAC
- end #Types
-end #OSCAR
+++ /dev/null
-module OSCAR
- module Types
- module TLV
-
- ###################################################
- # TLV Types
- #
- # used in authentication, login
- Type_Screen_Name = 0x0001;
- Type_Roasted_Password = 0x0002;
- Type_Client_ID_String = 0x0003;
- Type_Error_URL = 0x0004;
- Type_Connection_Address = 0x0005;
- Type_Connection_Cookie = 0x0006;
- Type_Error_Code = 0x0008;
- Type_Message_of_the_Day = 0x000B;
- Type_Client_Country = 0x000E;
- Type_Client_Language = 0x000F;
- Type_Client_Distribution = 0x0014;
- Type_Client_ID = 0x0016;
- Type_Client_Major_Version = 0x0017;
- Type_Client_Minor_Version = 0x0018;
- Type_Client_Lesser_Version = 0x0019;
- Type_Client_Build_Version = 0x001A;
-
- # used in client info, receiving messages
- Type_User_Class = 0x0001;
- User_Class_Unconfirmed = 0x0001;
- User_Class_Administrator = 0x0002;
- User_Class_AOL_Staff = 0x0004;
- User_Class_Commercial = 0x0008;
- User_Class_Free = 0x0010;
- User_Class_Away = 0x0020;
- User_Class_ICQ = 0x0040;
- User_Class_Wireless = 0x0080;
-
-
- Type_Signon_Time = 0x0003;
- Type_Automated_Response = 0x0004;
- Type_Member_Since = 0x0005;
- Type_User_Status = 0x0006;
- #top bytes - random flags
- User_Status_Webaware = 0x00010000;
- User_Status_Show_IP = 0x00020000;
- User_Status_Birthday = 0x00080000;
- User_Status_Webfront = 0x00200000;
- User_Status_Direct_Connection_Disabled = 0x01000000; #no DC
- User_Status_Direct_Connection_Authorized = 0x10000000; #DC after auth
- User_Status_Direct_Connection_Contacts = 0x20000000; #DC with buddies only
- #bottom bytes - status
- User_Status_Online = 0x00000000;
- User_Status_Away = 0x00000001;
- User_Status_Do_Not_Disturb = 0x00000002;
- User_Status_Not_Available = 0x00000004;
- User_Status_Occupied = 0x00000010;
- User_Status_Free_For_Chat = 0x00000020;
- User_Status_Invisible = 0x00000100;
-
- Type_External_IP = 0x000A;
-
- Type_Direct_Connection_Info = 0x000C;
- Direct_Connection_Disabled = 0x0000;
- Direct_Connection_HTTPS_Proxy = 0x0001;
- Direct_Connection_SOCKS_Proxy = 0x0002;
- Direct_Connection_Normal = 0x0004;
- Direct_Connection_Web = 0x0006; #= no direct connection
-
- Type_Client_Idle_Time = 0x000F;
-
- # used in service parameters
- Type_Profile_Max_Length = 0x0001;
- Type_Profile_Max_Capabilities = 0x0002;
- Type_Client_Cabilities_List = 0x0005;
-
- # used in buddy list parameters
- Type_Buddylist_Max_Size = 0x0001;
- Type_Buddylist_Max_Watchers = 0x0002;
-
- # used in privacy management parameters
- Type_Visible_List_Max_Size = 0x0001;
- Type_Invisible_List_Max_Size = 0x0002;
-
- # used for sending, receiving messages
- Type_Message_Data = 0x0002;
- Type_Server_Ack_Request = 0x0003;
- Type_Store_If_Offline = 0x0006;
-
- ###################################################
- # TLV Error Codes
- #
- Error_Messages = {
- 0x01 => "Invalid SNAC header.",
- 0x02 => "Server rate limit exceeded.",
- 0x03 => "Client rate limit exceeded.",
- 0x04 => "Recipient is not logged in.",
- 0x05 => "Requested service unavailable.",
- 0x06 => "Requested service not defined.",
- 0x07 => "You sent obsolete SNAC.",
- 0x08 => "Not supported by server.",
- 0x09 => "Not supported by client.",
- 0x0A => "Refused by client.",
- 0x0B => "Reply too big.",
- 0x0C => "Responses lost.",
- 0x0D => "Request denied.",
- 0x0E => "Incorrect SNAC format.",
- 0x0F => "Insufficient rights.",
- 0x10 => "In local permit/deny (recipient blocked).",
- 0x11 => "Sender too evil.",
- 0x12 => "Receiver too evil.",
- 0x13 => "User temporarily unavailable.",
- 0x14 => "No match.",
- 0x15 => "List overflow.",
- 0x16 => "Request ambiguous.",
- 0x17 => "Server queue full.",
- 0x18 => "Not while on AOL."
- }
-
- ###################################################
- # TLV Constructors
- #
- def construct_tlv_raw(type, data)
- tlv =
- [
- type,
- data.length,
- ].pack("nn") + data;
-
- Client.logger.info("TLV::construct_tlv_raw"){ "Built TLV of length #{tlv.length}, as hex: " + tlv.unpack("H*").join() + "." };
-
- return tlv;
- end
-
- def construct_tlv_empty(type)
- return construct_tlv_raw(type, "");
- end
-
- def construct_tlv_short(type, data)
- return construct_tlv_raw(type, [data].pack("n"));
- end
-
- def construct_tlv_int(type, data)
- return construct_tlv_raw(type, [data].pack("N"));
- end
-
- def construct_tlv_string(type, data)
- return construct_tlv_raw(type, data);
- end
-
- ###################################################
- # TLV Parsers
- #
- def parse_tlv_raw(data)
- if(data.length < 4)
- Client.logger.error("TLV::parse_tlv_raw"){ "Data isn't a proper TLV." };
- raise OscarError, "Communication error."
- end
- type, length = data.unpack("nn");
-
- data.slice!(0..3);
- if(!data || (data.length < length))
- Client.logger.error("TLV::parse_tlv_raw"){ "Expected length #{length} is greater than data length #{data.length}." };
- raise OscarError, "Communication error."
- end
- curdata = data.slice!(0..(length-1));
-
- Client.logger.info("TLV::parse_tlv_raw"){ "Parsing TLV: #{sprintf('0x%04x', type)}, #{sprintf('0x%04x', length)}." };
- if(length <= 16)
- Client.logger.info("TLV::parse_tlv_raw"){ "or as hex: " + curdata.unpack("H*").join() + "."};
- end
-
- return type, curdata, data;
- end
-
- def parse_tlv_short(data)
- type, val, consumed = parse_tlv_raw(data);
- return type, val.unpack("n").first(), consumed;
- end
-
- def parse_tlv_string(data)
- type, str, consumed = parse_tlv_raw(data);
- return type, str, consumed;
- end
- end
- end
-end
+++ /dev/null
-#!/usr/bin/ruby -Ilib
-require 'oscar/client'
-
-#[<uid>,<pw>,<info>]
-logins = [
- ["382837744", "abcdefg", "roscar"],
- ["469791016", "abcdefg", "roscar2"],
- ["429206696", "abcdefg", "roscar3"]
-]
-pair = logins[rand(logins.size)]
-puts "Using uid=#{pair[0]} pw=#{pair[1]} note: #{pair[2]}"
-
-cl = OSCAR::Client.new(pair[0])
-cl.auth(pair[1])
-
-cl.add_message_callback() {|uid,msg|
- puts "Got message -> #{uid}: #{msg}"
-}
-
-cl.send("330493054", "Hello")
-cl.send(pair[0], "Hello, im up an running")
-
-Thread.stop