
/*
 * Send ICQ commands, using protocol version 5.
 *
 * mICQ Copyright (C) © 2001-2007 Rüdiger Kuhlmann
 *
 * mICQ 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; version 2 dated June, 1991.
 *
 * mICQ 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.
 *
 * In addition, as a special exception permission is granted to link the
 * code of this release of mICQ with the OpenSSL project's "OpenSSL"
 * library, and distribute the linked executables.  You must obey the GNU
 * General Public License in all respects for all of the code used other
 * than "OpenSSL".  If you modify this file, you may extend this exception
 * to your version of the file, but you are not obligated to do so.  If you
 * do not wish to do so, delete this exception statement from your version
 * of this file.
 *
 * You should have received a copy of the GNU General Public License
 * along with this package; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * $Id: oldicq_client.c,v 1.15 2007-06-10 19:32:27 kuhlmann Exp $
 */

#include "micq.h"
#include "packet.h"
#include "connection.h"
#include "contact.h"
#include "oldicq_compat.h"
#include "oldicq_client.h"
#include "oldicq_util.h"
#include "preferences.h"
#include "util.h"
#include "conv.h"
#include "im_response.h"
#include "util_ui.h"
#include "buildmark.h"
#include <assert.h>
#if HAVE_NETINET_IN_H
#include <netinet/in.h> /* for htonl, htons */
#endif

/*
 * CMD_ACK - acknowledge a received packet.
 */
