Changeset - ab1a0140c26b
[Not reviewed]
CMakeLists.txt
Show inline comments
 
@@ -22,28 +22,28 @@ find_package(purple)
 
set(glib_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(glib)
 

	
 
if (NOT WIN32)
 
	set(popt_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(popt)
 
endif()
 

	
 
set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(event)
 

	
 
set(Swiften_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Swiften REQUIRED)
 
find_package(Swiften)
 

	
 
set(openssl_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(openssl REQUIRED)
 
find_package(openssl)
 

	
 
set(Boost_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
if (WIN32)
 
set(Boost_USE_STATIC_LIBS        ON)
 
set(Boost_USE_MULTITHREADED      ON)
 
set(Boost_USE_STATIC_RUNTIME    OFF)
 
endif()
 
find_package(Boost COMPONENTS program_options date_time system filesystem regex  signals REQUIRED)
 
message( STATUS "Found Boost: ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}")
 

	
 
set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Protobuf REQUIRED)
 
@@ -54,24 +54,27 @@ find_package(Communi)
 
set(log4cxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(log4cxx)
 

	
 
set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(event)
 

	
 
set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(pqxx)
 

	
 
set(dbus_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(dbus)
 

	
 
set(yahoo2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(yahoo2)
 

	
 
find_package(Doxygen)
 

	
 
INCLUDE(FindQt4)
 
FIND_PACKAGE(Qt4 COMPONENTS QtCore QtNetwork)
 

	
 
# ADD_DEFINITIONS(${SWIFTEN_CFLAGS})
 
ADD_DEFINITIONS(-DSUPPORT_LEGACY_CAPS)
 
ADD_DEFINITIONS(-DBOOST_FILESYSTEM_VERSION=2)
 

	
 
message("  Supported features")
 
message("-----------------------")
 

	
 
@@ -153,24 +156,32 @@ if (PROTOBUF_FOUND)
 
	endif()
 

	
 
	message("Frotz plugin      : yes")
 
	message("SMSTools3 plugin  : yes")
 

	
 
	if(${LIBDBUSGLIB_FOUND})
 
		message("Skype plugin      : yes")
 
		include_directories(${LIBDBUSGLIB_INCLUDE_DIRS})
 
	else()
 
		message("Skype plugin      : no (install dbus-glib-devel)")
 
	endif()
 

	
 
#	We have our own copy now...
 
# 	if(YAHOO2_FOUND)
 
		message("Libyahoo2 plugin  : yes")
 
# 		include_directories(${YAHOO2_INCLUDE_DIR})
 
# 	else()
 
# 		message("Libyahoo2 plugin  : no (install libyahoo2-devel)")
 
# 	endif()
 

	
 
else()
 
	message("Network plugins   : no (install libprotobuf-dev)")
 
	message("Libpurple plugin  : no (install libpurple and libprotobuf-dev)")
 
	message("IRC plugin        : no (install libircclient-qt and libprotobuf-dev)")
 
	message("Frotz plugin      : no (install libprotobuf-dev)")
 
	message("SMSTools3 plugin  : no (install libprotobuf-dev)")
 
endif()
 

	
 
if (LOG4CXX_FOUND)
 
	message("Log4cxx           : yes")
 
	include_directories(${LOG4CXX_INCLUDE_DIR})
 
	ADD_DEFINITIONS(-DWITH_LOG4CXX)
ChangeLog
Show inline comments
 
Version 2.0.0-beta3 (2012-XX-XX):
 
	General:
 
	* Log errors related to backend spawning (Show proper log message for
 
	  example when path to backend binary is not found).
 
	* Update buddies in database only when it's needed and do not execute
 
	  useless database statements.
 
	* Send roster changes also when buddy's group change.
 
	* Fixed bug when transport contact we in all groups.
 
	* Answer to disco#info IQ with proper node (#206).
 
	* Set SQLite3 as default database backend.
 
	* Fixed disconnecting from server caused by sending VCard response
 
	  with bad "from" attribute.
 
	* Added Munin plugin (Thanks to Askovpen).
 
	* Added support for more admin_jid JIDs (Thanks to Askovpen).
 
	* Fixed allowed_servers option.
 

	
 
	Libpurple:
 
	* prpl-gg: Fetch the contact list properly (#252).
 

	
 
	Skype:
 
	* Log more errors.
 

	
 
	Backend API:
 
	* Added Python NetworkPlugin class, so it is now easier to write backends
 
	  in Python (Thanks to Sarang).
 

	
 
Version 2.0.0-beta2 (2012-03-28):
 
	General:
 
	* Fixed bug when Roster Item Exchange and subscribe stanzas were sent
 
	  repeatedly.
 
	* Backends related logs now contain the backend PID.
 
	* Fixed username_mask setting.
 
	* Added new fields into statistics (backends_crashed, messages related
 
	  stats).
 
	* Chatstates are now not counted as incoming messages in stats.
 
	* Log4cxx is now optional dependency. Without Log4cxx, Spectrum 2 logs
 
	  to standard output.
 
	* Fixed crash when Log4cxx configuration file didn't exist.
backends/CMakeLists.txt
Show inline comments
 
if (PROTOBUF_FOUND)
 
	if ( PURPLE_LIBRARY AND PURPLE_INCLUDE_DIR )
 
		ADD_SUBDIRECTORY(libpurple)
 
	endif()
 

	
 
	if (IRC_FOUND)
 
		ADD_SUBDIRECTORY(libcommuni)
 
	endif()
 

	
 
	ADD_SUBDIRECTORY(smstools3)
 

	
 
	ADD_SUBDIRECTORY(swiften)
 

	
 
# 	if(YAHOO2_FOUND)
 
		ADD_SUBDIRECTORY(libyahoo2)
 
# 	endif()
 

	
 
	ADD_SUBDIRECTORY(template)
 
	
 
	ADD_SUBDIRECTORY(twitter)
 

	
 
if (NOT WIN32)
 
	ADD_SUBDIRECTORY(frotz)
 
	if (${LIBDBUSGLIB_FOUND})
 
		ADD_SUBDIRECTORY(skype)
 
	endif()
 
endif()
 

	
 
endif()
backends/libpurple/gen_dynamic_purple.py
Show inline comments
 
new file 100644
 
import sys
 
import os
 

	
 
# intialize for methods used in libpurple macros
 
methods = ["purple_connection_get_state(", "purple_conversation_get_im_data(",
 
			"purple_conversation_get_chat_data(", "purple_blist_node_get_type("]
 
macros = ["PURPLE_CONV_IM", "PURPLE_CONV_CHAT", "PURPLE_BLIST_NODE_IS_BUDDY", "PURPLE_CONNECTION_IS_CONNECTED"]
 
definitions = []
 

	
 
if len(sys.argv) != 2:
 
	print "Usage:", sys.argv[0], "<path_to_libpurple_dir_containing_libpurple_headers>"
 
	sys.exit(1)
 

	
 

	
 
def handle_file(cpp):
 
	global methods
 
	global macros
 
	sys.stdout.write("getting used methods in " + cpp + ": ")
 
	sys.stdout.flush()
 

	
 
	counter = 0
 

	
 
	new_file = ""
 
	f = open(cpp, "r")
 
	for line in f.readlines():
 
		new_line = ""
 
		index = 0
 
		while index < len(line):
 
			new_line += line[index]
 
			if line[index:].startswith("purple_") or line[index:].startswith("wpurple_") or line[index:].startswith("serv_"):
 
				if line[index:].find("=") != -1 and line[index:].find("=") < line[index:].find("("):
 
					index += 1
 
					continue
 
				if line[index-1] == "_" or line[index:].find("(") == -1 or line[index:].startswith("purple_commands_init") or line[index:].startswith("serv_addr"):
 
					index += 1
 
					continue
 
				m = line[index:line[index:].find("(")+index]
 
				index += len(m)
 
				if m.find("_wrapped") != -1:
 
					new_line += m[1:] + "("
 
					m = m.replace("_wrapped", "")
 
				else:
 
					new_line += m[1:] + "_wrapped("
 
				if not m + "(" in methods and len(m) != 0:
 
					methods += [m + "("]
 
					counter += 1
 
			index += 1
 

	
 
		for x in macros:
 
			if new_line.find(x + "_WRAPPED") == -1:
 
				new_line = new_line.replace(x, x + "_WRAPPED")
 
		new_file += new_line
 
	f.close()
 

	
 
	print counter, "new methods found"
 
	return new_file
 

	
 
def handle_header(header, method):
 
	global definitions
 

	
 
	f = open(os.path.join(sys.argv[1], header), "r")
 

	
 
	lines = f.readlines()
 
	for i in range(len(lines)):
 
		line = lines[i]
 
		if line.find(method) != -1:
 
			if line.startswith(method):
 
				line = lines[i-1][:-1] + line
 
			m = line[:-1]
 
			l = unicode(m).strip()
 
			if l.endswith(")"):
 
				continue
 

	
 
			if m.find("/*") > m.find(";"):
 
				m = m[:m.find("/*")]
 
				m.rstrip()
 
				if len(m) != 0:
 
					while m[-1] == " ":
 
						m = m[:-1]
 

	
 
			index = i;
 
			while not m.endswith(";"):
 
				index += 1
 
				m += " " + lines[index][:-1].lstrip()
 

	
 
			l = unicode(m).strip()
 
			if (l.startswith("#") or l.startswith("*") or l.startswith("/*") or l.count("***") != 0 or l.count("&&") != 0
 
				or l.endswith(")")):
 
				continue;
 

	
 
			m = m.replace("G_GNUC_NULL_TERMINATE", "")
 

	
 
			if not m in definitions:
 
				print "found", method[:-1], "in", header
 
				definitions += [m]
 
			break
 
	f.close()
 

	
 
def get_raw_args(d):
 
	return d[d.find("(")+1:-2]
 

	
 
def get_args(d):
 
	x = d[d.find("(")+1:-2]
 
	x = x.split(",")
 

	
 
	args = []
 
	for arg in x:
 
		y = arg.split(" ")
 
		if len(y) == 1:
 
			continue
 
		args += [y[-1].replace("*", "")]
 

	
 
	return args
 

	
 
def get_name(d):
 
	x = d[:d.find("(")+1].lstrip()
 
	if x.find("wpurple_") != -1:
 
		return x[x.find("wpurple_"):]
 
	if x.find("serv_") != -1:
 
		return x[x.find("serv_"):]
 
	return x[x.find("purple_"):]
 

	
 
def get_rtype(d):
 
	if d.find("wpurple_") != -1:
 
		return d[:d.find("wpurple_")].lstrip()
 
	if d.find("serv_") != -1:
 
		return d[:d.find("serv_")].lstrip()
 
	return d[:d.find("purple_")].lstrip()
 

	
 
def output():
 
	global definitions
 

	
 
	header = open("purple_defs.h", "w")
 
	print >> header, "#pragma once"
 
	print >> header, "#ifdef WIN32"
 

	
 
	print >> header, """
 
#include <Windows.h>
 
#include <purple.h>
 

	
 
#define PURPLE_BLIST_NODE_IS_CHAT_WRAPPED(n)    (purple_blist_node_get_type_wrapped(n) == PURPLE_BLIST_CHAT_NODE)
 
#define PURPLE_BLIST_NODE_IS_BUDDY_WRAPPED(n)   (purple_blist_node_get_type_wrapped(n) == PURPLE_BLIST_BUDDY_NODE)
 
#define PURPLE_BLIST_NODE_IS_CONTACT_WRAPPED(n) (purple_blist_node_get_type_wrapped(n) == PURPLE_BLIST_CONTACT_NODE)
 
#define PURPLE_BLIST_NODE_IS_GROUP_WRAPPED(n)   (purple_blist_node_get_type_wrapped(n) == PURPLE_BLIST_GROUP_NODE)
 

	
 
#define PURPLE_CONV_IM_WRAPPED(c) (purple_conversation_get_im_data_wrapped(c))
 
#define PURPLE_CONV_CHAT_WRAPPED(c) (purple_conversation_get_chat_data_wrapped(c))
 

	
 
#define PURPLE_CONNECTION_IS_CONNECTED_WRAPPED(gc) \
 
	(purple_connection_get_state_wrapped(gc) == PURPLE_CONNECTED)
 
"""
 

	
 
	for d in definitions:
 
		#typedef void (_cdecl * purple_util_set_user_wrapped_func)(const char *dir);
 
		print >> header, "typedef", get_rtype(d), "(_cdecl *", get_name(d)[:-1] + "_wrapped_fnc)(" + get_raw_args(d) + ");"
 
		#extern purple_util_set_user_wrapped_func purple_util_set_user_wrapped;
 
		print >> header, "extern", get_name(d)[:-1] + "_wrapped_fnc", get_name(d)[:-1] + "_wrapped;"
 
		print >> header, ""
 

	
 
	print >> header, ""
 
	print >> header, "#else"
 
	print >> header, ""
 

	
 
	print >> header, """
 
#define PURPLE_BLIST_NODE_IS_CHAT_WRAPPED PURPLE_BLIST_NODE_IS_CHAT
 
#define PURPLE_BLIST_NODE_IS_BUDDY_WRAPPED PURPLE_BLIST_NODE_IS_BUDDY
 
#define PURPLE_BLIST_NODE_IS_CONTACT_WRAPPED PURPLE_BLIST_NODE_IS_CONTACT
 
#define PURPLE_BLIST_NODE_IS_GROUP_WRAPPED PURPLE_BLIST_NODE_IS_GROUP
 

	
 
#define PURPLE_CONV_IM_WRAPPED PURPLE_CONV_IM
 
#define PURPLE_CONV_CHAT_WRAPPED PURPLE_CONV_CHAT
 

	
 
#define PURPLE_CONNECTION_IS_CONNECTED_WRAPPED PURPLE_CONNECTION_IS_CONNECTED	
 
"""
 

	
 
	for d in definitions:
 
		#define purple_util_set_user_wrapped purple_util_set_user
 
		print >> header, "#define", get_name(d)[:-1] + "_wrapped", get_name(d)[:-1]
 
			
 
	print >> header, "#endif"
 
	print >> header, ""
 
	print >> header, "bool resolvePurpleFunctions();"
 
	print >> header, ""
 

	
 

	
 
	cpp = open("purple_defs.cpp", "w")
 
	print >> cpp, "#include \"purple_defs.h\""
 
	print >> cpp, ""
 
	print >> cpp, "#ifdef WIN32"
 
	print >> cpp, "static HMODULE f_hPurple = NULL;"
 
	for d in definitions:
 
		#purple_util_set_user_wrapped_fnc purple_util_set_user_wrapped = NULL;
 
		print >> cpp, get_name(d)[:-1] + "_wrapped_fnc", get_name(d)[:-1] + "_wrapped = NULL;"
 

	
 
	print >> cpp, "#endif"
 

	
 
	print >> cpp, "bool resolvePurpleFunctions() {"
 
	print >> cpp, "#ifdef WIN32"
 
	print >> cpp, "\tf_hPurple = LoadLibrary(\"libpurple.dll\");"
 
	print >> cpp, "\tif (!f_hPurple)"
 
	print >> cpp, "\t\t\treturn false;"
 
	for d in definitions:
 
		#purple_util_set_user_wrapped = (purple_util_set_user_wrapped_func)GetProcAddress(f_hPurple, "purple_util_set_user_dir");
 
		print >> cpp, "\t" + get_name(d)[:-1] + "_wrapped = (" + get_name(d)[:-1] + "_wrapped_fnc)GetProcAddress(f_hPurple, \"" + get_name(d)[:-1] + "\");"
 
		#if (!purple_util_set_user_wrapped)
 
		print >> cpp, "\tif (!" + get_name(d)[:-1] + "_wrapped)"
 
		print >> cpp, "\t\treturn false;"
 
		print >> cpp, ""
 
	print >> cpp, "#endif"
 

	
 
	print >> cpp, "\treturn true;"
 
	print >> cpp, "}"
 
	print >> cpp, ""
 

	
 
	cpp.close()
 
	header.close()
 
		
 

	
 
for f in os.listdir("."):
 
	if not f.endswith(".cpp") or f.startswith("purple_defs"):
 
		continue
 
	new_file = handle_file(f)
 

	
 
	print "updating", f
 
	fd = open(f, "w")
 
	fd.write(new_file)
 
	fd.close()
 

	
 
for f in os.listdir(sys.argv[1]):
 
	if not f.endswith(".h"):
 
		continue
 
	for m in methods:
 
		handle_header(f, m)
 

	
 
sys.argv[1] = sys.argv[1] + "/win32"
 
for f in os.listdir(sys.argv[1]):
 
	if not f.endswith(".h"):
 
		continue
 
	for m in methods:
 
		handle_header(f, m)
 

	
 
for m in methods:
 
	found = False
 
	for d in definitions:
 
		if d.find(m[:-1]) != -1:
 
			found = True
 
			break
 
	if not found:
 
		print "NOT FOUND:", m
 

	
 
output()
backends/libpurple/geventloop.cpp
Show inline comments
 
@@ -19,24 +19,26 @@
 
 */
 

	
 
#include "geventloop.h"
 
#ifdef _WIN32
 
#include "win32/win32dep.h"
 
#undef read
 
#undef write
 
#endif
 
#ifdef WITH_LIBEVENT
 
#include "event.h"
 
#endif
 

	
 
#include "purple_defs.h"
 

	
 
#include "transport/logging.h"
 

	
 
DEFINE_LOGGER(logger, "EventLoop");
 

	
 
typedef struct _PurpleIOClosure {
 
	PurpleInputFunction function;
 
	guint result;
 
	gpointer data;
 
#ifdef WITH_LIBEVENT
 
	GSourceFunc function2;
 
	struct timeval timeout;
 
	struct event evfifo;
backends/libpurple/main.cpp
Show inline comments
 
@@ -18,45 +18,62 @@
 

	
 
#ifdef WITH_LIBEVENT
 
#include <event.h>
 
#endif
 

	
 
#ifdef WIN32
 
#include "win32/win32dep.h"
 
#define ssize_t SSIZE_T
 
#include <process.h>
 
#define getpid _getpid
 
#endif
 

	
 
#include "purple_defs.h"
 

	
 
DEFINE_LOGGER(logger_libpurple, "libpurple");
 
DEFINE_LOGGER(logger, "backend");
 

	
 
int main_socket;
 
static int writeInput;
 

	
 
using namespace Transport;
 

	
 
template <class T> T fromString(const std::string &str) {
 
	T i;
 
	std::istringstream os(str);
 
	os >> i;
 
	return i;
 
}
 

	
 
template <class T> std::string stringOf(T object) {
 
	std::ostringstream os;
 
	os << object;
 
	return (os.str());
 
}
 

	
 
static std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
 
    std::stringstream ss(s);
 
    std::string item;
 
    while(std::getline(ss, item, delim)) {
 
        elems.push_back(item);
 
    }
 
    return elems;
 
}
 

	
 

	
 
static std::vector<std::string> split(const std::string &s, char delim) {
 
    std::vector<std::string> elems;
 
    return split(s, delim, elems);
 
}
 

	
 
static void transportDataReceived(gpointer data, gint source, PurpleInputCondition cond);
 

	
 
class SpectrumNetworkPlugin;
 

	
 
GKeyFile *keyfile;
 
SpectrumNetworkPlugin *np;
 

	
 
static std::string replaceAll(
 
  std::string result,
 
  const std::string& replaceWhat,
 
  const std::string& replaceWithWhat)
 
{
 
@@ -278,24 +295,42 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
 
						default:
 
							continue;
 
					}
 
					break;
 
				}
 

	
 
				if (!found) {
 
					purple_account_set_string(account, key.c_str(), KEYFILE_STRING("purple", key).c_str());
 
				}
 
				i++;
 
			}
 
			g_strfreev (keys);
 

	
 
			char* contents;
 
			gsize length;
 
			gboolean ret = g_file_get_contents ("gfire.cfg", &contents, &length, NULL);
 
			if (ret) {
 
				purple_account_set_int(account, "version", fromString<int>(std::string(contents, length)));
 
			}
 

	
 

	
 
			if (KEYFILE_STRING("service", "protocol") == "prpl-novell") {
 
				std::string username(purple_account_get_username(account));
 
				std::vector <std::string> u = split(username, '@');
 
				purple_account_set_username(account, (const char*) u.front().c_str());
 
				std::vector <std::string> s = split(u.back(), ':'); 
 
				purple_account_set_string(account, "server", s.front().c_str());
 
				purple_account_set_int(account, "port", atoi(s.back().c_str()));  
 
			}
 

	
 
		}
 

	
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
			PurpleAccount *account = NULL;
 

	
 
			std::string name;
 
			std::string protocol;
 
			getProtocolAndName(legacyName, name, protocol);
 

	
 
			if (password.empty()) {
 
				LOG4CXX_INFO(logger,  name.c_str() << ": Empty password");
 
				np->handleDisconnected(user, 0, "Empty password.");
 
@@ -337,24 +372,28 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
 
			}
 

	
 
			// Set the status
 
			const PurpleStatusType *status_type = purple_account_get_status_type_with_primitive(account, PURPLE_STATUS_AVAILABLE);
 
			if (status_type != NULL) {
 
				purple_account_set_status(account, purple_status_type_get_id(status_type), TRUE, NULL);
 
			}
 
		}
 

	
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				if (purple_account_get_int(account, "version", 0) != 0) {
 
					std::string data = stringOf(purple_account_get_int(account, "version", 0));
 
					g_file_set_contents ("gfire.cfg", data.c_str(), data.size(), NULL);
 
				}
 
// 				VALGRIND_DO_LEAK_CHECK;
 
				m_sessions.erase(user);
 
				purple_account_disconnect(account);
 
				purple_account_set_enabled(account, "spectrum", FALSE);
 

	
 
				g_free(account->ui_data);
 
				account->ui_data = NULL;
 
				m_accounts.erase(account);
 

	
 
				purple_accounts_delete(account);
 
#ifndef WIN32
 
				malloc_trim(0);
 
@@ -644,25 +683,29 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
 
		}
 

	
 
		void handleFTContinueRequest(unsigned long ftID) {
 
			PurpleXfer *xfer = m_xfers[ftID];
 
			if (!xfer)
 
				return;
 
			FTData *ftData = (FTData *) xfer->ui_data;
 
			ftData->paused = false;
 
			purple_xfer_ui_ready(xfer);
 
		}
 

	
 
		void sendData(const std::string &string) {
 
#ifdef WIN32
 
			::send(main_socket, string.c_str(), string.size(), 0);
 
#else
 
			write(main_socket, string.c_str(), string.size());
 
#endif
 
			if (writeInput == 0)
 
				writeInput = purple_input_add(main_socket, PURPLE_INPUT_WRITE, &transportDataReceived, NULL);
 
		}
 

	
 
		void readyForData() {
 
			if (m_waitingXfers.empty())
 
				return;
 
			std::vector<PurpleXfer *> tmp;
 
			tmp.swap(m_waitingXfers);
 

	
 
			for (std::vector<PurpleXfer *>::const_iterator it = tmp.begin(); it != tmp.end(); it++) {
 
				FTData *ftData = (FTData *) (*it)->ui_data;
 
@@ -780,25 +823,26 @@ static std::vector<std::string> getGroups(PurpleBuddy *m_buddy) {
 
		groups.push_back("Buddies");
 
	}
 

	
 
	return groups;
 
}
 

	
 
static void buddyListNewNode(PurpleBlistNode *node) {
 
	if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
 
		return;
 
	PurpleBuddy *buddy = (PurpleBuddy *) node;
 
	PurpleAccount *account = purple_buddy_get_account(buddy);
 

	
 
	LOG4CXX_INFO(logger, "Buddy updated " << np->m_accounts[account] << " " << purple_buddy_get_name(buddy) << " " << getAlias(buddy));
 
	std::vector<std::string> groups = getGroups(buddy);
 
	LOG4CXX_INFO(logger, "Buddy updated " << np->m_accounts[account] << " " << purple_buddy_get_name(buddy) << " " << getAlias(buddy) << " group (" << groups.size() << ")=" << groups[0]);
 

	
 
	// Status
 
	pbnetwork::StatusType status = pbnetwork::STATUS_NONE;
 
	std::string message;
 
	getStatus(buddy, status, message);
 

	
 
	// Tooltip
 
	PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
 
	PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 

	
 
	bool blocked = false;
 
	if (KEYFILE_BOOL("service", "enable_privacy_lists")) {
 
@@ -1415,24 +1459,26 @@ debug_init(void)
 
{
 
#define REGISTER_G_LOG_HANDLER(name) \
 
	g_log_set_handler((name), \
 
		(GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \
 
										  | G_LOG_FLAG_RECURSION), \
 
					  spectrum_glib_log_handler, NULL)
 

	
 
	REGISTER_G_LOG_HANDLER(NULL);
 
	REGISTER_G_LOG_HANDLER("GLib");
 
	REGISTER_G_LOG_HANDLER("GModule");
 
	REGISTER_G_LOG_HANDLER("GLib-GObject");
 
	REGISTER_G_LOG_HANDLER("GThread");
 
	REGISTER_G_LOG_HANDLER("GConf");
 
	
 

	
 
#undef REGISTER_G_LOD_HANDLER
 
}
 

	
 
static PurpleCoreUiOps coreUiOps =
 
{
 
	NULL,
 
	debug_init,
 
	transport_core_ui_init,
 
	NULL,
 
	spectrum_ui_get_info,
 
	NULL,
 
@@ -1505,24 +1551,31 @@ static void buddyTypingStopped(PurpleAccount *account, const char *who, gpointer
 

	
 
static void gotAttention(PurpleAccount *account, const char *who, PurpleConversation *conv, guint type) {
 
	std::string w = purple_normalize(account, who);
 
	size_t pos = w.find("/");
 
	if (pos != std::string::npos)
 
		w.erase((int) pos, w.length() - (int) pos);
 
	np->handleAttention(np->m_accounts[account], w, "");
 
}
 

	
 
static bool initPurple() {
 
	bool ret;
 

	
 
	if (!resolvePurpleFunctions()) {
 
		LOG4CXX_ERROR(logger, "Unable to load libpurple.dll or some of the needed methods");
 
		return false;
 
	}
 

	
 
	purple_plugins_add_search_path("./plugins");
 

	
 
	purple_util_set_user_dir("./");
 
	remove("./accounts.xml");
 
	remove("./blist.xml");
 

	
 
	purple_debug_set_ui_ops(&debugUiOps);
 
	purple_debug_set_verbose(true);
 

	
 
	purple_core_set_ui_ops(&coreUiOps);
 
	if (KEYFILE_STRING("service", "eventloop") == "libev") {
 
		LOG4CXX_INFO(logger, "Will use libev based event loop");
 
	}
 
	else {
 
@@ -1580,25 +1633,29 @@ static bool initPurple() {
 
// 
 
// 		purple_commands_init();
 

	
 
	}
 
	return ret;
 
}
 

	
 

	
 
static void transportDataReceived(gpointer data, gint source, PurpleInputCondition cond) {
 
	if (cond & PURPLE_INPUT_READ) {
 
		char buffer[65535];
 
		char *ptr = buffer;
 
#ifdef WIN32
 
		ssize_t n = recv(source, ptr, sizeof(buffer), 0);
 
#else
 
		ssize_t n = read(source, ptr, sizeof(buffer));
 
#endif
 
		if (n <= 0) {
 
			LOG4CXX_INFO(logger, "Diconnecting from spectrum2 server");
 
			exit(errno);
 
		}
 
		std::string d = std::string(buffer, n);
 
		np->handleDataRead(d);
 
	}
 
	else {
 
		if (writeInput != 0) {
 
			purple_input_remove(writeInput);
 
			writeInput = 0;
 
		}
backends/libpurple/purple_defs.cpp
Show inline comments
 
new file 100644
 
#include "purple_defs.h"
 
bool resolvePurpleFunctions() {
 
	return true;
 
}
 

	
backends/libpurple/purple_defs.h
Show inline comments
 
new file 100644
 
#pragma once
 

	
 
bool resolvePurpleFunctions();
backends/libpurple/utils.cpp
Show inline comments
 
@@ -38,24 +38,26 @@
 
#include <netinet/ether.h>
 
#include "sys/socket.h"
 
#include <netdb.h>
 
#include <unistd.h>
 
#include <fcntl.h>
 
#else 
 
#include <process.h>
 
#define getpid _getpid 
 
#define ssize_t SSIZE_T
 
#include "win32/win32dep.h"
 
#endif
 

	
 
#include "purple_defs.h"
 

	
 
static GHashTable *ui_info = NULL;
 

	
 
void execute_purple_plugin_action(PurpleConnection *gc, const std::string &name) {
 
	PurplePlugin *plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? gc->prpl : NULL;
 
	if (plugin && PURPLE_PLUGIN_HAS_ACTIONS(plugin)) {
 
		PurplePluginAction *action = NULL;
 
		GList *actions, *l;
 

	
 
		actions = PURPLE_PLUGIN_ACTIONS(plugin, gc);
 

	
 
		for (l = actions; l != NULL; l = l->next) {
 
			if (l->data) {
 
@@ -135,17 +137,17 @@ int create_socket(char *host, int portno) {
 
	if ((hos = gethostbyname(host)) == NULL) {
 
		// strerror() will not work for gethostbyname() and hstrerror() 
 
		// is supposedly obsolete
 
		exit(1);
 
	}
 
	serv_addr.sin_addr.s_addr = *((unsigned long *) hos->h_addr_list[0]);
 

	
 
	if (connect(main_socket, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
 
		close(main_socket);
 
		main_socket = 0;
 
	}
 

	
 
	int flags = fcntl(main_socket, F_GETFL);
 
	flags |= O_NONBLOCK;
 
	fcntl(main_socket, F_SETFL, flags);
 
// 	int flags = fcntl(main_socket, F_GETFL);
 
// 	flags |= O_NONBLOCK;
 
// 	fcntl(main_socket, F_SETFL, flags);
 
	return main_socket;
 
}
backends/libyahoo2/CMakeLists.txt
Show inline comments
 
new file 100644
 
cmake_minimum_required(VERSION 2.6)
 
 
FILE(GLOB_RECURSE SRC *.c *.cpp)
 
 
ADD_DEFINITIONS(-DHAVE_STDINT_H=1)
 
 
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/yahoo)
 
 
ADD_EXECUTABLE(spectrum2_libyahoo2_backend ${SRC})
 
 
target_link_libraries(spectrum2_libyahoo2_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
 
INSTALL(TARGETS spectrum2_libyahoo2_backend RUNTIME DESTINATION bin)
 
backends/libyahoo2/httpfetch.cpp
Show inline comments
 
new file 100644
 

	
 
#include "httpfetch.h"
 
#include "transport/logging.h"
 

	
 
DEFINE_LOGGER(logger, "HTTPFetch");
 

	
 
static int url_to_host_port_path(const char *url,
 
	char *host, int *port, char *path, int *ssl)
 
{
 
	char *urlcopy = NULL;
 
	char *slash = NULL;
 
	char *colon = NULL;
 

	
 
	/*
 
	 * http://hostname
 
	 * http://hostname/
 
	 * http://hostname/path
 
	 * http://hostname/path:foo
 
	 * http://hostname:port
 
	 * http://hostname:port/
 
	 * http://hostname:port/path
 
	 * http://hostname:port/path:foo
 
	 * and https:// variants of the above
 
	 */
 

	
 
	if (strstr(url, "http://") == url) {
 
		urlcopy = strdup(url + 7);
 
	} else if (strstr(url, "https://") == url) {
 
		urlcopy = strdup(url + 8);
 
		*ssl = 1;
 
	} else {
 
		return 0;
 
	}
 

	
 
	slash = strchr(urlcopy, '/');
 
	colon = strchr(urlcopy, ':');
 

	
 
	if (!colon || (slash && slash < colon)) {
 
		if (*ssl)
 
			*port = 443;
 
		else
 
			*port = 80;
 
	} else {
 
		*colon = 0;
 
		*port = atoi(colon + 1);
 
	}
 

	
 
	if (!slash) {
 
		strcpy(path, "/");
 
	} else {
 
		strcpy(path, slash);
 
		*slash = 0;
 
	}
 

	
 
	strcpy(host, urlcopy);
 

	
 
	free(urlcopy);
 

	
 
	return 1;
 
}
 

	
 
HTTPFetch::HTTPFetch(Swift::BoostIOServiceThread *ioService, Swift::ConnectionFactory *factory) : m_ioService(ioService), m_factory(factory) {
 
	m_afterHeader = false;
 
}
 

	
 
HTTPFetch::~HTTPFetch() {
 
}
 

	
 
void HTTPFetch::_connected(boost::shared_ptr<Swift::Connection> conn, const std::string url, bool error) {
 
	if (error) {
 
		_disconnected(conn);
 
	}
 
	else {
 
		char host[255];
 
		int port = 80;
 
		char path[255];
 
		int ssl = 0;
 
		if (!url_to_host_port_path(url.c_str(), host, &port, path, &ssl))
 
			return;
 

	
 
		static char buff[2048];
 
		snprintf(buff, sizeof(buff),
 
			"GET %s HTTP/1.1\r\n"
 
			"Host: %s\r\n"
 
			"User-Agent: Mozilla/4.5 [en] (1/1)\r\n"
 
			"Accept: */*\r\n"
 
			"%s" "\r\n", path, host,
 
			"Connection: close\r\n");
 
		LOG4CXX_INFO(logger, "Sending " << buff << "\n");
 
		conn->write(Swift::createSafeByteArray(buff));
 
	}
 
}
 

	
 
void HTTPFetch::_disconnected(boost::shared_ptr<Swift::Connection> conn) {
 
	conn->onConnectFinished.disconnect_all_slots();
 
	conn->onDisconnected.disconnect_all_slots();
 
	conn->onDataRead.disconnect_all_slots();
 

	
 
	if (m_buffer.size() == 0) {
 
		onURLFetched("");
 
	}
 
	else {
 
		std::string img = m_buffer.substr(m_buffer.find("\r\n\r\n") + 4);
 
		onURLFetched(img);
 
	}
 
}
 

	
 
void HTTPFetch::_read(boost::shared_ptr<Swift::Connection> conn, boost::shared_ptr<Swift::SafeByteArray> data) {
 
	std::string d(data->begin(), data->end());
 
// 			std::cout << d << "\n";
 
	std::string img = d.substr(d.find("\r\n\r\n") + 4);
 
	if (d.find("Location: ") == std::string::npos) {
 
		m_buffer += d;
 
	}
 
	else {
 
		d = d.substr(d.find("Location: ") + 10);
 
		if (d.find("\r") == std::string::npos) {
 
			d = d.substr(0, d.find("\n"));
 
		}
 
		else {
 
			d = d.substr(0, d.find("\r"));
 
		}
 
		LOG4CXX_INFO(logger, "Next url is '" << d << "'");
 
		fetchURL(d);
 
		conn->onConnectFinished.disconnect_all_slots();
 
		conn->onDisconnected.disconnect_all_slots();
 
		conn->onDataRead.disconnect_all_slots();
 
	}
 
}
 

	
 
bool HTTPFetch::fetchURL(const std::string &url) {
 
	char host[255];
 
	int port = 80;
 
	char path[255];
 
	char buff[1024];
 
	int ssl = 0;
 
	if (!url_to_host_port_path(url.c_str(), host, &port, path, &ssl)) {
 
		LOG4CXX_ERROR(logger, "Invalid URL " << url);
 
		return false;
 
	}
 

	
 
	LOG4CXX_INFO(logger, "Connecting to " << host << ":" << port);
 

	
 
	boost::asio::ip::tcp::resolver resolver(*m_ioService->getIOService());
 
	boost::asio::ip::tcp::resolver::query query(host, "");
 
	boost::asio::ip::address address;
 
	for(boost::asio::ip::tcp::resolver::iterator i = resolver.resolve(query); i != boost::asio::ip::tcp::resolver::iterator(); ++i) {
 
		boost::asio::ip::tcp::endpoint end = *i;
 
		address = end.address();
 
		break;
 
	}
 

	
 
	boost::shared_ptr<Swift::Connection> conn = m_factory->createConnection();
 
	conn->onConnectFinished.connect(boost::bind(&HTTPFetch::_connected, this, conn, url, _1));
 
	conn->onDisconnected.connect(boost::bind(&HTTPFetch::_disconnected, this, conn));
 
	conn->onDataRead.connect(boost::bind(&HTTPFetch::_read, this, conn, _1));
 
	conn->connect(Swift::HostAddressPort(Swift::HostAddress(address), port));
 
	return true;
 
}
backends/libyahoo2/httpfetch.h
Show inline comments
 
new file 100644
 
#pragma once
 

	
 
// Transport includes
 
#include "transport/config.h"
 
#include "transport/networkplugin.h"
 
#include "transport/logging.h"
 

	
 
// Swiften
 
#include "Swiften/Swiften.h"
 
#include "Swiften/TLS/OpenSSL/OpenSSLContextFactory.h"
 

	
 
// for signal handler
 
#include "unistd.h"
 
#include "signal.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 

	
 
// Boost
 
#include <boost/algorithm/string.hpp>
 

	
 
using namespace boost::filesystem;
 
using namespace boost::program_options;
 
using namespace Transport;
 

	
 
class HTTPFetch {
 
	public:
 
		HTTPFetch(Swift::BoostIOServiceThread *ioSerice, Swift::ConnectionFactory *factory);
 
		virtual ~HTTPFetch();
 

	
 
		bool fetchURL(const std::string &url);
 

	
 
		boost::signal<void (const std::string &data)> onURLFetched;
 

	
 
	private:
 
		void _connected(boost::shared_ptr<Swift::Connection> conn, const std::string url, bool error);
 
		void _disconnected(boost::shared_ptr<Swift::Connection> conn);
 
		void _read(boost::shared_ptr<Swift::Connection> conn, boost::shared_ptr<Swift::SafeByteArray> data);
 

	
 
		Swift::BoostIOServiceThread *m_ioService;
 
		Swift::ConnectionFactory *m_factory;
 
		std::string m_buffer;
 
		bool m_afterHeader;
 
};
backends/libyahoo2/main.cpp
Show inline comments
 
new file 100644
 
// Transport includes
 
#include "transport/config.h"
 
#include "transport/networkplugin.h"
 
#include "transport/logging.h"
 

	
 
// Yahoo2
 
#include <yahoo2.h>
 
#include <yahoo2_callbacks.h>
 
#include <stdio.h>
 
#include <stdarg.h>
 
#include <stdlib.h>
 

	
 
#include "yahoohandler.h"
 
#include "yahoolocalaccount.h"
 
#include "httpfetch.h"
 

	
 
// Swiften
 
#include "Swiften/Swiften.h"
 
#include "Swiften/TLS/OpenSSL/OpenSSLContextFactory.h"
 

	
 
// for signal handler
 
#include "unistd.h"
 
#include "signal.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 

	
 
// Boost
 
#include <boost/algorithm/string.hpp>
 
using namespace boost::filesystem;
 
using namespace boost::program_options;
 
using namespace Transport;
 

	
 
class YahooHandler;
 
class YahooLocalAccount;
 

	
 
static std::string *currently_read_data;
 
static YahooLocalAccount *currently_writting_account;
 

	
 
YahooHandler::YahooHandler(YahooLocalAccount *account, int conn_tag, int handler_tag, void *data, yahoo_input_condition cond) :
 
	handler_tag(handler_tag), conn_tag(conn_tag), data(data), cond(cond), remove_later(false), account(account) {}
 

	
 
YahooHandler::~YahooHandler() {}
 

	
 
void YahooHandler::ready(std::string *buffer) {
 
	if (cond == YAHOO_INPUT_WRITE) {
 
		YahooLocalAccount *old = currently_writting_account;
 
		currently_writting_account = account;
 
		yahoo_write_ready(account->id, (void *) conn_tag, data);
 
		currently_writting_account = old;
 
	}
 
	else {
 
		if (!buffer) {
 
			return;
 
		}
 
		// yahoo_read_ready calls ext_yahoo_read(...) in a loop, so we just have to choose proper buffer from which will
 
		// that method read. We do that by static currently_read_data pointer.
 
		currently_read_data = buffer;
 
		// libyahoo2 reads data per 1024 bytes, so if we still have some data after the first ext_yahoo_read call,
 
		// we have to call yahoo_read_ready again...
 
		do {
 
			yahoo_read_ready(account->id, (void *) conn_tag, data);
 
		} while (currently_read_data->size() != 0);
 
	}
 
}
 

	
 

	
 
typedef struct {
 
	std::string yahoo_id;
 
	std::string name;
 
	int status;
 
	int away;
 
	std::string msg;
 
	std::string group;
 
} yahoo_account;
 

	
 
typedef struct {
 
	int id;
 
	char *label;
 
} yahoo_idlabel;
 

	
 
typedef struct {
 
	int id;
 
	char *who;
 
} yahoo_authorize_data;
 

	
 
DEFINE_LOGGER(logger, "Yahoo2");
 

	
 
// eventloop
 
Swift::SimpleEventLoop *loop_;
 

	
 
// Plugin
 
class YahooPlugin;
 
YahooPlugin * np = NULL;
 

	
 
class YahooPlugin : public NetworkPlugin {
 
	public:
 
		Swift::BoostNetworkFactories *m_factories;
 
		Swift::OpenSSLContextFactory *m_sslFactory;
 
		Swift::TLSConnectionFactory *m_tlsFactory;
 
		Swift::BoostIOServiceThread m_boostIOServiceThread;
 
		boost::shared_ptr<Swift::Connection> m_conn;
 

	
 
		YahooPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() {
 
			this->config = config;
 
			m_factories = new Swift::BoostNetworkFactories(loop);
 
			m_sslFactory = new Swift::OpenSSLContextFactory();
 
			m_tlsFactory = new Swift::TLSConnectionFactory(m_sslFactory, m_factories->getConnectionFactory());
 
			m_conn = m_factories->getConnectionFactory()->createConnection();
 
			m_conn->onDataRead.connect(boost::bind(&YahooPlugin::_handleDataRead, this, _1));
 
			m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port));
 

	
 
			LOG4CXX_INFO(logger, "Starting the plugin.");
 
		}
 

	
 
		// NetworkPlugin uses this method to send the data to networkplugin server
 
		void sendData(const std::string &string) {
 
			m_conn->write(Swift::createSafeByteArray(string));
 
		}
 

	
 
		// This method has to call handleDataRead with all received data from network plugin server
 
		void _handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data) {
 
			std::string d(data->begin(), data->end());
 
			handleDataRead(d);
 
		}
 

	
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
			YahooLocalAccount *account = new YahooLocalAccount(user, legacyName, password);
 
			m_users[user] = account;
 
			m_ids[account->id] = user;
 

	
 
			LOG4CXX_INFO(logger, user << ": Logging in the user as " << legacyName << " with id=" << account->id);
 
			account->login();
 
		}
 

	
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
			YahooLocalAccount *account = m_users[user];
 
			if (account) {
 
				yahoo_logoff(account->id);
 
				m_ids.erase(account->id);
 
				m_users.erase(user);
 
				delete account;
 
			}
 
		}
 

	
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "") {
 
			YahooLocalAccount *account = m_users[user];
 
			if (account) {
 
				LOG4CXX_INFO(logger, "Sending message from " << user << " to " << legacyName << ": " << message << ".");
 
				yahoo_send_im(account->id, NULL, legacyName.c_str(), message.c_str(), 0, 0);
 
				_yahoo_write_ready(account);
 
			}
 
		}
 

	
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
 
			LOG4CXX_INFO(logger, user << ": Added buddy " << buddyName << ".");
 
			handleBuddyChanged(user, buddyName, alias, groups, pbnetwork::STATUS_ONLINE);
 
		}
 

	
 
		void _avatar_fetched(HTTPFetch *fetch, int account_id, unsigned int id, const std::string &img) {
 
			handleVCard(m_ids[account_id], id, "", "", "", img);
 
			delete fetch;
 
		}
 

	
 
		void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
 
			YahooLocalAccount *account = m_users[user];
 
			if (!account) {
 
				return;
 
			}
 

	
 
			if (account->urls.find(legacyName) == account->urls.end()) {
 
				return;
 
			}
 

	
 
			HTTPFetch *fetch = new HTTPFetch(&m_boostIOServiceThread, m_factories->getConnectionFactory());
 
			fetch->onURLFetched.connect(boost::bind(&YahooPlugin::_avatar_fetched, this, fetch, account->id, id, _1));
 
			fetch->fetchURL(account->urls[legacyName]);
 
		}
 

	
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
 

	
 
		}
 

	
 
		YahooLocalAccount *getAccount(int id) {
 
			return m_users[m_ids[id]];
 
		}
 

	
 
		void _yahoo_remove_account(YahooLocalAccount *account) {
 
			m_ids.erase(account->id);
 
			m_users.erase(account->user);
 
			delete account;
 
		}
 

	
 
		void _yahoo_connect_finished(YahooLocalAccount *account, yahoo_connect_callback callback, void *data, int conn_tag, bool error) {
 
			currently_writting_account = account;
 
			if (error) {
 
				LOG4CXX_ERROR(logger, account->user << ": Connection error!");
 
				callback(NULL, 0, data);
 
// 				np->handleDisconnected(user, 0, "Connection error.");
 
			}
 
			else {
 
				LOG4CXX_INFO(logger, account->user << ": Connected");
 
				// We will have dangling pointer here, but we can't pass boost::shared_ptr here...
 
				callback((void *) conn_tag, 0, data);
 
			}
 
		}
 

	
 
		void _yahoo_write_ready(YahooLocalAccount *account) {
 
			// Find all WRITE handlers and inform that they really can write.
 
			for (std::map<int, YahooHandler *>::iterator it = account->handlers.begin(); it != account->handlers.end(); it++) {
 
				if (it->second->cond == YAHOO_INPUT_WRITE && !it->second->remove_later) {
 
					it->second->ready();
 
				}
 
			}
 
		}
 

	
 
		void _yahoo_data_read(YahooLocalAccount *account, int conn_tag, boost::shared_ptr<Swift::SafeByteArray> data) {
 
			std::string d(data->begin(), data->end());
 

	
 
			// Find the handler that handles READing for this conn_tag
 
			for (std::map<int, YahooHandler *>::iterator it = account->handlers_per_conn[conn_tag].begin(); it != account->handlers_per_conn[conn_tag].end(); it++) {
 
				if (it->second->cond == YAHOO_INPUT_READ && !it->second->remove_later) {
 
					std::string cpy(d);
 
					it->second->ready(&cpy);
 

	
 
					// Look like libyahoo2 needs to be informed it can write to socket after the read
 
					// even we have informed it before...
 
					_yahoo_write_ready(account);
 
					break;
 
				}
 
			}
 

	
 
			account->removeOldHandlers();
 
		}
 

	
 
		void _yahoo_data_written(YahooLocalAccount *account, int conn_tag) {
 
			LOG4CXX_INFO(logger, "data written");
 
			for (std::map<int, YahooHandler *>::iterator it = account->handlers_per_conn[conn_tag].begin(); it != account->handlers_per_conn[conn_tag].end(); it++) {
 
				if (it->second->cond == YAHOO_INPUT_WRITE) {
 
					it->second->ready();
 
				}
 
			}
 

	
 
			account->removeOldHandlers();
 
		}
 

	
 
		void _yahoo_disconnected(YahooLocalAccount *account, int conn_tag, const boost::optional<Swift::Connection::Error> &error) {
 
			for (std::map<int, YahooHandler *>::iterator it = account->handlers_per_conn[conn_tag].begin(); it != account->handlers_per_conn[conn_tag].end(); it++) {
 
				if (it->second->cond == YAHOO_INPUT_READ && !it->second->remove_later) {
 
					std::string cpy;
 
					it->second->ready(&cpy);
 
					_yahoo_write_ready(account);
 
					break;
 
				}
 
			}
 

	
 
			account->removeConn(conn_tag);
 
		}
 

	
 
		int _yahoo_connect_async(int id, const char *host, int port, yahoo_connect_callback callback, void *data, int use_ssl) {
 
			YahooLocalAccount *account = getAccount(id);
 
			if (!account) {
 
				LOG4CXX_ERROR(logger, "Unknown account id=" << id);
 
				return -1;
 
			}
 

	
 
			boost::asio::ip::tcp::resolver resolver(*m_boostIOServiceThread.getIOService());
 
			boost::asio::ip::tcp::resolver::query query(host, "");
 
			boost::asio::ip::address address;
 
			for(boost::asio::ip::tcp::resolver::iterator i = resolver.resolve(query); i != boost::asio::ip::tcp::resolver::iterator(); ++i) {
 
				boost::asio::ip::tcp::endpoint end = *i;
 
				address = end.address();
 
				break;
 
			}
 

	
 
			LOG4CXX_INFO(logger, m_ids[id] << ": Connecting " << host << ":" << port);
 
			int tag = account->conn_tag++;
 
			if (use_ssl) {
 
				account->conns[tag] = m_tlsFactory->createConnection();
 
			}
 
			else {
 
				account->conns[tag] = m_factories->getConnectionFactory()->createConnection();
 
			}
 
			account->conns[tag]->onConnectFinished.connect(boost::bind(&YahooPlugin::_yahoo_connect_finished, this, account, callback, data, tag, _1));
 
			account->conns[tag]->onDisconnected.connect(boost::bind(&YahooPlugin::_yahoo_disconnected, this, account, tag, _1));
 
			account->conns[tag]->onDataRead.connect(boost::bind(&YahooPlugin::_yahoo_data_read, this, account, tag, _1));
 
			account->conns[tag]->onDataWritten.connect(boost::bind(&YahooPlugin::_yahoo_data_written, this, account, tag));
 
			account->conns[tag]->connect(Swift::HostAddressPort(Swift::HostAddress(address), port));
 
			return tag;
 
		}
 

	
 
	private:
 
		Config *config;
 
		std::map<std::string, YahooLocalAccount *> m_users;
 
		std::map<int, std::string> m_ids;
 
};
 

	
 
static void spectrum_sigchld_handler(int sig)
 
{
 
	int status;
 
	pid_t pid;
 

	
 
	do {
 
		pid = waitpid(-1, &status, WNOHANG);
 
	} while (pid != 0 && pid != (pid_t)-1);
 

	
 
	if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
 
		char errmsg[BUFSIZ];
 
		snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
 
		perror(errmsg);
 
	}
 
}
 

	
 
static void ext_yahoo_got_conf_invite(int id, const char *me, const char *who, const char *room, const char *msg, YList *members) {
 
}
 

	
 
static void ext_yahoo_conf_userdecline(int id, const char *me, const char *who, const char *room, const char *msg) {
 
}
 

	
 
static void ext_yahoo_conf_userjoin(int id, const char *me, const char *who, const char *room) {
 
}
 

	
 
static void ext_yahoo_conf_userleave(int id, const char *me, const char *who, const char *room) {
 
}
 

	
 
static void ext_yahoo_conf_message(int id, const char *me, const char *who, const char *room, const char *msg, int utf8) {
 
}
 

	
 
static void ext_yahoo_chat_cat_xml(int id, const char *xml)  {
 
}
 

	
 
static void ext_yahoo_chat_join(int id, const char *me, const char *room, const char * topic, YList *members, void *fd) {
 
}
 

	
 
static void ext_yahoo_chat_userjoin(int id, const char *me, const char *room, struct yahoo_chat_member *who) {
 
}
 

	
 
static void ext_yahoo_chat_userleave(int id, const char *me, const char *room, const char *who) {
 
}
 

	
 
static void ext_yahoo_chat_message(int id, const char *me, const char *who, const char *room, const char *msg, int msgtype, int utf8) {
 
}
 

	
 
static void ext_yahoo_status_changed(int id, const char *who, int stat, const char *msg, int away, int idle, int mobile) {
 
	YahooLocalAccount *account = np->getAccount(id);
 
	if (!account) {
 
		return;
 
	}
 

	
 
	LOG4CXX_INFO(logger, account->user << ": " << who << " status changed");
 

	
 
	pbnetwork::StatusType status = pbnetwork::STATUS_NONE;
 
	switch (stat) {
 
		case YAHOO_STATUS_AVAILABLE:
 
			status = pbnetwork::STATUS_ONLINE;
 
			break;
 
		case YAHOO_STATUS_NOTATHOME:
 
		case YAHOO_STATUS_NOTATDESK:
 
		case YAHOO_STATUS_NOTINOFFICE:
 
		case YAHOO_STATUS_ONPHONE:
 
		case YAHOO_STATUS_ONVACATION:
 
		case YAHOO_STATUS_OUTTOLUNCH:
 
		case YAHOO_STATUS_STEPPEDOUT:
 
			status = pbnetwork::STATUS_AWAY;
 
			break;
 
		case YAHOO_STATUS_BRB:
 
			status = pbnetwork::STATUS_XA;
 
			break;
 
		case YAHOO_STATUS_BUSY:
 
			status = pbnetwork::STATUS_DND;
 
			break;
 
		case YAHOO_STATUS_OFFLINE:
 
			status = pbnetwork::STATUS_NONE;
 
			break;
 
		default:
 
			status = pbnetwork::STATUS_ONLINE;
 
			break;
 
	}
 

	
 
	yahoo_buddyicon_request(id, who);
 
	np->_yahoo_write_ready(account);
 

	
 
	np->handleBuddyChanged(account->user, who, "", std::vector<std::string>(), status, msg ? msg : "");
 
}
 

	
 
static void ext_yahoo_got_buddies(int id, YList * buds) {
 
	YahooLocalAccount *account = np->getAccount(id);
 
	if (!account) {
 
		return;
 
	}
 

	
 
	LOG4CXX_INFO(logger, account->user << ": Got buddy list");
 
	for(; buds; buds = buds->next) {
 
		struct yahoo_buddy *bud = (struct yahoo_buddy *) buds->data;
 

	
 
		std::vector<std::string> groups;
 
		groups.push_back(bud->group);
 
		np->handleBuddyChanged(account->user, bud->id, bud->real_name ? bud->real_name : "", groups, pbnetwork::STATUS_NONE);
 
	}
 

	
 
// 	yahoo_set_away(id, YAHOO_STATUS_AVAILABLE, "", 1);
 
	np->_yahoo_write_ready(account);
 
	np->handleConnected(account->user);
 
}
 

	
 
static void ext_yahoo_got_ignore(int id, YList * igns)
 
{
 
}
 

	
 
static void ext_yahoo_got_buzz(int id, const char *me, const char *who, long tm) {
 
}
 

	
 
static void ext_yahoo_got_im(int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8) {
 
	YahooLocalAccount *account = np->getAccount(id);
 
	if (!account) {
 
		return;
 
	}
 

	
 
	np->handleMessage(account->user, who, msg);
 
}
 

	
 
static void ext_yahoo_rejected(int id, const char *who, const char *msg) {
 
}
 

	
 
static void ext_yahoo_contact_added(int id, const char *myid, const char *who, const char *msg) {
 
}
 

	
 
static void ext_yahoo_typing_notify(int id, const char* me, const char *who, int stat) {
 
}
 

	
 
static void ext_yahoo_game_notify(int id, const char *me, const char *who, int stat, const char *msg)
 
{
 
}
 

	
 
static void ext_yahoo_mail_notify(int id, const char *from, const char *subj, int cnt) {
 
}
 

	
 
static void ext_yahoo_got_webcam_image(int id, const char *who, const unsigned char *image, unsigned int image_size, unsigned int real_size, unsigned int timestamp) {
 
}
 

	
 
static void ext_yahoo_webcam_viewer(int id, const char *who, int connect) {
 
}
 

	
 
static void ext_yahoo_webcam_closed(int id, const char *who, int reason) {
 
}
 

	
 
static void ext_yahoo_webcam_data_request(int id, int send) {
 
}
 

	
 
static void ext_yahoo_webcam_invite(int id, const char *me, const char *from) {
 
}
 

	
 
static void ext_yahoo_webcam_invite_reply(int id, const char *me, const char *from, int accept) {
 
}
 

	
 
static void ext_yahoo_system_message(int id, const char *me, const char *who, const char *msg) {
 
}
 

	
 
static void ext_yahoo_got_cookies(int id) {
 
}
 

	
 
static void ext_yahoo_login_response(int id, int succ, const char *url) {
 
	YahooLocalAccount *account = np->getAccount(id);
 
	if (!account) {
 
		return;
 
	}
 

	
 
	if (succ == YAHOO_LOGIN_OK) {
 
		account->status = yahoo_current_status(id);
 
		// We will fire handleConnected in Got Buddy List.
 
		return;
 
	}
 
	else if (succ == YAHOO_LOGIN_UNAME) {
 
		np->handleDisconnected(account->user, 0, "Could not log into Yahoo service - username not recognised.  Please verify that your username is correctly typed.");
 
	}
 
	else if (succ == YAHOO_LOGIN_PASSWD) {
 
		np->handleDisconnected(account->user, 0, "Could not log into Yahoo service - password incorrect.  Please verify that your password is correctly typed.");
 
	}
 
	else if (succ == YAHOO_LOGIN_LOCK) {
 
		np->handleDisconnected(account->user, 0, std::string("Could not log into Yahoo service.  Your account has been locked. Visit ") + url + " to reactivate it.");
 
	}
 
	else if (succ == YAHOO_LOGIN_DUPL) {
 
		np->handleDisconnected(account->user, 0, "You have been logged out of the yahoo service, possibly due to a duplicate login.");
 
	}
 
	else if (succ == YAHOO_LOGIN_SOCK) {
 
		np->handleDisconnected(account->user, 0, "The server closed the socket.");
 
	}
 
	else {
 
		np->handleDisconnected(account->user, 0, "Could not log in, unknown reason.");
 
	}
 

	
 
	np->handleLogoutRequest(account->user, "");
 
}
 

	
 
static void ext_yahoo_error(int id, const char *_err, int fatal, int num) {
 
	std::string err(_err);
 
	std::string msg("Yahoo Error: ");
 
	msg += err + " - ";
 
	switch(num) {
 
		case E_UNKNOWN:
 
			msg += "unknown error " + err;
 
			break;
 
		case E_CUSTOM:
 
			msg += "custom error " + err;
 
			break;
 
		case E_CONFNOTAVAIL:
 
			msg += err + " is not available for the conference";
 
			break;
 
		case E_IGNOREDUP:
 
			msg += err + " is already ignored";
 
			break;
 
		case E_IGNORENONE:
 
			msg += err +" is not in the ignore list";
 
			break;
 
		case E_IGNORECONF:
 
			msg += err + " is in buddy list - cannot ignore ";
 
			break;
 
		case E_SYSTEM:
 
			msg += "system error " + err;
 
			break;
 
		case E_CONNECTION:
 
			msg += err + "server connection error %s";
 
			break;
 
	}
 
	LOG4CXX_ERROR(logger, msg);
 

	
 
	YahooLocalAccount *account = np->getAccount(id);
 
	if (!account) {
 
		return;
 
	}
 

	
 
	if(fatal) {
 
		np->handleDisconnected(account->user, 0, msg);
 
		np->handleLogoutRequest(account->user, "");
 
	}
 
}
 

	
 
static int ext_yahoo_connect(const char *host, int port) {
 
	return -1;
 
}
 

	
 
static int ext_yahoo_add_handler(int id, void *fd, yahoo_input_condition cond, void *data) {
 
	YahooLocalAccount *account = np->getAccount(id);
 
	if (!account) {
 
		return -1;
 
	}
 

	
 
	int conn_tag = (unsigned long) fd;
 
	YahooHandler *handler = new YahooHandler(account, conn_tag, account->handler_tag++, data, cond);
 
	account->addHandler(handler);
 

	
 
	// We are ready to write right now, so why not...
 
	handler->ready();
 

	
 
	return handler->handler_tag;
 
}
 

	
 
static void ext_yahoo_remove_handler(int id, int tag) {
 
	YahooLocalAccount *account = np->getAccount(id);
 
	if (!account) {
 
		return;
 
	}
 

	
 
	if (account->handlers.find(tag) == account->handlers.end()) {
 
		return;
 
	}
 

	
 
	YahooHandler *handler = account->handlers[tag];
 
	handler->remove_later = true;
 
}
 

	
 
static int ext_yahoo_write(void *fd, char *buf, int len) {
 
	int conn_tag = (unsigned long) fd;
 
	YahooLocalAccount *account = currently_writting_account;
 

	
 
	std::string string(buf, len);
 
	account->conns[conn_tag]->write(Swift::createSafeByteArray(string));
 

	
 
	return len;
 
}
 

	
 
static int ext_yahoo_read(void *fd, char *buf, int len) {
 
	if (currently_read_data->size() < len) {
 
		len = currently_read_data->size();
 
	}
 
	memcpy(buf, currently_read_data->c_str(), len);
 
	currently_read_data->erase(0, len);
 
	return len;
 
}
 

	
 
static void ext_yahoo_close(void *fd) {
 
	// No need to do anything here. We close it properly in _yahoo_disconnected(...);
 
}
 

	
 
static int ext_yahoo_connect_async(int id, const char *host, int port, yahoo_connect_callback callback, void *data, int use_ssl) {
 
	return np->_yahoo_connect_async(id, host, port, callback, data, use_ssl);
 
}
 

	
 
static void ext_yahoo_got_file(int id, const char *me, const char *who, const char *msg, const char *fname, unsigned long fesize, char *trid) {
 
}
 

	
 
static void ext_yahoo_file_transfer_done(int id, int response, void *data) {
 
}
 

	
 
static char *ext_yahoo_get_ip_addr(const char *domain) {
 
	return NULL;
 
}
 

	
 
static void ext_yahoo_got_ft_data(int id, const unsigned char *in, int count, void *data) {
 
}
 

	
 
static void ext_yahoo_got_identities(int id, YList * ids) {
 
}
 

	
 
static void ext_yahoo_chat_yahoologout(int id, const char *me) {
 
}
 

	
 
static void ext_yahoo_chat_yahooerror(int id, const char *me) {
 
}
 

	
 
static void ext_yahoo_got_ping(int id, const char *errormsg){
 
}
 

	
 
static void ext_yahoo_got_search_result(int id, int found, int start, int total, YList *contacts) {
 
}
 

	
 
static void ext_yahoo_got_buddyicon_checksum(int id, const char *a, const char *b, int checksum) {
 
	LOG4CXX_INFO(logger, "got buddyicon_checksum");
 
}
 

	
 
static void ext_yahoo_got_buddy_change_group(int id, const char *me, const char *who, const char *old_group, const char *new_group) {
 
}
 

	
 
static void ext_yahoo_got_buddyicon(int id, const char *me, const char *who, const char *url, int checksum) {
 
	YahooLocalAccount *account = np->getAccount(id);
 
	if (!account) {
 
		return;
 
	}
 

	
 
	LOG4CXX_INFO(logger, account->user << ": got buddyicon of " << who);
 
	account->urls[who] = url;
 
}
 

	
 
static void ext_yahoo_buddyicon_uploaded(int id, const char *url) {
 
}
 

	
 
static void ext_yahoo_got_buddyicon_request(int id, const char *me, const char *who) {
 
	LOG4CXX_INFO(logger, "got buddyicon_request");
 
}
 

	
 
static int ext_yahoo_log(const char *fmt,...)
 
{
 
	static char log[8192];
 
	static std::string buffered;
 
	va_list ap;
 

	
 
	va_start(ap, fmt);
 

	
 
	vsnprintf(log, 8191, fmt, ap);
 
	buffered += log;
 
	if (buffered.find('\n') != std::string::npos) {
 
		buffered.erase(buffered.find('\n'), 1);
 
		LOG4CXX_INFO(logger, buffered);
 
		buffered.clear();
 
	}
 
	fflush(stderr);
 
	va_end(ap);
 
	return 0;
 
}
 

	
 
static void register_callbacks()
 
{
 
	static struct yahoo_callbacks yc;
 

	
 
	yc.ext_yahoo_login_response = ext_yahoo_login_response;
 
	yc.ext_yahoo_got_buddies = ext_yahoo_got_buddies;
 
	yc.ext_yahoo_got_ignore = ext_yahoo_got_ignore;
 
	yc.ext_yahoo_got_identities = ext_yahoo_got_identities;
 
	yc.ext_yahoo_got_cookies = ext_yahoo_got_cookies;
 
	yc.ext_yahoo_status_changed = ext_yahoo_status_changed;
 
	yc.ext_yahoo_got_im = ext_yahoo_got_im;
 
	yc.ext_yahoo_got_buzz = ext_yahoo_got_buzz;
 
	yc.ext_yahoo_got_conf_invite = ext_yahoo_got_conf_invite;
 
	yc.ext_yahoo_conf_userdecline = ext_yahoo_conf_userdecline;
 
	yc.ext_yahoo_conf_userjoin = ext_yahoo_conf_userjoin;
 
	yc.ext_yahoo_conf_userleave = ext_yahoo_conf_userleave;
 
	yc.ext_yahoo_conf_message = ext_yahoo_conf_message;
 
	yc.ext_yahoo_chat_cat_xml = ext_yahoo_chat_cat_xml;
 
	yc.ext_yahoo_chat_join = ext_yahoo_chat_join;
 
	yc.ext_yahoo_chat_userjoin = ext_yahoo_chat_userjoin;
 
	yc.ext_yahoo_chat_userleave = ext_yahoo_chat_userleave;
 
	yc.ext_yahoo_chat_message = ext_yahoo_chat_message;
 
	yc.ext_yahoo_chat_yahoologout = ext_yahoo_chat_yahoologout;
 
	yc.ext_yahoo_chat_yahooerror = ext_yahoo_chat_yahooerror;
 
	yc.ext_yahoo_got_webcam_image = ext_yahoo_got_webcam_image;
 
	yc.ext_yahoo_webcam_invite = ext_yahoo_webcam_invite;
 
	yc.ext_yahoo_webcam_invite_reply = ext_yahoo_webcam_invite_reply;
 
	yc.ext_yahoo_webcam_closed = ext_yahoo_webcam_closed;
 
	yc.ext_yahoo_webcam_viewer = ext_yahoo_webcam_viewer;
 
	yc.ext_yahoo_webcam_data_request = ext_yahoo_webcam_data_request;
 
	yc.ext_yahoo_got_file = ext_yahoo_got_file;
 
	yc.ext_yahoo_got_ft_data = ext_yahoo_got_ft_data;
 
	yc.ext_yahoo_get_ip_addr = ext_yahoo_get_ip_addr;
 
	yc.ext_yahoo_file_transfer_done = ext_yahoo_file_transfer_done;
 
	yc.ext_yahoo_contact_added = ext_yahoo_contact_added;
 
	yc.ext_yahoo_rejected = ext_yahoo_rejected;
 
	yc.ext_yahoo_typing_notify = ext_yahoo_typing_notify;
 
	yc.ext_yahoo_game_notify = ext_yahoo_game_notify;
 
	yc.ext_yahoo_mail_notify = ext_yahoo_mail_notify;
 
	yc.ext_yahoo_got_search_result = ext_yahoo_got_search_result;
 
	yc.ext_yahoo_system_message = ext_yahoo_system_message;
 
	yc.ext_yahoo_error = ext_yahoo_error;
 
	yc.ext_yahoo_log = ext_yahoo_log;
 
	yc.ext_yahoo_add_handler = ext_yahoo_add_handler;
 
	yc.ext_yahoo_remove_handler = ext_yahoo_remove_handler;
 
	yc.ext_yahoo_connect = ext_yahoo_connect;
 
	yc.ext_yahoo_connect_async = ext_yahoo_connect_async;
 
	yc.ext_yahoo_read = ext_yahoo_read;
 
	yc.ext_yahoo_write = ext_yahoo_write;
 
	yc.ext_yahoo_close = ext_yahoo_close;
 
	yc.ext_yahoo_got_buddyicon = ext_yahoo_got_buddyicon;
 
	yc.ext_yahoo_got_buddyicon_checksum = ext_yahoo_got_buddyicon_checksum;
 
	yc.ext_yahoo_buddyicon_uploaded = ext_yahoo_buddyicon_uploaded;
 
	yc.ext_yahoo_got_buddyicon_request = ext_yahoo_got_buddyicon_request;
 
	yc.ext_yahoo_got_ping = ext_yahoo_got_ping;
 
	yc.ext_yahoo_got_buddy_change_group = ext_yahoo_got_buddy_change_group;
 

	
 
	yahoo_register_callbacks(&yc);
 
}
 

	
 
int main (int argc, char* argv[]) {
 
	std::string host;
 
	int port;
 

	
 
	if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
 
		std::cout << "SIGCHLD handler can't be set\n";
 
		return -1;
 
	}
 

	
 
	boost::program_options::options_description desc("Usage: spectrum [OPTIONS] <config_file.cfg>\nAllowed options");
 
	desc.add_options()
 
		("host,h", value<std::string>(&host), "host")
 
		("port,p", value<int>(&port), "port")
 
		;
 
	try
 
	{
 
		boost::program_options::variables_map vm;
 
		boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
 
		boost::program_options::notify(vm);
 
	}
 
	catch (std::runtime_error& e)
 
	{
 
		std::cout << desc << "\n";
 
		exit(1);
 
	}
 
	catch (...)
 
	{
 
		std::cout << desc << "\n";
 
		exit(1);
 
	}
 

	
 

	
 
	if (argc < 5) {
 
		return 1;
 
	}
 

	
 
	Config config;
 
	if (!config.load(argv[5])) {
 
		std::cerr << "Can't open " << argv[1] << " configuration file.\n";
 
		return 1;
 
	}
 

	
 
	Logging::initBackendLogging(&config);
 

	
 
	register_callbacks();
 
	yahoo_set_log_level(YAHOO_LOG_DEBUG);
 

	
 
	Swift::SimpleEventLoop eventLoop;
 
	loop_ = &eventLoop;
 
	np = new YahooPlugin(&config, &eventLoop, host, port);
 
	loop_->run();
 

	
 
	return 0;
 
}
backends/libyahoo2/sample_client.example
Show inline comments
 
new file 100644
 
/*
 
 * sample yahoo client based on libyahoo2
 
 *
 
 * libyahoo2 is available at http://libyahoo2.sourceforge.net/
 
 *
 
 * $Revision: 373 $
 
 * $Date: 2010-06-02 13:55:25 -0700 (Wed, 02 Jun 2010) $
 
 * 
 
 * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
 
 * Copyright (C) 2009, Siddhesh Poyarekar <siddhesh.poyarekar@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program 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 General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *
 
 */
 

	
 
#if HAVE_CONFIG_H
 
# include <config.h>
 
#endif
 
#ifndef _WIN32
 
#include <netdb.h>
 
#include <sys/time.h>
 
#endif
 
#include <sys/types.h>
 
#ifndef _WIN32
 
#include <sys/socket.h>
 
#include <netinet/in.h>
 
#include <arpa/inet.h>
 

	
 
#include <termios.h>
 
#endif
 
#include <stdio.h>
 
#include <stdarg.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <ctype.h>
 
#include <time.h>
 
#include <sys/stat.h>
 
#include <fcntl.h>
 
#include <errno.h>
 
#include <openssl/ssl.h>
 
#if HAVE_UNISTD_H
 
#include <unistd.h>
 
#endif
 

	
 
/* Get these from http://libyahoo2.sourceforge.net/ */
 
#include <yahoo2.h>
 
#include <yahoo2_callbacks.h>
 
#include "yahoo_util.h"
 

	
 
#ifndef _WIN32
 
int fileno(FILE * stream);
 
#endif
 

	
 
#define MAX_PREF_LEN 255
 

	
 
static char *local_host = NULL;
 

	
 
static int do_mail_notify = 0;
 
static int do_yahoo_debug = 0;
 
static int ignore_system = 0;
 
static int do_typing_notify = 1;
 
static int accept_webcam_viewers = 1;
 
static int send_webcam_images = 0;
 
static int webcam_direction = YAHOO_WEBCAM_DOWNLOAD;
 
static time_t curTime = 0;
 
static time_t pingTimer = 0;
 
static time_t webcamTimer = 0;
 
static double webcamStart = 0;
 

	
 
/* id of the webcam connection (needed for uploading) */
 
static int webcam_id = 0;
 

	
 
static int poll_loop=1;
 

	
 
static void register_callbacks();
 

	
 
typedef struct {
 
	char yahoo_id[255];
 
	char password[255];
 
	int id;
 
	int fd;
 
	int status;
 
	char *msg;
 
} yahoo_local_account;
 

	
 
typedef struct {
 
	char yahoo_id[255];
 
	char name[255];
 
	int status;
 
	int away;
 
	char *msg;
 
	char group[255];
 
} yahoo_account;
 

	
 
typedef struct {
 
	int id;
 
	char *label;
 
} yahoo_idlabel;
 

	
 
typedef struct {
 
	int id;
 
	char *who;
 
} yahoo_authorize_data;
 

	
 
yahoo_idlabel yahoo_status_codes[] = {
 
	{YAHOO_STATUS_AVAILABLE, "Available"},
 
	{YAHOO_STATUS_BRB, "BRB"},
 
	{YAHOO_STATUS_BUSY, "Busy"},
 
	{YAHOO_STATUS_NOTATHOME, "Not Home"},
 
	{YAHOO_STATUS_NOTATDESK, "Not at Desk"},
 
	{YAHOO_STATUS_NOTINOFFICE, "Not in Office"},
 
	{YAHOO_STATUS_ONPHONE, "On Phone"},
 
	{YAHOO_STATUS_ONVACATION, "On Vacation"},
 
	{YAHOO_STATUS_OUTTOLUNCH, "Out to Lunch"},
 
	{YAHOO_STATUS_STEPPEDOUT, "Stepped Out"},
 
	{YAHOO_STATUS_INVISIBLE, "Invisible"},
 
	{YAHOO_STATUS_IDLE, "Idle"},
 
	{YAHOO_STATUS_OFFLINE, "Offline"},
 
	{YAHOO_STATUS_CUSTOM, "[Custom]"},
 
	{YPACKET_STATUS_NOTIFY, "Notify"},
 
	{0, NULL}
 
};
 

	
 
static void ext_yahoo_close(void *fd);
 

	
 
char * yahoo_status_code(enum yahoo_status s)
 
{
 
	int i;
 
	for(i=0; yahoo_status_codes[i].label; i++)
 
		if(yahoo_status_codes[i].id == s)
 
			return yahoo_status_codes[i].label;
 
	return "Unknown";
 
}
 

	
 
#define YAHOO_DEBUGLOG ext_yahoo_log
 

	
 
static int ext_yahoo_log(const char *fmt,...)
 
{
 
	va_list ap;
 

	
 
	va_start(ap, fmt);
 

	
 
	vfprintf(stderr, fmt, ap);
 
	fflush(stderr);
 
	va_end(ap);
 
	return 0;
 
}
 

	
 
#define LOG(x) if(do_yahoo_debug) { YAHOO_DEBUGLOG("%s:%d: ", __FILE__, __LINE__); \
 
	YAHOO_DEBUGLOG x; \
 
	YAHOO_DEBUGLOG("\n"); }
 

	
 
#define WARNING(x) if(do_yahoo_debug) { YAHOO_DEBUGLOG("%s:%d: warning: ", __FILE__, __LINE__); \
 
	YAHOO_DEBUGLOG x; \
 
	YAHOO_DEBUGLOG("\n"); }
 

	
 
#define print_message(x) { printf x; printf("\n"); }
 

	
 
static yahoo_local_account * ylad = NULL;
 
static YList * buddies = NULL;
 

	
 
static int expired(time_t timer)
 
{
 
	if (timer && curTime >= timer)
 
		return 1;
 

	
 
	return 0;
 
}
 

	
 
static void rearm(time_t *timer, int seconds)
 
{
 
	time(timer);
 
	*timer += seconds;
 
}
 

	
 
#ifndef _WIN32
 
FILE *popen(const char *command, const char *type);
 
int pclose(FILE *stream);
 
int gethostname(char *name, size_t len);
 
#endif
 

	
 
static char * get_local_addresses()
 
{
 
	static char addresses[1024];
 
	char buff[1024];
 
	struct hostent * hn;
 
#ifndef _WIN32
 
	char gateway[16];
 
	char  * c;
 
	int i;
 
	FILE * f;
 
	f = popen("netstat -nr", "r");
 
	if(!f)
 
			goto IP_TEST_2;
 
	while( fgets(buff, sizeof(buff), f)  != NULL ) {
 
			c = strtok( buff, " " );
 
			if( (strstr(c, "default") || strstr(c,"0.0.0.0") ) &&
 
							!strstr(c, "127.0.0" ) )
 
					break;
 
	}
 
	c = strtok( NULL, " " );
 
	pclose(f);
 

	
 
	strncpy(gateway,c, 16);
 

	
 

	
 

	
 
	for(i = strlen(gateway); gateway[i] != '.'; i-- )
 
		gateway[i] = 0;
 

	
 
	gateway[i] = 0;
 

	
 
	for(i = strlen(gateway); gateway[i] != '.'; i-- )
 
		gateway[i] = 0;
 

	
 
	f = popen("/sbin/ifconfig -a", "r");
 
	if(!f)
 
		goto IP_TEST_2;
 

	
 
	while( fgets(buff, sizeof(buff), f) != NULL ) {
 
		if( strstr(buff, "inet") && strstr(buff,gateway) )
 
			break;
 
	}
 
	pclose(f);
 

	
 
	c = strtok( buff, " " );
 
	c = strtok( NULL, " " );
 

	
 
	strncpy ( addresses, c, sizeof(addresses) );
 
	c = strtok(addresses, ":" );
 
	strncpy ( buff, c, sizeof(buff) );
 
	if((c=strtok(NULL, ":")))
 
		strncpy( buff, c, sizeof(buff) );
 

	
 
	strncpy(addresses, buff, sizeof(addresses));
 

	
 
	return addresses;
 
		
 
		
 
IP_TEST_2:
 
#endif /* _WIN32 */
 

	
 
	gethostname(buff,sizeof(buff));
 

	
 
	hn = gethostbyname(buff);
 
	if(hn)
 
		strncpy(addresses, inet_ntoa( *((struct in_addr*)hn->h_addr)), sizeof(addresses) );
 
	else
 
		addresses[0] = 0;
 

	
 
	return addresses;
 
}
 

	
 
static double get_time()
 
{
 
#ifndef _WIN32
 
	struct timeval ct;
 
	gettimeofday(&ct, 0);
 

	
 
	/* return time in milliseconds */
 
	return (ct.tv_sec * 1E3 + ct.tv_usec / 1E3);
 
#else
 
	return timeGetTime();
 
#endif
 
}
 

	
 
static int yahoo_ping_timeout_callback()
 
{
 
	print_message(("Sending a keep alive message"));
 
	yahoo_keepalive(ylad->id);
 
	rearm(&pingTimer, 600);
 
	return 1;
 
}
 

	
 
static int yahoo_webcam_timeout_callback(int id)
 
{
 
	static unsigned image_num = 0;
 
	unsigned char *image = NULL;
 
	unsigned int length = 0;
 
	unsigned int timestamp = get_time() - webcamStart;
 
	char fname[1024];
 
	FILE *f_image = NULL;
 
	struct stat s_image;
 

	
 
	if (send_webcam_images)
 
	{
 
		sprintf(fname, "images/image_%.3d.jpc", image_num++);
 
		if (image_num > 999) image_num = 0;
 
		if (stat(fname, &s_image) == -1)
 
			return -1;
 
		length = s_image.st_size;
 
		image = y_new0(unsigned char, length);
 

	
 
		if ((f_image = fopen(fname, "r")) != NULL) {
 
			fread(image, length, 1, f_image);
 
			fclose(f_image);
 
		} else {
 
			printf("Error reading from %s\n", fname);
 
		}
 
	}
 

	
 
	print_message(("Sending a webcam image (%d bytes)", length));
 
	yahoo_webcam_send_image(id, image, length, timestamp);
 
	FREE(image);
 
	rearm(&webcamTimer, 2);
 
	return 1;
 
}
 

	
 
YList * conferences = NULL;
 
typedef struct {
 
	int id;
 
	char * me;
 
	char * room_name;
 
	char * host;
 
	YList * members;
 
	int joined;
 
} conf_room;
 

	
 
static const char * get_buddy_name(const char * yid)
 
{
 
	YList * b;
 
	for (b = buddies; b; b = b->next) {
 
		yahoo_account * ya = b->data;
 
		if(!strcmp(yid, ya->yahoo_id))
 
			return ya->name&&*ya->name?ya->name:ya->yahoo_id;
 
	}
 

	
 
	return yid;
 
}
 

	
 
static conf_room * find_conf_room_by_name_and_id(int id, const char * me, const char * name)
 
{
 
	YList * l;
 
	for(l = conferences; l; l=l->next) {
 
		conf_room * cr = l->data;
 
		if(cr->id == id && !strcmp(name, cr->room_name) && (me == NULL || cr->me == NULL || !strcmp(me, cr->me))) {
 
			return cr;
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
static void ext_yahoo_got_conf_invite(int id, const char *me, const char *who, const char *room, const char *msg, YList *members)
 
{
 
	conf_room * cr = y_new0(conf_room, 1);
 
	cr->room_name = strdup(room);
 
	cr->me = strdup(me);
 
	cr->host = strdup(who);
 
	cr->members = members;
 
	cr->id = id;
 

	
 
	conferences = y_list_append(conferences, cr);
 

	
 
	print_message(("%s has invited you [%s] to a conference: %s\n"
 
				"with the message: %s",
 
				who, me, room, msg));
 

	
 
	for(; members; members=members->next)
 
		print_message(("\t%s", (char *)members->data));
 
	
 
}
 
static void ext_yahoo_conf_userdecline(int id, const char *me, const char *who, const char *room, const char *msg)
 
{
 
	YList * l;
 
	/* TODO: probably have to use the me arg to find the room */
 
	conf_room * cr = find_conf_room_by_name_and_id(id, me, room);
 
	
 
	if(cr)
 
	for(l = cr->members; l; l=l->next) {
 
		char * w = l->data;
 
		if(!strcmp(w, who)) {
 
			FREE(l->data);
 
			cr->members = y_list_remove_link(cr->members, l);
 
			y_list_free_1(l);
 
			break;
 
		}
 
	}
 

	
 
	print_message(("%s declined the invitation from %s to %s\n"
 
				"with the message: %s", who, me, room, msg));
 
}
 

	
 
static void ext_yahoo_conf_userjoin(int id, const char *me, const char *who, const char *room)
 
{
 
	conf_room * cr = find_conf_room_by_name_and_id(id, me, room);
 
	if(cr) {
 
	YList * l = NULL;
 
	for(l = cr->members; l; l=l->next) {
 
		char * w = l->data;
 
		if(!strcmp(w, who))
 
			break;
 
	}
 
	if(!l)
 
		cr->members = y_list_append(cr->members, strdup(who));
 
	}
 

	
 
	print_message(("%s joined the conference %s [%s]", who, room, me));
 

	
 
}
 

	
 
static void ext_yahoo_conf_userleave(int id, const char *me, const char *who, const char *room)
 
{
 
	YList * l;
 
	conf_room * cr = find_conf_room_by_name_and_id(id, me, room);
 
	
 
	if(cr)
 
	for(l = cr->members; l; l=l->next) {
 
		char * w = l->data;
 
		if(!strcmp(w, who)) {
 
			FREE(l->data);
 
			cr->members = y_list_remove_link(cr->members, l);
 
			y_list_free_1(l);
 
			break;
 
		}
 
	}
 

	
 
	print_message(("%s left the conference %s [%s]", who, room, me));
 
}
 

	
 
static void ext_yahoo_conf_message(int id, const char *me, const char *who, const char *room, const char *msg, int utf8)
 
{
 
	char * umsg = (char *)msg;
 

	
 
	if(utf8)
 
		umsg = y_utf8_to_str(msg);
 

	
 
	who = get_buddy_name(who);
 

	
 
	print_message(("%s (in %s [%s]): %s", who, room, me, umsg));
 

	
 
	if(utf8)
 
		FREE(umsg);
 
}
 

	
 
static void print_chat_member(struct yahoo_chat_member *ycm) 
 
{
 
	printf("%s (%s) ", ycm->id, ycm->alias);
 
	printf(" Age: %d Sex: ", ycm->age);
 
	if (ycm->attribs & YAHOO_CHAT_MALE) {
 
		printf("Male");
 
	} else if (ycm->attribs & YAHOO_CHAT_FEMALE) {
 
		printf("Female");
 
	} else {
 
		printf("Unknown");
 
	}
 
	if (ycm->attribs & YAHOO_CHAT_WEBCAM) {
 
		printf(" with webcam");
 
	}
 

	
 
	printf("  Location: %s", ycm->location);
 
}
 

	
 
static void ext_yahoo_chat_cat_xml(int id, const char *xml) 
 
{
 
	print_message(("%s", xml));
 
}
 

	
 
static void ext_yahoo_chat_join(int id, const char *me, const char *room, const char * topic, YList *members, void *fd)
 
{
 
	print_message(("You [%s] have joined the chatroom %s with topic %s", me, room, topic));
 

	
 
	while(members) {
 
		YList *n = members->next;
 

	
 
		printf("\t");
 
		print_chat_member(members->data);
 
		printf("\n");
 
		FREE(((struct yahoo_chat_member *)members->data)->id);
 
		FREE(((struct yahoo_chat_member *)members->data)->alias);
 
		FREE(((struct yahoo_chat_member *)members->data)->location);
 
		FREE(members->data);
 
		FREE(members);
 
		members=n;
 
	}
 
}
 

	
 
static void ext_yahoo_chat_userjoin(int id, const char *me, const char *room, struct yahoo_chat_member *who)
 
{
 
	print_chat_member(who);
 
	print_message((" joined the chatroom %s [%s]", room, me));
 
	FREE(who->id);
 
	FREE(who->alias);
 
	FREE(who->location);
 
	FREE(who);
 
}
 

	
 
static void ext_yahoo_chat_userleave(int id, const char *me, const char *room, const char *who)
 
{
 
	print_message(("%s left the chatroom %s [%s]", who, room, me));
 
}
 

	
 
static void ext_yahoo_chat_message(int id, const char *me, const char *who, const char *room, const char *msg, int msgtype, int utf8)
 
{
 
	char * umsg = (char *)msg;
 
	char * charpos;
 

	
 
	if(utf8)
 
		umsg = y_utf8_to_str(msg);
 
	/* Remove escapes */
 
	charpos = umsg;
 
	while(*charpos) {
 
		if (*charpos == 0x1b) {
 
			*charpos = ' ';
 
		}
 
		charpos++;
 
	}
 

	
 
	if (msgtype == 2) {
 
		print_message(("(in %s [%s]) %s %s", room, me, who, umsg));
 
	} else {
 
		print_message(("(in %s [%s]) %s: %s", room, me, who, umsg));
 
	}
 

	
 
	if(utf8)
 
		FREE(umsg);
 
}
 

	
 
static void ext_yahoo_status_changed(int id, const char *who, int stat, const char *msg, int away, int idle, int mobile)
 
{
 
	yahoo_account * ya=NULL;
 
	YList * b;
 
	char buf[1024];
 

	
 
	for(b = buddies; b; b = b->next) {
 
		if(!strcmp(((yahoo_account *)b->data)->yahoo_id, who)) {
 
			ya = b->data;
 
			break;
 
		}
 
	}
 
	
 
	if (msg == NULL) {
 
		sprintf(buf, "%s", yahoo_status_code(stat));
 
	}
 
	else if (stat == YAHOO_STATUS_CUSTOM) {
 
		sprintf(buf, "%s", msg);
 
	} else {
 
		sprintf(buf, "%s: %s", yahoo_status_code(stat), msg);
 
	}
 

	
 
	if (away > 0) {
 
		char away_buf[32];
 
		sprintf(away_buf, " away[%d]", away);
 
		strcat(buf, away_buf);
 
	}
 

	
 
	if (mobile > 0) {
 
		char mobile_buf[32];
 
		sprintf(mobile_buf, " mobile[%d]", mobile);
 
		strcat(buf, mobile_buf);
 
	}
 

	
 
	if (idle > 0) {
 
		char time_buf[32];
 
		sprintf(time_buf, " idle for %d:%02d:%02d", idle/3600, (idle/60)%60, idle%60);
 
		strcat(buf, time_buf);
 
	}
 

	
 
	print_message(("%s (%s) is now %s", ya?ya->name:who, who, buf))
 

	
 
	if(ya) {
 
		ya->status = stat;
 
		ya->away = away;
 
		if(msg) {
 
			FREE(ya->msg);
 
			ya->msg = strdup(msg);
 
		}
 
	}
 
}
 

	
 
static void ext_yahoo_got_buddies(int id, YList * buds)
 
{
 
	while(buddies) {
 
		FREE(buddies->data);
 
		buddies = buddies->next;
 
		if(buddies)
 
			FREE(buddies->prev);
 
	}
 
	for(; buds; buds = buds->next) {
 
		yahoo_account *ya = y_new0(yahoo_account, 1);
 
		struct yahoo_buddy *bud = buds->data;
 
		strncpy(ya->yahoo_id, bud->id, 255);
 
		if(bud->real_name)
 
			strncpy(ya->name, bud->real_name, 255);
 
		strncpy(ya->group, bud->group, 255);
 
		ya->status = YAHOO_STATUS_OFFLINE;
 
		buddies = y_list_append(buddies, ya);
 

	
 
/*		print_message(("%s is %s", bud->id, bud->real_name));*/
 
	}
 
}
 

	
 
static void ext_yahoo_got_ignore(int id, YList * igns)
 
{
 
}
 

	
 
static void ext_yahoo_got_buzz(int id, const char *me, const char *who, long tm)
 
{
 
	who = get_buddy_name(who);
 

	
 
	printf("\a");
 
	if(tm) {
 
		char timestr[255];
 

	
 
		strncpy(timestr, ctime((time_t *)&tm), sizeof(timestr));
 
		timestr[strlen(timestr) - 1] = '\0';
 

	
 
		print_message(("[Offline message at %s to %s from %s]: **DING**",
 
				timestr, me, who))
 
	} else
 
		print_message(("[%s]%s: **DING**", me, who))
 
}
 

	
 
static void ext_yahoo_got_im(int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8)
 
{
 
	char *umsg = (char *)msg;
 

	
 
	if(stat == 2) {
 
		LOG(("Error sending message from %s to %s", me, who));
 
		return;
 
	}
 

	
 
	if(!msg)
 
		return;
 

	
 
	if(utf8)
 
		umsg = y_utf8_to_str(msg);
 
	
 
	who = get_buddy_name(who);
 

	
 
	if(tm) {
 
		char timestr[255];
 

	
 
		strncpy(timestr, ctime((time_t *)&tm), sizeof(timestr));
 
		timestr[strlen(timestr) - 1] = '\0';
 

	
 
		print_message(("[Offline message at %s to %s from %s]: %s", 
 
				timestr, me, who, umsg))
 
	} else
 
		print_message(("[%s]%s: %s", me, who, umsg))
 

	
 
	if(utf8)
 
		FREE(umsg);
 
}
 

	
 
static void ext_yahoo_rejected(int id, const char *who, const char *msg)
 
{
 
	print_message(("%s has rejected you%s%s", who, 
 
				(msg?" with the message:\n":"."), 
 
				(msg?msg:"")));
 
}
 

	
 
static void ext_yahoo_contact_added(int id, const char *myid, const char *who, const char *msg)
 
{
 
	char buff[1024];
 

	
 
	snprintf(buff, sizeof(buff), "%s, the yahoo user %s has added you to their contact list", myid, who);
 
	if(msg) {
 
		strcat(buff, " with the following message:\n");
 
		strcat(buff, msg);
 
		strcat(buff, "\n");
 
	} else {
 
		strcat(buff, ".  ");
 
	}
 
	strcat(buff, "Do you want to allow this [Y/N]?");
 

	
 
/*	print_message((buff));
 
	scanf("%c", &choice);
 
	if(choice != 'y' && choice != 'Y')
 
		yahoo_reject_buddy(id, who, "Thanks, but no thanks.");
 
*/
 
}
 

	
 
static void ext_yahoo_typing_notify(int id, const char* me, const char *who, int stat)
 
{
 
	if(stat && do_typing_notify)
 
		print_message(("[%s]%s is typing...", me, who));
 
}
 

	
 
static void ext_yahoo_game_notify(int id, const char *me, const char *who, int stat, const char *msg)
 
{
 
}
 

	
 
static void ext_yahoo_mail_notify(int id, const char *from, const char *subj, int cnt)
 
{
 
	char buff[1024] = {0};
 
	
 
	if(!do_mail_notify)
 
		return;
 

	
 
	if(from && subj)
 
		snprintf(buff, sizeof(buff), 
 
				"You have new mail from %s about %s\n", 
 
				from, subj);
 
	if(cnt) {
 
		char buff2[100];
 
		snprintf(buff2, sizeof(buff2), 
 
				"You have %d message%s\n", 
 
				cnt, cnt==1?"":"s");
 
		strcat(buff, buff2);
 
	}
 

	
 
	if(buff[0])
 
		print_message((buff));
 
}
 

	
 
static void ext_yahoo_got_webcam_image(int id, const char *who,
 
		const unsigned char *image, unsigned int image_size, unsigned int real_size,
 
		unsigned int timestamp)
 
{
 
	static unsigned char *cur_image = NULL;
 
	static unsigned int cur_image_len = 0;
 
	static unsigned int image_num = 0;
 
	FILE* f_image;
 
	char fname[1024];
 

	
 
	/* copy image part to cur_image */
 
	if (real_size)
 
	{
 
		if (!cur_image) cur_image = y_new0(unsigned char, image_size);
 
		memcpy(cur_image + cur_image_len, image, real_size);
 
		cur_image_len += real_size;
 
	}
 

	
 
	if (image_size == cur_image_len)
 
	{
 
		print_message(("Received a image update at %d (%d bytes)",
 
			 timestamp, image_size));
 

	
 
		/* if we recieved an image then write it to file */
 
		if (image_size)
 
		{
 
			sprintf(fname, "images/%s_%.3d.jpc", who, image_num++);
 

	
 
			if ((f_image = fopen(fname, "w")) != NULL) {
 
				fwrite(cur_image, image_size, 1, f_image);
 
				fclose(f_image);
 
			} else {
 
				printf("Error writing to %s\n", fname);
 
			}
 
			FREE(cur_image);
 
			cur_image_len = 0;
 
			if (image_num > 999) image_num = 0;
 
		}
 
	}
 
}
 

	
 
static void ext_yahoo_webcam_viewer(int id, const char *who, int connect)
 
{
 
	switch (connect)
 
	{
 
		case 0:
 
			print_message(("%s has stopped viewing your webcam", who));
 
			break;
 
		case 1:
 
			print_message(("%s has started viewing your webcam", who));
 
			break;
 
		case 2:
 
			print_message(("%s is trying to view your webcam", who));
 
			yahoo_webcam_accept_viewer(id, who, accept_webcam_viewers);
 
			break;
 
	}
 
}
 

	
 
static void ext_yahoo_webcam_closed(int id, const char *who, int reason)
 
{
 
	switch(reason)
 
	{
 
		case 1:
 
			print_message(("%s stopped broadcasting", who));
 
			break;
 
		case 2:
 
			print_message(("%s cancelled viewing permission", who));
 
			break;
 
		case 3:
 
			print_message(("%s declines permission to view his/her webcam", who));
 
			break;
 
		case 4:
 
			print_message(("%s does not have his/her webcam online", who));
 
			break;
 
	}
 
}
 

	
 
static void ext_yahoo_webcam_data_request(int id, int send)
 
{
 
	webcam_id = id;
 

	
 
	if (send) {
 
		print_message(("Got request to start sending images"));
 
		if (!webcamTimer)
 
			rearm(&webcamTimer, 2);
 
	} else {
 
		print_message(("Got request to stop sending images"));
 
	}
 
	send_webcam_images = send;
 
}
 

	
 
static void ext_yahoo_webcam_invite(int id, const char *me, const char *from)
 
{
 
	print_message(("Got a webcam invitation to %s from %s", me, from));
 
}
 

	
 
static void ext_yahoo_webcam_invite_reply(int id, const char *me, const char *from, int accept)
 
{
 
	if(accept) {
 
		print_message(("[%s]%s accepted webcam invitation...", me, from));
 
	} else {
 
		print_message(("[%s]%s declined webcam invitation...", me, from));
 
	}
 
}
 

	
 
static void ext_yahoo_system_message(int id, const char *me, const char *who, const char *msg)
 
{
 
	if(ignore_system)
 
		return;
 

	
 
	print_message(("Yahoo System Message: %s", msg));
 
}
 

	
 
void yahoo_logout()
 
{
 
	if (ylad->id <= 0) {
 
		return;
 
	}
 

	
 
	pingTimer=0;
 

	
 
	while(conferences) {
 
		YList * n = conferences->next;
 
		conf_room * cr = conferences->data;
 
		if(cr->joined)
 
			yahoo_conference_logoff(ylad->id, NULL, cr->members, cr->room_name);
 
		FREE(cr->me);
 
		FREE(cr->room_name);
 
		FREE(cr->host);
 
		while(cr->members) {
 
			YList *n = cr->members->next;
 
			FREE(cr->members->data);
 
			FREE(cr->members);
 
			cr->members=n;
 
		}
 
		FREE(cr);
 
		FREE(conferences);
 
		conferences = n;
 
	}
 
	
 
	yahoo_logoff(ylad->id);
 
	yahoo_close(ylad->id);
 

	
 
	ylad->status = YAHOO_STATUS_OFFLINE;
 
	ylad->id = 0;
 

	
 
	poll_loop=0;
 

	
 
	print_message(("logged_out"));
 
}
 

	
 
static void ext_yahoo_login(yahoo_local_account * ylad, int login_mode)
 
{
 
	LOG(("ext_yahoo_login"));
 

	
 
	ylad->id = yahoo_init_with_attributes(ylad->yahoo_id, ylad->password, 
 
			"local_host", local_host,
 
			"pager_port", 5050,
 
			NULL);
 
	ylad->status = YAHOO_STATUS_OFFLINE;
 
	yahoo_login(ylad->id, login_mode);
 

	
 
/*	if (ylad->id <= 0) {
 
		print_message(("Could not connect to Yahoo server.  Please verify that you are connected to the net and the pager host and port are correctly entered."));
 
		return;
 
	}
 
*/
 
	rearm(&pingTimer, 600);
 
}
 

	
 
static void ext_yahoo_got_cookies(int id)
 
{
 
	/*yahoo_get_yab(id);*/
 
}
 

	
 
static void ext_yahoo_login_response(int id, int succ, const char *url)
 
{
 
	char buff[1024];
 

	
 
	if(succ == YAHOO_LOGIN_OK) {
 
		ylad->status = yahoo_current_status(id);
 
		print_message(("logged in"));
 
		return;
 
		
 
	} else if(succ == YAHOO_LOGIN_UNAME) {
 

	
 
		snprintf(buff, sizeof(buff), "Could not log into Yahoo service - username not recognised.  Please verify that your username is correctly typed.");
 
	} else if(succ == YAHOO_LOGIN_PASSWD) {
 

	
 
		snprintf(buff, sizeof(buff), "Could not log into Yahoo service - password incorrect.  Please verify that your password is correctly typed.");
 

	
 
	} else if(succ == YAHOO_LOGIN_LOCK) {
 
		
 
		snprintf(buff, sizeof(buff), "Could not log into Yahoo service.  Your account has been locked.\nVisit %s to reactivate it.", url);
 

	
 
	} else if(succ == YAHOO_LOGIN_DUPL) {
 

	
 
		snprintf(buff, sizeof(buff), "You have been logged out of the yahoo service, possibly due to a duplicate login.");
 
	} else if(succ == YAHOO_LOGIN_SOCK) {
 

	
 
		snprintf(buff, sizeof(buff), "The server closed the socket.");
 
	} else {
 
		snprintf(buff, sizeof(buff), "Could not log in, unknown reason: %d.", succ);
 
	}
 

	
 
	ylad->status = YAHOO_STATUS_OFFLINE;
 
	print_message((buff));
 
	yahoo_logout();
 
	poll_loop=0;
 
}
 

	
 
static void ext_yahoo_error(int id, const char *err, int fatal, int num)
 
{
 
	fprintf(stdout, "Yahoo Error: ");
 
	fprintf(stdout, "%s", err);
 
	switch(num) {
 
		case E_UNKNOWN:
 
			fprintf(stdout, "unknown error %s", err);
 
			break;
 
		case E_CUSTOM:
 
			fprintf(stdout, "custom error %s", err);
 
			break;
 
		case E_CONFNOTAVAIL:
 
			fprintf(stdout, "%s is not available for the conference", err);
 
			break;
 
		case E_IGNOREDUP:
 
			fprintf(stdout, "%s is already ignored", err);
 
			break;
 
		case E_IGNORENONE:
 
			fprintf(stdout, "%s is not in the ignore list", err);
 
			break;
 
		case E_IGNORECONF:
 
			fprintf(stdout, "%s is in buddy list - cannot ignore ", err);
 
			break;
 
		case E_SYSTEM:
 
			fprintf(stdout, "system error %s", err);
 
			break;
 
		case E_CONNECTION:
 
			fprintf(stdout, "server connection error %s", err);
 
			break;
 
	}
 
	fprintf(stdout, "\n");
 
	if(fatal)
 
		yahoo_logout();
 
}
 

	
 
void yahoo_set_current_state(int yahoo_state)
 
{
 
	if (ylad->status == YAHOO_STATUS_OFFLINE && yahoo_state != YAHOO_STATUS_OFFLINE) {
 
		ext_yahoo_login(ylad, yahoo_state);
 
		return;
 
	} else if (ylad->status != YAHOO_STATUS_OFFLINE && yahoo_state == YAHOO_STATUS_OFFLINE) {
 
		yahoo_logout();
 
		return;
 
	}
 

	
 
	ylad->status = yahoo_state;
 
	if(yahoo_state == YAHOO_STATUS_CUSTOM) {
 
		if(ylad->msg)
 
			yahoo_set_away(ylad->id, yahoo_state, ylad->msg, 1);
 
		else
 
			yahoo_set_away(ylad->id, yahoo_state, "delta p * delta x too large", 1);
 
	} else
 
		yahoo_set_away(ylad->id, yahoo_state, NULL, 1);
 
}
 

	
 
static int ext_yahoo_connect(const char *host, int port)
 
{
 
	WARNING(("This should not be used anymore. File a bug report."));
 
	return -1;
 
}
 

	
 
/*************************************
 
 * Callback handling code starts here
 
 */
 
YList *connections = NULL;
 
struct _conn {
 
	int fd;
 
	SSL *ssl;
 
	int use_ssl;
 
	int remove;
 
};
 

	
 
struct conn_handler {
 
	struct _conn *con;
 
	int id;
 
	int tag;
 
	yahoo_input_condition cond;
 
	int remove;
 
	void *data;
 
};
 

	
 
static int connection_tags=0;
 

	
 
static int ext_yahoo_add_handler(int id, void *d, yahoo_input_condition cond, void *data)
 
{
 
	struct conn_handler *h = y_new0(struct conn_handler, 1);
 

	
 
	h->id = id;
 
	h->tag = ++connection_tags;
 
	h->con = d;
 
	h->cond = cond;
 
	h->data = data;
 

	
 
	LOG(("Add %d(%d) for %d, tag %d", h->con->fd, cond, id, h->tag));
 

	
 
	connections = y_list_prepend(connections, h);
 

	
 
	return h->tag;
 
}
 

	
 
static void ext_yahoo_remove_handler(int id, int tag)
 
{
 
	YList *l;
 
	if (!tag)
 
		return;
 

	
 
	for(l = connections; l; l = y_list_next(l)) {
 
		struct conn_handler *c = l->data;
 
		if(c->tag == tag) {
 
			/* don't actually remove it, just mark it for removal */
 
			/* we'll remove when we start the next poll cycle */
 
			LOG(("Marking id:%d fd:%p tag:%d for removal",
 
				c->id, c->con, c->tag));
 
			c->remove = 1;
 
			return;
 
		}
 
	}
 
}
 

	
 
static SSL *do_ssl_connect(int fd)
 
{
 
	SSL *ssl;
 
	SSL_CTX *ctx;
 

	
 
	LOG(("SSL Handshake"));
 

	
 
	SSL_library_init ();
 
	ctx = SSL_CTX_new(SSLv23_client_method());
 
	ssl = SSL_new(ctx);
 
	SSL_CTX_free(ctx);
 
	SSL_set_fd(ssl, fd);
 

	
 
	if (SSL_connect(ssl) == 1)
 
		return ssl;
 

	
 
	return NULL;
 
}
 

	
 
struct connect_callback_data {
 
	yahoo_connect_callback callback;
 
	void * callback_data;
 
	int id;
 
	int tag;
 
};
 

	
 
static void connect_complete(void *data, struct _conn *source, yahoo_input_condition condition)
 
{
 
	struct connect_callback_data *ccd = data;
 
	int error, err_size = sizeof(error);
 

	
 
	ext_yahoo_remove_handler(0, ccd->tag);
 
	getsockopt(source->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&err_size);
 

	
 
	if(error)
 
		goto err;
 

	
 
	LOG(("Connected fd: %d, error: %d", source->fd, error));
 

	
 
	if (source->use_ssl) {
 
		source->ssl = do_ssl_connect(source->fd);
 

	
 
		if (!source->ssl) {
 
err:
 
			LOG(("SSL Handshake Failed!"));
 
			ext_yahoo_close(source);
 

	
 
			ccd->callback(NULL, 0, ccd->callback_data);
 
			FREE(ccd);
 
			return;
 
		}
 
	}
 

	
 
	fcntl(source->fd, F_SETFL, O_NONBLOCK);
 

	
 
	ccd->callback(source, error, ccd->callback_data);
 
	FREE(ccd);
 
}
 

	
 
void yahoo_callback(struct conn_handler *c, yahoo_input_condition cond)
 
{
 
	int ret=1;
 
	char buff[1024]={0};
 

	
 
	if(c->id < 0) {
 
		connect_complete(c->data, c->con, cond);
 
	} else {
 
		if(cond & YAHOO_INPUT_READ)
 
			ret = yahoo_read_ready(c->id, c->con, c->data);
 
		if(ret>0 && cond & YAHOO_INPUT_WRITE)
 
			ret = yahoo_write_ready(c->id, c->con, c->data);
 

	
 
		if(ret == -1)
 
			snprintf(buff, sizeof(buff), 
 
				"Yahoo read error (%d): %s", errno, strerror(errno));
 
		else if(ret == 0)
 
			snprintf(buff, sizeof(buff), 
 
				"Yahoo read error: Server closed socket");
 

	
 
		if(buff[0])
 
			print_message((buff));
 
	}
 
}
 

	
 
static int ext_yahoo_write(void *fd, char *buf, int len)
 
{
 
	struct _conn *c = fd;
 

	
 
	if (c->use_ssl)
 
		return SSL_write(c->ssl, buf, len);
 
	else
 
		return write(c->fd, buf, len);
 
}
 

	
 
static int ext_yahoo_read(void *fd, char *buf, int len)
 
{
 
	struct _conn *c = fd;
 

	
 
	if (c->use_ssl)
 
		return SSL_read(c->ssl, buf, len);
 
	else
 
		return read(c->fd, buf, len);
 
}
 

	
 
static void ext_yahoo_close(void *fd)
 
{
 
	struct _conn *c = fd;
 
	YList *l;
 

	
 
	if (c->use_ssl)
 
		SSL_free(c->ssl);
 

	
 
	close(c->fd);
 
	c->fd = 0;
 

	
 
	/* Remove all handlers */
 
	for (l = connections; l; l = y_list_next(l)) {
 
		struct conn_handler *h = l->data;
 

	
 
		if (h->con == c)
 
			h->remove = 1;
 
	}
 

	
 
	c->remove = 1;
 
}
 

	
 
static int ext_yahoo_connect_async(int id, const char *host, int port, 
 
		yahoo_connect_callback callback, void *data, int use_ssl)
 
{
 
	struct sockaddr_in serv_addr;
 
	static struct hostent *server;
 
	int servfd;
 
	struct connect_callback_data * ccd;
 
	int error;
 
	SSL *ssl = NULL;
 

	
 
	struct _conn *c;
 

	
 
	LOG(("Connecting to %s:%d", host, port));
 
	
 
	if(!(server = gethostbyname(host))) {
 
		errno=h_errno;
 
		return -1;
 
	}
 

	
 
	if((servfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 
		return -1;
 
	}
 

	
 
	memset(&serv_addr, 0, sizeof(serv_addr));
 
	serv_addr.sin_family = AF_INET;
 
	memcpy(&serv_addr.sin_addr.s_addr, *server->h_addr_list, server->h_length);
 
	serv_addr.sin_port = htons(port);
 

	
 
	c = y_new0(struct _conn, 1);
 
	c->fd = servfd;
 
	c->use_ssl = use_ssl;
 

	
 
	error = connect(servfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
 

	
 
	LOG(("Trying to connect: fd:%d error:%d", servfd, error));
 
	if(!error) {
 
		LOG(("Connected"));
 
		if (use_ssl) {
 
			ssl = do_ssl_connect(servfd);
 

	
 
			if (!ssl) {
 
				LOG(("SSL Handshake Failed!"));
 
				ext_yahoo_close(c);
 

	
 
				callback(NULL, 0, data);
 
				return -1;
 
			}
 
		}
 

	
 
		c->ssl = ssl;
 
		fcntl(c->fd, F_SETFL, O_NONBLOCK);
 

	
 
		callback(c, 0, data);
 
		return 0;
 
	} else if(error == -1 && errno == EINPROGRESS) {
 
		ccd = calloc(1, sizeof(struct connect_callback_data));
 
		ccd->callback = callback;
 
		ccd->callback_data = data;
 
		ccd->id = id;
 

	
 
		ccd->tag = ext_yahoo_add_handler(-1, c, YAHOO_INPUT_WRITE, ccd);
 
		return ccd->tag;
 
	} else {
 
		if(error == -1)
 
			LOG(("Connection failure: %s", strerror(errno)));
 

	
 
		ext_yahoo_close(c);
 

	
 
		callback(NULL, errno, data);
 
		return -1;
 
	}
 
}
 
/*
 
 * Callback handling code ends here
 
 ***********************************/
 

	
 
static void process_commands(char *line)
 
{
 
	char *cmd, *to, *msg;
 

	
 
	char *tmp, *start;
 
	char *copy = strdup(line);
 
	int yid = 0;
 

	
 
	enum yahoo_status state;
 

	
 
	start = cmd = copy;
 
	tmp = strchr(copy, ' ');
 
	if(tmp) {
 
		*tmp = '\0';
 
		copy = tmp+1;
 
	} else {
 
		copy = NULL;
 
	}
 

	
 
	if(!strncasecmp(cmd, "MSG", strlen("MSG"))) {
 
		/* send a message */
 
		to = copy;
 
		tmp = strchr(copy, ' ');
 
		if(tmp) {
 
			*tmp = '\0';
 
			copy = tmp+1;
 
		}
 
		msg = copy;
 
		if(to && msg) {
 
			if(!strcmp(msg, "\a"))
 
				yahoo_send_im(ylad->id, NULL, to, "<ding>", 0,0);
 
			else {
 
				msg = y_str_to_utf8(msg);
 
				yahoo_send_im(ylad->id, NULL, to, msg, 1,0);
 
				FREE(msg)
 
			}
 
		}
 
	} else if(!strncasecmp(cmd, "CMS", strlen("CMS"))) {
 
		/* send a message */
 
		conf_room * cr;
 
		to = copy;
 
		tmp = strchr(copy, ' ');
 
		if(tmp) {
 
			*tmp = '\0';
 
			copy = tmp+1;
 
		}
 
		msg = copy;
 
		cr = find_conf_room_by_name_and_id(ylad->id, NULL, to);
 
		if(!cr) {
 
			print_message(("no such room: %s", copy));
 
			goto end_parse;
 
		}
 
		if(msg)
 
			yahoo_conference_message(ylad->id, NULL, cr->members, to, msg, 0);
 
	} else if(!strncasecmp(cmd, "CLS", strlen("CLS"))) {
 
		YList * l;
 
		if(copy) {
 
			conf_room * cr = find_conf_room_by_name_and_id(ylad->id, NULL, copy);
 
			if(!cr) {
 
				print_message(("no such room: %s", copy));
 
				goto end_parse;
 
			}
 
			print_message(("Room: %s", copy));
 
			for(l = cr->members; l; l=l->next) {
 
				print_message(("%s", (char *)l->data))
 
			}
 
		} else {
 
			print_message(("All Rooms:"));
 
			for(l = conferences; l; l=l->next) {
 
				conf_room * cr = l->data;
 
				print_message(("%s", cr->room_name));
 
			}
 
		}
 

	
 
	} else if(!strncasecmp(cmd, "CCR", strlen("CCR"))) {
 
		conf_room * cr = y_new0(conf_room, 1);
 
		while((tmp = strchr(copy, ' ')) != NULL) {
 
			*tmp = '\0';
 
			if(!cr->room_name)
 
				cr->room_name = strdup(copy);
 
			else
 
				cr->members = y_list_append(cr->members,
 
						strdup(copy));
 
			copy = tmp+1;
 
		}
 
		cr->members = y_list_append(cr->members, strdup(copy));
 

	
 
		if(!cr->room_name || !cr->members) {
 
			FREE(cr);
 
		} else {
 
			cr->id = ylad->id;
 
			cr->joined = 1;
 
			conferences = y_list_append(conferences, cr);
 
			yahoo_conference_invite(ylad->id, NULL, cr->members, cr->room_name, "Join my conference");
 
			cr->members = y_list_append(cr->members,strdup(ylad->yahoo_id));
 
		}
 
	} else if(!strncasecmp(cmd, "CIN", strlen("CIN"))) {
 
		conf_room * cr;
 
		char * room=copy;
 
		YList * l1, *l = NULL;
 

	
 
		while((tmp = strchr(copy, ' ')) != NULL) {
 
			*tmp = '\0';
 
			copy = tmp+1;
 
			l = y_list_append(l, copy);
 
		}
 

	
 
		cr = find_conf_room_by_name_and_id(ylad->id, NULL, room);
 
		if(!cr) {
 
			print_message(("no such room: %s", room));
 
			y_list_free(l);
 
			goto end_parse;
 
		}
 

	
 
		for(l1 = l; l1; l1=l1->next) {
 
			char * w = l1->data;
 
			yahoo_conference_addinvite(ylad->id, NULL, w, room, cr->members, "Join my conference");
 
			cr->members = y_list_append(cr->members, strdup(w));
 
		}
 
		y_list_free(l);
 

	
 
	} else if(!strncasecmp(cmd, "CLN", strlen("CLN"))) {
 
		conf_room * cr = find_conf_room_by_name_and_id(ylad->id, NULL, copy);
 
		YList * l;
 
		if(!cr) {
 
			print_message(("no such room: %s", copy));
 
			goto end_parse;
 
		}
 

	
 
		cr->joined = 1;
 
		for(l = cr->members; l; l=l->next) {
 
			char * w = l->data;
 
			if(!strcmp(w, ylad->yahoo_id))
 
				break;
 
		}
 
		if(!l)
 
			cr->members = y_list_append(cr->members, strdup(ylad->yahoo_id));
 
		yahoo_conference_logon(ylad->id, NULL, cr->members, copy);
 

	
 
	} else if(!strncasecmp(cmd, "CLF", strlen("CLF"))) {
 
		conf_room * cr = find_conf_room_by_name_and_id(ylad->id, NULL, copy);
 
		
 
		if(!cr) {
 
			print_message(("no such room: %s", copy));
 
			goto end_parse;
 
		}
 

	
 
		yahoo_conference_logoff(ylad->id, NULL, cr->members, copy);
 

	
 
		conferences = y_list_remove(conferences, cr);
 
		FREE(cr->room_name);
 
		FREE(cr->host);
 
		while(cr->members) {
 
			YList *n = cr->members->next;
 
			FREE(cr->members->data);
 
			FREE(cr->members);
 
			cr->members=n;
 
		}
 
		FREE(cr);
 

	
 
	} else if(!strncasecmp(cmd, "CDC", strlen("CDC"))) {
 
		conf_room * cr;
 
		char * room = copy;
 
		tmp = strchr(copy, ' ');
 
		if(tmp) {
 
			*tmp = '\0';
 
			copy = tmp+1;
 
			msg = copy;
 
		} else {
 
			msg = "Thanks, but no thanks!";
 
		}
 
		
 
		cr = find_conf_room_by_name_and_id(ylad->id, NULL, room);
 
		if(!cr) {
 
			print_message(("no such room: %s", room));
 
			goto end_parse;
 
		}
 

	
 
		yahoo_conference_decline(ylad->id, NULL, cr->members, room,msg);
 

	
 
		conferences = y_list_remove(conferences, cr);
 
		FREE(cr->room_name);
 
		FREE(cr->host);
 
		while(cr->members) {
 
			YList *n = cr->members->next;
 
			FREE(cr->members->data);
 
			FREE(cr->members);
 
			cr->members=n;
 
		}
 
		FREE(cr);
 

	
 

	
 
	} else if(!strncasecmp(cmd, "CHL", strlen("CHL"))) {
 
		int roomid;
 
		roomid = atoi(copy);
 
		yahoo_get_chatrooms(ylad->id, roomid);
 
	} else if(!strncasecmp(cmd, "CHJ", strlen("CHJ"))) {
 
		char *roomid, *roomname;
 
/* Linux, FreeBSD, Solaris:1 */
 
/* 1600326591 */
 
		roomid = copy;
 
		tmp = strchr(copy, ' ');
 
		if(tmp) {
 
			*tmp = '\0';
 
			copy = tmp+1;
 
		}
 
		roomname = copy;
 
		if(roomid && roomname) {
 
			yahoo_chat_logon(ylad->id, NULL, roomname, roomid);
 
		}
 

	
 
	} else if(!strncasecmp(cmd, "CHM", strlen("CHM"))) {
 
		char *msg, *roomname;
 
		roomname = copy;
 
		tmp = strstr(copy, "  ");
 
		if(tmp) {
 
			*tmp = '\0';
 
			copy = tmp+2;
 
		}
 
		msg = copy;
 
		if(roomname && msg) {
 
			yahoo_chat_message(ylad->id, NULL, roomname, msg, 1, 0);
 
		}
 

	
 
	} else if(!strncasecmp(cmd, "CHX", strlen("CHX"))) {
 
		yahoo_chat_logoff(ylad->id, NULL);
 
	} else if(!strncasecmp(cmd, "STA", strlen("STA"))) {
 
		if(isdigit(copy[0])) {
 
			state = (enum yahoo_status)atoi(copy);
 
			copy = strchr(copy, ' ');
 
			if(state == 99) {
 
				if(copy)
 
					msg = copy;
 
				else
 
					msg = "delta x * delta p too large";
 
			} else
 
				msg = NULL;
 
		} else {
 
			state = YAHOO_STATUS_CUSTOM;
 
			msg = copy;
 
		}
 

	
 
		yahoo_set_away(ylad->id, state, msg, 1);
 

	
 
	} else if(!strncasecmp(cmd, "OFF", strlen("OFF"))) {
 
		/* go offline */
 
		printf("Going offline\n");
 
		poll_loop=0;
 
	} else if(!strncasecmp(cmd, "IDS", strlen("IDS"))) {
 
		/* print identities */
 
		const YList * ids = yahoo_get_identities(ylad->id);
 
		printf("Identities: ");
 
		for(; ids; ids = ids->next)
 
			printf("%s, ", (char *)ids->data);
 
		printf("\n");
 
	} else if(!strncasecmp(cmd, "AID", strlen("AID"))) {
 
		/* activate identity */
 
		yahoo_set_identity_status(ylad->id, copy, 1);
 
	} else if(!strncasecmp(cmd, "DID", strlen("DID"))) {
 
		/* deactivate identity */
 
		yahoo_set_identity_status(ylad->id, copy, 0);
 
	} else if(!strncasecmp(cmd, "LST", strlen("LST"))) {
 
		YList * b = buddies;
 
		for(; b; b=b->next) {
 
			yahoo_account * ya = b->data;
 
			if(ya->status == YAHOO_STATUS_OFFLINE)
 
				continue;
 
			if(ya->msg)
 
				print_message(("%s (%s) is now %s", ya->name, ya->yahoo_id, 
 
							ya->msg))
 
			else
 
				print_message(("%s (%s) is now %s", ya->name, ya->yahoo_id, 
 
						yahoo_status_code(ya->status)))
 
		}
 
	} else if(!strncasecmp(cmd, "NAM", strlen("NAM"))) {
 
		struct yab * yab;
 
		
 
		to = copy;
 
		tmp = strchr(copy, ' ');
 
		if(tmp) {
 
			*tmp = '\0';
 
			copy = tmp+1;
 
		}
 
		yid = atoi(copy);
 

	
 
		tmp = strchr(copy, ' ');
 
		if(tmp) {
 
			*tmp = '\0';
 
			copy = tmp+1;
 
		}
 
		msg = copy;
 

	
 
		if(to && msg) {
 
			yab = y_new0(struct yab, 1);
 
			yab->id = to;
 
			yab->yid = yid;	/* Only do this if you have got it from the server */
 
			yab->nname = msg;
 
			yahoo_set_yab(ylad->id, yab);
 
			FREE(yab);
 
		}
 
	} else if(!strncasecmp(cmd, "WCAM", strlen("WCAM"))) {
 
		if (copy)
 
		{
 
			printf("Viewing webcam (%s)\n", copy);
 
			webcam_direction = YAHOO_WEBCAM_DOWNLOAD;
 
			yahoo_webcam_get_feed(ylad->id, copy);
 
		} else {
 
			printf("Starting webcam\n");
 
			webcam_direction = YAHOO_WEBCAM_UPLOAD;
 
			yahoo_webcam_get_feed(ylad->id, NULL);
 
		}
 
	} else if(!strncasecmp(cmd, "WINV", strlen("WINV"))) {
 
		printf("Inviting %s to view webcam\n", copy);
 
		yahoo_webcam_invite(ylad->id, copy);
 
	} else {
 
		fprintf(stderr, "Unknown command: %s\n", cmd);
 
	}
 

	
 
end_parse:
 
	FREE(start);
 
}
 

	
 
#ifndef _WIN32
 
static void local_input_callback(int source)
 
{
 
	char line[1024] = {0};
 
	int i;
 
	char c;
 
	i=0; c=0;
 
	do {
 
		if(read(source, &c, 1) <= 0)
 
			c='\0';
 
		if(c == '\r')
 
			continue;
 
		if(c == '\n')
 
			break;
 
		if(c == '\b') {
 
			if(!i)
 
				continue;
 
			c = '\0';
 
			i--;
 
		}
 
		if(c) {
 
			line[i++] = c;
 
			line[i]='\0';
 
		}
 
	} while(i<1023 && c != '\n');
 

	
 
	if(line[0])
 
		process_commands(line);
 
}
 
#else
 
#include <conio.h>
 
static void local_input_callback(char c)
 
{
 
	static char line[1024] = {0};
 
	static int line_length = 0;
 

	
 
	if (c == '\b' || (int)c == 127) {
 
		if (line_length > 0) {
 
			_cputs("\b \b");
 
			line_length--;
 
		}
 
		return;
 
	}
 

	
 
	if (c == '\n' || c == '\r' || c == 3) {
 
		_cputs("\n");
 
		line[line_length] = 0;
 
		process_commands(line);
 
		line_length = 0;
 
		line[0] = 0;
 
		return;
 
	}
 

	
 
	_putch(c);
 
	line[line_length++] = c;
 
}
 
#endif
 

	
 
int main(int argc, char * argv[])
 
{
 
	int status;
 
	int log_level;
 
	int lfd=0;
 

	
 
	fd_set inp, outp;
 
	struct timeval tv;
 

	
 
#ifndef _WIN32
 
	int fd_stdin = fileno(stdin);
 
#endif
 
	YList *l=connections;
 

	
 
#ifdef _WIN32
 
	WSADATA wsa;	
 
	if (WSAStartup(MAKEWORD(2,2), &wsa))
 
		return -1;
 
#endif
 

	
 
	ylad = y_new0(yahoo_local_account, 1);
 

	
 
	local_host = strdup(get_local_addresses());
 

	
 
	printf("Yahoo Id: ");
 
	scanf("%s", ylad->yahoo_id);
 
	printf("Password: ");
 
#ifdef _WIN32
 
	scanf("%s", ylad->password);
 
#else
 
	{
 
		tcflag_t oflags;
 
		struct termios term;
 
		tcgetattr(fd_stdin, &term);
 
		oflags = term.c_lflag;
 
		term.c_lflag = oflags & ~(ECHO | ECHOK | ICANON);
 
		term.c_cc[VTIME] = 1;
 
		tcsetattr(fd_stdin, TCSANOW, &term);
 
		
 
		scanf("%s", ylad->password);
 

	
 
		term.c_lflag = oflags;
 
		term.c_cc[VTIME] = 0;
 
		tcsetattr(fd_stdin, TCSANOW, &term);
 
	}
 
	printf("\n");
 
#endif
 

	
 
	printf("Initial Status: ");
 
	scanf("%d", &status);
 

	
 
	printf("Log Level: ");
 
	scanf("%d", &log_level);
 
	do_yahoo_debug=log_level;
 

	
 
	register_callbacks();
 
	yahoo_set_log_level(log_level);
 

	
 
	ext_yahoo_login(ylad, status);
 

	
 
	while(poll_loop) {
 
		FD_ZERO(&inp);
 
		FD_ZERO(&outp);
 
#ifndef _WIN32
 
		FD_SET(fd_stdin, &inp);
 
		tv.tv_sec=1;
 
		tv.tv_usec=0;
 
#else
 
		tv.tv_sec=0;
 
		tv.tv_usec=1E4;
 
#endif
 
		lfd=0;
 

	
 
		for(l=connections; l; ) {
 
			struct conn_handler *c = l->data;
 
			if(c->remove) {
 
				YList *n = y_list_next(l);
 
				LOG(("Removing id:%d fd:%d", c->id, c->con->fd));
 
				connections = y_list_remove_link(connections, l);
 
				y_list_free_1(l);
 
				FREE(c);
 
				l=n;
 
			} else {
 
				if(c->cond & YAHOO_INPUT_READ)
 
					FD_SET(c->con->fd, &inp);
 
				if(c->cond & YAHOO_INPUT_WRITE)
 
					FD_SET(c->con->fd, &outp);
 
				if(lfd < c->con->fd)
 
					lfd = c->con->fd;
 
				l = y_list_next(l);
 
			}
 
		}
 

	
 
		select(lfd + 1, &inp, &outp, NULL, &tv);
 
		time(&curTime);
 

	
 
#ifndef _WIN32
 
		if(FD_ISSET(fd_stdin, &inp))	local_input_callback(0);
 
#else
 
		if (_kbhit()) local_input_callback(_getch());
 
#endif
 

	
 
		for(l = connections; l; l = y_list_next(l)) {
 
			struct conn_handler *c = l->data;
 
			if(c->con->remove) {
 
				FREE(c->con);
 
				c->con = NULL;
 
				continue;
 
			}
 
			if(c->remove)
 
				continue;
 
			if(FD_ISSET(c->con->fd, &inp))
 
				yahoo_callback(c, YAHOO_INPUT_READ);
 
			if(FD_ISSET(c->con->fd, &outp))
 
				yahoo_callback(c, YAHOO_INPUT_WRITE);
 
		}
 

	
 
		if(expired(pingTimer))		yahoo_ping_timeout_callback();
 
		if(expired(webcamTimer))	yahoo_webcam_timeout_callback(webcam_id);
 
	}
 
	LOG(("Exited loop"));
 

	
 
	while(connections) {
 
		YList *tmp = connections;
 
		struct conn_handler *c = connections->data;
 
		FREE(c);
 
		connections = y_list_remove_link(connections, connections);
 
		y_list_free_1(tmp);
 
	}
 

	
 
	yahoo_logout();
 

	
 
	FREE(ylad);
 

	
 
#ifdef _WIN32
 
	WSACleanup();
 
#endif
 
	return 0;
 
}
 

	
 
static void ext_yahoo_got_file(int id, const char *me, const char *who, const char *msg, const char *fname, 
 
	unsigned long fesize, char *trid)
 
{
 
	LOG(("Got a File transfer request (%s, %ld bytes) from %s", fname, fesize, who));
 
}
 

	
 
static void ext_yahoo_file_transfer_done(int id, int response, void *data)
 
{
 
}
 

	
 
static char *ext_yahoo_get_ip_addr(const char *domain)
 
{
 
	return NULL;
 
}
 

	
 
static void ext_yahoo_got_ft_data(int id, const unsigned char *in, int count, void *data)
 
{
 
}
 

	
 
static void ext_yahoo_got_identities(int id, YList * ids)
 
{
 
}
 

	
 
static void ext_yahoo_chat_yahoologout(int id, const char *me)
 
{ 
 
 	LOG(("got chat logout for %s", me));
 
}
 

	
 
static void ext_yahoo_chat_yahooerror(int id, const char *me)
 
{ 
 
 	LOG(("got chat error for %s", me));
 
}
 

	
 
static void ext_yahoo_got_ping(int id, const char *errormsg)
 
{ 
 
 	LOG(("got ping errormsg %s", errormsg));
 
}
 

	
 
static void ext_yahoo_got_search_result(int id, int found, int start, int total, YList *contacts)
 
{
 
	LOG(("got search result"));
 
}
 

	
 
static void ext_yahoo_got_buddyicon_checksum(int id, const char *a, const char *b, int checksum)
 
{
 
	LOG(("got buddy icon checksum"));
 
}
 

	
 
static void ext_yahoo_got_buddy_change_group(int id, const char *me, const char *who, 
 
	const char *old_group, const char *new_group)
 
{
 
}
 

	
 
static void ext_yahoo_got_buddyicon(int id, const char *a, const char *b, const char *c, int checksum)
 
{
 
	LOG(("got buddy icon"));
 
}
 

	
 
static void ext_yahoo_buddyicon_uploaded(int id, const char *url)
 
{
 
	LOG(("buddy icon uploaded"));
 
}
 

	
 
static void ext_yahoo_got_buddyicon_request(int id, const char *me, const char *who)
 
{
 
	LOG(("got buddy icon request from %s",who));
 
}
 

	
 
static void register_callbacks()
 
{
 
	static struct yahoo_callbacks yc;
 

	
 
	yc.ext_yahoo_login_response = ext_yahoo_login_response;
 
	yc.ext_yahoo_got_buddies = ext_yahoo_got_buddies;
 
	yc.ext_yahoo_got_ignore = ext_yahoo_got_ignore;
 
	yc.ext_yahoo_got_identities = ext_yahoo_got_identities;
 
	yc.ext_yahoo_got_cookies = ext_yahoo_got_cookies;
 
	yc.ext_yahoo_status_changed = ext_yahoo_status_changed;
 
	yc.ext_yahoo_got_im = ext_yahoo_got_im;
 
	yc.ext_yahoo_got_buzz = ext_yahoo_got_buzz;
 
	yc.ext_yahoo_got_conf_invite = ext_yahoo_got_conf_invite;
 
	yc.ext_yahoo_conf_userdecline = ext_yahoo_conf_userdecline;
 
	yc.ext_yahoo_conf_userjoin = ext_yahoo_conf_userjoin;
 
	yc.ext_yahoo_conf_userleave = ext_yahoo_conf_userleave;
 
	yc.ext_yahoo_conf_message = ext_yahoo_conf_message;
 
	yc.ext_yahoo_chat_cat_xml = ext_yahoo_chat_cat_xml;
 
	yc.ext_yahoo_chat_join = ext_yahoo_chat_join;
 
	yc.ext_yahoo_chat_userjoin = ext_yahoo_chat_userjoin;
 
	yc.ext_yahoo_chat_userleave = ext_yahoo_chat_userleave;
 
	yc.ext_yahoo_chat_message = ext_yahoo_chat_message;
 
	yc.ext_yahoo_chat_yahoologout = ext_yahoo_chat_yahoologout;
 
	yc.ext_yahoo_chat_yahooerror = ext_yahoo_chat_yahooerror;
 
	yc.ext_yahoo_got_webcam_image = ext_yahoo_got_webcam_image;
 
	yc.ext_yahoo_webcam_invite = ext_yahoo_webcam_invite;
 
	yc.ext_yahoo_webcam_invite_reply = ext_yahoo_webcam_invite_reply;
 
	yc.ext_yahoo_webcam_closed = ext_yahoo_webcam_closed;
 
	yc.ext_yahoo_webcam_viewer = ext_yahoo_webcam_viewer;
 
	yc.ext_yahoo_webcam_data_request = ext_yahoo_webcam_data_request;
 
	yc.ext_yahoo_got_file = ext_yahoo_got_file;
 
	yc.ext_yahoo_got_ft_data = ext_yahoo_got_ft_data;
 
	yc.ext_yahoo_get_ip_addr = ext_yahoo_get_ip_addr;
 
	yc.ext_yahoo_file_transfer_done = ext_yahoo_file_transfer_done;
 
	yc.ext_yahoo_contact_added = ext_yahoo_contact_added;
 
	yc.ext_yahoo_rejected = ext_yahoo_rejected;
 
	yc.ext_yahoo_typing_notify = ext_yahoo_typing_notify;
 
	yc.ext_yahoo_game_notify = ext_yahoo_game_notify;
 
	yc.ext_yahoo_mail_notify = ext_yahoo_mail_notify;
 
	yc.ext_yahoo_got_search_result = ext_yahoo_got_search_result;
 
	yc.ext_yahoo_system_message = ext_yahoo_system_message;
 
	yc.ext_yahoo_error = ext_yahoo_error;
 
	yc.ext_yahoo_log = ext_yahoo_log;
 
	yc.ext_yahoo_add_handler = ext_yahoo_add_handler;
 
	yc.ext_yahoo_remove_handler = ext_yahoo_remove_handler;
 
	yc.ext_yahoo_connect = ext_yahoo_connect;
 
	yc.ext_yahoo_connect_async = ext_yahoo_connect_async;
 
	yc.ext_yahoo_read = ext_yahoo_read;
 
	yc.ext_yahoo_write = ext_yahoo_write;
 
	yc.ext_yahoo_close = ext_yahoo_close;
 
	yc.ext_yahoo_got_buddyicon = ext_yahoo_got_buddyicon;
 
	yc.ext_yahoo_got_buddyicon_checksum = ext_yahoo_got_buddyicon_checksum;
 
	yc.ext_yahoo_buddyicon_uploaded = ext_yahoo_buddyicon_uploaded;
 
	yc.ext_yahoo_got_buddyicon_request = ext_yahoo_got_buddyicon_request;
 
	yc.ext_yahoo_got_ping = ext_yahoo_got_ping;
 
	yc.ext_yahoo_got_buddy_change_group = ext_yahoo_got_buddy_change_group;
 

	
 
	yahoo_register_callbacks(&yc);
 
}
 

	
backends/libyahoo2/yahoo/crypt.c
Show inline comments
 
new file 100644
 
/* One way encryption based on MD5 sum.
 
   Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
 
   This file is part of the GNU C Library.
 
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
 

	
 
   The GNU C Library 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 2.1 of the License, or (at your option) any later version.
 

	
 
   The GNU C Library 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 the GNU C Library; if not, write to the Free
 
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 
   02111-1307 USA.  */
 

	
 
/* warmenhoven took this file and made it work with the md5.[ch] we
 
 * already had. isn't that lovely. people should just use linux or
 
 * freebsd, crypt works properly on those systems. i hate solaris */
 

	
 
#if HAVE_CONFIG_H
 
#  include <config.h>
 
#endif
 

	
 
#if HAVE_STRING_H
 
#  include <string.h>
 
#elif HAVE_STRINGS_H
 
#  include <strings.h>
 
#endif
 

	
 
#include <stdlib.h>
 
#include "yahoo_util.h"
 

	
 
#include "md5.h"
 

	
 
/* Define our magic string to mark salt for MD5 "encryption"
 
   replacement.  This is meant to be the same as for other MD5 based
 
   encryption implementations.  */
 
static const char md5_salt_prefix[] = "$1$";
 

	
 
/* Table with characters for base64 transformation.  */
 
static const char b64t[64] =
 
	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 

	
 
char *yahoo_crypt(char *key, char *salt)
 
{
 
	char *buffer = NULL;
 
	int buflen = 0;
 
	int needed = 3 + strlen(salt) + 1 + 26 + 1;
 

	
 
	md5_byte_t alt_result[16];
 
	md5_state_t ctx;
 
	md5_state_t alt_ctx;
 
	size_t salt_len;
 
	size_t key_len;
 
	size_t cnt;
 
	char *cp;
 

	
 
	if (buflen < needed) {
 
		buflen = needed;
 
		if ((buffer = realloc(buffer, buflen)) == NULL)
 
			return NULL;
 
	}
 

	
 
	/* Find beginning of salt string.  The prefix should normally always
 
	   be present.  Just in case it is not.  */
 
	if (strncmp(md5_salt_prefix, salt, sizeof(md5_salt_prefix) - 1) == 0)
 
		/* Skip salt prefix.  */
 
		salt += sizeof(md5_salt_prefix) - 1;
 

	
 
	salt_len = MIN(strcspn(salt, "$"), 8);
 
	key_len = strlen(key);
 

	
 
	/* Prepare for the real work.  */
 
	md5_init(&ctx);
 

	
 
	/* Add the key string.  */
 
	md5_append(&ctx, (md5_byte_t *)key, key_len);
 

	
 
	/* Because the SALT argument need not always have the salt prefix we
 
	   add it separately.  */
 
	md5_append(&ctx, (md5_byte_t *)md5_salt_prefix,
 
		sizeof(md5_salt_prefix) - 1);
 

	
 
	/* The last part is the salt string.  This must be at most 8
 
	   characters and it ends at the first `$' character (for
 
	   compatibility which existing solutions).  */
 
	md5_append(&ctx, (md5_byte_t *)salt, salt_len);
 

	
 
	/* Compute alternate MD5 sum with input KEY, SALT, and KEY.  The
 
	   final result will be added to the first context.  */
 
	md5_init(&alt_ctx);
 

	
 
	/* Add key.  */
 
	md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
 

	
 
	/* Add salt.  */
 
	md5_append(&alt_ctx, (md5_byte_t *)salt, salt_len);
 

	
 
	/* Add key again.  */
 
	md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
 

	
 
	/* Now get result of this (16 bytes) and add it to the other
 
	   context.  */
 
	md5_finish(&alt_ctx, alt_result);
 

	
 
	/* Add for any character in the key one byte of the alternate sum.  */
 
	for (cnt = key_len; cnt > 16; cnt -= 16)
 
		md5_append(&ctx, alt_result, 16);
 
	md5_append(&ctx, alt_result, cnt);
 

	
 
	/* For the following code we need a NUL byte.  */
 
	alt_result[0] = '\0';
 

	
 
	/* The original implementation now does something weird: for every 1
 
	   bit in the key the first 0 is added to the buffer, for every 0
 
	   bit the first character of the key.  This does not seem to be
 
	   what was intended but we have to follow this to be compatible.  */
 
	for (cnt = key_len; cnt > 0; cnt >>= 1)
 
		md5_append(&ctx,
 
			(cnt & 1) != 0 ? alt_result : (md5_byte_t *)key, 1);
 

	
 
	/* Create intermediate result.  */
 
	md5_finish(&ctx, alt_result);
 

	
 
	/* Now comes another weirdness.  In fear of password crackers here
 
	   comes a quite long loop which just processes the output of the
 
	   previous round again.  We cannot ignore this here.  */
 
	for (cnt = 0; cnt < 1000; ++cnt) {
 
		/* New context.  */
 
		md5_init(&ctx);
 

	
 
		/* Add key or last result.  */
 
		if ((cnt & 1) != 0)
 
			md5_append(&ctx, (md5_byte_t *)key, key_len);
 
		else
 
			md5_append(&ctx, alt_result, 16);
 

	
 
		/* Add salt for numbers not divisible by 3.  */
 
		if (cnt % 3 != 0)
 
			md5_append(&ctx, (md5_byte_t *)salt, salt_len);
 

	
 
		/* Add key for numbers not divisible by 7.  */
 
		if (cnt % 7 != 0)
 
			md5_append(&ctx, (md5_byte_t *)key, key_len);
 

	
 
		/* Add key or last result.  */
 
		if ((cnt & 1) != 0)
 
			md5_append(&ctx, alt_result, 16);
 
		else
 
			md5_append(&ctx, (md5_byte_t *)key, key_len);
 

	
 
		/* Create intermediate result.  */
 
		md5_finish(&ctx, alt_result);
 
	}
 

	
 
	/* Now we can construct the result string.  It consists of three
 
	   parts.  */
 

	
 
	strncpy(buffer, md5_salt_prefix, MAX(0, buflen));
 
	cp = buffer + strlen(buffer);
 
	buflen -= sizeof(md5_salt_prefix);
 

	
 
	strncpy(cp, salt, MIN((size_t) buflen, salt_len));
 
	cp = cp + strlen(cp);
 
	buflen -= MIN((size_t) buflen, salt_len);
 

	
 
	if (buflen > 0) {
 
		*cp++ = '$';
 
		--buflen;
 
	}
 
#define b64_from_24bit(B2, B1, B0, N) \
 
	do { \
 
		unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \
 
		int n = (N); \
 
		while (n-- > 0 && buflen > 0) { \
 
			*cp++ = b64t[w & 0x3f]; \
 
			--buflen; \
 
			w >>= 6; \
 
		}\
 
	} while (0)
 

	
 
	b64_from_24bit(alt_result[0], alt_result[6], alt_result[12], 4);
 
	b64_from_24bit(alt_result[1], alt_result[7], alt_result[13], 4);
 
	b64_from_24bit(alt_result[2], alt_result[8], alt_result[14], 4);
 
	b64_from_24bit(alt_result[3], alt_result[9], alt_result[15], 4);
 
	b64_from_24bit(alt_result[4], alt_result[10], alt_result[5], 4);
 
	b64_from_24bit(0, 0, alt_result[11], 2);
 
	if (buflen <= 0) {
 
		FREE(buffer);
 
	} else
 
		*cp = '\0';	/* Terminate the string.  */
 

	
 
	/* Clear the buffer for the intermediate result so that people
 
	   attaching to processes or reading core dumps cannot get any
 
	   information.  We do it in this way to clear correct_words[]
 
	   inside the MD5 implementation as well.  */
 
	md5_init(&ctx);
 
	md5_finish(&ctx, alt_result);
 
	memset(&ctx, '\0', sizeof(ctx));
 
	memset(&alt_ctx, '\0', sizeof(alt_ctx));
 

	
 
	return buffer;
 
}
backends/libyahoo2/yahoo/libyahoo2.c
Show inline comments
 
new file 100644
 
/*
 
 * libyahoo2: libyahoo2.c
 
 *
 
 * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
 
 * YMSG16 code copyright (C) 2009, 
 
 * 		Siddhesh Poyarekar <siddhesh dot poyarekar at gmail dot com>
 
 *
 
 * Yahoo Search copyright (C) 2003, Konstantin Klyagin <konst AT konst.org.ua>
 
 *
 
 * Much of this code was taken and adapted from the yahoo module for
 
 * gaim released under the GNU GPL.  This code is also released under the 
 
 * GNU GPL.
 
 *
 
 * This code is derivitive of Gaim <http://gaim.sourceforge.net>
 
 * copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
 
 *	       1998-1999, Adam Fritzler <afritz@marko.net>
 
 *	       1998-2002, Rob Flynn <rob@marko.net>
 
 *	       2000-2002, Eric Warmenhoven <eric@warmenhoven.org>
 
 *	       2001-2002, Brian Macke <macke@strangelove.net>
 
 *		    2001, Anand Biligiri S <abiligiri@users.sf.net>
 
 *		    2001, Valdis Kletnieks
 
 *		    2002, Sean Egan <bj91704@binghamton.edu>
 
 *		    2002, Toby Gray <toby.gray@ntlworld.com>
 
 *
 
 * This library also uses code from other libraries, namely:
 
 *     Portions from libfaim copyright 1998, 1999 Adam Fritzler
 
 *     <afritz@auk.cx>
 
 *     Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto
 
 *     <hiro-y@kcn.ne.jp>
 
 *
 
 * YMSG16 authentication code based mostly on write-up at:
 
 * 	http://www.carbonize.co.uk/ymsg16.html
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program 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 General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *
 
 */
 

	
 
#if HAVE_CONFIG_H
 
# include <config.h>
 
#endif
 

	
 
#if HAVE_UNISTD_H
 
#include <unistd.h>
 
#endif
 
#include <errno.h>
 
#include <stdio.h>
 
#include <stdarg.h>
 

	
 
#if STDC_HEADERS
 
# include <string.h>
 
#else
 
# if !HAVE_STRCHR
 
#  define strchr index
 
#  define strrchr rindex
 
# endif
 
char *strchr(), *strrchr();
 
# if !HAVE_MEMCPY
 
#  define memcpy(d, s, n) bcopy ((s), (d), (n))
 
#  define memmove(d, s, n) bcopy ((s), (d), (n))
 
# endif
 
#endif
 

	
 
#include <sys/types.h>
 

	
 
#include <stdlib.h>
 
#include <ctype.h>
 

	
 
#include "sha1.h"
 
#include "md5.h"
 
#include "yahoo2.h"
 
#include "yahoo_httplib.h"
 
#include "yahoo_util.h"
 
#include "yahoo_fn.h"
 

	
 
#include "yahoo2_callbacks.h"
 
#include "yahoo_debug.h"
 
#ifdef __MINGW32__
 
#define snprintf _snprintf
 
#define vsnprintf _vsnprintf
 
#endif
 

	
 
struct yahoo_callbacks *yc = NULL;
 

	
 
void yahoo_register_callbacks(struct yahoo_callbacks *tyc)
 
{
 
	yc = tyc;
 
}
 

	
 
#define YAHOO_CALLBACK(x)	yc->x
 

	
 
static int yahoo_send_data(void *fd, void *data, int len);
 
static void _yahoo_http_connected(int id, void *fd, int error, void *data);
 
static void yahoo_connected(void *fd, int error, void *data);
 

	
 
int yahoo_log_message(char *fmt, ...)
 
{
 
	char out[1024];
 
	va_list ap;
 
	va_start(ap, fmt);
 
	vsnprintf(out, sizeof(out), fmt, ap);
 
	va_end(ap);
 
	return YAHOO_CALLBACK(ext_yahoo_log) ("%s", out);
 
}
 

	
 
int yahoo_connect(char *host, int port)
 
{
 
	return YAHOO_CALLBACK(ext_yahoo_connect) (host, port);
 
}
 

	
 
static enum yahoo_log_level log_level = YAHOO_LOG_NONE;
 

	
 
enum yahoo_log_level yahoo_get_log_level()
 
{
 
	return log_level;
 
}
 

	
 
int yahoo_set_log_level(enum yahoo_log_level level)
 
{
 
	enum yahoo_log_level l = log_level;
 
	log_level = level;
 
	return l;
 
}
 

	
 
/* default values for servers */
 
static char *default_pager_hosts[] = {	"scs.msg.yahoo.com",
 
					"scsa.msg.yahoo.com",
 
					"scsb.msg.yahoo.com",
 
					"scsc.msg.yahoo.com",
 
					NULL};
 

	
 
static int pager_port = 5050;
 
static int fallback_ports[] = { 23, 25, 80, 20, 119, 8001, 8002, 5050, 0 };
 

	
 
static char filetransfer_host[] = "filetransfer.msg.yahoo.com";
 
static int filetransfer_port = 80;
 
static char webcam_host[] = "webcam.yahoo.com";
 
static int webcam_port = 5100;
 
static char webcam_description[] = "";
 
static char local_host[] = "";
 
static int conn_type = Y_WCM_DSL;
 

	
 
static char profile_url[] = "http://profiles.yahoo.com/";
 

	
 
struct connect_callback_data {
 
	struct yahoo_data *yd;
 
	int tag;
 
	int i;
 
	int server_i;
 
};
 

	
 
struct yahoo_pair {
 
	int key;
 
	char *value;
 
};
 

	
 
struct yahoo_packet {
 
	unsigned short int service;
 
	unsigned int status;
 
	unsigned int id;
 
	YList *hash;
 
};
 

	
 
struct yahoo_search_state {
 
	int lsearch_type;
 
	char *lsearch_text;
 
	int lsearch_gender;
 
	int lsearch_agerange;
 
	int lsearch_photo;
 
	int lsearch_yahoo_only;
 
	int lsearch_nstart;
 
	int lsearch_nfound;
 
	int lsearch_ntotal;
 
};
 

	
 
struct data_queue {
 
	unsigned char *queue;
 
	int len;
 
};
 

	
 
struct yahoo_input_data {
 
	struct yahoo_data *yd;
 
	struct yahoo_webcam *wcm;
 
	struct yahoo_webcam_data *wcd;
 
	struct yahoo_search_state *ys;
 

	
 
	void *fd;
 
	enum yahoo_connection_type type;
 

	
 
	unsigned char *rxqueue;
 
	int rxlen;
 
	int read_tag;
 

	
 
	YList *txqueues;
 
	int write_tag;
 
};
 

	
 
struct yahoo_server_settings {
 
	char *pager_host;
 
	int pager_port;
 
	char *filetransfer_host;
 
	int filetransfer_port;
 
	char *webcam_host;
 
	int webcam_port;
 
	char *webcam_description;
 
	char *local_host;
 
	int conn_type;
 
	char **pager_host_list;
 
};
 

	
 
static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over);
 

	
 
static void yahoo_process_filetransfer(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt);
 
static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt);
 
static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt);
 

	
 
static void *_yahoo_default_server_settings()
 
{
 
	struct yahoo_server_settings *yss =
 
		y_new0(struct yahoo_server_settings, 1);
 

	
 
	/* Give preference to the default host list
 
	 * Make sure that only one of the two is set at any time 
 
	 */
 
	yss->pager_host = NULL;
 
	yss->pager_host_list = default_pager_hosts;
 

	
 
	yss->pager_port = pager_port;
 
	yss->filetransfer_host = strdup(filetransfer_host);
 
	yss->filetransfer_port = filetransfer_port;
 
	yss->webcam_host = strdup(webcam_host);
 
	yss->webcam_port = webcam_port;
 
	yss->webcam_description = strdup(webcam_description);
 
	yss->local_host = strdup(local_host);
 
	yss->conn_type = conn_type;
 

	
 
	return yss;
 
}
 

	
 
static void *_yahoo_assign_server_settings(va_list ap)
 
{
 
	struct yahoo_server_settings *yss = _yahoo_default_server_settings();
 
	char *key;
 
	char *svalue;
 
	int nvalue;
 
	char **pvalue;
 

	
 
	while (1) {
 
		key = va_arg(ap, char *);
 
		if (key == NULL)
 
			break;
 

	
 
		if (!strcmp(key, "pager_host")) {
 
			svalue = va_arg(ap, char *);
 
			free(yss->pager_host);
 
			yss->pager_host = strdup(svalue);
 
			yss->pager_host_list = NULL;
 
		} else if (!strcmp(key, "pager_host_list")) {
 
			pvalue = va_arg(ap, char **);
 
			yss->pager_host_list = pvalue;
 
			free(yss->pager_host);
 
			yss->pager_host = NULL;
 
		} else if (!strcmp(key, "pager_port")) {
 
			nvalue = va_arg(ap, int);
 
			yss->pager_port = nvalue;
 
		} else if (!strcmp(key, "filetransfer_host")) {
 
			svalue = va_arg(ap, char *);
 
			free(yss->filetransfer_host);
 
			yss->filetransfer_host = strdup(svalue);
 
		} else if (!strcmp(key, "filetransfer_port")) {
 
			nvalue = va_arg(ap, int);
 
			yss->filetransfer_port = nvalue;
 
		} else if (!strcmp(key, "webcam_host")) {
 
			svalue = va_arg(ap, char *);
 
			free(yss->webcam_host);
 
			yss->webcam_host = strdup(svalue);
 
		} else if (!strcmp(key, "webcam_port")) {
 
			nvalue = va_arg(ap, int);
 
			yss->webcam_port = nvalue;
 
		} else if (!strcmp(key, "webcam_description")) {
 
			svalue = va_arg(ap, char *);
 
			free(yss->webcam_description);
 
			yss->webcam_description = strdup(svalue);
 
		} else if (!strcmp(key, "local_host")) {
 
			svalue = va_arg(ap, char *);
 
			free(yss->local_host);
 
			yss->local_host = strdup(svalue);
 
		} else if (!strcmp(key, "conn_type")) {
 
			nvalue = va_arg(ap, int);
 
			yss->conn_type = nvalue;
 
		} else {
 
			WARNING(("Unknown key passed to yahoo_init, "
 
					"perhaps you didn't terminate the list "
 
					"with NULL"));
 
		}
 
	}
 

	
 
	return yss;
 
}
 

	
 
static void yahoo_free_server_settings(struct yahoo_server_settings *yss)
 
{
 
	if (!yss)
 
		return;
 

	
 
	free(yss->pager_host);
 
	free(yss->filetransfer_host);
 
	free(yss->webcam_host);
 
	free(yss->webcam_description);
 
	free(yss->local_host);
 

	
 
	free(yss);
 
}
 

	
 
static YList *conns = NULL;
 
static YList *inputs = NULL;
 
static int last_id = 0;
 

	
 
static void add_to_list(struct yahoo_data *yd)
 
{
 
	conns = y_list_prepend(conns, yd);
 
}
 

	
 
static struct yahoo_data *find_conn_by_id(int id)
 
{
 
	YList *l;
 
	for (l = conns; l; l = y_list_next(l)) {
 
		struct yahoo_data *yd = l->data;
 
		if (yd->client_id == id)
 
			return yd;
 
	}
 
	return NULL;
 
}
 

	
 
static void del_from_list(struct yahoo_data *yd)
 
{
 
	conns = y_list_remove(conns, yd);
 
}
 

	
 
/* call repeatedly to get the next one */
 
/*
 
static struct yahoo_input_data * find_input_by_id(int id)
 
{
 
	YList *l;
 
	for(l = inputs; l; l = y_list_next(l)) {
 
		struct yahoo_input_data *yid = l->data;
 
		if(yid->yd->client_id == id)
 
			return yid;
 
	}
 
	return NULL;
 
}
 
*/
 

	
 
static struct yahoo_input_data *find_input_by_id_and_webcam_user(int id,
 
	const char *who)
 
{
 
	YList *l;
 
	LOG(("find_input_by_id_and_webcam_user"));
 
	for (l = inputs; l; l = y_list_next(l)) {
 
		struct yahoo_input_data *yid = l->data;
 
		if (yid->type == YAHOO_CONNECTION_WEBCAM
 
			&& yid->yd->client_id == id && yid->wcm && ((who
 
					&& yid->wcm->user
 
					&& !strcmp(who, yid->wcm->user))
 
				|| !(yid->wcm->user && !who)))
 
			return yid;
 
	}
 
	return NULL;
 
}
 

	
 
static struct yahoo_input_data *find_input_by_id_and_type(int id,
 
	enum yahoo_connection_type type)
 
{
 
	YList *l;
 
	LOG(("find_input_by_id_and_type"));
 
	for (l = inputs; l; l = y_list_next(l)) {
 
		struct yahoo_input_data *yid = l->data;
 
		if (yid->type == type && yid->yd->client_id == id)
 
			return yid;
 
	}
 
	return NULL;
 
}
 

	
 
static struct yahoo_input_data *find_input_by_id_and_fd(int id, void *fd)
 
{
 
	YList *l;
 
	LOG(("find_input_by_id_and_fd"));
 
	for (l = inputs; l; l = y_list_next(l)) {
 
		struct yahoo_input_data *yid = l->data;
 
		if (yid->fd == fd && yid->yd->client_id == id)
 
			return yid;
 
	}
 
	return NULL;
 
}
 

	
 
static int count_inputs_with_id(int id)
 
{
 
	int c = 0;
 
	YList *l;
 
	LOG(("counting %d", id));
 
	for (l = inputs; l; l = y_list_next(l)) {
 
		struct yahoo_input_data *yid = l->data;
 
		if (yid->yd->client_id == id)
 
			c++;
 
	}
 
	LOG(("%d", c));
 
	return c;
 
}
 

	
 
extern char *yahoo_crypt(char *, char *);
 

	
 
/* Free a buddy list */
 
static void yahoo_free_buddies(YList *list)
 
{
 
	YList *l;
 

	
 
	for (l = list; l; l = l->next) {
 
		struct yahoo_buddy *bud = l->data;
 
		if (!bud)
 
			continue;
 

	
 
		FREE(bud->group);
 
		FREE(bud->id);
 
		FREE(bud->real_name);
 
		if (bud->yab_entry) {
 
			FREE(bud->yab_entry->fname);
 
			FREE(bud->yab_entry->lname);
 
			FREE(bud->yab_entry->nname);
 
			FREE(bud->yab_entry->id);
 
			FREE(bud->yab_entry->email);
 
			FREE(bud->yab_entry->hphone);
 
			FREE(bud->yab_entry->wphone);
 
			FREE(bud->yab_entry->mphone);
 
			FREE(bud->yab_entry);
 
		}
 
		FREE(bud);
 
		l->data = bud = NULL;
 
	}
 

	
 
	y_list_free(list);
 
}
 

	
 
/* Free an identities list */
 
static void yahoo_free_identities(YList *list)
 
{
 
	while (list) {
 
		YList *n = list;
 
		FREE(list->data);
 
		list = y_list_remove_link(list, list);
 
		y_list_free_1(n);
 
	}
 
}
 

	
 
/* Free webcam data */
 
static void yahoo_free_webcam(struct yahoo_webcam *wcm)
 
{
 
	if (wcm) {
 
		FREE(wcm->user);
 
		FREE(wcm->server);
 
		FREE(wcm->key);
 
		FREE(wcm->description);
 
		FREE(wcm->my_ip);
 
	}
 
	FREE(wcm);
 
}
 

	
 
static void yahoo_free_data(struct yahoo_data *yd)
 
{
 
	FREE(yd->user);
 
	FREE(yd->password);
 
	FREE(yd->cookie_y);
 
	FREE(yd->cookie_t);
 
	FREE(yd->cookie_b);
 
	FREE(yd->cookie_c);
 
	FREE(yd->login_cookie);
 
	FREE(yd->login_id);
 

	
 
	yahoo_free_buddies(yd->buddies);
 
	yahoo_free_buddies(yd->ignore);
 
	yahoo_free_identities(yd->identities);
 

	
 
	yahoo_free_server_settings(yd->server_settings);
 

	
 
	FREE(yd);
 
}
 

	
 
#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4)
 

	
 
static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service,
 
	enum ypacket_status status, int id)
 
{
 
	struct yahoo_packet *pkt = y_new0(struct yahoo_packet, 1);
 

	
 
	pkt->service = service;
 
	pkt->status = status;
 
	pkt->id = id;
 

	
 
	return pkt;
 
}
 

	
 
static void yahoo_packet_hash(struct yahoo_packet *pkt, int key,
 
	const char *value)
 
{
 
	struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
 
	pair->key = key;
 
	pair->value = strdup(value);
 
	pkt->hash = y_list_append(pkt->hash, pair);
 
}
 

	
 
static int yahoo_packet_length(struct yahoo_packet *pkt)
 
{
 
	YList *l;
 

	
 
	int len = 0;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		int tmp = pair->key;
 
		do {
 
			tmp /= 10;
 
			len++;
 
		} while (tmp);
 
		len += 2;
 
		len += strlen(pair->value);
 
		len += 2;
 
	}
 

	
 
	return len;
 
}
 

	
 
#define yahoo_put16(buf, data) ( \
 
		(*(buf) = (unsigned char)((data)>>8)&0xff), \
 
		(*((buf)+1) = (unsigned char)(data)&0xff),  \
 
		2)
 
#define yahoo_get16(buf) ((((*(buf))&0xff)<<8) + ((*((buf)+1)) & 0xff))
 
#define yahoo_put32(buf, data) ( \
 
		(*((buf)) = (unsigned char)((data)>>24)&0xff), \
 
		(*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
 
		(*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
 
		(*((buf)+3) = (unsigned char)(data)&0xff), \
 
		4)
 
#define yahoo_get32(buf) ((((*(buf)   )&0xff)<<24) + \
 
			 (((*((buf)+1))&0xff)<<16) + \
 
			 (((*((buf)+2))&0xff)<< 8) + \
 
			 (((*((buf)+3))&0xff)))
 

	
 
static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data,
 
	int len)
 
