diff plugins/icq/udp.c @ 1152:201ec77f3a60

[gaim-migrate @ 1162] icq. whoop de doo committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Tue, 28 Nov 2000 02:22:42 +0000
parents
children 0a766047b4fd
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/icq/udp.c	Tue Nov 28 02:22:42 2000 +0000
@@ -0,0 +1,785 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+$Id: udp.c 1162 2000-11-28 02:22:42Z warmenhoven $
+$Log$
+Revision 1.1  2000/11/28 02:22:42  warmenhoven
+icq. whoop de doo
+
+Revision 1.23  2000/07/09 22:19:35  bills
+added new *Close functions, use *Close functions instead of *Delete
+where correct, and misc cleanup
+
+Revision 1.22  2000/07/09 18:25:44  denis
+icq_UpdateNewUserInfo() now returns seq1 instead of seq2 since it
+isn't META function.
+
+Revision 1.21  2000/06/25 16:43:19  denis
+icq_SendMetaInfoReq() was added.
+All icq_*Meta*() functions now returns sequence number 2 because their
+replies from the server are synced with it.
+
+Revision 1.20  2000/06/15 01:50:39  bills
+removed *Seq functions
+
+Revision 1.19  2000/05/10 19:06:59  denis
+UDP outgoing packet queue was implemented.
+
+Revision 1.18  2000/05/03 18:34:43  denis
+icq_UpdateNewUserInfo() was added.
+All icq_UpdateMetaInfo*() now return their sequence number.
+
+Revision 1.17  2000/04/10 16:36:04  denis
+Some more Win32 compatibility from Guillaume Rosanis <grs@mail.com>
+
+Revision 1.16  2000/04/06 19:03:07  denis
+return sequence number
+
+Revision 1.15  2000/04/06 16:36:18  denis
+So called "Online List problem" bug with Long Contact List was fixed.
+icq_*Send*Seq() functions with specified sequence number were added.
+
+Revision 1.14  2000/04/05 14:37:02  denis
+Applied patch from "Guillaume R." <grs@mail.com> for basic Win32
+compatibility.
+
+Revision 1.13  1999/12/27 11:12:35  denis
+icq_UpdateMetaInfoSecurity() added for setting "My authorization is
+required", "Web Aware" and "IP Publishing".
+
+Revision 1.12  1999/10/14 11:43:28  denis
+icq_UpdateMetaInfo* functions added.
+
+Revision 1.11  1999/10/07 18:36:27  denis
+proxy.h file removed.
+
+Revision 1.10  1999/10/04 13:36:17  denis
+Cleanups.
+
+Revision 1.9  1999/09/29 20:15:30  bills
+tcp port wasn't being sent properly in login packet
+
+Revision 1.8  1999/09/29 17:13:45  denis
+Webaware functions enabled without success even with UDP v5 - need more
+investigations.
+
+Revision 1.7  1999/07/18 20:22:16  bills
+changed to use new byte-order functions & contact list functions
+
+Revision 1.6  1999/07/16 15:46:00  denis
+Cleaned up.
+
+Revision 1.5  1999/07/16 12:40:53  denis
+ICQ UDP v5 implemented.
+Encription for ICQ UDP v5 implemented.
+icq_Packet* unified interface for UDP packets implemented.
+Multipacket support of ICQ UDP v5 support added.
+
+Revision 1.4  1999/07/12 15:13:43  cproch
+- added definition of ICQLINK to hold session-specific global variabled
+  applications which have more than one connection are now possible
+- changed nearly every function defintion to support ICQLINK parameter
+
+Revision 1.3  1999/04/29 09:40:52  denis
+Unsuccessful attempt to implement web presence (webaware) feature
+
+Revision 1.2  1999/04/14 15:04:13  denis
+Cleanups for "strict" compiling (-ansi -pedantic)
+Switched from icq_Log callback to icq_Fmt function.
+
+Revision 1.1  1999/03/24 11:37:38  denis
+Underscored files with TCP stuff renamed.
+TCP stuff cleaned up
+Function names changed to corresponding names.
+icqlib.c splitted to many small files by subject.
+C++ comments changed to ANSI C comments.
+
+*/
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#include <winsock.h>
+#endif
+
+#include <stdlib.h>
+
+#include "icqtypes.h"
+#include "icqlib.h"
+#include "udp.h"
+#include "queue.h"
+
+#include "stdpackets.h"
+#include "icqbyteorder.h"
+
+static const BYTE icq_UDPTable[] = {
+  0x59, 0x60, 0x37, 0x6B, 0x65, 0x62, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x60, 0x57, 0x5B, 0x3D,
+  0x5E, 0x34, 0x6D, 0x36, 0x50, 0x3F, 0x6F, 0x67, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x47, 0x63, 0x39,
+  0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x43, 0x69, 0x48, 0x33, 0x31, 0x64, 0x35, 0x5A, 0x4A, 0x42,
+  0x56, 0x40, 0x67, 0x53, 0x41, 0x07, 0x6C, 0x49, 0x58, 0x3B, 0x4D, 0x46, 0x68, 0x43, 0x69, 0x48,
+  0x33, 0x31, 0x44, 0x65, 0x62, 0x46, 0x48, 0x53, 0x41, 0x07, 0x6C, 0x69, 0x48, 0x33, 0x51, 0x54,
+  0x5D, 0x4E, 0x6C, 0x49, 0x38, 0x4B, 0x55, 0x4A, 0x62, 0x46, 0x48, 0x33, 0x51, 0x34, 0x6D, 0x36,
+  0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x63, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64, 0x35, 0x5A,
+  0x6A, 0x52, 0x6E, 0x3C, 0x51, 0x34, 0x6D, 0x36, 0x50, 0x5F, 0x5F, 0x3F, 0x4F, 0x37, 0x4B, 0x35,
+  0x5A, 0x4A, 0x62, 0x66, 0x58, 0x3B, 0x4D, 0x66, 0x58, 0x5B, 0x5D, 0x4E, 0x6C, 0x49, 0x58, 0x3B,
+  0x4D, 0x66, 0x58, 0x3B, 0x4D, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64,
+  0x55, 0x6A, 0x32, 0x3E, 0x44, 0x45, 0x52, 0x6E, 0x3C, 0x31, 0x64, 0x55, 0x6A, 0x52, 0x4E, 0x6C,
+  0x69, 0x48, 0x53, 0x61, 0x4C, 0x39, 0x30, 0x6F, 0x47, 0x63, 0x59, 0x60, 0x57, 0x5B, 0x3D, 0x3E,
+  0x64, 0x35, 0x3A, 0x3A, 0x5A, 0x6A, 0x52, 0x4E, 0x6C, 0x69, 0x48, 0x53, 0x61, 0x6C, 0x49, 0x58,
+  0x3B, 0x4D, 0x46, 0x68, 0x63, 0x39, 0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x67, 0x53, 0x41, 0x25, 0x41,
+  0x3C, 0x51, 0x54, 0x3D, 0x5E, 0x54, 0x5D, 0x4E, 0x4C, 0x39, 0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F,
+  0x47, 0x43, 0x69, 0x48, 0x33, 0x51, 0x54, 0x5D, 0x6E, 0x3C, 0x31, 0x64, 0x35, 0x5A, 0x00, 0x00,
+};
+
+void icq_UDPCheckCode(icq_Packet *p)
+{
+  DWORD num1, num2;
+  DWORD r1,r2;
+
+  num1 = p->data[8];
+  num1 <<= 8;
+  num1 += p->data[4];
+  num1 <<= 8;
+  num1 += p->data[2];
+  num1 <<= 8;
+  num1 += p->data[6];
+
+  r1 = 0x18 + (rand() % (p->length - 0x18));
+  r2 = rand() & 0xff;
+
+  num2 = r1;
+  num2 <<= 8;
+  num2 += p->data[r1];
+  num2 <<= 8;
+  num2 += r2;   
+  num2 <<= 8;
+  num2 += icq_UDPTable[r2];
+  num2 ^= 0xFF00FF;
+
+  icq_PacketGoto(p, 0x14);
+  icq_PacketAppend32(p, num1 ^ num2);
+}
+
+DWORD icq_UDPScramble(DWORD cc)
+{
+  DWORD a[5];
+
+  a[0] = cc & 0x0000001F;
+  a[1] = cc & 0x03E003E0;
+  a[2] = cc & 0xF8000400;
+  a[3] = cc & 0x0000F800;
+  a[4] = cc & 0x041F0000;
+
+  a[0] <<= 0x0C;
+  a[1] <<= 0x01;
+  a[2] >>= 0x0A;
+  a[3] <<= 0x10;
+  a[4] >>= 0x0F;
+
+  return a[0] + a[1] + a[2] + a[3] + a[4];
+}
+
+void icq_UDPEncode(icq_Packet *p)
+{
+  DWORD checkcode;
+  DWORD code1, code2, code3;
+  DWORD pos;
+  DWORD data;
+
+  icq_UDPCheckCode(p);
+  icq_PacketGoto(p, 20);
+  checkcode = icq_PacketRead32(p);
+  code1 = p->length * 0x68656c6cL;
+  code2 = code1 + checkcode;
+  pos = 0x0A;
+
+  for(; pos < p->length; pos+=4)
+  {
+    code3 = code2 + icq_UDPTable[pos & 0xFF];
+    data = icqtohl(*(DWORD *)((p->data)+pos));
+    data ^= code3;
+    *(DWORD*)((p->data)+pos)=htoicql(data);
+  }
+  checkcode = icq_UDPScramble(checkcode);
+  *(DWORD *)((p->data)+0x14)=htoicql(checkcode);
+}
+
+/*********************************************************
+icq_UDPSockWrite and icq_UDPSockRead are for _UDP_ packets
+proxy support for TCP sockets is different!
+*********************************************************/
+int icq_UDPSockWriteDirect(ICQLINK *link, icq_Packet *p)
+{
+  char tmpbuf[ICQ_PACKET_DATA_SIZE];
+
+  if(link->icq_UDPSok <= 3)
+  {
+    icq_FmtLog(link, ICQ_LOG_ERROR, "Bad socket!\n");
+    return -1;
+  }
+
+  icq_UDPEncode(p);
+  if(!link->icq_UseProxy)
+  {
+#ifdef _WIN32
+    return send(link->icq_UDPSok, p->data, p->length, 0);
+#else
+    return write(link->icq_UDPSok, p->data, p->length);
+#endif
+  }
+  else
+  {
+    tmpbuf[0] = 0; /* reserved */
+    tmpbuf[1] = 0; /* reserved */
+    tmpbuf[2] = 0; /* standalone packet */
+    tmpbuf[3] = 1; /* address type IP v4 */
+    *(unsigned long*)&tmpbuf[4] = htonl(link->icq_ProxyDestIP);
+    *(unsigned short*)&tmpbuf[8] = htons(link->icq_ProxyDestPort);
+    memcpy(&tmpbuf[10], p->data, p->length);
+#ifdef _WIN32
+    return send(link->icq_UDPSok, tmpbuf, p->length+10, 0)-10;
+#else
+    return write(link->icq_UDPSok, tmpbuf, p->length+10)-10;
+#endif
+  }
+}
+
+int icq_UDPSockWrite(ICQLINK *link, icq_Packet *p)
+{
+  icq_Packet *qp;
+  WORD cmd = icq_PacketReadUDPOutCmd(p);
+  if(cmd != UDP_CMD_ACK && cmd != UDP_CMD_SEND_TEXT_CODE)
+  {
+    qp = (icq_Packet*)malloc(sizeof(icq_Packet));
+    memcpy(qp, p, sizeof(icq_Packet));
+    icq_UDPQueuePut(link, qp, 1);
+    if(link->icq_SetTimeout)
+      link->icq_SetTimeout(link, icq_UDPQueueInterval(link));
+  }
+  return icq_UDPSockWriteDirect(link, p);
+}
+
+int icq_UDPSockRead(ICQLINK *link, icq_Packet *p)
+{
+  int res;
+  char tmpbuf[ICQ_PACKET_DATA_SIZE];
+
+  if(!link->icq_UseProxy)
+  {
+#ifdef _WIN32
+    res = recv(link->icq_UDPSok, p->data, ICQ_PACKET_DATA_SIZE, 0);
+#else
+    res = read(link->icq_UDPSok, p->data, ICQ_PACKET_DATA_SIZE);
+#endif
+    p->length = res;
+    return res;
+  }
+  else
+  {
+#ifdef _WIN32
+    res = recv(link->icq_UDPSok, tmpbuf, ICQ_PACKET_DATA_SIZE, 0);
+#else
+    res = read(link->icq_UDPSok, tmpbuf, ICQ_PACKET_DATA_SIZE);
+#endif
+    if(res<0)
+      return res;
+    memcpy(p->data, &tmpbuf[10], res-10);
+    p->length = res-10;
+    return res-10;
+  }
+}
+
+void icq_HandleTimeout(ICQLINK *link)
+{
+  icq_UDPQueueItem *ptr = 0;
+  icq_Packet *sp = 0, *pack = 0;
+  int attempt;
+  while(icq_UDPQueueInterval(link) == 0)
+  {
+    ptr = (icq_UDPQueueItem*)list_first(link->icq_UDPQueue);
+    attempt = ptr->attempts + 1;
+    if(attempt > 6)
+    {
+      icq_Disconnect(link);
+      if(link->icq_Disconnected)
+        link->icq_Disconnected(link);
+      return;
+    }
+    pack = icq_UDPQueueGet(link);
+    sp = (icq_Packet*)malloc(sizeof(icq_Packet));
+    memcpy(sp, pack, sizeof(icq_Packet));
+    icq_UDPQueuePut(link, pack, attempt);
+    if(link->icq_SetTimeout)
+      link->icq_SetTimeout(link, icq_UDPQueueInterval(link));
+    icq_UDPSockWriteDirect(link, sp);
+    icq_PacketDelete(sp);
+  }
+}
+
+/****************************************
+This must be called every 2 min.
+so the server knows we're still alive.
+JAVA client sends two different commands
+so we do also :)
+*****************************************/
+WORD icq_KeepAlive(ICQLINK *link) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdSeqPacket(link, UDP_CMD_KEEP_ALIVE, link->icq_UDPSeqNum1++);
+  icq_PacketAppend32(p, rand());
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+
+/*  icq_Packet *p = icq_UDPCreateStdPacket(UDP_CMD_KEEP_ALIVE);
+  icq_UDPSockWrite(icq_UDPSok, p);
+  icq_PacketDelete(p);*/
+/*  p = icq_UDPCreateStdPacket(UDP_CMD_KEEP_ALIVE2);
+  icq_UDPSockWrite(icq_Sok, p);
+  icq_PacketDelete(p);*/
+
+  icq_FmtLog(link, ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
+
+  return link->icq_UDPSeqNum1-1;
+}
+
+/**********************************
+This must be called to remove
+messages from the server
+***********************************/
+void icq_SendGotMessages(ICQLINK *link) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_ACK_MESSAGES);
+  icq_PacketAppend32(p, rand());
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+}
+
+/*************************************
+this sends over the contact list
+*************************************/
+void icq_SendContactList(ICQLINK *link) /* V5 */
+{
+  char num_used;
+  icq_ContactItem *ptr = icq_ContactGetFirst(link);
+
+  while(ptr)
+  {
+    icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_CONT_LIST);
+
+    num_used = 0;
+    icq_PacketAdvance(p,1);
+    while(ptr && num_used<64)
+    {
+      icq_PacketAppend32(p, ptr->uin);
+      num_used++;
+      ptr = icq_ContactGetNext(ptr);
+    }
+    icq_PacketGotoUDPOutData(p, 0);
+    icq_PacketAppend8(p, num_used);
+    icq_UDPSockWrite(link, p);
+    icq_PacketDelete(p);
+  }
+}
+
+void icq_SendNewUser(ICQLINK *link, unsigned long uin) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_ADD_TO_LIST);
+  icq_PacketAppend32(p, uin);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+}
+
+/*************************************
+this sends over the visible list
+that allows certain users to see you
+if you're invisible.
+*************************************/
+void icq_SendVisibleList(ICQLINK *link) /* V5 */
+{
+  char num_used;
+  icq_ContactItem *ptr = icq_ContactGetFirst(link);
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_VIS_LIST);
+
+  num_used = 0;
+  icq_PacketAdvance(p,1);
+  while(ptr)
+  {
+    if(ptr->vis_list)
+    {
+      icq_PacketAppend32(p, ptr->uin);
+      num_used++;
+    }
+    ptr = icq_ContactGetNext(ptr);
+  }
+  if(num_used != 0)
+  {
+    icq_PacketGotoUDPOutData(p, 0);
+    icq_PacketAppend8(p, num_used);
+    icq_UDPSockWrite(link, p);
+  }
+  icq_PacketDelete(p);
+}
+
+void icq_SendInvisibleList(ICQLINK *link) /* V5 */
+{
+  char num_used;
+  icq_ContactItem *ptr = icq_ContactGetFirst(link);
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_INVIS_LIST);
+
+  num_used = 0;
+  icq_PacketAdvance(p,1);
+  while(ptr)
+  {
+    if(ptr->vis_list)
+    {
+      icq_PacketAppend32(p, ptr->uin);
+      num_used++;
+    }
+    ptr = icq_ContactGetNext(ptr);
+  }
+  if(num_used != 0)
+  {
+    icq_PacketGotoUDPOutData(p, 0);
+    icq_PacketAppend8(p, num_used);
+    icq_UDPSockWrite(link, p);
+  }
+  icq_PacketDelete(p);
+}
+
+/**************************************
+This sends the second login command
+this is necessary to finish logging in.
+***************************************/
+void icq_SendLogin1(ICQLINK *link) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_LOGIN_1);
+  icq_PacketAppend32(p, rand());
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+}
+
+/************************************
+This procedure logins into the server with icq_Uin and pass
+on the socket icq_Sok and gives our ip and port.
+It does NOT wait for any kind of a response.
+*************************************/
+void icq_Login(ICQLINK *link, DWORD status) /* V5 */
+{
+  icq_Packet *p;
+
+  memset(link->icq_UDPServMess, FALSE, sizeof(link->icq_UDPServMess));
+  link->icq_UDPSession = rand() & 0x3FFFFFFF;
+  link->icq_UDPSeqNum1 = rand() & 0x7FFF;
+  link->icq_UDPSeqNum2 = 1;
+
+  p = icq_UDPCreateStdPacket(link, UDP_CMD_LOGIN);
+  icq_PacketAppend32(p, time(0L));
+  icq_PacketAppend32n(p, link->icq_TCPSrvPort);
+  /*icq_PacketAppend16(p, 0);
+  icq_PacketAppend16n(p, htons(link->icq_OurPort));*/
+  icq_PacketAppendString(p, link->icq_Password);
+  icq_PacketAppend32(p, LOGIN_X1_DEF);
+  icq_PacketAppend32n(p, htonl(link->icq_OurIP));
+  if(link->icq_UseProxy)
+    icq_PacketAppend8(p, LOGIN_SNDONLY_TCP);
+  else
+    icq_PacketAppend8(p, LOGIN_SNDRCV_TCP);
+  icq_PacketAppend32(p, status);
+  icq_PacketAppend32(p, LOGIN_X3_DEF);
+  icq_PacketAppend32(p, LOGIN_X4_DEF);
+  icq_PacketAppend32(p, LOGIN_X5_DEF);
+
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+}
+
+/**********************
+Logs off ICQ
+***********************/
+void icq_Logout(ICQLINK *link) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdSeqPacket(link, UDP_CMD_SEND_TEXT_CODE, link->icq_UDPSeqNum1++);
+  icq_PacketAppendString(p, "B_USER_DISCONNECTED");
+  icq_PacketAppend8(p, 5);
+  icq_PacketAppend8(p, 0);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+}
+
+/*******************************
+This routine sends the aknowlegement cmd to the
+server it appears that this must be done after
+everything the server sends us
+*******************************/
+void icq_UDPAck(ICQLINK *link, int seq) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdSeqPacket(link, UDP_CMD_ACK, seq);
+  icq_PacketAppend32(p, rand());
+
+  icq_FmtLog(link, ICQ_LOG_MESSAGE, "Acking\n");
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+}
+
+/***************************************************
+Sends a message thru the server to uin.  Text is the
+message to send.
+***************************************************/
+WORD icq_UDPSendMessage(ICQLINK *link, DWORD uin, const char *text) /* V5 */
+{
+  char buf[512]; /* message may be only 450 bytes long */
+  icq_Packet *p;
+
+  strncpy(buf, text, 512);
+  icq_RusConv("kw", buf);
+
+  p = icq_UDPCreateStdPacket(link, UDP_CMD_SEND_THRU_SRV);
+  icq_PacketAppend32(p, uin);
+  icq_PacketAppend16(p, TYPE_MSG);
+  icq_PacketAppendString(p, buf);
+
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum1-1;
+}
+
+WORD icq_UDPSendURL(ICQLINK *link, DWORD uin, const char *url, const char *descr) /* V5 */
+{
+  char buf1[512], buf2[512];
+  icq_Packet *p;
+
+  strncpy(buf1, descr, 512);
+  icq_RusConv("kw", buf1);
+  strncpy(buf2, url, 512);
+
+  p = icq_UDPCreateStdPacket(link, UDP_CMD_SEND_THRU_SRV);
+  icq_PacketAppend32(p, uin);
+  icq_PacketAppend16(p, TYPE_URL);
+  icq_PacketAppend16(p, strlen(buf1)+strlen(buf2)+2); /* length + the NULL + 0xFE delimiter */
+  icq_PacketAppendStringFE(p, buf1);
+  icq_PacketAppendString0(p, buf2);
+
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum1-1;
+}
+
+/**************************************************
+Sends a authorization to the server so the Mirabilis
+client can add the user.
+***************************************************/
+WORD icq_SendAuthMsg(ICQLINK *link, DWORD uin) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_SEND_THRU_SRV);
+  icq_PacketAppend32(p, uin);
+  icq_PacketAppend32(p, TYPE_AUTH);
+  icq_PacketAppend16(p, 0);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+
+  return link->icq_UDPSeqNum1-1;
+}
+
+/**************************************************
+Changes the users status on the server
+***************************************************/
+void icq_ChangeStatus(ICQLINK *link, DWORD status) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_STATUS_CHANGE);
+  icq_PacketAppend32(p, status);
+  link->icq_Status = status;
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+}
+
+/********************************************************
+Sends a request to the server for info on a specific user
+*********************************************************/
+WORD icq_SendInfoReq(ICQLINK *link, DWORD uin) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_INFO_REQ);
+  icq_PacketAppend32(p, uin);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum1-1;
+}
+
+/********************************************************
+Sends a request to the server for info on a specific user
+*********************************************************/
+WORD icq_SendExtInfoReq(ICQLINK *link, DWORD uin) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_EXT_INFO_REQ);
+  icq_PacketAppend32(p, uin);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum1-1;
+}
+
+/**************************************************************
+Initializes a server search for the information specified
+***************************************************************/
+void icq_SendSearchReq(ICQLINK *link, const char *email, const char *nick, const char *first,
+                       const char *last) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_SEARCH_USER);
+  icq_PacketAppendString(p, nick);
+  icq_PacketAppendString(p, first);
+  icq_PacketAppendString(p, last);
+  icq_PacketAppendString(p, email);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+}
+
+/**************************************************************
+Initializes a server search for the information specified
+***************************************************************/
+void icq_SendSearchUINReq(ICQLINK *link, DWORD uin) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_SEARCH_UIN);
+  icq_PacketAppend32(p, uin);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+}
+
+/**************************************************
+Registers a new uin in the ICQ network
+***************************************************/
+void icq_RegNewUser(ICQLINK *link, const char *pass) /* V5 */
+{
+  char pass8[9];
+  icq_Packet *p = icq_UDPCreateStdSeqPacket(link, UDP_CMD_REG_NEW_USER, link->icq_UDPSeqNum1++);
+  strncpy(pass8, pass, 8);
+  icq_PacketAppendString(p, pass8);
+  icq_PacketAppend32(p, 0xA0);
+  icq_PacketAppend32(p, 0x2461);
+  icq_PacketAppend32(p, 0xA00000);
+  icq_PacketAppend32(p, 0x00);
+  icq_PacketGoto(p, 6);
+  icq_PacketAppend32(p, 0);
+  icq_PacketAppend32(p, rand());
+  icq_UDPSockWrite(link, p);
+  icq_FmtLog(link, ICQ_LOG_MESSAGE, "Send RegNewUser packet to the server\n");
+  icq_PacketDelete(p);
+}
+
+WORD icq_UpdateUserInfo(ICQLINK *link, const char *nick, const char *first, const char *last,
+                        const char *email) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_UPDATE_INFO);
+  icq_PacketAppendString(p, nick);
+  icq_PacketAppendString(p, first);
+  icq_PacketAppendString(p, last);
+  icq_PacketAppendString(p, email);
+/* auth (byte)? */
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum1-1;
+}
+
+WORD icq_UpdateAuthInfo(ICQLINK *link, DWORD auth) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_UPDATE_AUTH);
+  icq_PacketAppend32(p, auth); /* NOT auth? */
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum1-1;
+}
+
+WORD icq_UpdateMetaInfoSet(ICQLINK *link, const char *nick, const char *first, const char *last,
+                           const char *email, const char *email2, const char *email3,
+                           const char *city, const char *state, const char *phone, const char *fax,
+                           const char *street, const char *cellular, unsigned long zip,
+                           unsigned short cnt_code, unsigned char cnt_stat, unsigned char emailhide)
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_META_USER);
+  icq_PacketAppend16(p, META_CMD_SET_INFO);
+  icq_PacketAppendString(p, nick);
+  icq_PacketAppendString(p, first);
+  icq_PacketAppendString(p, last);
+  icq_PacketAppendString(p, email);
+  icq_PacketAppendString(p, email2);
+  icq_PacketAppendString(p, email3);
+  icq_PacketAppendString(p, city);
+  icq_PacketAppendString(p, state);
+  icq_PacketAppendString(p, phone);
+  icq_PacketAppendString(p, fax);
+  icq_PacketAppendString(p, street);
+  icq_PacketAppendString(p, cellular);
+  icq_PacketAppend32(p, zip);
+  icq_PacketAppend16(p, cnt_code);
+  icq_PacketAppend8(p, cnt_stat);
+  icq_PacketAppend8(p, emailhide);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum2-1;
+}
+
+WORD icq_UpdateMetaInfoHomepage(ICQLINK *link, unsigned char age, const char *homepage,
+                                unsigned char year, unsigned char month, unsigned char day,
+                                unsigned char lang1, unsigned char lang2, unsigned char lang3)
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_META_USER);
+  icq_PacketAppend16(p, META_CMD_SET_HOMEPAGE);
+  icq_PacketAppend8(p, age);
+  icq_PacketAppend16(p, 0x0200);
+  icq_PacketAppendString(p, homepage);
+  icq_PacketAppend8(p, year);
+  icq_PacketAppend8(p, month);
+  icq_PacketAppend8(p, day);
+  icq_PacketAppend8(p, 0xFF /* lang1 */);
+  icq_PacketAppend8(p, 0xFF /* lang2 */);
+  icq_PacketAppend8(p, 0xFF /* lang3 */);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum2-1;
+}
+
+WORD icq_UpdateMetaInfoAbout(ICQLINK *link, const char *about)
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_META_USER);
+  icq_PacketAppend16(p, META_CMD_SET_ABOUT);
+  icq_PacketAppendString(p, about);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum2-1;
+}
+
+WORD icq_UpdateMetaInfoSecurity(ICQLINK *link, unsigned char reqauth, unsigned char webpresence,
+                                unsigned char pubip)
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_META_USER);
+  icq_PacketAppend16(p, META_CMD_SET_SECURE);
+  icq_PacketAppend8(p, !reqauth);
+  icq_PacketAppend8(p, webpresence);
+  icq_PacketAppend8(p, pubip);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum2-1;
+}
+
+WORD icq_UpdateNewUserInfo(ICQLINK *link, const char *nick, const char *first, const char *last,
+                           const char *email) /* V5 */
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_NEW_USER_INFO);
+  icq_PacketAppendString(p, nick);
+  icq_PacketAppendString(p, first);
+  icq_PacketAppendString(p, last);
+  icq_PacketAppendString(p, email);
+  icq_PacketAppend8(p, 1);
+  icq_PacketAppend8(p, 1);
+  icq_PacketAppend8(p, 1);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum1-1;
+}
+
+WORD icq_SendMetaInfoReq(ICQLINK *link, unsigned long uin)
+{
+  icq_Packet *p = icq_UDPCreateStdPacket(link, UDP_CMD_META_USER);
+  icq_PacketAppend16(p, META_CMD_REQ_INFO);
+  icq_PacketAppend32(p, uin);
+  icq_UDPSockWrite(link, p);
+  icq_PacketDelete(p);
+  return link->icq_UDPSeqNum2-1;
+}