summaryrefslogtreecommitdiff
path: root/icqj_mod/chan_04close.c
blob: e7636af8d3e64815622b385f7b1a68c21f47e2f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
// ---------------------------------------------------------------------------80
//                ICQ plugin for Miranda Instant Messenger
//                ________________________________________
//
// Copyright © 2000,2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede
// Copyright © 2001,2002 Jon Keating, Richard Hughes
// Copyright © 2002,2003,2004 Martin Öberg, Sam Kothari, Robert Rainwater
// Copyright © 2004,2005,2006 Joe Kucera
//
// 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.
//
// -----------------------------------------------------------------------------
//
// File name      : $Source: /cvsroot/miranda/miranda/protocols/IcqOscarJ/chan_04close.c,v $
// Revision       : $Revision: 3316 $
// Last change on : $Date: 2006-07-12 20:26:25 +0400 (Ср, 12 июл 2006) $
// Last change by : $Author: jokusoftware $
//
// DESCRIPTION:
//
//  Describe me here please...
//
// -----------------------------------------------------------------------------

#include "icqoscar.h"


extern HANDLE hServerConn;
extern HANDLE hServerPacketRecver;

static void handleMigration(serverthread_info *info);
static int connectNewServer(serverthread_info *info);
static void handleRuntimeError(WORD wError);
static void handleSignonError(WORD wError);

static void NetLib_SafeCloseHandle(HANDLE *hConnection);


void handleCloseChannel(unsigned char *buf, WORD datalen, serverthread_info *info)
{
  oscar_tlv_chain* chain = NULL;
  WORD wError;


  // Parse server reply, prepare reconnection
  if (!info->bLoggedIn && datalen && !info->newServerReady)
    handleLoginReply(buf, datalen, info);

  if (info->isMigrating)
    handleMigration(info);

  if (!info->bLoggedIn && info->newServerReady)
  {
    if (!connectNewServer(info))
    { // Connecting failed
      if (info->isMigrating)
        icq_LogUsingErrorCode(LOG_ERROR, GetLastError(), "Unable to connect to migrated ICQ communication server");
      else
        icq_LogUsingErrorCode(LOG_ERROR, GetLastError(), "Unable to connect to ICQ communication server");

      info->isMigrating = 0;
    }
    info->newServerReady = 0;

    return;
  }

  if (chain = readIntoTLVChain(&buf, datalen, 0))
  {
    // TLV 9 errors (runtime errors?)
    wError = getWordFromChain(chain, 0x09, 1);
    if (wError)
    {
      SetCurrentStatus(ID_STATUS_OFFLINE);

      handleRuntimeError(wError);
    }

    disposeChain(&chain);
  }
  // Server closed connection on error, or sign off
  NetLib_SafeCloseHandle(&hServerConn);
}



static int IsGatewayModeActive()
{
  return ICQGetContactSettingByte(NULL, "UseGateway", 0) && ICQGetContactSettingByte(NULL, "NLUseProxy", 0);
}



static void NetLib_SafeCloseHandle(HANDLE *hConnection)
{
  if (*hConnection) Netlib_MyCloseHandle(*hConnection);

  *hConnection = NULL;
}



void handleLoginReply(unsigned char *buf, WORD datalen, serverthread_info *info)
{
  oscar_tlv_chain* chain = NULL;
  WORD wError;

  icq_sendCloseConnection(); // imitate icq5 behaviour

  if (!(chain = readIntoTLVChain(&buf, datalen, 0)))
  {
    NetLog_Server("Error: Missing chain on close channel");
    NetLib_SafeCloseHandle(&hServerConn);
    return; // Invalid data
  }

  // TLV 8 errors (signon errors?)
  wError = getWordFromChain(chain, 0x08, 1);
  if (wError)
  {
    handleSignonError(wError);

    // we return only if the server did not gave us cookie (possible to connect with soft error)
    if (!getLenFromChain(chain, 0x06, 1)) 
    {
      disposeChain(&chain);
      NetLib_SafeCloseHandle(&hServerConn);
      return; // Failure
    }
  }

  // We are in the login phase and no errors were reported.
  // Extract communication server info.
  info->newServer = getStrFromChain(chain, 0x05, 1);
  info->cookieData = getStrFromChain(chain, 0x06, 1);
  info->cookieDataLen = getLenFromChain(chain, 0x06, 1);

  // We dont need this anymore
  disposeChain(&chain);

  if (!info->newServer || !info->cookieData)
  {
    icq_LogMessage(LOG_FATAL, "You could not sign on because the server returned invalid data. Try again.");

    SAFE_FREE(&info->newServer);
    SAFE_FREE(&info->cookieData);
    info->cookieDataLen = 0;

    NetLib_SafeCloseHandle(&hServerConn);
    return; // Failure
  }

  NetLog_Server("Authenticated.");
  info->newServerReady = 1;

  return;
}