{
 
	int pos = 0;
 

	
 
	while (pos + 1 < len) {
 
		char *key, *value = NULL;
 
		int accept;
 
		int x;
 

	
 
		struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
 

	
 
		key = malloc(len + 1);
 
		x = 0;
 
		while (pos + 1 < len) {
 
			if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
 
				break;
 
			key[x++] = data[pos++];
 
		}
 
		key[x] = 0;
 
		pos += 2;
 
		pair->key = strtol(key, NULL, 10);
 
		free(key);
 

	
 
		accept = x;
 

	
 
		if (pos + 1 > len) {
 
			/* Malformed packet! (Truncated--garbage or something) */
 
			accept = 0;
 
		}
 

	
 
		/* if x is 0 there was no key, so don't accept it */
 
		if (accept)
 
			value = malloc(len - pos + 1);
 
		x = 0;
 
		while (pos + 1 < len) {
 
			if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
 
				break;
 
			if (accept)
 
				value[x++] = data[pos++];
 
		}
 
		if (accept)
 
			value[x] = 0;
 
		pos += 2;
 
		if (accept) {
 
			pair->value = strdup(value);
 
			FREE(value);
 
			pkt->hash = y_list_append(pkt->hash, pair);
 
			DEBUG_MSG(("Key: %d  \tValue: %s", pair->key,
 
					pair->value));
 
		} else {
 
			FREE(pair);
 
		}
 
	}
 
}
 

	
 