void CmdPktCmdAck (Connection *conn, UDWORD seq)
{
    Packet *pak = PacketCv5 (conn, CMD_ACK);
    PacketWrite4    (pak, rand ());
    PacketWriteAt4  (pak, CMD_v5_OFF_SEQ, seq);
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_SEND_MESSAGE - send a message through ICQ.
 */
void CmdPktCmdSendMessage (Connection *conn, Contact *cont, const char *text, UDWORD type)
{
    Packet *pak;
    
    if (!cont)
        return;
    
    IMIntMsg (cont, NOW, ims_offline, INT_MSGACK_V5, text);
                    
    pak = PacketCv5 (conn, CMD_SEND_MESSAGE);
    PacketWrite4    (pak, cont->uin);
    PacketWrite2    (pak, type);
    PacketWriteLNTS (pak, c_out_to_split (text, cont));
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_TCP_REQUEST - request peer to open a TCP connection.
 */
void CmdPktCmdTCPRequest (Connection *conn, Contact *cont, UDWORD port)
{
    Packet *pak;

    if (!cont || !conn->assoc || !(conn->assoc->connect & CONNECT_OK))
        return;

    pak = PacketCv5 (conn, CMD_TCP_REQUEST);
    PacketWrite4 (pak, cont->uin);
    PacketWrite4 (pak, conn->our_local_ip);
    PacketWrite4 (pak, conn->assoc->port);
    PacketWrite1 (pak, conn->assoc->status);
    PacketWrite4 (pak, port);
    PacketWrite4 (pak, conn->assoc->port);
    PacketWrite2 (pak, 2);
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_LOGIN - start login process by sending login request.
 */
void CmdPktCmdLogin (Connection *conn)
{
    Packet *pak;
    
    if (conn->version != 5)
        return;

    if (conn->our_seq2 != 1)
    {
        conn->our_seq     = rand () & 0x3fff;
        conn->our_seq2    = 1;
        conn->our_session = rand () & 0x3fffffff;
    }
    
    assert (conn->passwd);
    assert (*conn->passwd);
    assert (strlen (conn->passwd) <= 8);
    
    pak = PacketCv5 (conn, CMD_LOGIN);
    PacketWrite4 (pak, time (NULL));
    PacketWrite4 (pak, conn->assoc && conn->assoc->connect & CONNECT_OK ?
                       conn->assoc->port : 0);
    PacketWriteLNTS (pak, c_out (conn->passwd));
    PacketWrite4 (pak, 0x000000d5);
    PacketWrite4 (pak, conn->our_local_ip);
    PacketWrite1 (pak, conn->assoc && conn->assoc->connect & CONNECT_OK ?
                       conn->assoc->status : 0);         /* 1=firewall | 2=proxy | 4=tcp */
    PacketWrite4 (pak, IcqFromStatus (prG->status));
    PacketWrite2 (pak, conn->assoc && conn->assoc->connect & CONNECT_OK ?
                       conn->assoc->version : 0);
    PacketWrite2 (pak, 0);
    PacketWrite4 (pak, 0x822c01ec);   /* 0x00d50008, 0x00780008 */
    PacketWrite4 (pak, 0x00000050);
    PacketWrite4 (pak, 0x00000003);
    PacketWrite4 (pak, BUILD_MICQ_OLD);
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_REG_NEW_USER - request a new user ID.
 */
void CmdPktCmdRegNewUser (Connection *conn, const char *pass)
{
    Packet *pak = PacketCv5 (conn, CMD_REG_NEW_USER);

    assert (strlen (pass) <= 9);

    PacketWriteAt4  (pak, CMD_v5_OFF_UIN, 0); /* no UIN */
    PacketWriteLNTS (pak, c_out (pass));
    PacketWrite4    (pak, 0xA0);
    PacketWrite4    (pak, 0x2461);
    PacketWrite4    (pak, 0xA00000);
    PacketWrite4    (pak, 0x00);
    
    if (!conn->our_session)
        PacketWriteAt4 (pak, CMD_v5_OFF_SESS, rand () & 0x3fffffff);

    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_CONTACT_LIST - send over the contact list.
 */
void CmdPktCmdContactList (Connection *conn)
{
    ContactGroup *cg = conn->contacts;
    Contact *cont;
    Packet *pak = NULL;
    UWORD pbytes = 0;
    int i, j;

    for (i = j = 0; (cont = ContactIndex (cg, j)); j++)
    {
        if (!pak)
        {
            pak = PacketCv5 (conn, CMD_CONTACT_LIST);
            pbytes = PacketWritePos (pak);
            PacketWrite1 (pak, 0);
        }
        PacketWrite4 (pak, cont->uin);
        i++;

        if (i < MAX_CONTS_PACKET)
            continue;

        PacketWriteAt1 (pak, pbytes, i);
        PacketEnqueuev5 (pak, conn);
        pak = NULL;
    }
    if (pak)
    {
        PacketWriteAt1 (pak, pbytes, i);
        PacketEnqueuev5 (pak, conn);
    }
}

/*
 * CMD_SEARCH_UIN - (not implemented)
 */

/*
 * CMD_SEARCH_USER - search for a user.
 */
void CmdPktCmdSearchUser (Connection *conn, const char *email, const char *nick,
                          const char *first, const char *last)
{
    Packet *pak = PacketCv5 (conn, CMD_SEARCH_USER);
    PacketWriteLNTS (pak, c_out (nick));
    PacketWriteLNTS (pak, c_out (first));
    PacketWriteLNTS (pak, c_out (last));
    PacketWriteLNTS (pak, c_out (email));
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_KEEP_ALIVE, CMD_KEEP_ALIVE2 - send keep alive packets.
 */
void CmdPktCmdKeepAlive (Connection *conn)
{
    Packet *pak = PacketCv5 (conn, CMD_KEEP_ALIVE);
    PacketWrite4 (pak, rand ());
    PacketEnqueuev5 (pak, conn);
#if 1
    pak = PacketCv5 (conn, CMD_KEEP_ALIVE2);
    PacketWrite4 (pak, rand ());
    PacketEnqueuev5 (pak, conn);
#endif
}

/*
 * CMD_SEND_TEXT_CODE - sends a text-based command to server.
 */
void CmdPktCmdSendTextCode (Connection *conn, const char *text)
{
    Packet *pak = PacketCv5 (conn, CMD_SEND_TEXT_CODE);
    PacketWriteLNTS (pak, c_out (text));
    PacketWrite1    (pak, 5);
    PacketWrite1    (pak, 0);
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_ACK_MESSAGES - acknowledge the receipt of all offline messages.
 */
void CmdPktCmdAckMessages (Connection *conn)
{
    Packet *pak = PacketCv5 (conn, CMD_ACK_MESSAGES);
    PacketWrite2 (pak, rand ());
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_LOGIN_1 - finishes login process.
 */
void CmdPktCmdLogin1 (Connection *conn)
{
    Packet *pak = PacketCv5 (conn, CMD_LOGIN_1);
    PacketWrite4 (pak, rand ());
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_MSG_TO_NEW_USER - (not implemented)
 */

/*
 * CMD_INFO_REQ - (not implemented)
 */

/*
 * CMD_EXT_INFO_REQ - request extended information on a user (unused)
 */
void CmdPktCmdExtInfoReq (Connection *conn, Contact *cont)
{
    Packet *pak;
    
    if (!cont)
        return;
    pak = PacketCv5 (conn, CMD_EXT_INFO_REQ);
    PacketWrite4 (pak, cont->uin);
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_CHANGE_PW - (not implemented) (obsolete)
 */

/*
 * CMD_NEW_USER_INFO - (not implemented)
 */

/*
 * CMD_UPDATE_EXT_INFO - (not implemented)
 */

/*
 * CMD_QUERY_SERVERS - (not implemented)
 */

/*
 * CMD_QUERY_ADDONS - (not implemented)
 */

/*
 * CMD_STATUS_CHANGE - change status.
 */
void CmdPktCmdStatusChange (Connection *conn, status_t status)
{
    Packet *pak = PacketCv5 (conn, CMD_STATUS_CHANGE);
    PacketWrite4 (pak, IcqFromStatus (status));
    PacketEnqueuev5 (pak, conn);

    conn->status = status;
    conn->nativestatus = 0;
}

/*
 * CMD_NEW_USER_1 - (not implemented)
 */

/*
 * CMD_UPDATE_INFO - update basic info on server. (unused)
 */
void CmdPktCmdUpdateInfo (Connection *conn, const char *email, const char *nick,
                          const char *first, const char *last, BOOL auth)
{
    Packet *pak = PacketCv5 (conn, CMD_UPDATE_INFO);
    PacketWriteLNTS (pak, c_out (nick));
    PacketWriteLNTS (pak, c_out (first));
    PacketWriteLNTS (pak, c_out (last));
    PacketWriteLNTS (pak, c_out (email));
    PacketEnqueuev5 (pak, conn);

    pak = PacketCv5 (conn, CMD_AUTH_UPDATE);
    PacketWrite4 (pak, auth);
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_AUTH_UPDATE -> see CMD_UPDATE_INFO, CMD_META_USER
 */

/*
 * CMD_KEEP_ALIVE2 -> see CMD_KEEP_ALIVE
 */

/*
 * CMD_LOGIN_2 - (not implemented) (obsolete)
 */

/*
 * CMD_ADD_TO_LIST - (not implemented)
 */

/*
 * CMD_RAND_SET - set random chat group.
 */
void CmdPktCmdRandSet (Connection *conn, UDWORD group)
{
    Packet *pak = PacketCv5 (conn, CMD_RAND_SET);
    PacketWrite4 (pak, group);
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_RAND_SEARCH - search a random user from given chat group.
 */
UDWORD CmdPktCmdRandSearch (Connection *conn, UDWORD group)
{
    Packet *pak = PacketCv5 (conn, CMD_RAND_SEARCH);
    UDWORD ref = pak->ref;
    PacketWrite4 (pak, group);
    PacketEnqueuev5 (pak, conn);
    return ref;
}

/*
 * CMD_META_USER : META_SET_GENERAL_INFO - Update general information.
 */
void CmdPktCmdMetaGeneral (Connection *conn, Contact *cont)
{
    Packet *pak = PacketCv5 (conn, CMD_META_USER);
    PacketWrite2    (pak, META_SET_GENERAL_INFO_v5);
    if (cont->meta_general)
    {
        PacketWriteLNTS (pak, c_out (cont->meta_general->nick));
        PacketWriteLNTS (pak, c_out (cont->meta_general->first));
        PacketWriteLNTS (pak, c_out (cont->meta_general->last));
        PacketWriteLNTS (pak, c_out (cont->meta_general->email));
        PacketWriteLNTS (pak, cont->meta_email ? c_out (cont->meta_email->text) : "");
        PacketWriteLNTS (pak, cont->meta_email && cont->meta_email->next ? c_out (cont->meta_email->next->text) : "");
        PacketWriteLNTS (pak, c_out (cont->meta_general->city));
        PacketWriteLNTS (pak, c_out (cont->meta_general->state));
        PacketWriteLNTS (pak, c_out (cont->meta_general->phone));
        PacketWriteLNTS (pak, c_out (cont->meta_general->fax));
        PacketWriteLNTS (pak, c_out (cont->meta_general->street));
        PacketWriteLNTS (pak, c_out (cont->meta_general->cellular));
        PacketWrite4    (pak, atoi (cont->meta_general->zip));
        PacketWrite2    (pak, cont->meta_general->country);
        PacketWrite1    (pak, cont->meta_general->tz);
        PacketWrite1    (pak, !cont->meta_general->auth);
        PacketWrite1    (pak, cont->meta_general->webaware);
        PacketWrite1    (pak, cont->meta_general->hideip);
    }
    else
    {
        PacketWriteLNTS (pak, c_out (cont->nick));
        PacketWriteLNTS (pak, "");
        PacketWriteLNTS (pak, "<unset>");
        PacketWriteLNTS (pak, "<unset>");
        PacketWriteLNTS (pak, "");
        PacketWriteLNTS (pak, "");
        PacketWriteLNTS (pak, "");
        PacketWriteLNTS (pak, "");
        PacketWriteLNTS (pak, "");
        PacketWriteLNTS (pak, "");
        PacketWriteLNTS (pak, "");
        PacketWriteLNTS (pak, "");
        PacketWrite4    (pak, 0);
        PacketWrite2    (pak, 0);
        PacketWrite1    (pak, 0);
        PacketWrite1    (pak, 0);
        PacketWrite1    (pak, 0);
        PacketWrite1    (pak, 0);
    }
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_META_USER : META_SET_WORK_INFO - (not implemented)
 */

/*
 * CMD_META_USER : META_SET_MORE_INFO - Update additional information.
 */
void CmdPktCmdMetaMore (Connection *conn, Contact *cont)
{
    Packet *pak = PacketCv5 (conn, CMD_META_USER);
    PacketWrite2    (pak, META_SET_MORE_INFO);
    if (cont->meta_more)
    {
        PacketWrite2    (pak, cont->meta_more->age);
        PacketWrite1    (pak, cont->meta_more->sex);
        PacketWriteLNTS (pak, c_out (cont->meta_more->homepage));
        PacketWrite2    (pak, cont->meta_more->year);
        PacketWrite2    (pak, cont->meta_more->month);
        PacketWrite2    (pak, cont->meta_more->day);
        PacketWrite2    (pak, cont->meta_more->lang1);
        PacketWrite2    (pak, cont->meta_more->lang2);
        PacketWrite2    (pak, cont->meta_more->lang3);
    }
    else
    {
        PacketWrite2    (pak, 0);
        PacketWrite1    (pak, 0);
        PacketWriteLNTS (pak, "");
        PacketWrite2    (pak, 0);
        PacketWrite2    (pak, 0);
        PacketWrite2    (pak, 0);
        PacketWrite2    (pak, 0);
        PacketWrite2    (pak, 0);
        PacketWrite2    (pak, 0);
    }
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_META_USER : META_SET_ABOUT_INFO - Set "about" information.
 */
void CmdPktCmdMetaAbout (Connection *conn, const char *about)
{
    Packet *pak = PacketCv5 (conn, CMD_META_USER);
    PacketWrite2    (pak, META_SET_ABOUT_INFO);
    PacketWriteLNTS (pak, c_out (about));
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_META_USER : META_INFO_SECURE - (not implemented)
 */

/*
 * CMD_META_USER : META_SET_PASS - set a new password.
 */
void CmdPktCmdMetaPass (Connection *conn, char *pass)
{
    Packet *pak = PacketCv5 (conn, CMD_META_USER);

    assert (strlen (pass) <= 9);

    PacketWrite2    (pak, META_SET_PASS);
    PacketWriteLNTS (pak, c_out (pass));
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_META_USER : META_REQ_INFO - request information on user.
 */
UDWORD CmdPktCmdMetaReqInfo (Connection *conn, Contact *cont)
{
    Packet *pak = PacketCv5 (conn, CMD_META_USER);
    UDWORD ref = pak->ref;
    PacketWrite2 (pak, META_REQ_INFO_v5);
    PacketWrite4 (pak, cont->uin);
    PacketEnqueuev5 (pak, conn);
    return ref;
}

/*
 * CMD_META_USER : META_SEARCH_WP - do an extensive white page search.
 */
void CmdPktCmdMetaSearchWP (Connection *conn, MetaWP *user)
{
    Packet *pak = PacketCv5 (conn, CMD_META_USER);
    PacketWrite2    (pak, META_SEARCH_WP);
    PacketWriteLNTS (pak, c_out (user->first));
    PacketWriteLNTS (pak, c_out (user->last));
    PacketWriteLNTS (pak, c_out (user->nick));
    PacketWriteLNTS (pak, c_out (user->email));
    PacketWrite2    (pak, user->minage);
    PacketWrite2    (pak, user->maxage);
    PacketWrite1    (pak, user->sex);
    PacketWrite2    (pak, user->language);
    PacketWriteLNTS (pak, c_out (user->city));
    PacketWriteLNTS (pak, c_out (user->state));
    PacketWrite2    (pak, user->country);
    PacketWriteLNTS (pak, c_out (user->company));
    PacketWriteLNTS (pak, c_out (user->department));
    PacketWriteLNTS (pak, c_out (user->position));

/*  Now it gets REALLY shakey, as I don't know even what
    these particular bits of information are.
    If you know, fill them in. Pretty sure they are
    interests, organizations, homepage and something else
    but not sure what order. -KK */
    
    PacketWrite1      (pak, 0);
    PacketWrite2      (pak, 0);
    PacketWrite1      (pak, 1);
    PacketWrite4      (pak, 0);
    PacketWrite1      (pak, 1);
    PacketWrite4      (pak, 0);
    PacketWrite1      (pak, 1);
    PacketWrite4      (pak, 0);
    PacketWrite1      (pak, 1);
    PacketWrite2      (pak, 0);
    PacketWrite1      (pak, user->online);
    PacketEnqueuev5 (pak, conn);
}

/*
 * CMD_META_USER : META_SET_WEB_PRESENCE - (not implemented)
 */

/*
 * CMD_INVIS_LIST - send list of contacts that should never see you
 */
void CmdPktCmdInvisList (Connection *conn)
{
    ContactGroup *cg = conn->contacts;
    Contact *cont;
    Packet *pak = NULL;
    UWORD pbytes = 0;
    int i, j;

    for (i = j = 0; (cont = ContactIndex (cg, j)); j++)
    {
        if (!ContactPrefVal (cont, CO_HIDEFROM))
            continue;

        if (!pak)
        {
            pak = PacketCv5 (conn, CMD_INVIS_LIST);
            pbytes = PacketWritePos (pak);
            PacketWrite1 (pak, 0);
        }
        PacketWrite4 (pak, cont->uin);
        i++;

        if (i < MAX_CONTS_PACKET)
            continue;

        PacketWriteAt1 (pak, pbytes, i);
        PacketEnqueuev5 (pak, conn);
        pak = NULL;
    }
    if (pak)
    {
        PacketWriteAt1 (pak, pbytes, i);
        PacketEnqueuev5 (pak, conn);
    }
}

/*
 * CMD_VIS_LIST - send list of contacts that may see you while being invisible
 */
void CmdPktCmdVisList (Connection *conn)
{
    ContactGroup *cg = conn->contacts;
    Contact *cont;
    Packet *pak = NULL;
    UWORD pbytes = 0;
    int i, j;

    for (i = j = 0; (cont = ContactIndex (cg, j)); j++)
    {
        if (!ContactPrefVal (cont, CO_INTIMATE))
            continue;

        if (!pak)
        {
            pak = PacketCv5 (conn, CMD_INVIS_LIST);
            pbytes = PacketWritePos (pak);
            PacketWrite1 (pak, 0);
        }
        PacketWrite4 (pak, cont->uin);
        i++;

        if (i < MAX_CONTS_PACKET)
            continue;

        PacketWriteAt1 (pak, pbytes, i);
        PacketEnqueuev5 (pak, conn);
        pak = NULL;
    }
    if (pak)
    {
        PacketWriteAt1 (pak, pbytes, i);
        PacketEnqueuev5 (pak, conn);
    }
}

/*
 * CMD_UPDATE_LIST - update contact visible/invisible status
 */
void CmdPktCmdUpdateList (Connection *conn, Contact *cont, int which, BOOL add)
{
    Packet *pak;
    
    if (!cont)
        return;
    pak = PacketCv5 (conn, CMD_UPDATE_LIST);
    PacketWrite4 (pak, cont->uin);
    PacketWrite1 (pak, which);
    PacketWrite1 (pak, add);
    PacketEnqueuev5 (pak, conn);
}