static int connectNewServer(serverthread_info *info)
{
  WORD servport;
  NETLIBOPENCONNECTION nloc = {0};
  int res = 0;

  if (!IsGatewayModeActive())
  { // close connection only if not in gateway mode
    Netlib_CloseHandle(hServerConn);
    hServerConn = NULL;
  }

  /* Get the ip and port */
  parseServerAddress(info->newServer, &servport);

  nloc.cbSize = sizeof(nloc);
  nloc.flags = 0;
  nloc.szHost = info->newServer;
  nloc.wPort = servport;

  if (!IsGatewayModeActive())
  {
    { /* Time to release packet receiver, connection already closed */
      Netlib_CloseHandle(hServerPacketRecver);
      hServerPacketRecver = NULL; // clear the variable

      NetLog_Server("Closed connection to login server");
    }

    NetLog_Server("Connecting to %s", info->newServer);
    hServerConn = NetLib_OpenConnection(ghServerNetlibUser, &nloc);
    if (hServerConn)
    { /* Time to recreate the packet receiver */
      hServerPacketRecver = (HANDLE)CallService(MS_NETLIB_CREATEPACKETRECVER, (WPARAM)hServerConn, 8192);
      if (!hServerPacketRecver)
      {
        NetLog_Server("Error: Failed to create packet receiver.");
      }
      else // we need to reset receiving structs
      {
        info->bReinitRecver = 1;
        res = 1;
      }
    }
  }
  else
  { // TODO: We should really do some checks here
    NetLog_Server("Walking in Gateway to %s", info->newServer);
    // TODO: This REQUIRES more work (most probably some kind of mid-netlib module)
    icq_httpGatewayWalkTo(hServerConn, &nloc);
    res = 1;
  }
  if (!res) SAFE_FREE(&info->cookieData);

  // Free allocated memory
  // NOTE: "cookie" will get freed when we have connected to the communication server.
  SAFE_FREE(&info->newServer);

  return res;
}



static void handleMigration(serverthread_info *info)
{
  // Check the data that was saved when the migration was announced
  NetLog_Server("Migrating to %s", info->newServer);
  if (!info->newServer || !info->cookieData)
  {
    icq_LogMessage(LOG_FATAL, "You have been disconnected from the ICQ network because the current server shut down.");

    SAFE_FREE(&info->newServer);
    SAFE_FREE(&info->cookieData);
    info->newServerReady = 0;
    info->isMigrating = 0;
  }
}



static void handleSignonError(WORD wError)
{
  switch (wError)
  {

  case 0x01: // Unregistered uin
  case 0x04: // Incorrect uin or password
  case 0x05: // Mismatch uin or password
  case 0x06: // Internal Client error (bad input to authorizer)
  case 0x07: // Invalid account
    ICQBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD);
    icq_LogFatalParam("Connection failed.\nYour ICQ number or password was rejected (%d).", wError);
    break;

  case 0x02: // Service temporarily unavailable
  case 0x0D: // Bad database status
  case 0x10: // Service temporarily offline
  case 0x14: // Reservation map error
  case 0x15: // Reservation link error
  case 0x1A: // Reservation timeout
    icq_LogFatalParam("Connection failed.\nThe server is temporarily unavailable (%d).", wError);
    break;

  case 0x16: // The users num connected from this IP has reached the maximum
  case 0x17: // The users num connected from this IP has reached the maximum (reserved)
    icq_LogFatalParam("Connection failed.\nServer has too many connections from your IP (%d).", wError);
    break;

  case 0x18: // Rate limit exceeded (reserved)
  case 0x1D: // Rate limit exceeded
    icq_LogFatalParam("Connection failed.\nYou have connected too quickly,\nplease wait and retry 10 to 20 minutes later (%d).", wError);
    break;

  case 0x1B: // You are using an older version of ICQ. Upgrade required
    icq_LogMessage(LOG_FATAL, "Connection failed.\nThe server did not accept this client version.");
    break;

  case 0x1C: // You are using an older version of ICQ. Upgrade recommended
    icq_LogMessage(LOG_WARNING, "The server sent warning, this version is getting old.\nTry to look for a new one.");
    break;

  case 0x1E: // Can't register on the ICQ network
    icq_LogMessage(LOG_FATAL, "Connection failed.\nYou were rejected by the server for an unknown reason.\nThis can happen if the UIN is already connected.");
    break;

  case 0x0C: // Invalid database fields, MD5 login not supported
    icq_LogMessage(LOG_FATAL, "Connection failed.\nSecure (MD5) login is not supported on this account.");
    break;

  case 0:
    break;

  case 0x08: // Deleted account
  case 0x09: // Expired account
  case 0x0A: // No access to database
  case 0x0B: // No access to resolver
  case 0x0E: // Bad resolver status
  case 0x11: // Suspended account
  case 0x19: // User too heavily warned
  case 0x22: // Account suspended due to your age
  case 0x2A: // Blocked account

  default:
    icq_LogFatalParam("Connection failed.\nUnknown error during sign on: 0x%02x", wError);
    break;
  }
}



static void handleRuntimeError(WORD wError)
{
  switch (wError)
  {

  case 0x01:
  {
    ICQBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_OTHERLOCATION);
    icq_LogMessage(LOG_FATAL, "You have been disconnected from the ICQ network because you logged on from another location using the same ICQ number.");
    break;
  }

  default:
    icq_LogFatalParam("Unknown runtime error: 0x%02x", wError);
    break;
  }
}