static void yahoo_packet_write(struct yahoo_packet *pkt, unsigned char *data)
 
{
 
	YList *l;
 
	int pos = 0;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		unsigned char buf[100];
 

	
 
		snprintf((char *)buf, sizeof(buf), "%d", pair->key);
 
		strcpy((char *)data + pos, (char *)buf);
 
		pos += strlen((char *)buf);
 
		data[pos++] = 0xc0;
 
		data[pos++] = 0x80;
 

	
 
		strcpy((char *)data + pos, pair->value);
 
		pos += strlen(pair->value);
 
		data[pos++] = 0xc0;
 
		data[pos++] = 0x80;
 
	}
 
}
 

	
 
static void yahoo_dump_unhandled(struct yahoo_packet *pkt)
 
{
 
	YList *l;
 

	
 
	NOTICE(("Service: 0x%02x\tStatus: %d", pkt->service, pkt->status));
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		NOTICE(("\t%d => %s", pair->key, pair->value));
 
	}
 
}
 

	
 
static void yahoo_packet_dump(unsigned char *data, int len)
 
{
 
	if (yahoo_get_log_level() >= YAHOO_LOG_DEBUG) {
 
		int i;
 
		for (i = 0; i < len; i++) {
 
			if ((i % 8 == 0) && i)
 
				YAHOO_CALLBACK(ext_yahoo_log) (" ");
 
			if ((i % 16 == 0) && i)
 
				YAHOO_CALLBACK(ext_yahoo_log) ("\n");
 
			YAHOO_CALLBACK(ext_yahoo_log) ("%02x ", data[i]);
 
		}
 
		YAHOO_CALLBACK(ext_yahoo_log) ("\n");
 
		for (i = 0; i < len; i++) {
 
			if ((i % 8 == 0) && i)
 
				YAHOO_CALLBACK(ext_yahoo_log) (" ");
 
			if ((i % 16 == 0) && i)
 
				YAHOO_CALLBACK(ext_yahoo_log) ("\n");
 
			if (isprint(data[i]))
 
				YAHOO_CALLBACK(ext_yahoo_log) (" %c ", data[i]);
 
			else
 
				YAHOO_CALLBACK(ext_yahoo_log) (" . ");
 
		}
 
		YAHOO_CALLBACK(ext_yahoo_log) ("\n");
 
	}
 
}
 

	
 
static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
	"abcdefghijklmnopqrstuvwxyz" "0123456789._";
 
static void to_y64(unsigned char *out, const unsigned char *in, int inlen)
 
/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
 
{
 
	for (; inlen >= 3; inlen -= 3) {
 
		*out++ = base64digits[in[0] >> 2];
 
		*out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
 
		*out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
 
		*out++ = base64digits[in[2] & 0x3f];
 
		in += 3;
 
	}
 
	if (inlen > 0) {
 
		unsigned char fragment;
 

	
 
		*out++ = base64digits[in[0] >> 2];
 
		fragment = (in[0] << 4) & 0x30;
 
		if (inlen > 1)
 
			fragment |= in[1] >> 4;
 
		*out++ = base64digits[fragment];
 
		*out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c];
 
		*out++ = '-';
 
	}
 
	*out = '\0';
 
}
 

	
 
static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data,
 
	int length)
 
{
 
	struct data_queue *tx = y_new0(struct data_queue, 1);
 
	tx->queue = y_new0(unsigned char, length);
 
	tx->len = length;
 
	memcpy(tx->queue, data, length);
 

	
 
	yid->txqueues = y_list_append(yid->txqueues, tx);
 

	
 
	if (!yid->write_tag)
 
		yid->write_tag =
 
			YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->
 
			client_id, yid->fd, YAHOO_INPUT_WRITE, yid);
 
}
 

	
 
static void yahoo_send_packet(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt, int extra_pad)
 
{
 
	int pktlen = yahoo_packet_length(pkt);
 
	int len = YAHOO_PACKET_HDRLEN + pktlen;
 
	unsigned char *data;
 
	int pos = 0;
 

	
 
	if (!yid->fd)
 
		return;
 

	
 
	data = y_new0(unsigned char, len + 1);
 

	
 
	memcpy(data + pos, "YMSG", 4);
 
	pos += 4;
 
	pos += yahoo_put16(data + pos, YAHOO_PROTO_VER);	/* version [latest 12 0x000c] */
 
	pos += yahoo_put16(data + pos, 0x0000);	/* HIWORD pkt length??? */
 
	pos += yahoo_put16(data + pos, pktlen + extra_pad);	/* LOWORD pkt length? */
 
	pos += yahoo_put16(data + pos, pkt->service);	/* service */
 
	pos += yahoo_put32(data + pos, pkt->status);	/* status [4bytes] */
 
	pos += yahoo_put32(data + pos, pkt->id);	/* session [4bytes] */
 

	
 
	yahoo_packet_write(pkt, data + pos);
 

	
 
	yahoo_packet_dump(data, len);
 

	
 
	if (yid->type == YAHOO_CONNECTION_FT)
 
		yahoo_send_data(yid->fd, data, len);
 
	else
 
		yahoo_add_to_send_queue(yid, data, len);
 
	FREE(data);
 
}
 

	
 
static void yahoo_packet_free(struct yahoo_packet *pkt)
 
{
 
	while (pkt->hash) {
 
		struct yahoo_pair *pair = pkt->hash->data;
 
		YList *tmp;
 
		FREE(pair->value);
 
		FREE(pair);
 
		tmp = pkt->hash;
 
		pkt->hash = y_list_remove_link(pkt->hash, pkt->hash);
 
		y_list_free_1(tmp);
 
	}
 
	FREE(pkt);
 
}
 

	
 
static int yahoo_send_data(void *fd, void *data, int len)
 
{
 
	int ret;
 
	int e;
 

	
 
	if (fd == NULL)
 
		return -1;
 

	
 
	yahoo_packet_dump(data, len);
 

	
 
	do {
 
		ret = YAHOO_CALLBACK(ext_yahoo_write) (fd, data, len);
 
	} while (ret == -1 && errno == EINTR);
 
	e = errno;
 

	
 
	if (ret == -1) {
 
		LOG(("wrote data: ERR %s", strerror(errno)));
 
	} else {
 
		LOG(("wrote data: OK"));
 
	}
 

	
 
	errno = e;
 
	return ret;
 
}
 

	
 
void yahoo_close(int id)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	if (!yd)
 
		return;
 

	
 
	del_from_list(yd);
 

	
 
	yahoo_free_data(yd);
 
	if (id == last_id)
 
		last_id--;
 
}
 

	
 
static void yahoo_input_close(struct yahoo_input_data *yid)
 
{
 
	inputs = y_list_remove(inputs, yid);
 

	
 
	LOG(("yahoo_input_close(read)"));
 
	YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id,
 
		yid->read_tag);
 
	LOG(("yahoo_input_close(write)"));
 
	YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id,
 
		yid->write_tag);
 
	yid->read_tag = yid->write_tag = 0;
 
	if (yid->fd)
 
		YAHOO_CALLBACK(ext_yahoo_close) (yid->fd);
 
	yid->fd = 0;
 
	FREE(yid->rxqueue);
 
	if (count_inputs_with_id(yid->yd->client_id) == 0) {
 
		LOG(("closing %d", yid->yd->client_id));
 
		yahoo_close(yid->yd->client_id);
 
	}
 
	yahoo_free_webcam(yid->wcm);
 
	if (yid->wcd)
 
		FREE(yid->wcd);
 
	if (yid->ys) {
 
		FREE(yid->ys->lsearch_text);
 
		FREE(yid->ys);
 
	}
 
	FREE(yid);
 
}
 

	
 
static int is_same_bud(const void *a, const void *b)
 
{
 
	const struct yahoo_buddy *subject = a;
 
	const struct yahoo_buddy *object = b;
 

	
 
	return strcmp(subject->id, object->id);
 
}
 

	
 
static char *getcookie(char *rawcookie)
 
{
 
	char *cookie = NULL;
 
	char *tmpcookie;
 
	char *cookieend;
 

	
 
	if (strlen(rawcookie) < 2)
 
		return NULL;
 

	
 
	tmpcookie = strdup(rawcookie + 2);
 
	cookieend = strchr(tmpcookie, ';');
 

	
 
	if (cookieend)
 
		*cookieend = '\0';
 

	
 
	cookie = strdup(tmpcookie);
 
	FREE(tmpcookie);
 
	/* cookieend=NULL;  not sure why this was there since the value is not preserved in the stack -dd */
 

	
 
	return cookie;
 
}
 

	
 
static char *getlcookie(char *cookie)
 
{
 
	char *tmp;
 
	char *tmpend;
 
	char *login_cookie = NULL;
 

	
 
	tmpend = strstr(cookie, "n=");
 
	if (tmpend) {
 
		tmp = strdup(tmpend + 2);
 
		tmpend = strchr(tmp, '&');
 
		if (tmpend)
 
			*tmpend = '\0';
 
		login_cookie = strdup(tmp);
 
		FREE(tmp);
 
	}
 

	
 
	return login_cookie;
 
}
 

	
 
static void yahoo_process_notify(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *msg = NULL;
 
	char *from = NULL;
 
	char *to = NULL;
 
	int stat = 0;
 
	int accept = 0;
 
	char *ind = NULL;
 
	YList *l;
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 4)
 
			from = pair->value;
 
		if (pair->key == 5)
 
			to = pair->value;
 
		if (pair->key == 49)
 
			msg = pair->value;
 
		if (pair->key == 13)
 
			stat = atoi(pair->value);
 
		if (pair->key == 14)
 
			ind = pair->value;
 
		if (pair->key == 16) {	/* status == -1 */
 
			NOTICE((pair->value));
 
			return;
 
		}
 

	
 
	}
 

	
 
	if (!msg)
 
		return;
 

	
 
	if (!strncasecmp(msg, "TYPING", strlen("TYPING")))
 
		YAHOO_CALLBACK(ext_yahoo_typing_notify) (yd->client_id, to,
 
			from, stat);
 
	else if (!strncasecmp(msg, "GAME", strlen("GAME")))
 
		YAHOO_CALLBACK(ext_yahoo_game_notify) (yd->client_id, to, from,
 
			stat, ind);
 
	else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
 
		if (!strcmp(ind, " ")) {
 
			YAHOO_CALLBACK(ext_yahoo_webcam_invite) (yd->client_id,
 
				to, from);
 
		} else {
 
			accept = atoi(ind);
 
			/* accept the invitation (-1 = deny 1 = accept) */
 
			if (accept < 0)
 
				accept = 0;
 
			YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply) (yd->
 
				client_id, to, from, accept);
 
		}
 
	} else
 
		LOG(("Got unknown notification: %s", msg));
 
}
 

	
 
static void yahoo_process_conference(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *msg = NULL;
 
	char *host = NULL;
 
	char *who = NULL;
 
	char *room = NULL;
 
	char *id = NULL;
 
	int utf8 = 0;
 
	YList *members = NULL;
 
	YList *l;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 50)
 
			host = pair->value;
 

	
 
		if (pair->key == 52) {	/* invite */
 
			members = y_list_append(members, strdup(pair->value));
 
		}
 
		if (pair->key == 53)	/* logon */
 
			who = pair->value;
 
		if (pair->key == 54)	/* decline */
 
			who = pair->value;
 
		if (pair->key == 55)	/* unavailable (status == 2) */
 
			who = pair->value;
 
		if (pair->key == 56)	/* logoff */
 
			who = pair->value;
 

	
 
		if (pair->key == 57)
 
			room = pair->value;
 

	
 
		if (pair->key == 58)	/* join message */
 
			msg = pair->value;
 
		if (pair->key == 14)	/* decline/conf message */
 
			msg = pair->value;
 

	
 
		if (pair->key == 13) ;
 
		if (pair->key == 16)	/* error */
 
			msg = pair->value;
 

	
 
		if (pair->key == 1)	/* my id */
 
			id = pair->value;
 
		if (pair->key == 3)	/* message sender */
 
			who = pair->value;
 

	
 
		if (pair->key == 97)
 
			utf8 = atoi(pair->value);
 
	}
 

	
 
	if (!room)
 
		return;
 

	
 
	if (host) {
 
		for (l = members; l; l = l->next) {
 
			char *w = l->data;
 
			if (!strcmp(w, host))
 
				break;
 
		}
 
		if (!l)
 
			members = y_list_append(members, strdup(host));
 
	}
 
	/* invite, decline, join, left, message -> status == 1 */
 

	
 
	switch (pkt->service) {
 
	case YAHOO_SERVICE_CONFINVITE:
 
		if (pkt->status == 2) ;
 
		else if (members)
 
			YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd->
 
				client_id, id, host, room, msg, members);
 
		else if (msg)
 
			YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, msg, 0,
 
				E_CONFNOTAVAIL);
 
		break;
 
	case YAHOO_SERVICE_CONFADDINVITE:
 
		if (pkt->status == 1)
 
			YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd->
 
				client_id, id, host, room, msg, members);
 
		break;
 
	case YAHOO_SERVICE_CONFDECLINE:
 
		if (who)
 
			YAHOO_CALLBACK(ext_yahoo_conf_userdecline) (yd->
 
				client_id, id, who, room, msg);
 
		break;
 
	case YAHOO_SERVICE_CONFLOGON:
 
		if (who)
 
			YAHOO_CALLBACK(ext_yahoo_conf_userjoin) (yd->client_id,
 
				id, who, room);
 
		break;
 
	case YAHOO_SERVICE_CONFLOGOFF:
 
		if (who)
 
			YAHOO_CALLBACK(ext_yahoo_conf_userleave) (yd->client_id,
 
				id, who, room);
 
		break;
 
	case YAHOO_SERVICE_CONFMSG:
 
		if (who)
 
			YAHOO_CALLBACK(ext_yahoo_conf_message) (yd->client_id,
 
				id, who, room, msg, utf8);
 
		break;
 
	}
 
}
 

	
 
static void yahoo_process_chat(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	char *msg = NULL;
 
	char *id = NULL;
 
	char *who = NULL;
 
	char *room = NULL;
 
	char *topic = NULL;
 
	YList *members = NULL;
 
	struct yahoo_chat_member *currentmember = NULL;
 
	int msgtype = 1;
 
	int utf8 = 0;
 
	int firstjoin = 0;
 
	int membercount = 0;
 
	int chaterr = 0;
 
	YList *l;
 

	
 
	yahoo_dump_unhandled(pkt);
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 

	
 
		if (pair->key == 1) {
 
			/* My identity */
 
			id = pair->value;
 
		}
 

	
 
		if (pair->key == 104) {
 
			/* Room name */
 
			room = pair->value;
 
		}
 

	
 
		if (pair->key == 105) {
 
			/* Room topic */
 
			topic = pair->value;
 
		}
 

	
 
		if (pair->key == 108) {
 
			/* Number of members in this packet */
 
			membercount = atoi(pair->value);
 
		}
 

	
 
		if (pair->key == 109) {
 
			/* message sender */
 
			who = pair->value;
 

	
 
			if (pkt->service == YAHOO_SERVICE_CHATJOIN) {
 
				currentmember =
 
					y_new0(struct yahoo_chat_member, 1);
 
				currentmember->id = strdup(pair->value);
 
				members = y_list_append(members, currentmember);
 
			}
 
		}
 

	
 
		if (pair->key == 110) {
 
			/* age */
 
			if (pkt->service == YAHOO_SERVICE_CHATJOIN)
 
				currentmember->age = atoi(pair->value);
 
		}
 

	
 
		if (pair->key == 113) {
 
			/* attribs */
 
			if (pkt->service == YAHOO_SERVICE_CHATJOIN)
 
				currentmember->attribs = atoi(pair->value);
 
		}
 

	
 
		if (pair->key == 141) {
 
			/* alias */
 
			if (pkt->service == YAHOO_SERVICE_CHATJOIN)
 
				currentmember->alias = strdup(pair->value);
 
		}
 

	
 
		if (pair->key == 142) {
 
			/* location */
 
			if (pkt->service == YAHOO_SERVICE_CHATJOIN)
 
				currentmember->location = strdup(pair->value);
 
		}
 

	
 
		if (pair->key == 130) {
 
			/* first join */
 
			firstjoin = 1;
 
		}
 

	
 
		if (pair->key == 117) {
 
			/* message */
 
			msg = pair->value;
 
		}
 

	
 
		if (pair->key == 124) {
 
			/* Message type */
 
			msgtype = atoi(pair->value);
 
		}
 
		if (pair->key == 114) {
 
			/* message error not sure what all the pair values mean */
 
			/* but -1 means no session in room */
 
			chaterr = atoi(pair->value);
 
		}
 
	}
 

	
 
	if (!room) {
 
		if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) {	/* yahoo originated chat logout */
 
			YAHOO_CALLBACK(ext_yahoo_chat_yahoologout) (yid->yd->
 
				client_id, id);
 
			return;
 
		}
 
		if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr) {
 
			YAHOO_CALLBACK(ext_yahoo_chat_yahooerror) (yid->yd->
 
				client_id, id);
 
			return;
 
		}
 

	
 
		WARNING(("We didn't get a room name, ignoring packet"));
 
		return;
 
	}
 

	
 
	switch (pkt->service) {
 
	case YAHOO_SERVICE_CHATJOIN:
 
		if (y_list_length(members) != membercount) {
 
			WARNING(("Count of members doesn't match No. of members we got"));
 
		}
 
		if (firstjoin && members) {
 
			YAHOO_CALLBACK(ext_yahoo_chat_join) (yid->yd->client_id,
 
				id, room, topic, members, yid->fd);
 
		} else if (who) {
 
			if (y_list_length(members) != 1) {
 
				WARNING(("Got more than 1 member on a normal join"));
 
			}
 
			/* this should only ever have one, but just in case */
 
			while (members) {
 
				YList *n = members->next;
 
				currentmember = members->data;
 
				YAHOO_CALLBACK(ext_yahoo_chat_userjoin) (yid->
 
					yd->client_id, id, room, currentmember);
 
				y_list_free_1(members);
 
				members = n;
 
			}
 
		}
 
		break;
 
	case YAHOO_SERVICE_CHATEXIT:
 
		if (who) {
 
			YAHOO_CALLBACK(ext_yahoo_chat_userleave) (yid->yd->
 
				client_id, id, room, who);
 
		}
 
		break;
 
	case YAHOO_SERVICE_COMMENT:
 
		if (who) {
 
			YAHOO_CALLBACK(ext_yahoo_chat_message) (yid->yd->
 
				client_id, id, who, room, msg, msgtype, utf8);
 
		}
 
		break;
 
	}
 
}
 

	
 
static void yahoo_process_message(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	YList *l;
 
	YList *messages = NULL;
 

	
 
	struct m {
 
		int i_31;
 
		int i_32;
 
		char *to;
 
		char *from;
 
		long tm;
 
		char *msg;
 
		int utf8;
 
		char *gunk;
 
	} *message = y_new0(struct m, 1);
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 1 || pair->key == 4) {
 
			if (!message->from)
 
				message->from = pair->value;
 
		} else if (pair->key == 5)
 
			message->to = pair->value;
 
		else if (pair->key == 15)
 
			message->tm = strtol(pair->value, NULL, 10);
 
		else if (pair->key == 97)
 
			message->utf8 = atoi(pair->value);
 
		/* This comes when the official client sends us a message */
 
		else if (pair->key == 429)
 
			message->gunk = pair->value;
 
		/* user message *//* sys message */
 
		else if (pair->key == 14 || pair->key == 16)
 
			message->msg = pair->value;
 
		else if (pair->key == 31) {
 
			if (message->i_31) {
 
				messages = y_list_append(messages, message);
 
				message = y_new0(struct m, 1);
 
			}
 
			message->i_31 = atoi(pair->value);
 
		} else if (pair->key == 32)
 
			message->i_32 = atoi(pair->value);
 
		else
 
			LOG(("yahoo_process_message: status: %d, key: %d, value: %s", pkt->status, pair->key, pair->value));
 
	}
 

	
 
	messages = y_list_append(messages, message);
 

	
 
	for (l = messages; l; l = l->next) {
 
		message = l->data;
 
		if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) {
 
			YAHOO_CALLBACK(ext_yahoo_system_message) (yd->client_id,
 
				message->to, message->from, message->msg);
 
		} else if (pkt->status <= 2 || pkt->status == 5) {
 
			/* Confirm message receipt if we got the gunk */
 
			if(message->gunk) {
 
				struct yahoo_packet *outpkt;
 
                        
 
				outpkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_CONFIRM,
 
					YPACKET_STATUS_DEFAULT, 0);
 
				yahoo_packet_hash(outpkt, 1, yd->user);
 
				yahoo_packet_hash(outpkt, 5, message->from);
 
				yahoo_packet_hash(outpkt, 302, "430");
 
				yahoo_packet_hash(outpkt, 430, message->gunk);
 
				yahoo_packet_hash(outpkt, 303, "430");
 
				yahoo_packet_hash(outpkt, 450, "0");
 
				yahoo_send_packet(yid, outpkt, 0);
 
                        
 
				yahoo_packet_free(outpkt);
 
			}
 

	
 
			if (!strcmp(message->msg, "<ding>"))
 
				YAHOO_CALLBACK(ext_yahoo_got_buzz) (yd->client_id,
 
					message->to, message->from, message->tm);
 
			else
 
				YAHOO_CALLBACK(ext_yahoo_got_im) (yd->client_id,
 
					message->to, message->from, message->msg,
 
					message->tm, pkt->status, message->utf8);
 
		} else if (pkt->status == 0xffffffff) {
 
			YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id,
 
				message->msg, 0, E_SYSTEM);
 
		}
 
		FREE(message);
 
	}
 

	
 
	y_list_free(messages);
 
}
 

	
 
/*
 
 * Here's what multi-level packets look like. Data in brackets is the value.
 
 *
 
 * 3 level:
 
 * =======
 
 * 
 
 * 302 (318) - Beginning level 1
 
 * 	300 (318) - Begin level 2
 
 * 	302 (319) - End level 2 header
 
 * 		300 (319) - Begin level 3
 
 * 		301 (319) - End level 3
 
 * 	303 (319) - End level 2
 
 * 303 (318) - End level 1
 
 *
 
 * 2 level:
 
 * =======
 
 *
 
 * 302 (315) - Beginning level 1
 
 * 	300 (315) - Begin level 2
 
 * 	301 (315) - End level 2
 
 * 303 (315) - End level 1
 
 *
 
 */
 
static void yahoo_process_status(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	YList *l;
 
	struct yahoo_data *yd = yid->yd;
 

	
 
	struct yahoo_process_status_entry *u;
 

	
 
	YList *users = 0;
 

	
 
	if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
 
			YAHOO_LOGIN_DUPL, NULL);
 
		return;
 
	}
 

	
 
	/* 
 
	 * Status updates may be spread accross multiple packets and not 
 
	 * even on buddy boundaries, so keeping some state is important. 
 
	 * So, continue where we left off, and only add a user entry to 
 
	 * the list once it's complete (301-315 End buddy).
 
	 */
 
	u = yd->half_user;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 

	
 
		switch (pair->key) {
 
		case 300:	/* Begin buddy */
 
			if (!strcmp(pair->value, "315") && !u) {
 
				u = yd->half_user = y_new0(struct yahoo_process_status_entry, 1);
 
			}
 
			break;
 
		case 301:	/* End buddy */
 
			if (!strcmp(pair->value, "315") && u) {
 
				users = y_list_prepend(users, u);
 
				u = yd->half_user = NULL;
 
			}
 
			break;
 
		case 0:	/* we won't actually do anything with this */
 
			NOTICE(("key %d:%s", pair->key, pair->value));
 
			break;
 
		case 1:	/* we don't get the full buddy list here. */
 
			if (!yd->logged_in) {
 
				yd->logged_in = 1;
 
				if (yd->current_status < 0)
 
					yd->current_status = yd->initial_status;
 
				YAHOO_CALLBACK(ext_yahoo_login_response) (yd->
 
					client_id, YAHOO_LOGIN_OK, NULL);
 
			}
 
			break;
 
		case 8:	/* how many online buddies we have */
 
			NOTICE(("key %d:%s", pair->key, pair->value));
 
			break;
 
		case 7:	/* the current buddy */
 
			if (!u) {
 
				/* This will only happen in case of a single level message */
 
				u = y_new0(struct yahoo_process_status_entry, 1);
 
				users = y_list_prepend(users, u);
 
			}
 
			u->name = pair->value;
 
			break;
 
		case 10:	/* state */
 
			u->state = strtol(pair->value, NULL, 10);
 
			break;
 
		case 19:	/* custom status message */
 
			u->msg = pair->value;
 
			break;
 
		case 47:	/* is it an away message or not. Not applicable for YMSG16 anymore */
 
			u->away = atoi(pair->value);
 
			break;
 
		case 137:	/* seconds idle */
 
			u->idle = atoi(pair->value);
 
			break;
 
		case 11:	/* this is the buddy's session id */
 
			u->buddy_session = atoi(pair->value);
 
			break;
 
		case 17:	/* in chat? */
 
			u->f17 = atoi(pair->value);
 
			break;
 
		case 13:	/* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
 
			u->flags = atoi(pair->value);
 
			break;
 
		case 60:	/* SMS -> 1 MOBILE USER */
 
			/* sometimes going offline makes this 2, but invisible never sends it */
 
			u->mobile = atoi(pair->value);
 
			break;
 
		case 138:
 
			u->f138 = atoi(pair->value);
 
			break;
 
		case 184:
 
			u->f184 = pair->value;
 
			break;
 
		case 192:
 
			u->f192 = atoi(pair->value);
 
			break;
 
		case 10001:
 
			u->f10001 = atoi(pair->value);
 
			break;
 
		case 10002:
 
			u->f10002 = atoi(pair->value);
 
			break;
 
		case 198:
 
			u->f198 = atoi(pair->value);
 
			break;
 
		case 197:
 
			u->f197 = pair->value;
 
			break;
 
		case 205:
 
			u->f205 = pair->value;
 
			break;
 
		case 213:
 
			u->f213 = atoi(pair->value);
 
			break;
 
		case 16:	/* Custom error message */
 
			YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id,
 
				pair->value, 0, E_CUSTOM);
 
			break;
 
		default:
 
			WARNING(("unknown status key %d:%s", pair->key,
 
					pair->value));
 
			break;
 
		}
 
	}
 

	
 
	while (users) {
 
		YList *t = users;
 
		struct yahoo_process_status_entry *u = users->data;
 

	
 
		if (u->name != NULL) {
 
			if (pkt->service ==
 
				YAHOO_SERVICE_LOGOFF
 
				/*|| u->flags == 0 No flags for YMSG16 */ ) {
 
				YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->
 
					client_id, u->name,
 
					YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
 
			} else {
 
				/* Key 47 always seems to be 1 for YMSG16 */
 
				if (!u->state)
 
					u->away = 0;
 
				else
 
					u->away = 1;
 

	
 
				YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->
 
					client_id, u->name, u->state, u->msg,
 
					u->away, u->idle, u->mobile);
 
			}
 
		}
 

	
 
		users = y_list_remove_link(users, users);
 
		y_list_free_1(t);
 
		FREE(u);
 
	}
 
}
 

	
 
static void yahoo_process_buddy_list(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	YList *l;
 
	int last_packet = 0;
 
	char *cur_group = NULL;
 
	struct yahoo_buddy *newbud = NULL;
 

	
 
	/* we could be getting multiple packets here */
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 

	
 
		switch (pair->key) {
 
		case 300:
 
		case 301:
 
		case 302:
 
			break;	/* Separators. Our logic does not need them */
 
		case 303:
 
			if (318 == atoi(pair->value))
 
				last_packet = 1;
 
			break;
 
		case 65:
 
			cur_group = strdup(pair->value);
 
			break;
 
		case 7:
 
			newbud = y_new0(struct yahoo_buddy, 1);
 
			newbud->id = strdup(pair->value);
 
			if (cur_group)
 
				newbud->group = strdup(cur_group);
 
			else if (yd->buddies) {
 
				struct yahoo_buddy *lastbud =
 
					(struct yahoo_buddy *)y_list_nth(yd->
 
					buddies,
 
					y_list_length(yd->buddies) - 1)->data;
 
				newbud->group = strdup(lastbud->group);
 
			} else
 
				newbud->group = strdup("Buddies");
 

	
 
			yd->buddies = y_list_append(yd->buddies, newbud);
 

	
 
			break;
 
		}
 
	}
 

	
 
	/* we could be getting multiple packets here */
 
	if (pkt->hash && !last_packet)
 
		return;
 

	
 
	/* Logged in */
 
	if (!yd->logged_in) {
 
		yd->logged_in = 1;
 
		if (yd->current_status < 0)
 
			yd->current_status = yd->initial_status;
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
 
			YAHOO_LOGIN_OK, NULL);
 

	
 
		yahoo_set_away(yd->client_id, yd->initial_status, NULL,
 
			(yd->initial_status == YAHOO_STATUS_AVAILABLE) ? 0 : 1);
 

	
 
		yahoo_get_yab(yd->client_id);
 
	}
 

	
 
	YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, yd->buddies);
 

	
 
}
 

	
 
static void yahoo_process_list(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	YList *l;
 

	
 
	/* we could be getting multiple packets here */
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 

	
 
		switch (pair->key) {
 
		case 89:	/* identities */
 
			{
 
				char **identities =
 
					y_strsplit(pair->value, ",", -1);
 
				int i;
 
				for (i = 0; identities[i]; i++)
 
					yd->identities =
 
						y_list_append(yd->identities,
 
						strdup(identities[i]));
 
				y_strfreev(identities);
 
			}
 
			YAHOO_CALLBACK(ext_yahoo_got_identities) (yd->client_id,
 
				yd->identities);
 
			break;
 
		case 59:	/* cookies */
 
			if (pair->value[0] == 'Y') {
 
				FREE(yd->cookie_y);
 
				FREE(yd->login_cookie);
 

	
 
				yd->cookie_y = getcookie(pair->value);
 
				yd->login_cookie = getlcookie(yd->cookie_y);
 

	
 
			} else if (pair->value[0] == 'T') {
 
				FREE(yd->cookie_t);
 
				yd->cookie_t = getcookie(pair->value);
 

	
 
			} else if (pair->value[0] == 'C') {
 
				FREE(yd->cookie_c);
 
				yd->cookie_c = getcookie(pair->value);
 
			}
 

	
 
			break;
 
		case 3:	/* my id */
 
		case 90:	/* 1 */
 
		case 100:	/* 0 */
 
		case 101:	/* NULL */
 
		case 102:	/* NULL */
 
		case 93:	/* 86400/1440 */
 
			break;
 
		}
 
	}
 

	
 
	if (yd->cookie_y && yd->cookie_t)	/* We don't get cookie_c anymore */
 
		YAHOO_CALLBACK(ext_yahoo_got_cookies) (yd->client_id);
 
}
 

	
 
static void yahoo_process_verify(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 

	
 
	if (pkt->status != 0x01) {
 
		DEBUG_MSG(("expected status: 0x01, got: %d", pkt->status));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
 
			YAHOO_LOGIN_LOCK, "");
 
		return;
 
	}
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 

	
 
}
 

	
 
static void yahoo_process_picture_checksum(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *from;
 
	char *to;
 
	int checksum = 0;
 
	YList *l;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 

	
 
		switch (pair->key) {
 
		case 1:
 
		case 4:
 
			from = pair->value;
 
		case 5:
 
			to = pair->value;
 
			break;
 
		case 212:
 
			break;
 
		case 192:
 
			checksum = atoi(pair->value);
 
			break;
 
		}
 
	}
 

	
 
	YAHOO_CALLBACK(ext_yahoo_got_buddyicon_checksum) (yd->client_id, to,
 
		from, checksum);
 
}
 

	
 
static void yahoo_process_picture(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *url;
 
	char *from;
 
	char *to;
 
	int status = 0;
 
	int checksum = 0;
 
	YList *l;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 

	
 
		switch (pair->key) {
 
		case 1:
 
		case 4:	/* sender */
 
			from = pair->value;
 
			break;
 
		case 5:	/* we */
 
			to = pair->value;
 
			break;
 
		case 13:	/* request / sending */
 
			status = atoi(pair->value);
 
			break;
 
		case 20:	/* url */
 
			url = pair->value;
 
			break;
 
		case 192:	/*checksum */
 
			checksum = atoi(pair->value);
 
			break;
 
		}
 
	}
 

	
 
	switch (status) {
 
	case 1:		/* this is a request, ignore for now */
 
		YAHOO_CALLBACK(ext_yahoo_got_buddyicon_request) (yd->client_id,
 
			to, from);
 
		break;
 
	case 2:		/* this is cool - we get a picture :) */
 
		YAHOO_CALLBACK(ext_yahoo_got_buddyicon) (yd->client_id, to,
 
			from, url, checksum);
 
		break;
 
	}
 
}
 

	
 
static void yahoo_process_picture_upload(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	YList *l;
 
	char *url;
 

	
 
	if (pkt->status != 1)
 
		return;		/* something went wrong */
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 

	
 
		switch (pair->key) {
 
		case 5:	/* we */
 
			break;
 
		case 20:	/* url */
 
			url = pair->value;
 
			break;
 
		case 27:	/* local filename */
 
			break;
 
		case 38:	/* time */
 
			break;
 
		}
 
	}
 

	
 
	YAHOO_CALLBACK(ext_yahoo_buddyicon_uploaded) (yd->client_id, url);
 
}
 

	
 
void yahoo_login(int id, int initial)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	struct connect_callback_data *ccd;
 
	struct yahoo_server_settings *yss;
 
	int tag;
 

	
 
	char *host;
 

	
 
	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
 
	yid->yd = yd;
 
	yid->type = YAHOO_CONNECTION_PAGER;
 
	inputs = y_list_prepend(inputs, yid);
 

	
 
	yd->initial_status = initial;
 
	yss = yd->server_settings;
 

	
 
	ccd = y_new0(struct connect_callback_data, 1);
 
	ccd->yd = yd;
 

	
 
	host = yss->pager_host;
 

	
 
	if (!host)
 
		host = yss->pager_host_list[0];
 

	
 
	tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id,
 
		host, yss->pager_port, yahoo_connected, ccd, 0);
 

	
 
	/*
 
	 * if tag <= 0, then callback has already been called
 
	 * so ccd will have been freed
 
	 */
 
	if (tag > 0)
 
		ccd->tag = tag;
 
	else if (tag < 0)
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
 
			YAHOO_LOGIN_SOCK, NULL);
 
}
 

	
 
static void yahoo_auth_https(struct yahoo_data *yd)
 
{
 
	char url[256];
 
	char *user_encoded, *pass_encoded, *seed_encoded;
 

	
 
	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
 

	
 
	yid->yd = yd;
 
	yid->type = YAHOO_CONNECTION_AUTH;
 

	
 
	inputs = y_list_prepend(inputs, yid);
 

	
 
	user_encoded = yahoo_urlencode(yid->yd->user);
 
	pass_encoded = yahoo_urlencode(yid->yd->password);
 
	seed_encoded = yahoo_urlencode(yid->yd->seed);
 

	
 
	snprintf(url, sizeof(url),
 
		"https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts="
 
		"&login=%s&passwd=%s&chal=%s", user_encoded, pass_encoded,
 
		seed_encoded);
 

	
 
	yahoo_http_get(yid->yd->client_id, url, NULL, 1, 0,
 
		_yahoo_http_connected, yid);
 

	
 
	FREE(user_encoded);
 
	FREE(pass_encoded);
 
	FREE(seed_encoded);
 
}
 

	
 
static void yahoo_process_auth(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	char *seed = NULL;
 
	char *sn = NULL;
 
	YList *l = pkt->hash;
 
	int m = 0;
 
	struct yahoo_data *yd = yid->yd;
 

	
 
	while (l) {
 
		struct yahoo_pair *pair = l->data;
 

	
 
		switch (pair->key) {
 
		case 94:
 
			seed = pair->value;
 
			break;
 
		case 1:
 
			sn = pair->value;
 
			break;
 
		case 13:
 
			m = atoi(pair->value);
 
			break;
 
		}
 
		l = l->next;
 
	}
 

	
 
	if (!seed)
 
		return;
 

	
 
	yd->seed = strdup(seed);
 

	
 
	if (m==2)
 
		yahoo_auth_https(yd);
 
	else {
 
		/* call error */
 
		WARNING(("unknown auth type %d", m));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 
	}
 
}
 

	
 
static void yahoo_send_auth(struct yahoo_data *yd)
 
{
 

	
 
	struct yahoo_packet *packet;
 

	
 
	unsigned char crypt_hash[25];
 

	
 
	md5_byte_t result[16];
 
	md5_state_t ctx;
 

	
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(yd->client_id,
 
			YAHOO_CONNECTION_PAGER);
 

	
 
	/* HTTPS */
 
	md5_init(&ctx);
 
	md5_append(&ctx, (md5_byte_t *)yd->crumb, strlen(yd->crumb));
 
	md5_append(&ctx, (md5_byte_t *)yd->seed, strlen(yd->seed));
 
	md5_finish(&ctx, result);
 

	
 
	to_y64(crypt_hash, result, 16);
 

	
 
	packet = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP,
 
		yd->initial_status, yd->session_id);
 
	yahoo_packet_hash(packet, 1, yd->user);
 
	yahoo_packet_hash(packet, 0, yd->user);
 
	yahoo_packet_hash(packet, 277, yd->cookie_y);
 
	yahoo_packet_hash(packet, 278, yd->cookie_t);
 
	yahoo_packet_hash(packet, 307, (char *)crypt_hash);
 
	yahoo_packet_hash(packet, 244, "4194239");	/* Rekkanoryo says this is the build number */
 
	yahoo_packet_hash(packet, 2, yd->user);
 
	yahoo_packet_hash(packet, 2, "1");
 
	yahoo_packet_hash(packet, 59, yd->cookie_b);
 
	yahoo_packet_hash(packet, 98, "us");	/* TODO Put country code */
 
	yahoo_packet_hash(packet, 135, "9.0.0.2152");
 

	
 
	yahoo_send_packet(yid, packet, 0);
 

	
 
	yahoo_packet_free(packet);
 
}
 

	
 
static void yahoo_process_auth_resp(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *login_id;
 
	char *handle;
 
	char *url = NULL;
 
	int login_status = -1;
 

	
 
	YList *l;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 0)
 
			login_id = pair->value;
 
		else if (pair->key == 1)
 
			handle = pair->value;
 
		else if (pair->key == 20)
 
			url = pair->value;
 
		else if (pair->key == 66)
 
			login_status = atoi(pair->value);
 
	}
 

	
 
	if (pkt->status == YPACKET_STATUS_DISCONNECTED) {
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
 
			login_status, url);
 
		/*      yahoo_logoff(yd->client_id); */
 
	}
 
}
 

	
 
static void yahoo_process_mail(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *who = NULL;
 
	char *email = NULL;
 
	char *subj = NULL;
 
	int count = 0;
 
	YList *l;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 9)
 
			count = strtol(pair->value, NULL, 10);
 
		else if (pair->key == 43)
 
			who = pair->value;
 
		else if (pair->key == 42)
 
			email = pair->value;
 
		else if (pair->key == 18)
 
			subj = pair->value;
 
		else
 
			LOG(("key: %d => value: %s", pair->key, pair->value));
 
	}
 

	
 
	if (who && email && subj) {
 
		char from[1024];
 
		snprintf(from, sizeof(from), "%s (%s)", who, email);
 
		YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, from,
 
			subj, count);
 
	} else if (count > 0)
 
		YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, NULL,
 
			NULL, count);
 
}
 

	
 
static void yahoo_process_new_contact(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *me = NULL;
 
	char *who = NULL;
 
	char *msg = NULL;
 
	int online = -1;
 

	
 
	YList *l;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 4)
 
			who = pair->value;
 
		else if (pair->key == 5)
 
			me = pair->value;
 
		else if (pair->key == 14)
 
			msg = pair->value;
 
		else if (pair->key == 13)
 
			online = strtol(pair->value, NULL, 10);
 
	}
 

	
 
	if (who && online < 0)
 
		YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, me, who,
 
			msg);
 
	else if (online == 2)
 
		YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg);
 
}
 

	
 
/* UNUSED? */
 
static void yahoo_process_contact(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *id = NULL;
 
	char *who = NULL;
 
	char *msg = NULL;
 
	char *name = NULL;
 
	long tm = 0L;
 
	int state = YAHOO_STATUS_AVAILABLE;
 
	int online = 0;
 
	int away = 0;
 
	int idle = 0;
 
	int mobile = 0;
 

	
 
	YList *l;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 1)
 
			id = pair->value;
 
		else if (pair->key == 3)
 
			who = pair->value;
 
		else if (pair->key == 14)
 
			msg = pair->value;
 
		else if (pair->key == 7)
 
			name = pair->value;
 
		else if (pair->key == 10)
 
			state = strtol(pair->value, NULL, 10);
 
		else if (pair->key == 15)
 
			tm = strtol(pair->value, NULL, 10);
 
		else if (pair->key == 13)
 
			online = strtol(pair->value, NULL, 10);
 
		else if (pair->key == 47)
 
			away = strtol(pair->value, NULL, 10);
 
		else if (pair->key == 137)
 
			idle = strtol(pair->value, NULL, 10);
 
		else if (pair->key == 60)
 
			mobile = strtol(pair->value, NULL, 10);
 

	
 
	}
 

	
 
	if (id)
 
		YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, id, who,
 
			msg);
 
	else if (name)
 
		YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, name,
 
			state, msg, away, idle, mobile);
 
	else if (pkt->status == 0x07)
 
		YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg);
 
}
 

	
 
static void yahoo_process_buddyadd(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *who = NULL;
 
	char *where = NULL;
 
	int status = 0;
 
	char *me = NULL;
 

	
 
	struct yahoo_buddy *bud = NULL;
 

	
 
	YList *l;
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 1)
 
			me = pair->value;
 
		if (pair->key == 7)
 
			who = pair->value;
 
		if (pair->key == 65)
 
			where = pair->value;
 
		if (pair->key == 66)
 
			status = strtol(pair->value, NULL, 10);
 
	}
 

	
 
	if (!who)
 
		return;
 
	if (!where)
 
		where = "Unknown";
 

	
 
	bud = y_new0(struct yahoo_buddy, 1);
 
	bud->id = strdup(who);
 
	bud->group = strdup(where);
 
	bud->real_name = NULL;
 

	
 
	yd->buddies = y_list_append(yd->buddies, bud);
 

	
 
	/* A non-zero status (i've seen 2) seems to mean the buddy is already 
 
	 * added and is online */
 
	if (status) {
 
		LOG(("Setting online see packet for info"));
 
		yahoo_dump_unhandled(pkt);
 
		YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, who,
 
			YAHOO_STATUS_AVAILABLE, NULL, 0, 0, 0);
 
	}
 
}
 

	
 
static void yahoo_process_buddydel(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	char *who = NULL;
 
	char *where = NULL;
 
	int unk_66 = 0;
 
	char *me = NULL;
 
	struct yahoo_buddy *bud;
 

	
 
	YList *buddy;
 

	
 
	YList *l;
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 1)
 
			me = pair->value;
 
		else if (pair->key == 7)
 
			who = pair->value;
 
		else if (pair->key == 65)
 
			where = pair->value;
 
		else if (pair->key == 66)
 
			unk_66 = strtol(pair->value, NULL, 10);
 
		else
 
			DEBUG_MSG(("unknown key: %d = %s", pair->key,
 
					pair->value));
 
	}
 

	
 
	if (!who || !where)
 
		return;
 

	
 
	bud = y_new0(struct yahoo_buddy, 1);
 
	bud->id = strdup(who);
 
	bud->group = strdup(where);
 

	
 
	buddy = y_list_find_custom(yd->buddies, bud, is_same_bud);
 

	
 
	FREE(bud->id);
 
	FREE(bud->group);
 
	FREE(bud);
 

	
 
	if (buddy) {
 
		bud = buddy->data;
 
		yd->buddies = y_list_remove_link(yd->buddies, buddy);
 
		y_list_free_1(buddy);
 

	
 
		FREE(bud->id);
 
		FREE(bud->group);
 
		FREE(bud->real_name);
 
		FREE(bud);
 

	
 
		bud = NULL;
 
	}
 
}
 

	
 
static void yahoo_process_ignore(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	char *who = NULL;
 
	int status = 0;
 
	char *me = NULL;
 
	int un_ignore = 0;
 

	
 
	YList *l;
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 0)
 
			who = pair->value;
 
		if (pair->key == 1)
 
			me = pair->value;
 
		if (pair->key == 13)	/* 1 == ignore, 2 == unignore */
 
			un_ignore = strtol(pair->value, NULL, 10);
 
		if (pair->key == 66)
 
			status = strtol(pair->value, NULL, 10);
 
	}
 

	
 
	/*
 
	 * status
 
	 *      0  - ok
 
	 *      2  - already in ignore list, could not add
 
	 *      3  - not in ignore list, could not delete
 
	 *      12 - is a buddy, could not add
 
	 */
 

	
 
/*	if(status)
 
		YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, who, 0, status);
 
*/
 
}
 

	
 
static void yahoo_process_voicechat(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	char *who = NULL;
 
	char *me = NULL;
 
	char *room = NULL;
 
	char *voice_room = NULL;
 

	
 
	YList *l;
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 4)
 
			who = pair->value;
 
		if (pair->key == 5)
 
			me = pair->value;
 
		if (pair->key == 13)
 
			voice_room = pair->value;
 
		if (pair->key == 57)
 
			room = pair->value;
 
	}
 

	
 
	NOTICE(("got voice chat invite from %s in %s to identity %s", who, room,
 
			me));
 
	/* 
 
	 * send: s:0 1:me 5:who 57:room 13:1
 
	 * ????  s:4 5:who 10:99 19:-1615114531
 
	 * gotr: s:4 5:who 10:99 19:-1615114615
 
	 * ????  s:1 5:me 4:who 57:room 13:3room
 
	 * got:  s:1 5:me 4:who 57:room 13:1room
 
	 * rej:  s:0 1:me 5:who 57:room 13:3
 
	 * rejr: s:4 5:who 10:99 19:-1617114599
 
	 */
 
}
 

	
 
static void yahoo_process_ping(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	char *errormsg = NULL;
 

	
 
	YList *l;
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 16)
 
			errormsg = pair->value;
 
	}
 

	
 
	NOTICE(("got ping packet"));
 
	YAHOO_CALLBACK(ext_yahoo_got_ping) (yid->yd->client_id, errormsg);
 
}
 

	
 
static void yahoo_process_buddy_change_group(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	YList *l;
 
	char *me = NULL;
 
	char *who = NULL;
 
	char *old_group = NULL;
 
	char *new_group = NULL;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 1)
 
			me = pair->value;
 
		if (pair->key == 7)
 
			who = pair->value;
 
		if (pair->key == 224)
 
			old_group = pair->value;
 
		if (pair->key == 264)
 
			new_group = pair->value;
 
	}
 

	
 
	YAHOO_CALLBACK(ext_yahoo_got_buddy_change_group) (yid->yd->client_id,
 
		me, who, old_group, new_group);
 
}
 

	
 
static void _yahoo_webcam_get_server_connected(void *fd, int error, void *d)
 
{
 
	struct yahoo_input_data *yid = d;
 
	char *who = yid->wcm->user;
 
	char *data = NULL;
 
	char *packet = NULL;
 
	unsigned char magic_nr[] = { 0, 1, 0 };
 
	unsigned char header_len = 8;
 
	unsigned int len = 0;
 
	unsigned int pos = 0;
 

	
 
	if (error || !fd) {
 
		FREE(who);
 
		FREE(yid);
 
		return;
 
	}
 

	
 
	yid->fd = fd;
 
	inputs = y_list_prepend(inputs, yid);
 

	
 
	/* send initial packet */
 
	if (who)
 
		data = strdup("<RVWCFG>");
 
	else
 
		data = strdup("<RUPCFG>");
 
	yahoo_add_to_send_queue(yid, data, strlen(data));
 
	FREE(data);
 

	
 
	/* send data */
 
	if (who) {
 
		data = strdup("g=");
 
		data = y_string_append(data, who);
 
		data = y_string_append(data, "\r\n");
 
	} else {
 
		data = strdup("f=1\r\n");
 
	}
 
	len = strlen(data);
 
	packet = y_new0(char, header_len + len);
 
	packet[pos++] = header_len;
 
	memcpy(packet + pos, magic_nr, sizeof(magic_nr));
 
	pos += sizeof(magic_nr);
 
	pos += yahoo_put32(packet + pos, len);
 
	memcpy(packet + pos, data, len);
 
	pos += len;
 
	yahoo_add_to_send_queue(yid, packet, pos);
 
	FREE(packet);
 
	FREE(data);
 

	
 
	yid->read_tag =
 
		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
 
		YAHOO_INPUT_READ, yid);
 
}
 

	
 
static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who,
 
	char *key)
 
{
 
	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
 
	struct yahoo_server_settings *yss = y->yd->server_settings;
 

	
 
	yid->type = YAHOO_CONNECTION_WEBCAM_MASTER;
 
	yid->yd = y->yd;
 
	yid->wcm = y_new0(struct yahoo_webcam, 1);
 
	yid->wcm->user = who ? strdup(who) : NULL;
 
	yid->wcm->direction = who ? YAHOO_WEBCAM_DOWNLOAD : YAHOO_WEBCAM_UPLOAD;
 
	yid->wcm->key = strdup(key);
 

	
 
	YAHOO_CALLBACK(ext_yahoo_connect_async) (yid->yd->client_id,
 
		yss->webcam_host, yss->webcam_port,
 
		_yahoo_webcam_get_server_connected, yid, 0);
 

	
 
}
 

	
 
static YList *webcam_queue = NULL;
 
static void yahoo_process_webcam_key(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	char *me = NULL;
 
	char *key = NULL;
 
	char *who = NULL;
 

	
 
	YList *l;
 
	yahoo_dump_unhandled(pkt);
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		if (pair->key == 5)
 
			me = pair->value;
 
		if (pair->key == 61)
 
			key = pair->value;
 
	}
 

	
 
	l = webcam_queue;
 
	if (!l)
 
		return;
 
	who = l->data;
 
	webcam_queue = y_list_remove_link(webcam_queue, webcam_queue);
 
	y_list_free_1(l);
 
	yahoo_webcam_get_server(yid, who, key);
 
	FREE(who);
 
}
 

	
 
static void yahoo_packet_process(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
 
	switch (pkt->service) {
 
	case YAHOO_SERVICE_USERSTAT:
 
	case YAHOO_SERVICE_LOGON:
 
	case YAHOO_SERVICE_LOGOFF:
 
	case YAHOO_SERVICE_ISAWAY:
 
	case YAHOO_SERVICE_ISBACK:
 
	case YAHOO_SERVICE_GAMELOGON:
 
	case YAHOO_SERVICE_GAMELOGOFF:
 
	case YAHOO_SERVICE_IDACT:
 
	case YAHOO_SERVICE_IDDEACT:
 
	case YAHOO_SERVICE_Y6_STATUS_UPDATE:
 
	case YAHOO_SERVICE_Y8_STATUS:
 
		yahoo_process_status(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_NOTIFY:
 
		yahoo_process_notify(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_MESSAGE:
 
	case YAHOO_SERVICE_GAMEMSG:
 
	case YAHOO_SERVICE_SYSMESSAGE:
 
		yahoo_process_message(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_NEWMAIL:
 
		yahoo_process_mail(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_Y7_AUTHORIZATION:
 
		yahoo_process_new_contact(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_NEWCONTACT:
 
		yahoo_process_contact(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_LIST:
 
		yahoo_process_list(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_VERIFY:
 
		yahoo_process_verify(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_AUTH:
 
		yahoo_process_auth(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_AUTHRESP:
 
		yahoo_process_auth_resp(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_CONFINVITE:
 
	case YAHOO_SERVICE_CONFADDINVITE:
 
	case YAHOO_SERVICE_CONFDECLINE:
 
	case YAHOO_SERVICE_CONFLOGON:
 
	case YAHOO_SERVICE_CONFLOGOFF:
 
	case YAHOO_SERVICE_CONFMSG:
 
		yahoo_process_conference(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_CHATONLINE:
 
	case YAHOO_SERVICE_CHATGOTO:
 
	case YAHOO_SERVICE_CHATJOIN:
 
	case YAHOO_SERVICE_CHATLEAVE:
 
	case YAHOO_SERVICE_CHATEXIT:
 
	case YAHOO_SERVICE_CHATLOGOUT:
 
	case YAHOO_SERVICE_CHATPING:
 
	case YAHOO_SERVICE_COMMENT:
 
		yahoo_process_chat(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_P2PFILEXFER:
 
	case YAHOO_SERVICE_Y7_FILETRANSFER:
 
		yahoo_process_filetransfer(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_Y7_FILETRANSFERINFO:
 
		yahoo_process_filetransferinfo(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_Y7_FILETRANSFERACCEPT:
 
		yahoo_process_filetransferaccept(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_ADDBUDDY:
 
		yahoo_process_buddyadd(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_REMBUDDY:
 
		yahoo_process_buddydel(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_IGNORECONTACT:
 
		yahoo_process_ignore(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_VOICECHAT:
 
		yahoo_process_voicechat(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_WEBCAM:
 
		yahoo_process_webcam_key(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_PING:
 
		yahoo_process_ping(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_Y7_CHANGE_GROUP:
 
		yahoo_process_buddy_change_group(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_IDLE:
 
	case YAHOO_SERVICE_MAILSTAT:
 
	case YAHOO_SERVICE_CHATINVITE:
 
	case YAHOO_SERVICE_CALENDAR:
 
	case YAHOO_SERVICE_NEWPERSONALMAIL:
 
	case YAHOO_SERVICE_ADDIDENT:
 
	case YAHOO_SERVICE_ADDIGNORE:
 
	case YAHOO_SERVICE_GOTGROUPRENAME:
 
	case YAHOO_SERVICE_GROUPRENAME:
 
	case YAHOO_SERVICE_PASSTHROUGH2:
 
	case YAHOO_SERVICE_CHATLOGON:
 
	case YAHOO_SERVICE_CHATLOGOFF:
 
	case YAHOO_SERVICE_CHATMSG:
 
	case YAHOO_SERVICE_REJECTCONTACT:
 
	case YAHOO_SERVICE_PEERTOPEER:
 
		WARNING(("unhandled service 0x%02x", pkt->service));
 
		yahoo_dump_unhandled(pkt);
 
		break;
 
	case YAHOO_SERVICE_PICTURE:
 
		yahoo_process_picture(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_PICTURE_CHECKSUM:
 
		yahoo_process_picture_checksum(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_PICTURE_UPLOAD:
 
		yahoo_process_picture_upload(yid, pkt);
 
		break;
 
	case YAHOO_SERVICE_Y8_LIST:	/* Buddy List */
 
		yahoo_process_buddy_list(yid, pkt);
 
		break;
 
	default:
 
		WARNING(("unknown service 0x%02x", pkt->service));
 
		yahoo_dump_unhandled(pkt);
 
		break;
 
	}
 
}
 

	
 
static struct yahoo_packet *yahoo_getdata(struct yahoo_input_data *yid)
 
{
 
	struct yahoo_packet *pkt;
 
	struct yahoo_data *yd = yid->yd;
 
	int pos = 0;
 
	int pktlen;
 

	
 
	if (!yd)
 
		return NULL;
 

	
 
	DEBUG_MSG(("rxlen is %d", yid->rxlen));
 
	if (yid->rxlen < YAHOO_PACKET_HDRLEN) {
 
		DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN"));
 
		return NULL;
 
	}
 

	
 
	pos += 4;		/* YMSG */
 
	pos += 2;
 
	pos += 2;
 

	
 
	pktlen = yahoo_get16(yid->rxqueue + pos);
 
	pos += 2;
 
	DEBUG_MSG(("%d bytes to read, rxlen is %d", pktlen, yid->rxlen));
 

	
 
	if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) {
 
		DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen"));
 
		return NULL;
 
	}
 

	
 
	LOG(("reading packet"));
 
	yahoo_packet_dump(yid->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
 

	
 
	pkt = yahoo_packet_new(0, 0, 0);
 

	
 
	pkt->service = yahoo_get16(yid->rxqueue + pos);
 
	pos += 2;
 
	pkt->status = yahoo_get32(yid->rxqueue + pos);
 
	pos += 4;
 
	DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service,
 
			pkt->status));
 
	pkt->id = yahoo_get32(yid->rxqueue + pos);
 
	pos += 4;
 

	
 
	yd->session_id = pkt->id;
 

	
 
	yahoo_packet_read(pkt, yid->rxqueue + pos, pktlen);
 

	
 
	yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
 
	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
 
	if (yid->rxlen > 0) {
 
		unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN
 
			+ pktlen, yid->rxlen);
 
		FREE(yid->rxqueue);
 
		yid->rxqueue = tmp;
 
		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
 
				yid->rxqueue));
 
	} else {
 
		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
 
		FREE(yid->rxqueue);
 
	}
 

	
 
	return pkt;
 
}
 

	
 
static struct yab *yahoo_yab_read(unsigned char *d, int len)
 
{
 
	char *st, *en;
 
	char *data = (char *)d;
 
	struct yab *yab = NULL;
 

	
 
	data[len] = '\0';
 

	
 
	DEBUG_MSG(("Got yab: %s", data));
 
	st = en = strstr(data, "e0=\"");
 
	if (st) {
 
		yab = y_new0(struct yab, 1);
 

	
 
		st += strlen("e0=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->email = yahoo_xmldecode(st);
 
	}
 

	
 
	if (!en)
 
		return NULL;
 

	
 
	st = strstr(en, "id=\"");
 
	if (st) {
 
		st += strlen("id=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->yid = atoi(yahoo_xmldecode(st));
 
	}
 

	
 
	st = strstr(en, "fn=\"");
 
	if (st) {
 
		st += strlen("fn=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->fname = yahoo_xmldecode(st);
 
	}
 

	
 
	st = strstr(en, "ln=\"");
 
	if (st) {
 
		st += strlen("ln=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->lname = yahoo_xmldecode(st);
 
	}
 

	
 
	st = strstr(en, "nn=\"");
 
	if (st) {
 
		st += strlen("nn=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->nname = yahoo_xmldecode(st);
 
	}
 

	
 
	st = strstr(en, "yi=\"");
 
	if (st) {
 
		st += strlen("yi=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->id = yahoo_xmldecode(st);
 
	}
 

	
 
	st = strstr(en, "hphone=\"");
 
	if (st) {
 
		st += strlen("hphone=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->hphone = yahoo_xmldecode(st);
 
	}
 

	
 
	st = strstr(en, "wphone=\"");
 
	if (st) {
 
		st += strlen("wphone=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->wphone = yahoo_xmldecode(st);
 
	}
 

	
 
	st = strstr(en, "mphone=\"");
 
	if (st) {
 
		st += strlen("mphone=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->mphone = yahoo_xmldecode(st);
 
	}
 

	
 
	st = strstr(en, "dbid=\"");
 
	if (st) {
 
		st += strlen("dbid=\"");
 
		en = strchr(st, '"');
 
		*en++ = '\0';
 
		yab->dbid = atoi(st);
 
	}
 

	
 
	return yab;
 
}
 

	
 
static struct yab *yahoo_getyab(struct yahoo_input_data *yid)
 
{
 
	struct yab *yab = NULL;
 
	int pos = 0, end = 0;
 
	struct yahoo_data *yd = yid->yd;
 

	
 
	if (!yd)
 
		return NULL;
 

	
 
	do {
 
		DEBUG_MSG(("rxlen is %d", yid->rxlen));
 

	
 
		if (yid->rxlen <= strlen("<ct"))
 
			return NULL;
 

	
 
		/* start with <ct */
 
		while (pos < yid->rxlen - strlen("<ct") + 1
 
			&& memcmp(yid->rxqueue + pos, "<ct", strlen("<ct")))
 
			pos++;
 

	
 
		if (pos >= yid->rxlen - 1)
 
			return NULL;
 

	
 
		end = pos + 2;
 
		/* end with > */
 
		while (end < yid->rxlen - strlen(">")
 
			&& memcmp(yid->rxqueue + end, ">", strlen(">")))
 
			end++;
 

	
 
		if (end >= yid->rxlen - 1)
 
			return NULL;
 

	
 
		yab = yahoo_yab_read(yid->rxqueue + pos, end + 2 - pos);
 

	
 
		yid->rxlen -= end + 1;
 
		DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen,
 
				yid->rxqueue));
 
		if (yid->rxlen > 0) {
 
			unsigned char *tmp =
 
				y_memdup(yid->rxqueue + end + 1, yid->rxlen);
 
			FREE(yid->rxqueue);
 
			yid->rxqueue = tmp;
 
			DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
 
					yid->rxqueue));
 
		} else {
 
			DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
 
			FREE(yid->rxqueue);
 
		}
 

	
 
	} while (!yab && end < yid->rxlen - 1);
 

	
 
	return yab;
 
}
 

	
 
static char *yahoo_getwebcam_master(struct yahoo_input_data *yid)
 
{
 
	unsigned int pos = 0;
 
	unsigned int len = 0;
 
	unsigned int status = 0;
 
	char *server = NULL;
 
	struct yahoo_data *yd = yid->yd;
 

	
 
	if (!yid || !yd)
 
		return NULL;
 

	
 
	DEBUG_MSG(("rxlen is %d", yid->rxlen));
 

	
 
	len = yid->rxqueue[pos++];
 
	if (yid->rxlen < len)
 
		return NULL;
 

	
 
	/* extract status (0 = ok, 6 = webcam not online) */
 
	status = yid->rxqueue[pos++];
 

	
 
	if (status == 0) {
 
		pos += 2;	/* skip next 2 bytes */
 
		server = y_memdup(yid->rxqueue + pos, 16);
 
		pos += 16;
 
	} else if (status == 6) {
 
		YAHOO_CALLBACK(ext_yahoo_webcam_closed)
 
			(yd->client_id, yid->wcm->user, 4);
 
	}
 

	
 
	/* skip rest of the data */
 

	
 
	yid->rxlen -= len;
 
	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
 
	if (yid->rxlen > 0) {
 
		unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
 
		FREE(yid->rxqueue);
 
		yid->rxqueue = tmp;
 
		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
 
				yid->rxqueue));
 
	} else {
 
		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
 
		FREE(yid->rxqueue);
 
	}
 

	
 
	return server;
 
}
 

	
 
static int yahoo_get_webcam_data(struct yahoo_input_data *yid)
 
{
 
	unsigned char reason = 0;
 
	unsigned int pos = 0;
 
	unsigned int begin = 0;
 
	unsigned int end = 0;
 
	unsigned int closed = 0;
 
	unsigned char header_len = 0;
 
	char *who;
 
	int connect = 0;
 
	struct yahoo_data *yd = yid->yd;
 

	
 
	if (!yd)
 
		return -1;
 

	
 
	if (!yid->wcm || !yid->wcd || !yid->rxlen)
 
		return -1;
 

	
 
	DEBUG_MSG(("rxlen is %d", yid->rxlen));
 

	
 
	/* if we are not reading part of image then read header */
 
	if (!yid->wcd->to_read) {
 
		header_len = yid->rxqueue[pos++];
 
		yid->wcd->packet_type = 0;
 

	
 
		if (yid->rxlen < header_len)
 
			return 0;
 

	
 
		if (header_len >= 8) {
 
			reason = yid->rxqueue[pos++];
 
			/* next 2 bytes should always be 05 00 */
 
			pos += 2;
 
			yid->wcd->data_size = yahoo_get32(yid->rxqueue + pos);
 
			pos += 4;
 
			yid->wcd->to_read = yid->wcd->data_size;
 
		}
 
		if (header_len >= 13) {
 
			yid->wcd->packet_type = yid->rxqueue[pos++];
 
			yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos);
 
			pos += 4;
 
		}
 

	
 
		/* skip rest of header */
 
		pos = header_len;
 
	}
 

	
 
	begin = pos;
 
	pos += yid->wcd->to_read;
 
	if (pos > yid->rxlen)
 
		pos = yid->rxlen;
 

	
 
	/* if it is not an image then make sure we have the whole packet */
 
	if (yid->wcd->packet_type != 0x02) {
 
		if ((pos - begin) != yid->wcd->data_size) {
 
			yid->wcd->to_read = 0;
 
			return 0;
 
		} else {
 
			yahoo_packet_dump(yid->rxqueue + begin, pos - begin);
 
		}
 
	}
 

	
 
	DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type,
 
			yid->wcd->data_size));
 

	
 
	/* find out what kind of packet we got */
 
	switch (yid->wcd->packet_type) {
 
	case 0x00:
 
		/* user requests to view webcam (uploading) */
 
		if (yid->wcd->data_size &&
 
			yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) {
 
			end = begin;
 
			while (end <= yid->rxlen && yid->rxqueue[end++] != 13) ;
 
			if (end > begin) {
 
				who = y_memdup(yid->rxqueue + begin,
 
					end - begin);
 
				who[end - begin - 1] = 0;
 
				YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->
 
					client_id, who + 2, 2);
 
				FREE(who);
 
			}
 
		}
 

	
 
		if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) {
 
			/* timestamp/status field */
 
			/* 0 = declined viewing permission */
 
			/* 1 = accepted viewing permission */
 
			if (yid->wcd->timestamp == 0) {
 
				YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->
 
					client_id, yid->wcm->user, 3);
 
			}
 
		}
 
		break;
 
	case 0x01:		/* status packets?? */
 
		/* timestamp contains status info */
 
		/* 00 00 00 01 = we have data?? */
 
		break;
 
	case 0x02:		/* image data */
 
		YAHOO_CALLBACK(ext_yahoo_got_webcam_image) (yd->client_id,
 
			yid->wcm->user, yid->rxqueue + begin,
 
			yid->wcd->data_size, pos - begin, yid->wcd->timestamp);
 
		break;
 
	case 0x05:		/* response packets when uploading */
 
		if (!yid->wcd->data_size) {
 
			YAHOO_CALLBACK(ext_yahoo_webcam_data_request) (yd->
 
				client_id, yid->wcd->timestamp);
 
		}
 
		break;
 
	case 0x07:		/* connection is closing */
 
		switch (reason) {
 
		case 0x01:	/* user closed connection */
 
			closed = 1;
 
			break;
 
		case 0x0F:	/* user cancelled permission */
 
			closed = 2;
 
			break;
 
		}
 
		YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id,
 
			yid->wcm->user, closed);
 
		break;
 
	case 0x0C:		/* user connected */
 
	case 0x0D:		/* user disconnected */
 
		if (yid->wcd->data_size) {
 
			who = y_memdup(yid->rxqueue + begin, pos - begin + 1);
 
			who[pos - begin] = 0;
 
			if (yid->wcd->packet_type == 0x0C)
 
				connect = 1;
 
			else
 
				connect = 0;
 
			YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id,
 
				who, connect);
 
			FREE(who);
 
		}
 
		break;
 
	case 0x13:		/* user data */
 
		/* i=user_ip (ip of the user we are viewing) */
 
		/* j=user_ext_ip (external ip of the user we */
 
		/*                are viewing) */
 
		break;
 
	case 0x17:		/* ?? */
 
		break;
 
	}
 
	yid->wcd->to_read -= pos - begin;
 

	
 
	yid->rxlen -= pos;
 
	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
 
	if (yid->rxlen > 0) {
 
		unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
 
		FREE(yid->rxqueue);
 
		yid->rxqueue = tmp;
 
		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
 
				yid->rxqueue));
 
	} else {
 
		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
 
		FREE(yid->rxqueue);
 
	}
 

	
 
	/* If we read a complete packet return success */
 
	if (!yid->wcd->to_read)
 
		return 1;
 

	
 
	return 0;
 
}
 

	
 
int yahoo_write_ready(int id, void *fd, void *data)
 
{
 
	struct yahoo_input_data *yid = data;
 
	int len;
 
	struct data_queue *tx;
 

	
 
	LOG(("write callback: id=%d fd=%p data=%p", id, fd, data));
 
	if (!yid || !yid->txqueues)
 
		return -2;
 

	
 
	tx = yid->txqueues->data;
 
	LOG(("writing %d bytes", tx->len));
 
	len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len));
 

	
 
	if (len == -1 && errno == EAGAIN)
 
		return 1;
 

	
 
	if (len <= 0) {
 
		int e = errno;
 
		DEBUG_MSG(("len == %d (<= 0)", len));
 
		while (yid->txqueues) {
 
			YList *l = yid->txqueues;
 
			tx = l->data;
 
			free(tx->queue);
 
			free(tx);
 
			yid->txqueues =
 
				y_list_remove_link(yid->txqueues,
 
				yid->txqueues);
 
			y_list_free_1(l);
 
		}
 
		LOG(("yahoo_write_ready(%d, %p) len < 0", id, fd));
 
		YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, yid->write_tag);
 
		yid->write_tag = 0;
 
		errno = e;
 
		return 0;
 
	}
 

	
 
	tx->len -= len;
 
	if (tx->len > 0) {
 
		unsigned char *tmp = y_memdup(tx->queue + len, tx->len);
 
		FREE(tx->queue);
 
		tx->queue = tmp;
 
	} else {
 
		YList *l = yid->txqueues;
 
		free(tx->queue);
 
		free(tx);
 
		yid->txqueues =
 
			y_list_remove_link(yid->txqueues, yid->txqueues);
 
		y_list_free_1(l);
 
		/*
 
		   if(!yid->txqueues) 
 
		   LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd));
 
		 */
 
		if (!yid->txqueues) {
 
			LOG(("yahoo_write_ready(%d, %p) !txqueues", id, fd));
 
			YAHOO_CALLBACK(ext_yahoo_remove_handler) (id,
 
				yid->write_tag);
 
			yid->write_tag = 0;
 
		}
 
	}
 

	
 
	return 1;
 
}
 

	
 
static void yahoo_process_pager_connection(struct yahoo_input_data *yid,
 
	int over)
 
{
 
	struct yahoo_packet *pkt;
 
	struct yahoo_data *yd = yid->yd;
 
	int id = yd->client_id;
 

	
 
	if (over)
 
		return;
 

	
 
	while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER)
 
		&& (pkt = yahoo_getdata(yid)) != NULL) {
 

	
 
		yahoo_packet_process(yid, pkt);
 

	
 
		yahoo_packet_free(pkt);
 
	}
 
}
 

	
 
static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid,
 
	int over)
 
{
 
	if (over)
 
		return;
 

	
 
	if (strstr((char *)yid->rxqueue + (yid->rxlen - 20), "</content>")) {
 
		YAHOO_CALLBACK(ext_yahoo_chat_cat_xml) (yid->yd->client_id,
 
			(char *)yid->rxqueue);
 
	}
 
}
 

	
 
static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over)
 
{
 
	struct yahoo_data *yd = yid->yd;
 
	struct yab *yab;
 
	YList *buds;
 
	int changed = 0;
 
	int id = yd->client_id;
 
	int yab_used = 0;
 

	
 
	LOG(("Got data for YAB"));
 

	
 
	if (over)
 
		return;
 

	
 
	while (find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB)
 
		&& (yab = yahoo_getyab(yid)) != NULL) {
 
		if (!yab->id)
 
			continue;
 

	
 
		changed = 1;
 
		yab_used = 0;
 
		for (buds = yd->buddies; buds; buds = buds->next) {
 
			struct yahoo_buddy *bud = buds->data;
 
			if (!strcmp(bud->id, yab->id)) {
 
				yab_used = 1;
 
				bud->yab_entry = yab;
 
				if (yab->nname) {
 
					bud->real_name = strdup(yab->nname);
 
				} else if (yab->fname && yab->lname) {
 
					bud->real_name = y_new0(char,
 
						strlen(yab->fname) +
 
						strlen(yab->lname) + 2);
 
					sprintf(bud->real_name, "%s %s",
 
						yab->fname, yab->lname);
 
				} else if (yab->fname) {
 
					bud->real_name = strdup(yab->fname);
 
				}
 
				break;	/* for */
 
			}
 
		}
 

	
 
		if (!yab_used) {
 
			FREE(yab->fname);
 
			FREE(yab->lname);
 
			FREE(yab->nname);
 
			FREE(yab->id);
 
			FREE(yab->email);
 
			FREE(yab->hphone);
 
			FREE(yab->wphone);
 
			FREE(yab->mphone);
 
			FREE(yab);
 
		}
 

	
 
	}
 

	
 
	if (changed)
 
		YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id,
 
			yd->buddies);
 
}
 

	
 
static void yahoo_process_search_connection(struct yahoo_input_data *yid,
 
	int over)
 
{
 
	struct yahoo_found_contact *yct = NULL;
 
	char *p = (char *)yid->rxqueue, *np, *cp;
 
	int k, n;
 
	int start = 0, found = 0, total = 0;
 
	YList *contacts = NULL;
 
	struct yahoo_input_data *pyid =
 
		find_input_by_id_and_type(yid->yd->client_id,
 
		YAHOO_CONNECTION_PAGER);
 

	
 
	if (!over || !pyid)
 
		return;
 

	
 
	if (p && (p = strstr(p, "\r\n\r\n"))) {
 
		p += 4;
 

	
 
		for (k = 0; (p = strchr(p, 4)) && (k < 4); k++) {
 
			p++;
 
			n = atoi(p);
 
			switch (k) {
 
			case 0:
 
				found = pyid->ys->lsearch_nfound = n;
 
				break;
 
			case 2:
 
				start = pyid->ys->lsearch_nstart = n;
 
				break;
 
			case 3:
 
				total = pyid->ys->lsearch_ntotal = n;
 
				break;
 
			}
 
		}
 

	
 
		if (p)
 
			p++;
 

	
 
		k = 0;
 
		while (p && *p) {
 
			cp = p;
 
			np = strchr(p, 4);
 

	
 
			if (!np)
 
				break;
 
			*np = 0;
 
			p = np + 1;
 

	
 
			switch (k++) {
 
			case 1:
 
				if (strlen(cp) > 2
 
					&& y_list_length(contacts) < total) {
 
					yct = y_new0(struct yahoo_found_contact,
 
						1);
 
					contacts = y_list_append(contacts, yct);
 
					yct->id = cp + 2;
 
				} else {
 
					*p = 0;
 
				}
 
				break;
 
			case 2:
 
				yct->online = !strcmp(cp, "2") ? 1 : 0;
 
				break;
 
			case 3:
 
				yct->gender = cp;
 
				break;
 
			case 4:
 
				yct->age = atoi(cp);
 
				break;
 
			case 5:
 
				/* not worth the context switch for strcmp */
 
				if (cp[0] != '\005' || cp[1] != '\000')
 
					yct->location = cp;
 
				k = 0;
 
				break;
 
			}
 
		}
 
	}
 

	
 
	YAHOO_CALLBACK(ext_yahoo_got_search_result) (yid->yd->client_id, found,
 
		start, total, contacts);
 

	
 
	while (contacts) {
 
		YList *node = contacts;
 
		contacts = y_list_remove_link(contacts, node);
 
		free(node->data);
 
		y_list_free_1(node);
 
	}
 
}
 

	
 
static void _yahoo_webcam_connected(void *fd, int error, void *d)
 
{
 
	struct yahoo_input_data *yid = d;
 
	struct yahoo_webcam *wcm = yid->wcm;
 
	struct yahoo_data *yd = yid->yd;
 
	char conn_type[100];
 
	char *data = NULL;
 
	char *packet = NULL;
 
	unsigned char magic_nr[] = { 1, 0, 0, 0, 1 };
 
	unsigned header_len = 0;
 
	unsigned int len = 0;
 
	unsigned int pos = 0;
 

	
 
	if (error || !fd) {
 
		FREE(yid);
 
		return;
 
	}
 

	
 
	yid->fd = fd;
 
	inputs = y_list_prepend(inputs, yid);
 

	
 
	LOG(("Connected"));
 
	/* send initial packet */
 
	switch (wcm->direction) {
 
	case YAHOO_WEBCAM_DOWNLOAD:
 
		data = strdup("<REQIMG>");
 
		break;
 
	case YAHOO_WEBCAM_UPLOAD:
 
		data = strdup("<SNDIMG>");
 
		break;
 
	default:
 
		return;
 
	}
 
	yahoo_add_to_send_queue(yid, data, strlen(data));
 
	FREE(data);
 

	
 
	/* send data */
 
	switch (wcm->direction) {
 
	case YAHOO_WEBCAM_DOWNLOAD:
 
		header_len = 8;
 
		data = strdup("a=2\r\nc=us\r\ne=21\r\nu=");
 
		data = y_string_append(data, yd->user);
 
		data = y_string_append(data, "\r\nt=");
 
		data = y_string_append(data, wcm->key);
 
		data = y_string_append(data, "\r\ni=");
 
		data = y_string_append(data, wcm->my_ip);
 
		data = y_string_append(data, "\r\ng=");
 
		data = y_string_append(data, wcm->user);
 
		data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
 
		snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
 
		data = y_string_append(data, conn_type);
 
		data = y_string_append(data, "\r\n");
 
		break;
 
	case YAHOO_WEBCAM_UPLOAD:
 
		header_len = 13;
 
		data = strdup("a=2\r\nc=us\r\nu=");
 
		data = y_string_append(data, yd->user);
 
		data = y_string_append(data, "\r\nt=");
 
		data = y_string_append(data, wcm->key);
 
		data = y_string_append(data, "\r\ni=");
 
		data = y_string_append(data, wcm->my_ip);
 
		data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
 
		snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
 
		data = y_string_append(data, conn_type);
 
		data = y_string_append(data, "\r\nb=");
 
		data = y_string_append(data, wcm->description);
 
		data = y_string_append(data, "\r\n");
 
		break;
 
	}
 

	
 
	len = strlen(data);
 
	packet = y_new0(char, header_len + len);
 
	packet[pos++] = header_len;
 
	packet[pos++] = 0;
 
	switch (wcm->direction) {
 
	case YAHOO_WEBCAM_DOWNLOAD:
 
		packet[pos++] = 1;
 
		packet[pos++] = 0;
 
		break;
 
	case YAHOO_WEBCAM_UPLOAD:
 
		packet[pos++] = 5;
 
		packet[pos++] = 0;
 
		break;
 
	}
 

	
 
	pos += yahoo_put32(packet + pos, len);
 
	if (wcm->direction == YAHOO_WEBCAM_UPLOAD) {
 
		memcpy(packet + pos, magic_nr, sizeof(magic_nr));
 
		pos += sizeof(magic_nr);
 
	}
 
	memcpy(packet + pos, data, len);
 
	yahoo_add_to_send_queue(yid, packet, header_len + len);
 
	FREE(packet);
 
	FREE(data);
 

	
 
	yid->read_tag =
 
		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id,
 
		yid->fd, YAHOO_INPUT_READ, yid);
 
}
 

	
 
static void yahoo_webcam_connect(struct yahoo_input_data *y)
 
{
 
	struct yahoo_webcam *wcm = y->wcm;
 
	struct yahoo_input_data *yid;
 
	struct yahoo_server_settings *yss;
 

	
 
	if (!wcm || !wcm->server || !wcm->key)
 
		return;
 

	
 
	yid = y_new0(struct yahoo_input_data, 1);
 
	yid->type = YAHOO_CONNECTION_WEBCAM;
 
	yid->yd = y->yd;
 

	
 
	/* copy webcam data to new connection */
 
	yid->wcm = y->wcm;
 
	y->wcm = NULL;
 

	
 
	yss = y->yd->server_settings;
 

	
 
	yid->wcd = y_new0(struct yahoo_webcam_data, 1);
 

	
 
	LOG(("Connecting to: %s:%d", wcm->server, wcm->port));
 
	YAHOO_CALLBACK(ext_yahoo_connect_async) (y->yd->client_id, wcm->server,
 
		wcm->port, _yahoo_webcam_connected, yid, 0);
 

	
 
}
 

	
 
static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid,
 
	int over)
 
{
 
	char *server;
 
	struct yahoo_server_settings *yss;
 

	
 
	if (over)
 
		return;
 

	
 
	server = yahoo_getwebcam_master(yid);
 

	
 
	if (server) {
 
		yss = yid->yd->server_settings;
 
		yid->wcm->server = strdup(server);
 
		yid->wcm->port = yss->webcam_port;
 
		yid->wcm->conn_type = yss->conn_type;
 
		yid->wcm->my_ip = strdup(yss->local_host);
 
		if (yid->wcm->direction == YAHOO_WEBCAM_UPLOAD)
 
			yid->wcm->description = strdup(yss->webcam_description);
 
		yahoo_webcam_connect(yid);
 
		FREE(server);
 
	}
 
}
 

	
 
static void yahoo_process_webcam_connection(struct yahoo_input_data *yid,
 
	int over)
 
{
 
	int id = yid->yd->client_id;
 
	void *fd = yid->fd;
 

	
 
	if (over)
 
		return;
 

	
 
	/* as long as we still have packets available keep processing them */
 
	while (find_input_by_id_and_fd(id, fd)
 
		&& yahoo_get_webcam_data(yid) == 1) ;
 
}
 

	
 
static void yahoo_process_auth_connection(struct yahoo_input_data *yid,
 
	int over)
 
{
 
	char *line_end;
 
	char *token;
 
	char *cookie;
 

	
 
	int error_code = 0;
 
	int is_ymsgr = 0;
 

	
 
	/* Wait till we get everything */
 
	if (!over)
 
		return;
 

	
 
	if (!yid->rxqueue) {
 
		LOG(("Whoops! Closed it just like that??\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 
		return;
 
	}
 

	
 
	token = strstr((char *)yid->rxqueue, "\r\n\r\n");
 

	
 
	if (!token) {
 
		LOG(("Could not find anything after the HTTP headers? huh?\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 
		return;
 
	}
 

	
 
	*token = '\0';
 
	token += 4;
 

	
 
	/* Skip the first number */
 
	token = strstr(token, "\r\n");
 

	
 
	if (!token) {
 
		LOG(("Well I tried to skip the first number and found... nothing\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 
		return;
 
	}
 

	
 
	token += 2;
 

	
 
	line_end = strstr(token, "\r\n");
 

	
 
	if (line_end) {
 
		*line_end = '\0';
 

	
 
		line_end += 2;
 
	}
 

	
 
	error_code = atoi((char *)token);
 

	
 
	switch (error_code) {
 
	case 0:
 
		/* successful */
 
		break;
 
	case 1212:
 
		/* Incorrect ID or password */
 
		LOG(("Incorrect ID or password\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_PASSWD, NULL);
 

	
 
		return;
 
	case 1213:
 
		/* Security lock from too many failed login attempts */
 
		LOG(("Security lock from too many failed login attempts\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_LOCK, "");
 

	
 
		return;
 

	
 
	case 1214:
 
		/* Security lock */
 
		LOG(("Security lock\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_LOCK, "");
 

	
 
		return;
 

	
 
	case 1235:
 
		/* User ID not taken yet */
 
		LOG(("User ID not taken yet\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNAME, NULL);
 

	
 
		return;
 

	
 
	case 1216:
 
		/* Seems to be a lock, but shows the same generic User ID/Password failure */
 
		LOG(("Seems to be a lock, but shows the same generic User ID/Password failure\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_PASSWD, NULL);
 

	
 
		return;
 
	case 100:
 
		/* Username and password cannot be blank */
 
		LOG(("Username and password cannot be blank\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_PASSWD, NULL);
 

	
 
		return;
 
	default:
 
		/* Unknown error code */
 
		LOG(("Unknown Error\n"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 

	
 
		return;
 
	}
 

	
 
	if (line_end && !strncmp(line_end, "ymsgr=", 6)) {
 
		is_ymsgr = 1;
 
	} else if (strncmp(line_end, "crumb=", 6)) {
 
		LOG(("Oops! There was no ymsgr=. Where do I get my token from now :("));
 
		LOG(("I got this:\n\n%s\n", line_end));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 
		return;
 
		/* Error */
 
	}
 

	
 
	token = line_end + 6;
 

	
 
	line_end = strstr(token, "\r\n");
 

	
 
	if (line_end) {
 
		*line_end = '\0';
 
		line_end += 2;
 
	}
 

	
 
	/* Go for the crumb */
 
	if (is_ymsgr) {
 
		char url[256];
 
		char *token_enc;
 
		struct yahoo_input_data *crumb_yid =
 
			y_new0(struct yahoo_input_data, 1);
 

	
 
		crumb_yid->yd = yid->yd;
 
		crumb_yid->type = YAHOO_CONNECTION_AUTH;
 

	
 
		inputs = y_list_prepend(inputs, crumb_yid);
 

	
 
		token_enc = yahoo_urlencode(token);
 

	
 
		snprintf(url, sizeof(url),
 
			"https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts="
 
			"&token=%s", token_enc);
 

	
 
		yahoo_http_get(crumb_yid->yd->client_id, url, NULL, 1, 0,
 
			_yahoo_http_connected, crumb_yid);
 

	
 
		FREE(token_enc);
 

	
 
		return;
 
	}
 

	
 
	/* token is actually crumb */
 

	
 
	if (!line_end) {
 
		/* We did not get our cookies. Cry. */
 
		LOG(("NO Cookies!"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 
		return;
 
	}
 

	
 
	cookie = strstr((char *)yid->rxqueue, "Set-Cookie: Y=");
 

	
 
	if (!cookie) {
 
		/* Cry. */
 
		LOG(("NO Y Cookie!"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 
		return;
 
	}
 

	
 
	cookie += 14;
 

	
 
	line_end = strstr(cookie, "\r\n");
 
	*line_end = '\0';
 

	
 
	yid->yd->cookie_y = strdup(cookie);
 
	*line_end = ';';
 

	
 
	cookie = strstr((char *)yid->rxqueue, "Set-Cookie: T=");
 
	if (!cookie) {
 
		/* Cry. */
 
		LOG(("NO T Cookie!"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 
		return;
 
	}
 

	
 
	cookie += 14;
 

	
 
	line_end = strstr(cookie, "\r\n");
 
	*line_end = '\0';
 

	
 
	yid->yd->cookie_t = strdup(cookie);
 
	*line_end = ';';
 

	
 
	cookie = strstr((char *)yid->rxqueue, "Set-Cookie: B=");
 
	if (!cookie) {
 
		/* Cry. */
 
		LOG(("NO B Cookie!"));
 
		YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->client_id,
 
			YAHOO_LOGIN_UNKNOWN, NULL);
 
		return;
 
	}
 

	
 
	cookie += 14;
 

	
 
	line_end = strstr(cookie, "\r\n");
 
	*line_end = '\0';
 

	
 
	yid->yd->cookie_b = strdup(cookie);
 

	
 
	yid->yd->crumb = strdup(token);
 

	
 
	yahoo_send_auth(yid->yd);
 
}
 

	
 
static void (*yahoo_process_connection[]) (struct yahoo_input_data *,
 
	int over) = {
 
yahoo_process_pager_connection, yahoo_process_ft_connection,
 
		yahoo_process_yab_connection,
 
		yahoo_process_webcam_master_connection,
 
		yahoo_process_webcam_connection,
 
		yahoo_process_chatcat_connection,
 
		yahoo_process_search_connection, yahoo_process_auth_connection};
 

	
 
int yahoo_read_ready(int id, void *fd, void *data)
 
{
 
	struct yahoo_input_data *yid = data;
 
	char buf[1024];
 
	int len;
 

	
 
	LOG(("read callback: id=%d fd=%p data=%p", id, fd, data));
 
	if (!yid)
 
		return -2;
 

	
 
	do {
 
		len = YAHOO_CALLBACK(ext_yahoo_read) (fd, buf, sizeof(buf));
 
	} while (len == -1 && errno == EINTR);
 

	
 
	if (len == -1 && (errno == EAGAIN || errno == EINTR))	/* we'll try again later */
 
		return 1;
 

	
 
	if (len <= 0) {
 
		int e = errno;
 
		DEBUG_MSG(("len == %d (<= 0)", len));
 

	
 
		if (yid->type == YAHOO_CONNECTION_PAGER) {
 
			YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->
 
				client_id, YAHOO_LOGIN_SOCK, NULL);
 
		}
 

	
 
		yahoo_process_connection[yid->type] (yid, 1);
 
		yahoo_input_close(yid);
 

	
 
		/* no need to return an error, because we've already fixed it */
 
		if (len == 0)
 
			return 1;
 

	
 
		errno = e;
 
		LOG(("read error: %s", strerror(errno)));
 
		return -1;
 
	}
 

	
 
	yid->rxqueue =
 
		y_renew(unsigned char, yid->rxqueue, len + yid->rxlen + 1);
 
	memcpy(yid->rxqueue + yid->rxlen, buf, len);
 
	yid->rxlen += len;
 
	yid->rxqueue[yid->rxlen] = 0;
 

	
 
	yahoo_process_connection[yid->type] (yid, 0);
 

	
 
	return len;
 
}
 

	
 
int yahoo_init_with_attributes(const char *username, const char *password, ...)
 
{
 
	va_list ap;
 
	struct yahoo_data *yd;
 

	
 
	yd = y_new0(struct yahoo_data, 1);
 

	
 
	if (!yd)
 
		return 0;
 

	
 
	yd->user = strdup(username);
 
	yd->password = strdup(password);
 

	
 
	yd->initial_status = -1;
 
	yd->current_status = -1;
 

	
 
	yd->client_id = ++last_id;
 

	
 
	add_to_list(yd);
 

	
 
	va_start(ap, password);
 
	yd->server_settings = _yahoo_assign_server_settings(ap);
 
	va_end(ap);
 

	
 
	return yd->client_id;
 
}
 

	
 
int yahoo_init(const char *username, const char *password)
 
{
 
	return yahoo_init_with_attributes(username, password, NULL);
 
}
 

	
 
static void yahoo_connected(void *fd, int error, void *data)
 
{
 
	struct connect_callback_data *ccd = data;
 
	struct yahoo_data *yd = ccd->yd;
 
	struct yahoo_packet *pkt;
 
	struct yahoo_input_data *yid;
 
	struct yahoo_server_settings *yss = yd->server_settings;
 

	
 
	if (error) {
 
		int tag;
 
		if (fallback_ports[ccd->i]) {
 
			char *host = yss->pager_host;
 

	
 
			if (!host)
 
				host = yss->pager_host_list[ccd->server_i];
 

	
 
			yss->pager_port = fallback_ports[ccd->i++];
 
			tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->
 
				client_id, host, yss->pager_port,
 
				yahoo_connected, ccd, 0);
 

	
 
			if (tag > 0)
 
				ccd->tag = tag;
 
		} else if (yss->pager_host_list
 
				&& yss->pager_host_list[ccd->server_i]) {
 

	
 
			/* Get back to the default port */
 
			yss->pager_port = pager_port;
 
			ccd->server_i++;
 
			LOG(("Fallback: Connecting to %s:%d", yss->pager_host_list[ccd->server_i], yss->pager_port));
 

	
 
			ccd->i = 0;
 
			tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id,
 
				yss->pager_host_list[ccd->server_i], yss->pager_port,
 
				yahoo_connected, ccd, 0);
 
		} else {
 
			FREE(ccd);
 
			YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
 
				YAHOO_LOGIN_SOCK, NULL);
 
		}
 
		return;
 
	}
 

	
 
	FREE(ccd);
 

	
 
	/* fd == NULL && error == 0 means connect was cancelled */
 
	if (!fd)
 
		return;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 
	NOTICE(("Sending initial packet"));
 

	
 
	yahoo_packet_hash(pkt, 1, yd->user);
 

	
 
	yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER);
 
	yid->fd = fd;
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 

	
 
	yid->read_tag =
 
		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id,
 
		yid->fd, YAHOO_INPUT_READ, yid);
 
}
 

	
 
void *yahoo_get_fd(int id)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	if (!yid)
 
		return 0;
 
	else
 
		return yid->fd;
 
}
 

	
 
void yahoo_send_buzz(int id, const char *from, const char *who)
 
{
 
	yahoo_send_im(id, from, who, "<ding>", 1, 0);
 
}
 

	
 
void yahoo_send_im(int id, const char *from, const char *who, const char *what,
 
	int utf8, int picture)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_packet *pkt = NULL;
 
	struct yahoo_data *yd;
 
	char pic_str[10];
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE,
 
		yd->session_id);
 

	
 
	snprintf(pic_str, sizeof(pic_str), "%d", picture);
 

	
 
	if (from && strcmp(from, yd->user))
 
		yahoo_packet_hash(pkt, 0, yd->user);
 
	yahoo_packet_hash(pkt, 1, from ? from : yd->user);
 
	yahoo_packet_hash(pkt, 5, who);
 
	yahoo_packet_hash(pkt, 14, what);
 

	
 
	if (utf8)
 
		yahoo_packet_hash(pkt, 97, "1");
 

	
 
	yahoo_packet_hash(pkt, 63, ";0");	/* imvironment name; or ;0 */
 
	yahoo_packet_hash(pkt, 64, "0");
 
	yahoo_packet_hash(pkt, 206, pic_str);
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_send_typing(int id, const char *from, const char *who, int typ)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 5, who);
 
	yahoo_packet_hash(pkt, 1, from ? from : yd->user);
 
	yahoo_packet_hash(pkt, 14, " ");
 
	yahoo_packet_hash(pkt, 13, typ ? "1" : "0");
 
	yahoo_packet_hash(pkt, 49, "TYPING");
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 
	int old_status;
 
	char s[4];
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	old_status = yd->current_status;
 

	
 
	if (msg) {
 
		yd->current_status = YAHOO_STATUS_CUSTOM;
 
	} else {
 
		yd->current_status = state;
 
	}
 

	
 
	/* Thank you libpurple :) */
 
	if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
 
		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE,
 
			YAHOO_STATUS_AVAILABLE, 0);
 
		yahoo_packet_hash(pkt, 13, "2");
 
		yahoo_send_packet(yid, pkt, 0);
 
		yahoo_packet_free(pkt);
 

	
 
		return;
 
	}
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE,
 
		yd->current_status, yd->session_id);
 
	snprintf(s, sizeof(s), "%d", yd->current_status);
 
	yahoo_packet_hash(pkt, 10, s);
 

	
 
	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
 
		yahoo_packet_hash(pkt, 19, msg);
 
	} else {
 
		yahoo_packet_hash(pkt, 19, "");
 
	}
 

	
 
	yahoo_packet_hash(pkt, 47, (away == 2) ? "2" : (away) ? "1" : "0");
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 

	
 
	if (old_status == YAHOO_STATUS_INVISIBLE) {
 
		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE,
 
			YAHOO_STATUS_AVAILABLE, 0);
 
		yahoo_packet_hash(pkt, 13, "1");
 
		yahoo_send_packet(yid, pkt, 0);
 
		yahoo_packet_free(pkt);
 
	}
 
}
 

	
 
void yahoo_logoff(int id)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	LOG(("yahoo_logoff: current status: %d", yd->current_status));
 

	
 
	if (yd->current_status != -1) {
 
		pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF,
 
			YPACKET_STATUS_DEFAULT, yd->session_id);
 
		yd->current_status = -1;
 

	
 
		if (pkt) {
 
			yahoo_send_packet(yid, pkt, 0);
 
			yahoo_packet_free(pkt);
 
		}
 
	}
 

	
 
/*	do {
 
		yahoo_input_close(yid);
 
	} while((yid = find_input_by_id(id)));*/
 

	
 
}
 

	
 
void yahoo_get_list(int id)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	if (pkt) {
 
		yahoo_send_packet(yid, pkt, 0);
 
		yahoo_packet_free(pkt);
 
	}
 
}
 

	
 
static void _yahoo_http_connected(int id, void *fd, int error, void *data)
 
{
 
	struct yahoo_input_data *yid = data;
 
	if (fd == NULL || error) {
 
		inputs = y_list_remove(inputs, yid);
 
		FREE(yid);
 
		return;
 
	}
 

	
 
	yid->fd = fd;
 
	yid->read_tag =
 
		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
 
		YAHOO_INPUT_READ, yid);
 
}
 

	
 
/* FIXME Get address book from address.yahoo.com instead */
 
void yahoo_get_yab(int id)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	struct yahoo_input_data *yid;
 
	char url[1024];
 
	char buff[2048];
 

	
 
	if (!yd)
 
		return;
 

	
 
	yid = y_new0(struct yahoo_input_data, 1);
 
	yid->yd = yd;
 
	yid->type = YAHOO_CONNECTION_YAB;
 

	
 
	LOG(("Sending request for Address Book"));
 

	
 
	snprintf(url, 1024,
 
		"http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us"
 
		"&diffs=1&t=0&tags=short&rt=0&prog-ver=8.1.0.249&useutf8=1&legenc=codepage-1252");
 

	
 
	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
 

	
 
	inputs = y_list_prepend(inputs, yid);
 

	
 
	yahoo_http_get(yid->yd->client_id, url, buff, 0, 0,
 
		_yahoo_http_connected, yid);
 
}
 

	
 
struct yahoo_post_data {
 
	struct yahoo_input_data *yid;
 
	char *data;
 
};
 

	
 
static void _yahoo_http_post_connected(int id, void *fd, int error, void *data)
 
{
 
	struct yahoo_post_data *yad = data;
 
	struct yahoo_input_data *yid = yad->yid;
 
	char *buff = yad->data;
 

	
 
	if (!fd) {
 
		inputs = y_list_remove(inputs, yid);
 
		FREE(yid);
 
		return;
 
	}
 

	
 
	YAHOO_CALLBACK(ext_yahoo_write) (fd, buff, strlen(buff));
 

	
 
	yid->fd = fd;
 
	yid->read_tag =
 
		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
 
		YAHOO_INPUT_READ, yid);
 

	
 
	FREE(buff);
 
	FREE(yad);
 
}
 

	
 
/* FIXME This is also likely affected */
 
void yahoo_set_yab(int id, struct yab *yab)
 
{
 
	struct yahoo_post_data *yad = y_new0(struct yahoo_post_data, 1);
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	struct yahoo_input_data *yid;
 
	char url[1024];
 
	char buff[1024];
 
	char post[1024];
 
	int size = 0;
 

	
 
	if (!yd)
 
		return;
 

	
 
	yid = y_new0(struct yahoo_input_data, 1);
 
	yid->type = YAHOO_CONNECTION_YAB;
 
	yid->yd = yd;
 

	
 
	if(yab->yid)
 
		size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 
			"<ab k=\"%s\" cc=\"%d\">"
 
			"<ct id=\"%d\" e=\"1\" yi=\"%s\" nn=\"%s\" />"
 
			"</ab>", yd->user, 9, yab->yid,	/* Don't know why */
 
			yab->id, yab->nname?yab->nname:"");
 
	else
 
		size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 
			"<ab k=\"%s\" cc=\"%d\">"
 
			"<ct a=\"1\" yi=\"%s\" nn=\"%s\" />"
 
			"</ab>", yd->user, 1,	/* Don't know why */
 
			yab->id, yab->nname?yab->nname:"");
 

	
 
	yad->yid = yid;
 
	yad->data = strdup(post);
 

	
 
	strcpy(url, "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us"
 
		"&sync=1&tags=short&noclear=1&useutf8=1&legenc=codepage-1252");
 

	
 
	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
 

	
 
	inputs = y_list_prepend(inputs, yid);
 

	
 
	yahoo_http_post(yid->yd->client_id, url, buff, size,
 
		_yahoo_http_post_connected, yad);
 
}
 

	
 
void yahoo_set_identity_status(int id, const char *identity, int active)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(active ? YAHOO_SERVICE_IDACT :
 
		YAHOO_SERVICE_IDDEACT, YPACKET_STATUS_DEFAULT, yd->session_id);
 
	yahoo_packet_hash(pkt, 3, identity);
 
	if (pkt) {
 
		yahoo_send_packet(yid, pkt, 0);
 
		yahoo_packet_free(pkt);
 
	}
 
}
 

	
 
void yahoo_refresh(int id)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 
	if (pkt) {
 
		yahoo_send_packet(yid, pkt, 0);
 
		yahoo_packet_free(pkt);
 
	}
 
}
 

	
 
void yahoo_keepalive(int id)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_chat_keepalive(int id)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_add_buddy(int id, const char *who, const char *group,
 
	const char *msg)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	if (!yd->logged_in)
 
		return;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 
	if (msg != NULL)	/* add message/request "it's me add me" */
 
		yahoo_packet_hash(pkt, 14, msg);
 
	else
 
		yahoo_packet_hash(pkt, 14, "");
 
	yahoo_packet_hash(pkt, 65, group);
 
	yahoo_packet_hash(pkt, 97, "1");
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 302, "319");
 
	yahoo_packet_hash(pkt, 300, "319");
 
	yahoo_packet_hash(pkt, 7, who);
 
	yahoo_packet_hash(pkt, 334, "0");
 
	yahoo_packet_hash(pkt, 301, "319");
 
	yahoo_packet_hash(pkt, 303, "319");
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_remove_buddy(int id, const char *who, const char *group)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 7, who);
 
	yahoo_packet_hash(pkt, 65, group);
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_confirm_buddy(int id, const char *who, int reject, const char *msg)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	if (!yd->logged_in)
 
		return;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_AUTHORIZATION,
 
		YPACKET_STATUS_DEFAULT, yd->session_id);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 5, who);
 
	if (reject)
 
		yahoo_packet_hash(pkt, 13, "2");
 
	else {
 
		yahoo_packet_hash(pkt, 241, "0");
 
		yahoo_packet_hash(pkt, 13, "1");
 
	}
 

	
 
	yahoo_packet_hash(pkt, 334, "0");
 

	
 
	if (reject) {
 
		yahoo_packet_hash(pkt, 14, msg ? msg : "");
 
		yahoo_packet_hash(pkt, 97, "1");
 
	}
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_ignore_buddy(int id, const char *who, int unignore)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	if (!yd->logged_in)
 
		return;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT,
 
		YPACKET_STATUS_DEFAULT, yd->session_id);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 7, who);
 
	yahoo_packet_hash(pkt, 13, unignore ? "2" : "1");
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_stealth_buddy(int id, const char *who, int unstealth)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	if (!yd->logged_in)
 
		return;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_STEALTH_PERM,
 
		YPACKET_STATUS_DEFAULT, yd->session_id);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 7, who);
 
	yahoo_packet_hash(pkt, 31, unstealth ? "2" : "1");
 
	yahoo_packet_hash(pkt, 13, "2");
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_change_buddy_group(int id, const char *who, const char *old_group,
 
	const char *new_group)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_CHANGE_GROUP,
 
		YPACKET_STATUS_DEFAULT, yd->session_id);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 302, "240");
 
	yahoo_packet_hash(pkt, 300, "240");
 
	yahoo_packet_hash(pkt, 7, who);
 
	yahoo_packet_hash(pkt, 224, old_group);
 
	yahoo_packet_hash(pkt, 264, new_group);
 
	yahoo_packet_hash(pkt, 301, "240");
 
	yahoo_packet_hash(pkt, 303, "240");
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_group_rename(int id, const char *old_group, const char *new_group)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt = NULL;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME,
 
		YPACKET_STATUS_DEFAULT, yd->session_id);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 65, old_group);
 
	yahoo_packet_hash(pkt, 67, new_group);
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_conference_addinvite(int id, const char *from, const char *who,
 
	const char *room, const YList *members, const char *msg)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE,
 
		YPACKET_STATUS_DEFAULT, yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 51, who);
 
	yahoo_packet_hash(pkt, 57, room);
 
	yahoo_packet_hash(pkt, 58, msg);
 
	yahoo_packet_hash(pkt, 13, "0");
 
	for (; members; members = members->next) {
 
		yahoo_packet_hash(pkt, 52, (char *)members->data);
 
		yahoo_packet_hash(pkt, 53, (char *)members->data);
 
	}
 
	/* 52, 53 -> other members? */
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_conference_invite(int id, const char *from, YList *who,
 
	const char *room, const char *msg)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 50, yd->user);
 
	for (; who; who = who->next) {
 
		yahoo_packet_hash(pkt, 52, (char *)who->data);
 
	}
 
	yahoo_packet_hash(pkt, 57, room);
 
	yahoo_packet_hash(pkt, 58, msg);
 
	yahoo_packet_hash(pkt, 13, "0");
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_conference_logon(int id, const char *from, YList *who,
 
	const char *room)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 3, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 57, room);
 
	for (; who; who = who->next)
 
		yahoo_packet_hash(pkt, 3, (char *)who->data);
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_conference_decline(int id, const char *from, YList *who,
 
	const char *room, const char *msg)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE,
 
		YPACKET_STATUS_DEFAULT, yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 3, (from ? from : yd->user));
 
	for (; who; who = who->next)
 
		yahoo_packet_hash(pkt, 3, (char *)who->data);
 
	yahoo_packet_hash(pkt, 57, room);
 
	yahoo_packet_hash(pkt, 14, msg);
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_conference_logoff(int id, const char *from, YList *who,
 
	const char *room)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 3, (from ? from : yd->user));
 
	for (; who; who = who->next)
 
		yahoo_packet_hash(pkt, 3, (char *)who->data);
 

	
 
	yahoo_packet_hash(pkt, 57, room);
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_conference_message(int id, const char *from, YList *who,
 
	const char *room, const char *msg, int utf8)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 53, (from ? from : yd->user));
 
	for (; who; who = who->next)
 
		yahoo_packet_hash(pkt, 53, (char *)who->data);
 

	
 
	yahoo_packet_hash(pkt, 57, room);
 
	yahoo_packet_hash(pkt, 14, msg);
 

	
 
	if (utf8)
 
		yahoo_packet_hash(pkt, 97, "1");
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_get_chatrooms(int id, int chatroomid)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	struct yahoo_input_data *yid;
 
	char url[1024];
 
	char buff[1024];
 

	
 
	if (!yd)
 
		return;
 

	
 
	yid = y_new0(struct yahoo_input_data, 1);
 
	yid->yd = yd;
 
	yid->type = YAHOO_CONNECTION_CHATCAT;
 

	
 
	if (chatroomid == 0) {
 
		snprintf(url, 1024,
 
			"http://insider.msg.yahoo.com/ycontent/?chatcat=0");
 
	} else {
 
		snprintf(url, 1024,
 
			"http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0",
 
			chatroomid);
 
	}
 

	
 
	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
 

	
 
	inputs = y_list_prepend(inputs, yid);
 

	
 
	yahoo_http_get(yid->yd->client_id, url, buff, 0, 0,
 
		_yahoo_http_connected, yid);
 
}
 

	
 
void yahoo_chat_logon(int id, const char *from, const char *room,
 
	const char *roomid)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 109, yd->user);
 
	yahoo_packet_hash(pkt, 6, "abcde");
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 104, room);
 
	yahoo_packet_hash(pkt, 129, roomid);
 
	yahoo_packet_hash(pkt, 62, "2");	/* ??? */
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_chat_message(int id, const char *from, const char *room,
 
	const char *msg, const int msgtype, const int utf8)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 
	char buf[2];
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 
	yahoo_packet_hash(pkt, 104, room);
 
	yahoo_packet_hash(pkt, 117, msg);
 

	
 
	snprintf(buf, sizeof(buf), "%d", msgtype);
 
	yahoo_packet_hash(pkt, 124, buf);
 

	
 
	if (utf8)
 
		yahoo_packet_hash(pkt, 97, "1");
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_chat_logoff(int id, const char *from)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_buddyicon_request(int id, const char *who)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 5, who);
 
	yahoo_packet_hash(pkt, 13, "1");
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_send_picture_info(int id, const char *who, const char *url,
 
	int checksum)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 
	char checksum_str[10];
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	snprintf(checksum_str, sizeof(checksum_str), "%d", checksum);
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT,
 
		0);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 4, yd->user);
 
	yahoo_packet_hash(pkt, 5, who);
 
	yahoo_packet_hash(pkt, 13, "2");
 
	yahoo_packet_hash(pkt, 20, url);
 
	yahoo_packet_hash(pkt, 192, checksum_str);
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_send_picture_update(int id, const char *who, int type)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 
	char type_str[10];
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	snprintf(type_str, sizeof(type_str), "%d", type);
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPDATE,
 
		YPACKET_STATUS_DEFAULT, 0);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 5, who);
 
	yahoo_packet_hash(pkt, 206, type_str);
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_send_picture_checksum(int id, const char *who, int checksum)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 
	char checksum_str[10];
 

	
 
	if (!yid)
 
		return;
 

	
 
	yd = yid->yd;
 

	
 
	snprintf(checksum_str, sizeof(checksum_str), "%d", checksum);
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM,
 
		YPACKET_STATUS_DEFAULT, 0);
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	if (who != 0)
 
		yahoo_packet_hash(pkt, 5, who);
 
	yahoo_packet_hash(pkt, 192, checksum_str);
 
	yahoo_packet_hash(pkt, 212, "1");
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_webcam_close_feed(int id, const char *who)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_webcam_user(id, who);
 

	
 
	if (yid)
 
		yahoo_input_close(yid);
 
}
 

	
 
void yahoo_webcam_get_feed(int id, const char *who)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_data *yd;
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 

	
 
	/* 
 
	 * add the user to the queue.  this is a dirty hack, since
 
	 * the yahoo server doesn't tell us who's key it's returning,
 
	 * we have to just hope that it sends back keys in the same 
 
	 * order that we request them.
 
	 * The queue is popped in yahoo_process_webcam_key
 
	 */
 
	webcam_queue = y_list_append(webcam_queue, who ? strdup(who) : NULL);
 

	
 
	yd = yid->yd;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YPACKET_STATUS_DEFAULT,
 
		yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	if (who != NULL)
 
		yahoo_packet_hash(pkt, 5, who);
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length,
 
	unsigned int timestamp)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
 
	unsigned char *packet;
 
	unsigned char header_len = 13;
 
	unsigned int pos = 0;
 

	
 
	if (!yid)
 
		return;
 

	
 
	packet = y_new0(unsigned char, header_len);
 

	
 
	packet[pos++] = header_len;
 
	packet[pos++] = 0;
 
	packet[pos++] = 5;	/* version byte?? */
 
	packet[pos++] = 0;
 
	pos += yahoo_put32(packet + pos, length);
 
	packet[pos++] = 2;	/* packet type, image */
 
	pos += yahoo_put32(packet + pos, timestamp);
 
	yahoo_add_to_send_queue(yid, packet, header_len);
 
	FREE(packet);
 

	
 
	if (length)
 
		yahoo_add_to_send_queue(yid, image, length);
 
}
 

	
 
void yahoo_webcam_accept_viewer(int id, const char *who, int accept)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
 
	char *packet = NULL;
 
	char *data = NULL;
 
	unsigned char header_len = 13;
 
	unsigned int pos = 0;
 
	unsigned int len = 0;
 

	
 
	if (!yid)
 
		return;
 

	
 
	data = strdup("u=");
 
	data = y_string_append(data, (char *)who);
 
	data = y_string_append(data, "\r\n");
 
	len = strlen(data);
 

	
 
	packet = y_new0(char, header_len + len);
 
	packet[pos++] = header_len;
 
	packet[pos++] = 0;
 
	packet[pos++] = 5;	/* version byte?? */
 
	packet[pos++] = 0;
 
	pos += yahoo_put32(packet + pos, len);
 
	packet[pos++] = 0;	/* packet type */
 
	pos += yahoo_put32(packet + pos, accept);
 
	memcpy(packet + pos, data, len);
 
	FREE(data);
 
	yahoo_add_to_send_queue(yid, packet, header_len + len);
 
	FREE(packet);
 
}
 

	
 
void yahoo_webcam_invite(int id, const char *who)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_packet *pkt;
 

	
 
	if (!yid)
 
		return;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY,
 
		yid->yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 49, "WEBCAMINVITE");
 
	yahoo_packet_hash(pkt, 14, " ");
 
	yahoo_packet_hash(pkt, 13, "0");
 
	yahoo_packet_hash(pkt, 1, yid->yd->user);
 
	yahoo_packet_hash(pkt, 5, who);
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
static void yahoo_search_internal(int id, int t, const char *text, int g,
 
	int ar, int photo, int yahoo_only, int startpos, int total)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	struct yahoo_input_data *yid;
 
	char url[1024];
 
	char buff[1024];
 
	char *ctext, *p;
 

	
 
	if (!yd)
 
		return;
 

	
 
	yid = y_new0(struct yahoo_input_data, 1);
 
	yid->yd = yd;
 
	yid->type = YAHOO_CONNECTION_SEARCH;
 

	
 
	/*
 
	   age range
 
	   .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+
 
	 */
 

	
 
	snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total,
 
		startpos);
 

	
 
	ctext = strdup(text);
 
	while ((p = strchr(ctext, ' ')))
 
		*p = '+';
 

	
 
	snprintf(url, 1024,
 
		"http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s",
 
		ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "",
 
		startpos ? buff : "");
 

	
 
	FREE(ctext);
 

	
 
	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
 

	
 
	inputs = y_list_prepend(inputs, yid);
 
	yahoo_http_get(yid->yd->client_id, url, buff, 0, 0,
 
		_yahoo_http_connected, yid);
 
}
 

	
 
void yahoo_search(int id, enum yahoo_search_type t, const char *text,
 
	enum yahoo_search_gender g, enum yahoo_search_agerange ar, int photo,
 
	int yahoo_only)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_search_state *yss;
 

	
 
	if (!yid)
 
		return;
 

	
 
	if (!yid->ys)
 
		yid->ys = y_new0(struct yahoo_search_state, 1);
 

	
 
	yss = yid->ys;
 

	
 
	FREE(yss->lsearch_text);
 
	yss->lsearch_type = t;
 
	yss->lsearch_text = strdup(text);
 
	yss->lsearch_gender = g;
 
	yss->lsearch_agerange = ar;
 
	yss->lsearch_photo = photo;
 
	yss->lsearch_yahoo_only = yahoo_only;
 

	
 
	yahoo_search_internal(id, t, text, g, ar, photo, yahoo_only, 0, 0);
 
}
 

	
 
void yahoo_search_again(int id, int start)
 
{
 
	struct yahoo_input_data *yid =
 
		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	struct yahoo_search_state *yss;
 

	
 
	if (!yid || !yid->ys)
 
		return;
 

	
 
	yss = yid->ys;
 

	
 
	if (start == -1)
 
		start = yss->lsearch_nstart + yss->lsearch_nfound;
 

	
 
	yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text,
 
		yss->lsearch_gender, yss->lsearch_agerange,
 
		yss->lsearch_photo, yss->lsearch_yahoo_only,
 
		start, yss->lsearch_ntotal);
 
}
 

	
 
void yahoo_send_picture(int id, const char *name, unsigned long size,
 
	yahoo_get_fd_callback callback, void *data)
 
{
 
	/* Not Implemented */
 
}
 

	
 
/* File Transfer */
 
static YList *active_file_transfers = NULL;
 

	
 
enum {
 
	FT_STATE_HEAD = 1,
 
	FT_STATE_RECV,
 
	FT_STATE_RECV_START,
 
	FT_STATE_SEND
 
};
 

	
 
struct send_file_data {
 
	int client_id;
 
	char *id;
 
	char *who;
 
	char *filename;
 
	char *ip_addr;
 
	char *token;
 
	int size;
 

	
 
	struct yahoo_input_data *yid;
 
	int state;
 

	
 
	yahoo_get_fd_callback callback;
 
	void *data;
 
};
 

	
 
static char *yahoo_get_random(void)
 
{
 
	int i = 0;
 
	int r = 0;
 
	int c = 0;
 
	char out[25];
 

	
 
	out[24] = '\0';
 
	out[23] = '$';
 
	out[22] = '$';
 
	
 
	for (i = 0; i < 22; i++) {
 
		if(r == 0)
 
			r = rand();
 

	
 
		c = r%61;
 

	
 
		if(c<26)
 
			out[i] = c + 'a';
 
		else if (c<52)
 
			out[i] = c - 26 + 'A';
 
		else
 
			out[i] = c - 52 + '0';
 

	
 
		r /= 61;
 
	}
 

	
 
	return strdup(out);
 
}
 

	
 
static int _are_same_id(const void *sfd1, const void *id)
 
{
 
	return strcmp(((struct send_file_data *)sfd1)->id, (char *)id);
 
}
 

	
 
static int _are_same_yid(const void *sfd1, const void *yid)
 
{
 
	if(((struct send_file_data *)sfd1)->yid == yid)
 
		return 0;
 
	else
 
		return 1;
 
}
 

	
 
static struct send_file_data *yahoo_get_active_transfer(char *id)
 
{
 
	YList *l = y_list_find_custom(active_file_transfers, id,
 
		_are_same_id);
 

	
 
	if(l)
 
		return (struct send_file_data *)l->data;
 
	
 
	return NULL;
 
}
 

	
 
static struct send_file_data *yahoo_get_active_transfer_with_yid(void *yid)
 
{
 
	YList *l = y_list_find_custom(active_file_transfers, yid,
 
		_are_same_yid);
 

	
 
	if(l)
 
		return (struct send_file_data *)l->data;
 
	
 
	return NULL;
 
}
 

	
 
static void yahoo_add_active_transfer(struct send_file_data *sfd)
 
{
 
	active_file_transfers = y_list_prepend(active_file_transfers, sfd);
 
}
 

	
 
static void yahoo_remove_active_transfer(struct send_file_data *sfd)
 
{
 
	active_file_transfers = y_list_remove(active_file_transfers, sfd);
 
	free(sfd->id);
 
	free(sfd->who);
 
	free(sfd->filename);
 
	free(sfd->ip_addr);
 
	FREE(sfd);
 
}
 

	
 
static void _yahoo_ft_upload_connected(int id, void *fd, int error, void *data)
 
{
 
	struct send_file_data *sfd = data;
 
	struct yahoo_input_data *yid = sfd->yid;
 

	
 
	if (!fd) {
 
		inputs = y_list_remove(inputs, yid);
 
		FREE(yid);
 
		return;
 
	}
 

	
 
	sfd->callback(id, fd, error, sfd->data);
 

	
 
	yid->fd = fd;
 
	yid->read_tag =
 
		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
 
		YAHOO_INPUT_READ, yid);
 
}
 

	
 
static void yahoo_file_transfer_upload(struct yahoo_data *yd,
 
	struct send_file_data *sfd)
 
{
 
	char url[256];
 
	char buff[4096];
 
	char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL;
 

	
 
	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
 

	
 
	yid->yd = yd;
 
	yid->type = YAHOO_CONNECTION_FT;
 

	
 
	inputs = y_list_prepend(inputs, yid);
 
	sfd->yid = yid;
 
	sfd->state = FT_STATE_SEND;
 

	
 
	token_enc = yahoo_urlencode(sfd->token);
 
	sender_enc = yahoo_urlencode(yd->user);
 
	recv_enc = yahoo_urlencode(sfd->who);
 

	
 
	snprintf(url, sizeof(url), 
 
		"http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr,
 
		token_enc, sender_enc, recv_enc);
 

	
 
	snprintf(buff, sizeof(buff), "T=%s; Y=%s", yd->cookie_t, yd->cookie_y);
 

	
 
	yahoo_http_post(yd->client_id, url, buff, sfd->size,
 
		_yahoo_ft_upload_connected, sfd);
 

	
 
	FREE(token_enc);
 
	FREE(sender_enc);
 
	FREE(recv_enc);
 
}
 

	
 
static void yahoo_init_ft_recv(struct yahoo_data *yd,
 
	struct send_file_data *sfd)
 
{
 
	char url[256];
 
	char buff[1024];
 
	char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL;
 

	
 
	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
 

	
 
	yid->yd = yd;
 
	yid->type = YAHOO_CONNECTION_FT;
 

	
 
	inputs = y_list_prepend(inputs, yid);
 
	sfd->yid = yid;
 
	sfd->state = FT_STATE_HEAD;
 

	
 
	token_enc = yahoo_urlencode(sfd->token);
 
	sender_enc = yahoo_urlencode(sfd->who);
 
	recv_enc = yahoo_urlencode(yd->user);
 

	
 
	snprintf(url, sizeof(url), 
 
		"http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr,
 
		token_enc, sender_enc, recv_enc);
 

	
 
	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
 

	
 
	yahoo_http_head(yid->yd->client_id, url, buff, 0, NULL,
 
		_yahoo_http_connected, yid);
 

	
 
	FREE(token_enc);
 
	FREE(sender_enc);
 
	FREE(recv_enc);
 
}
 

	
 
static void yahoo_file_transfer_accept(struct yahoo_input_data *yid,
 
	struct send_file_data *sfd)
 
{
 
	struct yahoo_packet *pkt;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERACCEPT,
 
		YPACKET_STATUS_DEFAULT, yid->yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, yid->yd->user);
 
	yahoo_packet_hash(pkt, 5, sfd->who);
 
	yahoo_packet_hash(pkt, 265, sfd->id);
 
	yahoo_packet_hash(pkt, 27, sfd->filename);
 
	yahoo_packet_hash(pkt, 249, "3");
 
	yahoo_packet_hash(pkt, 251, sfd->token);
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 

	
 
	yahoo_init_ft_recv(yid->yd, sfd);
 
}
 

	
 
static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	YList *l;
 
	struct send_file_data *sfd;
 
	char *who = NULL;
 
	char *filename = NULL;
 
	char *id = NULL;
 
	char *token = NULL;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		switch (pair->key) {
 
		case 4:
 
			who = pair->value;
 
			break;
 
		case 5:
 
			/* Me... don't care */
 
			break;
 
		case 249:
 
			break;
 
		case 265:
 
			id = pair->value;
 
			break;
 
		case 251:
 
			token = pair->value;
 
			break;
 
		case 27:
 
			filename = pair->value;
 
			break;
 
		}
 
	}
 

	
 
	sfd = yahoo_get_active_transfer(id);
 

	
 
	if (sfd) {
 
		sfd->token = strdup(token);
 

	
 
		yahoo_file_transfer_upload(yid->yd, sfd);
 
	}
 
	else {
 
		YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
 
			(yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN,
 
			sfd->data);
 

	
 
		yahoo_remove_active_transfer(sfd);
 
	}
 
}
 

	
 
static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	YList *l;
 
	char *who = NULL;
 
	char *filename = NULL;
 
	char *id = NULL;
 
	char *token = NULL;
 
	char *ip_addr = NULL;
 

	
 
	struct send_file_data *sfd;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		switch (pair->key) {
 
		case 1:
 
		case 4:
 
			who = pair->value;
 
			break;
 
		case 5:
 
			/* Me... don't care */
 
			break;
 
		case 249:
 
			break;
 
		case 265:
 
			id = pair->value;
 
			break;
 
		case 250:
 
			ip_addr = pair->value;
 
			break;
 
		case 251:
 
			token = pair->value;
 
			break;
 
		case 27:
 
			filename = pair->value;
 
			break;
 
		}
 
	}
 

	
 
	sfd = yahoo_get_active_transfer(id);
 

	
 
	if (sfd) {
 
		sfd->token = strdup(token);
 
		sfd->ip_addr = strdup(ip_addr);
 

	
 
		yahoo_file_transfer_accept(yid, sfd);
 
	}
 
	else {
 
		YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
 
			(yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN,
 
			sfd->data);
 

	
 
		yahoo_remove_active_transfer(sfd);
 
	}
 
}
 

	
 
static void yahoo_send_filetransferinfo(struct yahoo_data *yd,
 
	struct send_file_data *sfd)
 
{
 
	struct yahoo_input_data *yid;
 
	struct yahoo_packet *pkt;
 

	
 
	yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER);
 
	sfd->ip_addr = YAHOO_CALLBACK(ext_yahoo_get_ip_addr)("relay.yahoo.com");
 

	
 
	if (!sfd->ip_addr) {
 
		YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
 
			(yd->client_id, YAHOO_FILE_TRANSFER_RELAY, sfd->data);
 

	
 
		yahoo_remove_active_transfer(sfd);
 

	
 
		return;
 
	}
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERINFO,
 
		YPACKET_STATUS_DEFAULT, yd->session_id);
 

	
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 5, sfd->who);
 
	yahoo_packet_hash(pkt, 265, sfd->id);
 
	yahoo_packet_hash(pkt, 27, sfd->filename);
 
	yahoo_packet_hash(pkt, 249, "3");
 
	yahoo_packet_hash(pkt, 250, sfd->ip_addr);
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
static void yahoo_process_filetransfer(struct yahoo_input_data *yid,
 
	struct yahoo_packet *pkt)
 
{
 
	YList *l;
 
	char *who = NULL;
 
	char *filename = NULL;
 
	char *msg = NULL;
 
	char *id = NULL;
 
	int action = 0;
 
	int size = 0;
 
	struct yahoo_data *yd = yid->yd;
 

	
 
	struct send_file_data *sfd;
 

	
 
	for (l = pkt->hash; l; l = l->next) {
 
		struct yahoo_pair *pair = l->data;
 
		switch (pair->key) {
 
		case 4:
 
			who = pair->value;
 
			break;
 
		case 5:
 
			/* Me... don't care */
 
			break;
 
		case 222:
 
			action = atoi(pair->value);
 
			break;
 
		case 265:
 
			id = pair->value;
 
			break;
 
		case 266: /* Don't know */
 
			break;
 
		case 302: /* Start Data? */
 
			break;
 
		case 300:
 
			break;
 
		case 27:
 
			filename = pair->value;
 
			break;
 
		case 28:
 
			size = atoi(pair->value);
 
			break;
 
		case 14:
 
			msg = pair->value;
 
		case 301: /* End Data? */
 
			break;
 
		case 303:
 
			break;
 

	
 
		}
 
	}
 

	
 
	if (action == YAHOO_FILE_TRANSFER_INIT) {
 
		/* Received a FT request from buddy */
 
		sfd = y_new0(struct send_file_data, 1);
 
        
 
		sfd->client_id = yd->client_id;
 
		sfd->id = strdup(id);
 
		sfd->who = strdup(who);
 
		sfd->filename = strdup(filename);
 
		sfd->size = size;
 
        
 
		yahoo_add_active_transfer(sfd);
 

	
 
		YAHOO_CALLBACK(ext_yahoo_got_file) (yd->client_id, yd->user,
 
			who, msg, filename, size, sfd->id);
 
	}
 
	else {
 
		/* Response to our request */
 
		sfd = yahoo_get_active_transfer(id);
 

	
 
		if (sfd && action == YAHOO_FILE_TRANSFER_ACCEPT) {
 
			yahoo_send_filetransferinfo(yd, sfd);
 
		}
 
		else if (!sfd || action == YAHOO_FILE_TRANSFER_REJECT) {
 
			YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
 
				(yd->client_id, YAHOO_FILE_TRANSFER_REJECT,
 
				sfd->data);
 

	
 
			yahoo_remove_active_transfer(sfd);
 
		}
 
	}
 
}
 

	
 
void yahoo_send_file(int id, const char *who, const char *msg,
 
	const char *name, unsigned long size,
 
	yahoo_get_fd_callback callback, void *data)
 
{
 
	struct yahoo_packet *pkt = NULL;
 
	char size_str[10];
 
	struct yahoo_input_data *yid;
 
	struct yahoo_data *yd;
 
	struct send_file_data *sfd;
 
	
 
	yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
 
	yd = find_conn_by_id(id);
 
	sfd = y_new0(struct send_file_data, 1);
 

	
 
	sfd->client_id = id;
 
	sfd->id = yahoo_get_random();
 
	sfd->who = strdup(who);
 
	sfd->filename = strdup(name);
 
	sfd->size = size;
 
	sfd->callback = callback;
 
	sfd->data = data;
 

	
 
	yahoo_add_active_transfer(sfd);
 

	
 
	if (!yd)
 
		return;
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER,
 
		YPACKET_STATUS_DEFAULT, yd->session_id);
 

	
 
	snprintf(size_str, sizeof(size_str), "%ld", size);
 

	
 
	yahoo_packet_hash(pkt, 1, yd->user);
 
	yahoo_packet_hash(pkt, 5, who);
 
	yahoo_packet_hash(pkt, 265, sfd->id);
 
	yahoo_packet_hash(pkt, 222, "1");
 
	yahoo_packet_hash(pkt, 266, "1");
 
	yahoo_packet_hash(pkt, 302, "268");
 
	yahoo_packet_hash(pkt, 300, "268");
 
	yahoo_packet_hash(pkt, 27, name);
 
	yahoo_packet_hash(pkt, 28, size_str);
 
	yahoo_packet_hash(pkt, 301, "268");
 
	yahoo_packet_hash(pkt, 303, "268");
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 
}
 

	
 
void yahoo_send_file_transfer_response(int client_id, int response, char *id, void *data)
 
{
 
	struct yahoo_packet *pkt = NULL;
 
	char resp[2];
 
	struct yahoo_input_data *yid;
 
	
 
	struct send_file_data *sfd = yahoo_get_active_transfer(id);
 

	
 
	sfd->data = data;
 

	
 
	yid = find_input_by_id_and_type(client_id, YAHOO_CONNECTION_PAGER);
 

	
 
	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER,
 
		YPACKET_STATUS_DEFAULT, yid->yd->session_id);
 

	
 
	snprintf(resp, sizeof(resp), "%d", response);
 

	
 
	yahoo_packet_hash(pkt, 1, yid->yd->user);
 
	yahoo_packet_hash(pkt, 5, sfd->who);
 
	yahoo_packet_hash(pkt, 265, sfd->id);
 
	yahoo_packet_hash(pkt, 222, resp);
 

	
 
	yahoo_send_packet(yid, pkt, 0);
 

	
 
	yahoo_packet_free(pkt);
 

	
 
	if(response == YAHOO_FILE_TRANSFER_REJECT)
 
		yahoo_remove_active_transfer(sfd);
 
}
 

	
 
static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over)
 
{
 
	struct send_file_data *sfd;
 
	struct yahoo_data *yd = yid->yd;
 
	
 
	sfd = yahoo_get_active_transfer_with_yid(yid);
 

	
 
	if (!sfd) {
 
		LOG(("Something funny happened. yid %p has no sfd.\n", yid));
 
		return;
 
	}
 

	
 
	/* 
 
	 * We want to handle only the complete data with HEAD since we don't
 
	 * want a situation where both the GET and HEAD are active.
 
	 * With SEND, we really can't do much with partial response
 
	 */
 
	if ((sfd->state == FT_STATE_HEAD || sfd->state == FT_STATE_SEND) 
 
			&& !over)
 
		return;
 

	
 
	if (sfd->state == FT_STATE_HEAD) {
 
		/* Do a GET */
 
		char url[256];
 
		char buff[1024];
 
		char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL;
 

	
 
		struct yahoo_input_data *yid_ft = 
 
			y_new0(struct yahoo_input_data, 1);
 
        
 
		yid_ft->yd = yid->yd;
 
		yid_ft->type = YAHOO_CONNECTION_FT;
 
        
 
		inputs = y_list_prepend(inputs, yid_ft);
 
		sfd->yid = yid_ft;
 
		sfd->state = FT_STATE_RECV;
 

	
 
		token_enc = yahoo_urlencode(sfd->token);
 
		sender_enc = yahoo_urlencode(sfd->who);
 
		recv_enc = yahoo_urlencode(yd->user);
 
        
 
		snprintf(url, sizeof(url), 
 
			"http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr,
 
			token_enc, sender_enc, recv_enc);
 

	
 
		snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y,
 
			yd->cookie_t);
 

	
 

	
 
		yahoo_http_get(yd->client_id, url, buff, 1, 1,
 
			_yahoo_http_connected, yid_ft);
 

	
 
		FREE(token_enc);
 
		FREE(sender_enc);
 
		FREE(recv_enc);
 
	}
 
	else if (sfd->state == FT_STATE_RECV || 
 
		sfd->state == FT_STATE_RECV_START) {
 

	
 
		unsigned char *data_begin = NULL;
 

	
 
		if (yid->rxlen == 0)
 
			yahoo_remove_active_transfer(sfd);
 

	
 
		if (sfd->state != FT_STATE_RECV_START &&
 
			(data_begin = 
 
				(unsigned char *)strstr((char *)yid->rxqueue,
 
				"\r\n\r\n"))) {
 

	
 
			sfd->state = FT_STATE_RECV_START;
 

	
 
			yid->rxlen -= 4+(data_begin-yid->rxqueue)/sizeof(char);
 
			data_begin += 4;
 

	
 
			if (yid->rxlen > 0)
 
				YAHOO_CALLBACK(ext_yahoo_got_ft_data) 
 
					(yd->client_id, data_begin,
 
					yid->rxlen, sfd->data);
 
		}
 
		else if (sfd->state == FT_STATE_RECV_START)
 
			YAHOO_CALLBACK(ext_yahoo_got_ft_data) (yd->client_id,
 
				yid->rxqueue, yid->rxlen, sfd->data);
 

	
 
		FREE(yid->rxqueue);
 
		yid->rxqueue = NULL;
 
		yid->rxlen = 0;
 
	}
 
	else if (sfd->state == FT_STATE_SEND) {
 
		/* Sent file completed */
 
		int len = 0;
 
		char *off = strstr((char *)yid->rxqueue, "Content-Length: ");
 

	
 
		if (off) {
 
			off += 16;
 
			len = atoi(off);
 
		}
 

	
 
		if (len < sfd->size)
 
			YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
 
				(yd->client_id,
 
				YAHOO_FILE_TRANSFER_FAILED, sfd->data);
 
		else
 
			YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
 
				(yd->client_id,
 
				YAHOO_FILE_TRANSFER_DONE, sfd->data);
 

	
 
		yahoo_remove_active_transfer(sfd);
 
	}
 
}
 

	
 
/* End File Transfer */
 

	
 
enum yahoo_status yahoo_current_status(int id)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	if (!yd)
 
		return YAHOO_STATUS_OFFLINE;
 
	return yd->current_status;
 
}
 

	
 
const YList *yahoo_get_buddylist(int id)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	if (!yd)
 
		return NULL;
 
	return yd->buddies;
 
}
 

	
 
const YList *yahoo_get_ignorelist(int id)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	if (!yd)
 
		return NULL;
 
	return yd->ignore;
 
}
 

	
 
const YList *yahoo_get_identities(int id)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	if (!yd)
 
		return NULL;
 
	return yd->identities;
 
}
 

	
 
const char *yahoo_get_cookie(int id, const char *which)
 
{
 
	struct yahoo_data *yd = find_conn_by_id(id);
 
	if (!yd)
 
		return NULL;
 
	if (!strncasecmp(which, "y", 1))
 
		return yd->cookie_y;
 
	if (!strncasecmp(which, "b", 1))
 
		return yd->cookie_b;
 
	if (!strncasecmp(which, "t", 1))
 
		return yd->cookie_t;
 
	if (!strncasecmp(which, "c", 1))
 
		return yd->cookie_c;
 
	if (!strncasecmp(which, "login", 5))
 
		return yd->login_cookie;
 
	return NULL;
 
}
 

	
 
const char *yahoo_get_profile_url(void)
 
{
 
	return profile_url;
 
}

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)