1 /*
2
3 client_entry.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2001 - 2014 Pekka Riikonen
8
9 The contents of this file are subject to one of the Licenses specified
10 in the COPYING file; You may not use this file except in compliance
11 with the License.
12
13 The software distributed under the License is distributed on an "AS IS"
14 basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15 KIND, either expressed or implied. See the COPYING file for more
16 information.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************ Client Searching Locally **************************/
26
27 /* Finds entry for client by the client's ID. Returns the entry or NULL
28 if the entry was not found. */
29
silc_client_get_client_by_id(SilcClient client,SilcClientConnection conn,SilcClientID * client_id)30 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
31 SilcClientConnection conn,
32 SilcClientID *client_id)
33 {
34 SilcIDCacheEntry id_cache;
35 SilcClientEntry client_entry;
36
37 if (!client || !conn || !client_id)
38 return NULL;
39
40 SILC_LOG_DEBUG(("Finding client by ID (%s)",
41 silc_id_render(client_id, SILC_ID_CLIENT)));
42
43 silc_mutex_lock(conn->internal->lock);
44
45 /* Find ID from cache */
46 if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
47 &id_cache)) {
48 silc_mutex_unlock(conn->internal->lock);
49 return NULL;
50 }
51
52 client_entry = id_cache->context;
53
54 /* Reference */
55 silc_client_ref_client(client, conn, client_entry);
56 silc_mutex_unlock(conn->internal->lock);
57
58 SILC_LOG_DEBUG(("Found"));
59
60 return client_entry;
61 }
62
63 /* Finds clients by nickname from local cache. */
64
silc_client_get_clients_local_ext(SilcClient client,SilcClientConnection conn,const char * nickname,SilcBool get_all,SilcBool get_valid)65 SilcDList silc_client_get_clients_local_ext(SilcClient client,
66 SilcClientConnection conn,
67 const char *nickname,
68 SilcBool get_all,
69 SilcBool get_valid)
70 {
71 SilcIDCacheEntry id_cache;
72 SilcList list;
73 SilcDList clients;
74 SilcClientEntry entry;
75 char nick[128 + 1], *nicknamec, *parsed = NULL, *format = NULL;
76 char server[256 + 1];
77
78 if (!client || !conn || !nickname)
79 return NULL;
80
81 /* Get nickname from nickname@server string */
82 silc_parse_userfqdn(nickname, nick, sizeof(nick), server, sizeof(server));
83
84 /* Parse nickname in case it is formatted */
85 if (!silc_client_nickname_parse(client, conn, (char *)nick, &parsed))
86 return NULL;
87
88 if (!get_all && parsed)
89 format = (char *)nick;
90 if (!parsed) {
91 parsed = silc_memdup(nick, strlen(nick));
92 if (!parsed)
93 return NULL;
94 }
95
96 SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
97
98 /* Normalize nickname for search */
99 nicknamec = silc_identifier_check(parsed, strlen(parsed),
100 SILC_STRING_UTF8, 128, NULL);
101 if (!nicknamec) {
102 silc_free(parsed);
103 return NULL;
104 }
105
106 clients = silc_dlist_init();
107 if (!clients) {
108 silc_free(nicknamec);
109 silc_free(parsed);
110 return NULL;
111 }
112
113 silc_mutex_lock(conn->internal->lock);
114
115 /* Find from cache */
116 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
117 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
118 &list)) {
119 silc_mutex_unlock(conn->internal->lock);
120 silc_free(nicknamec);
121 silc_free(parsed);
122 silc_dlist_uninit(clients);
123 return NULL;
124 }
125 silc_list_start(list);
126
127 if (!format && get_all) {
128 /* Take all without any further checking */
129 while ((id_cache = silc_list_get(list))) {
130 entry = id_cache->context;
131 if (!entry)
132 continue;
133 if (!get_valid || entry->internal.valid) {
134 silc_client_ref_client(client, conn, id_cache->context);
135 silc_dlist_add(clients, id_cache->context);
136 }
137 }
138 } else {
139 /* Check multiple cache entries for exact match */
140 while ((id_cache = silc_list_get(list))) {
141 entry = id_cache->context;
142 if (!entry)
143 continue;
144
145 /* If server was provided, find entries that either have no server
146 set or have the same server. Ignore those that have different
147 server. */
148 if (server[0] && entry->server &&
149 !silc_utf8_strcasecmp(entry->server, server))
150 continue;
151
152 if (silc_utf8_strcasecmp(entry->nickname,
153 format ? format : parsed) &&
154 (!get_valid || entry->internal.valid)) {
155 silc_client_ref_client(client, conn, entry);
156 silc_dlist_add(clients, entry);
157
158 /* If format is NULL, we find one exact match with the base
159 nickname (parsed). */
160 if (!format)
161 break;
162 }
163 }
164 }
165
166 silc_mutex_unlock(conn->internal->lock);
167
168 silc_free(nicknamec);
169 silc_free(parsed);
170
171 if (!silc_dlist_count(clients)) {
172 silc_dlist_uninit(clients);
173 return NULL;
174 }
175
176 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
177
178 silc_dlist_start(clients);
179 return clients;
180 }
181
182 /* Finds clients by nickname from local cache. */
183
silc_client_get_clients_local(SilcClient client,SilcClientConnection conn,const char * nickname,SilcBool return_all)184 SilcDList silc_client_get_clients_local(SilcClient client,
185 SilcClientConnection conn,
186 const char *nickname,
187 SilcBool return_all)
188 {
189 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
190 TRUE);
191 }
192
193 /********************** Client Resolving from Server ************************/
194
195 /* Resolving context */
196 typedef struct {
197 SilcDList clients;
198 SilcGetClientCallback completion;
199 void *context;
200 SilcClientEntry client_entry;
201 } *SilcClientGetClientInternal;
202
203 /* Resolving command callback */
204
silc_client_get_clients_cb(SilcClient client,SilcClientConnection conn,SilcCommand command,SilcStatus status,SilcStatus error,void * context,va_list ap)205 static SilcBool silc_client_get_clients_cb(SilcClient client,
206 SilcClientConnection conn,
207 SilcCommand command,
208 SilcStatus status,
209 SilcStatus error,
210 void *context,
211 va_list ap)
212 {
213 SilcClientGetClientInternal i = context;
214 SilcClientEntry client_entry;
215
216 if (error != SILC_STATUS_OK) {
217 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
218
219 if (i->client_entry) {
220 i->client_entry->internal.resolve_cmd_ident = 0;
221 silc_client_unref_client(client, conn, i->client_entry);
222 }
223
224 if (i->completion)
225 i->completion(client, conn, error, NULL, i->context);
226 goto out;
227 }
228
229 /* Add the returned client to list */
230 if (i->completion) {
231 client_entry = va_arg(ap, SilcClientEntry);
232 silc_client_ref_client(client, conn, client_entry);
233 silc_dlist_add(i->clients, client_entry);
234 client_entry->internal.resolve_cmd_ident = 0;
235 }
236
237 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
238 /* Deliver the clients to the caller */
239 if (i->completion) {
240 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
241
242 if (i->client_entry) {
243 i->client_entry->internal.resolve_cmd_ident = 0;
244 silc_client_unref_client(client, conn, i->client_entry);
245 }
246
247 silc_dlist_start(i->clients);
248 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
249 }
250 goto out;
251 }
252
253 return TRUE;
254
255 out:
256 silc_client_list_free(client, conn, i->clients);
257 silc_free(i);
258 return FALSE;
259 }
260
261 /* Resolves client information from server by the client ID. */
262
263 SilcUInt16
silc_client_get_client_by_id_resolve(SilcClient client,SilcClientConnection conn,SilcClientID * client_id,SilcBuffer attributes,SilcGetClientCallback completion,void * context)264 silc_client_get_client_by_id_resolve(SilcClient client,
265 SilcClientConnection conn,
266 SilcClientID *client_id,
267 SilcBuffer attributes,
268 SilcGetClientCallback completion,
269 void *context)
270 {
271 SilcClientGetClientInternal i;
272 SilcClientEntry client_entry;
273 SilcBuffer idp;
274 SilcUInt16 cmd_ident;
275
276 if (!client || !conn | !client_id) {
277 SILC_LOG_ERROR(("Missing arguments to "
278 "silc_client_get_clients_by_id_resolve call"));
279 return 0;
280 }
281
282 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
283 silc_id_render(client_id, SILC_ID_CLIENT)));
284
285 i = silc_calloc(1, sizeof(*i));
286 if (!i)
287 return 0;
288 i->completion = completion;
289 i->context = context;
290 i->clients = silc_dlist_init();
291 if (!i->clients) {
292 silc_free(i);
293 return 0;
294 }
295
296 /* Attach to resolving, if on going */
297 client_entry = silc_client_get_client_by_id(client, conn, client_id);
298 if (client_entry && client_entry->internal.resolve_cmd_ident) {
299 SILC_LOG_DEBUG(("Attach to existing resolving"));
300 silc_client_unref_client(client, conn, client_entry);
301 silc_client_command_pending(conn, SILC_COMMAND_NONE,
302 client_entry->internal.resolve_cmd_ident,
303 silc_client_get_clients_cb, i);
304 return client_entry->internal.resolve_cmd_ident;
305 }
306
307 /* Send the command */
308 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
309 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
310 silc_client_get_clients_cb, i,
311 2, 3, silc_buffer_datalen(attributes),
312 4, silc_buffer_datalen(idp));
313 if (!cmd_ident && completion)
314 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
315
316 if (client_entry && cmd_ident) {
317 client_entry->internal.resolve_cmd_ident = cmd_ident;
318 i->client_entry = client_entry;
319 } else {
320 silc_client_unref_client(client, conn, client_entry);
321 }
322
323 silc_buffer_free(idp);
324
325 return cmd_ident;
326 }
327
328 /* Finds client entry or entries by the `nickname' and `server'. The
329 completion callback will be called when the client entries has been
330 found. Used internally by the library. */
331
silc_client_get_clients_i(SilcClient client,SilcClientConnection conn,SilcCommand command,const char * nickname,const char * server,SilcBuffer attributes,SilcGetClientCallback completion,void * context)332 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
333 SilcClientConnection conn,
334 SilcCommand command,
335 const char *nickname,
336 const char *server,
337 SilcBuffer attributes,
338 SilcGetClientCallback completion,
339 void *context)
340 {
341 SilcClientGetClientInternal i;
342 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
343 int len;
344
345 SILC_LOG_DEBUG(("Resolve client by %s command",
346 silc_get_command_name(command)));
347
348 if (!client || !conn) {
349 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
350 return 0;
351 }
352 if (!nickname && !attributes) {
353 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
354 return 0;
355 }
356
357 /* Parse server name from the nickname if set */
358 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
359 serv, sizeof(serv)) == 2)
360 server = (const char *)serv;
361 nickname = (const char *)nick;
362
363 /* Parse nickname in case it is formatted */
364 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
365 nickname = (const char *)parsed;
366
367 i = silc_calloc(1, sizeof(*i));
368 if (!i) {
369 silc_free(parsed);
370 return 0;
371 }
372 i->clients = silc_dlist_init();
373 if (!i->clients) {
374 silc_free(parsed);
375 silc_free(i);
376 return 0;
377 }
378 i->completion = completion;
379 i->context = context;
380
381 memset(userhost, 0, sizeof(userhost));
382 if (nickname && server) {
383 len = strlen(nickname) + strlen(server) + 3;
384 silc_strncat(userhost, len, nickname, strlen(nickname));
385 silc_strncat(userhost, len, "@", 1);
386 silc_strncat(userhost, len, server, strlen(server));
387 } else if (nickname) {
388 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
389 }
390 silc_free(parsed);
391
392 /* Send the command */
393 if (command == SILC_COMMAND_IDENTIFY)
394 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
395 silc_client_get_clients_cb, i,
396 1, 1, userhost, strlen(userhost));
397 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
398 silc_client_get_clients_cb, i,
399 2, 1, userhost, strlen(userhost),
400 3, silc_buffer_datalen(attributes));
401 }
402
403 /* Get clients from server with IDENTIFY command */
404
silc_client_get_clients(SilcClient client,SilcClientConnection conn,const char * nickname,const char * server,SilcGetClientCallback completion,void * context)405 SilcUInt16 silc_client_get_clients(SilcClient client,
406 SilcClientConnection conn,
407 const char *nickname,
408 const char *server,
409 SilcGetClientCallback completion,
410 void *context)
411 {
412 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
413 nickname, server, NULL,
414 completion, context);
415 }
416
417 /* Get clients from server with WHOIS command */
418
silc_client_get_clients_whois(SilcClient client,SilcClientConnection conn,const char * nickname,const char * server,SilcBuffer attributes,SilcGetClientCallback completion,void * context)419 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
420 SilcClientConnection conn,
421 const char *nickname,
422 const char *server,
423 SilcBuffer attributes,
424 SilcGetClientCallback completion,
425 void *context)
426 {
427 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
428 nickname, server, attributes,
429 completion, context);
430 }
431
432 /* ID list resolving context */
433 typedef struct {
434 SilcGetClientCallback completion;
435 void *context;
436 SilcBuffer client_id_list;
437 SilcUInt32 list_count;
438 } *GetClientsByListInternal;
439
silc_client_get_clients_list_cb(SilcClient client,SilcClientConnection conn,SilcCommand command,SilcStatus status,SilcStatus error,void * context,va_list ap)440 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
441 SilcClientConnection conn,
442 SilcCommand command,
443 SilcStatus status,
444 SilcStatus error,
445 void *context,
446 va_list ap)
447 {
448 GetClientsByListInternal i = context;
449 SilcClientEntry client_entry;
450 SilcDList clients;
451 SilcUInt16 idp_len;
452 SilcID id;
453 int c;
454
455 /* Process the list after all replies have been received */
456 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
457 status != SILC_STATUS_LIST_END)
458 return TRUE;
459
460 SILC_LOG_DEBUG(("Resolved all clients"));
461
462 clients = silc_dlist_init();
463 if (!clients) {
464 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
465 goto out;
466 }
467
468 for (c = 0; c < i->list_count; c++) {
469 /* Get Client ID */
470 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
471 idp_len += 4;
472 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
473 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
474 goto out;
475 }
476
477 /* Get client entry */
478 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
479 if (client_entry)
480 silc_dlist_add(clients, client_entry);
481
482 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
483 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
484 goto out;
485 }
486 }
487
488 silc_dlist_start(clients);
489 status = SILC_STATUS_OK;
490 if (i->completion)
491 i->completion(client, conn, status, clients, i->context);
492
493 out:
494 if (status != SILC_STATUS_OK && i->completion)
495 i->completion(client, conn, status, NULL, i->context);
496
497 silc_client_list_free(client, conn, clients);
498 silc_buffer_free(i->client_id_list);
499 silc_free(i);
500
501 return FALSE;
502 }
503
504 /* Gets client entries by the list of client ID's `client_id_list'. This
505 always resolves those client ID's it does not know yet from the server
506 so this function might take a while. The `client_id_list' is a list
507 of ID Payloads added one after other. JOIN command reply and USERS
508 command reply for example returns this sort of list. The `completion'
509 will be called after the entries are available. */
510
silc_client_get_clients_by_list(SilcClient client,SilcClientConnection conn,SilcUInt32 list_count,SilcBuffer client_id_list,SilcGetClientCallback completion,void * context)511 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
512 SilcClientConnection conn,
513 SilcUInt32 list_count,
514 SilcBuffer client_id_list,
515 SilcGetClientCallback completion,
516 void *context)
517 {
518 GetClientsByListInternal in;
519 SilcClientEntry entry;
520 unsigned char **res_argv = NULL;
521 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
522 SilcUInt16 idp_len, cmd_ident;
523 SilcID id;
524 va_list tmp;
525 int i;
526
527 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
528
529 if (!client || !conn || !client_id_list)
530 return 0;
531
532 in = silc_calloc(1, sizeof(*in));
533 if (!in)
534 return 0;
535 in->completion = completion;
536 in->context = context;
537 in->list_count = list_count;
538 in->client_id_list = silc_buffer_copy(client_id_list);
539 if (!in->client_id_list)
540 goto err;
541
542 for (i = 0; i < list_count; i++) {
543 /* Get Client ID */
544 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
545 idp_len += 4;
546 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
547 goto err;
548
549 /* Check if we have this client cached already. If we don't have the
550 entry or it has incomplete info, then resolve it from the server. */
551 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
552 if (!entry || !entry->nickname[0] || !entry->username[0] ||
553 !entry->realname) {
554 if (!res_argv) {
555 res_argv = silc_calloc(list_count, sizeof(*res_argv));
556 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
557 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
558 if (!res_argv || !res_argv_lens || !res_argv_types) {
559 silc_client_unref_client(client, conn, entry);
560 goto err;
561 }
562 }
563
564 res_argv[res_argc] = client_id_list->data;
565 res_argv_lens[res_argc] = idp_len;
566 res_argv_types[res_argc] = res_argc + 4;
567 res_argc++;
568 }
569 silc_client_unref_client(client, conn, entry);
570
571 if (!silc_buffer_pull(client_id_list, idp_len))
572 goto err;
573 }
574 silc_buffer_start(client_id_list);
575
576 /* Query the unknown client information from server */
577 if (res_argc) {
578 cmd_ident = silc_client_command_send_argv(client,
579 conn, SILC_COMMAND_WHOIS,
580 silc_client_get_clients_list_cb,
581 in, res_argc, res_argv,
582 res_argv_lens,
583 res_argv_types);
584 silc_free(res_argv);
585 silc_free(res_argv_lens);
586 silc_free(res_argv_types);
587 return cmd_ident;
588 }
589
590 /* We have the clients in cache, get them and call the completion */
591 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
592 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
593 return 0;
594
595 err:
596 silc_buffer_free(in->client_id_list);
597 silc_free(in);
598 silc_free(res_argv);
599 silc_free(res_argv_lens);
600 silc_free(res_argv_types);
601 return 0;
602 }
603
604 #if 0
605 typedef struct {
606 SilcClient client;
607 SilcClientConnection conn;
608 SilcChannelID channel_id;
609 SilcGetClientCallback completion;
610 void *context;
611 int res_count;
612 } *GetClientsByChannelInternal;
613
614 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
615 {
616 GetClientsByChannelInternal i = context;
617 SilcClientEntry *clients = NULL;
618 SilcUInt32 clients_count = 0;
619 SilcBool found = FALSE;
620 SilcChannelEntry channel;
621 SilcHashTableList htl;
622 SilcChannelUser chu;
623
624 if (i->res_count) {
625 i->res_count--;
626 if (i->res_count)
627 return;
628 }
629
630 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
631 if (channel && !silc_hash_table_count(channel->user_list)) {
632 clients = silc_calloc(silc_hash_table_count(channel->user_list),
633 sizeof(*clients));
634 silc_hash_table_list(channel->user_list, &htl);
635 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
636 clients[clients_count++] = chu->client;
637 silc_hash_table_list_reset(&htl);
638 found = TRUE;
639 }
640
641 if (found) {
642 i->completion(i->client, i->conn, clients, clients_count, i->context);
643 silc_free(clients);
644 } else {
645 i->completion(i->client, i->conn, NULL, 0, i->context);
646 }
647
648 silc_free(i);
649 }
650
651 /* Gets client entries by the channel entry indicated by `channel'. Thus,
652 it resolves the clients currently on that channel. */
653
654 void silc_client_get_clients_by_channel(SilcClient client,
655 SilcClientConnection conn,
656 SilcChannelEntry channel,
657 SilcGetClientCallback completion,
658 void *context)
659 {
660 GetClientsByChannelInternal in;
661 SilcHashTableList htl;
662 SilcChannelUser chu;
663 SilcClientEntry entry;
664 unsigned char **res_argv = NULL;
665 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
666 SilcBuffer idp;
667 SilcBool wait_res = FALSE;
668
669 assert(client && conn && channel);
670
671 SILC_LOG_DEBUG(("Start"));
672
673 in = silc_calloc(1, sizeof(*in));
674 in->client = client;
675 in->conn = conn;
676 in->channel_id = *channel->id;
677 in->completion = completion;
678 in->context = context;
679
680 /* If user list does not exist, send USERS command. */
681 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
682 SILC_LOG_DEBUG(("Sending USERS"));
683 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
684 silc_client_command_reply_users_i, 0,
685 ++conn->cmd_ident);
686 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
687 conn->cmd_ident, 1, 2, channel->channel_name,
688 strlen(channel->channel_name));
689 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
690 silc_client_command_get_clients_by_channel_cb,
691 in);
692 return;
693 }
694
695 silc_hash_table_list(channel->user_list, &htl);
696 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
697 entry = chu->client;
698
699 /* If the entry has incomplete info, then resolve it from the server. */
700 if (!entry->nickname[0] || !entry->realname) {
701 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
702 /* Attach to this resolving and wait until it finishes */
703 silc_client_command_pending(
704 conn, SILC_COMMAND_NONE,
705 entry->resolve_cmd_ident,
706 silc_client_command_get_clients_by_channel_cb,
707 (void *)in);
708 wait_res = TRUE;
709 in->res_count++;
710 continue;
711 }
712 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
713 entry->resolve_cmd_ident = conn->cmd_ident + 1;
714
715 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
716
717 /* No we don't have it, query it from the server. Assemble argument
718 table that will be sent for the WHOIS command later. */
719 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
720 (res_argc + 1));
721 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
722 (res_argc + 1));
723 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
724 (res_argc + 1));
725 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
726 res_argv_lens[res_argc] = idp->len;
727 res_argv_types[res_argc] = res_argc + 4;
728 res_argc++;
729
730 silc_buffer_free(idp);
731 }
732 }
733 silc_hash_table_list_reset(&htl);
734
735 /* Query the client information from server if the list included clients
736 that we don't know about. */
737 if (res_argc) {
738 SilcBuffer res_cmd;
739
740 /* Send the WHOIS command to server */
741 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
742 res_argc, res_argv, res_argv_lens,
743 res_argv_types, ++conn->cmd_ident);
744 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
745 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
746 TRUE);
747
748 /* Register our own command reply for this command */
749 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
750 silc_client_command_reply_whois_i, 0,
751 conn->cmd_ident);
752
753 /* Process the applications request after reply has been received */
754 silc_client_command_pending(
755 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
756 silc_client_command_get_clients_by_channel_cb,
757 (void *)in);
758 in->res_count++;
759
760 silc_buffer_free(res_cmd);
761 silc_free(res_argv);
762 silc_free(res_argv_lens);
763 silc_free(res_argv_types);
764 return;
765 }
766
767 if (wait_res)
768 return;
769
770 /* We have the clients in cache, get them and call the completion */
771 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
772 }
773 #endif /* 0 */
774
775
776 /************************** Client Entry Routines ***************************/
777
778 /* Creates new client entry and adds it to the ID cache. Returns pointer
779 to the new entry. */
780
silc_client_add_client(SilcClient client,SilcClientConnection conn,char * nickname,char * username,char * userinfo,SilcClientID * id,SilcUInt32 mode)781 SilcClientEntry silc_client_add_client(SilcClient client,
782 SilcClientConnection conn,
783 char *nickname, char *username,
784 char *userinfo, SilcClientID *id,
785 SilcUInt32 mode)
786 {
787 SilcClientEntry client_entry;
788 char *nick = NULL, parsed[128 + 1];
789
790 SILC_LOG_DEBUG(("Adding new client entry"));
791
792 /* Save the client infos */
793 client_entry = silc_calloc(1, sizeof(*client_entry));
794 if (!client_entry)
795 return NULL;
796
797 silc_rwlock_alloc(&client_entry->internal.lock);
798 silc_atomic_init32(&client_entry->internal.refcnt, 0);
799 silc_atomic_init32(&client_entry->internal.deleted, 1);
800 client_entry->id = *id;
801 client_entry->mode = mode;
802 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
803
804 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
805 client_entry->server, sizeof(client_entry->server));
806 if (nickname && client->internal->params->full_nicknames)
807 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
808 "%s", nickname);
809 else if (nickname)
810 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
811 "%s", parsed);
812
813 silc_parse_userfqdn(username, client_entry->username,
814 sizeof(client_entry->username),
815 client_entry->hostname,
816 sizeof(client_entry->hostname));
817
818 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
819 NULL, NULL, NULL, TRUE);
820 if (!client_entry->channels) {
821 silc_free(client_entry->realname);
822 silc_atomic_uninit32(&client_entry->internal.deleted);
823 silc_atomic_uninit32(&client_entry->internal.refcnt);
824 silc_rwlock_free(client_entry->internal.lock);
825 silc_free(client_entry);
826 return NULL;
827 }
828
829 /* Normalize nickname */
830 if (client_entry->nickname[0]) {
831 nick = silc_identifier_check(parsed, strlen(parsed),
832 SILC_STRING_UTF8, 128, NULL);
833 if (!nick) {
834 silc_hash_table_free(client_entry->channels);
835 silc_free(client_entry->realname);
836 silc_atomic_uninit32(&client_entry->internal.deleted);
837 silc_atomic_uninit32(&client_entry->internal.refcnt);
838 silc_rwlock_free(client_entry->internal.lock);
839 silc_free(client_entry);
840 return NULL;
841 }
842 }
843
844 silc_mutex_lock(conn->internal->lock);
845
846 /* Add client to cache, the normalized nickname is saved to cache */
847 if (!silc_idcache_add(conn->internal->client_cache, nick,
848 &client_entry->id, client_entry)) {
849 silc_free(nick);
850 silc_hash_table_free(client_entry->channels);
851 silc_free(client_entry->realname);
852 silc_atomic_uninit32(&client_entry->internal.deleted);
853 silc_atomic_uninit32(&client_entry->internal.refcnt);
854 silc_rwlock_free(client_entry->internal.lock);
855 silc_free(client_entry);
856 silc_mutex_unlock(conn->internal->lock);
857 return NULL;
858 }
859
860 client_entry->nickname_normalized = nick;
861
862 silc_mutex_unlock(conn->internal->lock);
863 silc_client_ref_client(client, conn, client_entry);
864
865 /* Format the nickname */
866 silc_client_nickname_format(client, conn, client_entry, FALSE);
867
868 if (client_entry->nickname[0])
869 client_entry->internal.valid = TRUE;
870
871 SILC_LOG_DEBUG(("Added %p", client_entry));
872
873 return client_entry;
874 }
875
876 /* Updates the `client_entry' with the new information sent as argument.
877 This handles entry locking internally. */
878
silc_client_update_client(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,const char * nickname,const char * username,const char * userinfo,SilcUInt32 mode)879 void silc_client_update_client(SilcClient client,
880 SilcClientConnection conn,
881 SilcClientEntry client_entry,
882 const char *nickname,
883 const char *username,
884 const char *userinfo,
885 SilcUInt32 mode)
886 {
887 char *nick = NULL, parsed[128 + 1];
888
889 SILC_LOG_DEBUG(("Update client entry"));
890
891 silc_rwlock_wrlock(client_entry->internal.lock);
892
893 if (!client_entry->realname && userinfo)
894 client_entry->realname = strdup(userinfo);
895
896 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
897 silc_parse_userfqdn(username, client_entry->username,
898 sizeof(client_entry->username),
899 client_entry->hostname,
900 sizeof(client_entry->username));
901
902 if (!client_entry->nickname[0] && nickname) {
903 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
904 client_entry->server, sizeof(client_entry->server));
905 if (client->internal->params->full_nicknames)
906 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
907 "%s", nickname);
908 else
909 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
910 "%s", parsed);
911
912 /* Normalize nickname */
913 nick = silc_identifier_check(parsed, strlen(parsed),
914 SILC_STRING_UTF8, 128, NULL);
915 if (!nick) {
916 silc_rwlock_unlock(client_entry->internal.lock);
917 return;
918 }
919
920 /* Format nickname */
921 silc_client_nickname_format(client, conn, client_entry,
922 client_entry == conn->local_entry);
923
924 /* Update cache entry */
925 silc_mutex_lock(conn->internal->lock);
926 silc_idcache_update_by_context(conn->internal->client_cache,
927 client_entry, NULL, nick, TRUE);
928 silc_mutex_unlock(conn->internal->lock);
929 client_entry->nickname_normalized = nick;
930 client_entry->internal.valid = TRUE;
931 }
932 client_entry->mode = mode;
933
934 silc_rwlock_unlock(client_entry->internal.lock);
935 }
936
937 /* Change a client's nickname. Must be called with `client_entry' locked. */
938
silc_client_change_nickname(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,const char * new_nick,SilcClientID * new_id,const unsigned char * idp,SilcUInt32 idp_len)939 SilcBool silc_client_change_nickname(SilcClient client,
940 SilcClientConnection conn,
941 SilcClientEntry client_entry,
942 const char *new_nick,
943 SilcClientID *new_id,
944 const unsigned char *idp,
945 SilcUInt32 idp_len)
946 {
947 char *tmp;
948
949 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
950 new_nick));
951
952 /* Normalize nickname */
953 tmp = silc_identifier_check(new_nick, strlen(new_nick),
954 SILC_STRING_UTF8, 128, NULL);
955 if (!tmp)
956 return FALSE;
957
958 /* Update the client entry */
959 silc_mutex_lock(conn->internal->lock);
960 if (!silc_idcache_update_by_context(conn->internal->client_cache,
961 client_entry, new_id, tmp, TRUE)) {
962 silc_free(tmp);
963 silc_mutex_unlock(conn->internal->lock);
964 return FALSE;
965 }
966 silc_mutex_unlock(conn->internal->lock);
967
968 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
969 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
970 client_entry->nickname_normalized = tmp;
971 silc_client_nickname_format(client, conn, client_entry,
972 client_entry == conn->local_entry);
973
974 /* For my client entry, update ID and set new ID to packet stream */
975 if (client_entry == conn->local_entry) {
976 if (idp && idp_len) {
977 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
978 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
979 }
980 if (new_id)
981 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
982 0, NULL);
983 }
984
985 client_entry->internal.valid = TRUE;
986 return TRUE;
987 }
988
989 /* Deletes the client entry and frees all memory. */
990
silc_client_del_client_entry(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry)991 void silc_client_del_client_entry(SilcClient client,
992 SilcClientConnection conn,
993 SilcClientEntry client_entry)
994 {
995 silc_free(client_entry->realname);
996 silc_free(client_entry->nickname_normalized);
997 silc_free(client_entry->internal.key);
998 if (client_entry->public_key)
999 silc_pkcs_public_key_free(client_entry->public_key);
1000 silc_hash_table_free(client_entry->channels);
1001 if (client_entry->internal.send_key)
1002 silc_cipher_free(client_entry->internal.send_key);
1003 if (client_entry->internal.receive_key)
1004 silc_cipher_free(client_entry->internal.receive_key);
1005 if (client_entry->internal.hmac_send)
1006 silc_hmac_free(client_entry->internal.hmac_send);
1007 if (client_entry->internal.hmac_receive)
1008 silc_hmac_free(client_entry->internal.hmac_receive);
1009 silc_client_ftp_session_free_client(client, client_entry);
1010 if (client_entry->internal.op)
1011 silc_async_abort(client_entry->internal.op, NULL, NULL);
1012 client_entry->internal.op = NULL;
1013 if (client_entry->internal.ke)
1014 silc_client_abort_key_agreement(client, conn, client_entry);
1015 silc_atomic_uninit32(&client_entry->internal.deleted);
1016 silc_atomic_uninit32(&client_entry->internal.refcnt);
1017 silc_rwlock_free(client_entry->internal.lock);
1018 silc_free(client_entry);
1019 }
1020
1021 /* Removes client from the cache by the client entry. */
1022
silc_client_del_client(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry)1023 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1024 SilcClientEntry client_entry)
1025 {
1026 if (!client_entry)
1027 return FALSE;
1028
1029 SILC_LOG_DEBUG(("Marking client entry %p deleted", client_entry));
1030
1031 if (silc_atomic_sub_int32(&client_entry->internal.deleted, 1) != 0) {
1032 SILC_LOG_DEBUG(("Client entry %p already marked deleted", client_entry));
1033 return FALSE;
1034 }
1035
1036 /* Abort ongoing operation */
1037 if (client_entry->internal.op) {
1038 SILC_LOG_DEBUG(("Aborting ongoing operation %p",
1039 client_entry->internal.op));
1040 silc_async_abort(client_entry->internal.op, NULL, NULL);
1041 client_entry->internal.op = NULL;
1042 }
1043
1044 silc_client_unref_client(client, conn, client_entry);
1045 return TRUE;
1046 }
1047
1048 /* Internal routine used to find client by ID and if not found this creates
1049 new client entry and returns it. */
1050
silc_client_get_client(SilcClient client,SilcClientConnection conn,SilcClientID * client_id)1051 SilcClientEntry silc_client_get_client(SilcClient client,
1052 SilcClientConnection conn,
1053 SilcClientID *client_id)
1054 {
1055 SilcClientEntry client_entry;
1056
1057 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1058 if (!client_entry) {
1059 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1060 client_id, 0);
1061 if (!client_entry)
1062 return NULL;
1063 silc_client_ref_client(client, conn, client_entry);
1064 }
1065
1066 return client_entry;
1067 }
1068
1069 /* Lock client */
1070
silc_client_lock_client(SilcClientEntry client_entry)1071 void silc_client_lock_client(SilcClientEntry client_entry)
1072 {
1073 silc_rwlock_rdlock(client_entry->internal.lock);
1074 }
1075
1076 /* Unlock client */
1077
silc_client_unlock_client(SilcClientEntry client_entry)1078 void silc_client_unlock_client(SilcClientEntry client_entry)
1079 {
1080 silc_rwlock_unlock(client_entry->internal.lock);
1081 }
1082
1083 /* Take reference of client entry */
1084
silc_client_ref_client(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry)1085 SilcClientEntry silc_client_ref_client(SilcClient client,
1086 SilcClientConnection conn,
1087 SilcClientEntry client_entry)
1088 {
1089 silc_atomic_add_int32(&client_entry->internal.refcnt, 1);
1090 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1091 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1,
1092 silc_atomic_get_int32(&client_entry->internal.refcnt)));
1093 return client_entry;
1094 }
1095
1096 /* Release reference of client entry */
1097
silc_client_unref_client(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry)1098 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1099 SilcClientEntry client_entry)
1100 {
1101 SilcBool ret;
1102
1103 if (!client_entry)
1104 return;
1105
1106 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1107 silc_atomic_get_int32(&client_entry->internal.refcnt),
1108 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1));
1109
1110 if (silc_atomic_sub_int32(&client_entry->internal.refcnt, 1) > 0)
1111 return;
1112
1113 SILC_LOG_DEBUG(("Deleting client %p (%d)", client_entry,
1114 silc_atomic_get_int32(&client_entry->internal.deleted)));
1115
1116 silc_mutex_lock(conn->internal->lock);
1117 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1118 client_entry, NULL);
1119 silc_mutex_unlock(conn->internal->lock);
1120
1121 if (ret) {
1122 /* Remove from channels */
1123 silc_client_remove_from_channels(client, conn, client_entry);
1124
1125 /* Free the client entry data */
1126 silc_client_del_client_entry(client, conn, client_entry);
1127 }
1128 }
1129
1130 /* Free client entry list */
1131
silc_client_list_free(SilcClient client,SilcClientConnection conn,SilcDList client_list)1132 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1133 SilcDList client_list)
1134 {
1135 SilcClientEntry client_entry;
1136
1137 if (client_list) {
1138 silc_dlist_start(client_list);
1139 while ((client_entry = silc_dlist_get(client_list)))
1140 silc_client_unref_client(client, conn, client_entry);
1141
1142 silc_dlist_uninit(client_list);
1143 }
1144 }
1145
1146 /* Formats the nickname of the client specified by the `client_entry'.
1147 If the format is specified by the application this will format the
1148 nickname and replace the old nickname in the client entry. If the
1149 format string is not specified then this function has no effect.
1150 Returns the client entry that was formatted. */
1151
silc_client_nickname_format(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,SilcBool priority)1152 SilcClientEntry silc_client_nickname_format(SilcClient client,
1153 SilcClientConnection conn,
1154 SilcClientEntry client_entry,
1155 SilcBool priority)
1156 {
1157 char *cp;
1158 char newnick[128 + 1];
1159 int i, off = 0, len;
1160 SilcDList clients;
1161 SilcClientEntry entry, unformatted = NULL;
1162 SilcBool formatted = FALSE;
1163
1164 if (!client->internal->params->nickname_format[0])
1165 return client_entry;
1166 if (!client_entry->nickname[0])
1167 return NULL;
1168
1169 SILC_LOG_DEBUG(("Format nickname"));
1170
1171 /* Get all clients with same nickname. Do not perform the formatting
1172 if there aren't any clients with same nickname unless the application
1173 is forcing us to do so. */
1174 clients = silc_client_get_clients_local_ext(client, conn,
1175 client_entry->nickname,
1176 TRUE, FALSE);
1177 if (!clients)
1178 return NULL;
1179 if (silc_dlist_count(clients) == 1 && !priority &&
1180 !client->internal->params->nickname_force_format) {
1181 silc_client_list_free(client, conn, clients);
1182 return client_entry;
1183 }
1184
1185 /* Is the requested client formatted already */
1186 if (client_entry->nickname_normalized &&
1187 !silc_utf8_strcasecmp(client_entry->nickname,
1188 client_entry->nickname_normalized))
1189 formatted = TRUE;
1190
1191 if (client->internal->params->nickname_force_format)
1192 formatted = FALSE;
1193
1194 /* Find unformatted client entry */
1195 while ((entry = silc_dlist_get(clients))) {
1196 if (!entry->internal.valid)
1197 continue;
1198 if (entry == client_entry)
1199 continue;
1200 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1201 unformatted = entry;
1202 break;
1203 }
1204 }
1205
1206 /* If there are no other unformatted clients and the requested client is
1207 unformatted, just return it. */
1208 if (!unformatted && !formatted) {
1209 silc_client_list_free(client, conn, clients);
1210 return client_entry;
1211 }
1212
1213 /* If priority formatting then the requested client will get the
1214 unformatted nickname, and the unformatted client will get a new
1215 formatted nickname. */
1216 if (priority) {
1217 if (formatted) {
1218 /* Simply change the client's nickname to unformatted */
1219 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1220 &cp))
1221 return NULL;
1222
1223 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1224 "%s", cp);
1225 silc_free(cp);
1226 }
1227
1228 if (!unformatted) {
1229 /* There was no other unformatted client */
1230 silc_client_list_free(client, conn, clients);
1231 return client_entry;
1232 }
1233
1234 /* Now format the previously unformatted client */
1235 client_entry = unformatted;
1236 formatted = FALSE;
1237 }
1238
1239 /* If already formatted just return it */
1240 if (formatted) {
1241 silc_client_list_free(client, conn, clients);
1242 return client_entry;
1243 }
1244
1245 memset(newnick, 0, sizeof(newnick));
1246 cp = client->internal->params->nickname_format;
1247 while (cp && *cp) {
1248 if (*cp == '%') {
1249 cp++;
1250 continue;
1251 }
1252
1253 switch(*cp) {
1254 case 'n':
1255 /* Nickname */
1256 if (!client_entry->nickname[0])
1257 break;
1258 len = strlen(client_entry->nickname);
1259 memcpy(&newnick[off], client_entry->nickname, len);
1260 off += len;
1261 break;
1262 case 'h':
1263 /* Stripped hostname */
1264 if (!client_entry->hostname[0])
1265 break;
1266 len = strcspn(client_entry->hostname, ".");
1267 i = strcspn(client_entry->hostname, "-");
1268 if (i < len)
1269 len = i;
1270 memcpy(&newnick[off], client_entry->hostname, len);
1271 off += len;
1272 break;
1273 case 'H':
1274 /* Full hostname */
1275 if (!client_entry->hostname[0])
1276 break;
1277 len = strlen(client_entry->hostname);
1278 memcpy(&newnick[off], client_entry->hostname, len);
1279 off += len;
1280 break;
1281 case 'a':
1282 /* Ascending number */
1283 {
1284 char tmp[6];
1285 int num, max = 1;
1286
1287 if (silc_dlist_count(clients) == 1)
1288 break;
1289
1290 silc_dlist_start(clients);
1291 while ((entry = silc_dlist_get(clients))) {
1292 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1293 continue;
1294 if (strlen(entry->nickname) <= off)
1295 continue;
1296 num = atoi(&entry->nickname[off]);
1297 if (num > max)
1298 max = num;
1299 }
1300
1301 memset(tmp, 0, sizeof(tmp));
1302 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1303 len = strlen(tmp);
1304 memcpy(&newnick[off], tmp, len);
1305 off += len;
1306 }
1307 break;
1308 default:
1309 /* Some other character in the string */
1310 memcpy(&newnick[off], cp, 1);
1311 off++;
1312 break;
1313 }
1314
1315 cp++;
1316 }
1317
1318 newnick[off] = 0;
1319 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
1320 memcpy(client_entry->nickname, newnick, strlen(newnick));
1321 silc_client_list_free(client, conn, clients);
1322
1323 return client_entry;
1324 }
1325
1326 /* Parses nickname according to nickname format string */
1327
silc_client_nickname_parse(SilcClient client,SilcClientConnection conn,char * nickname,char ** ret_nick)1328 SilcBool silc_client_nickname_parse(SilcClient client,
1329 SilcClientConnection conn,
1330 char *nickname,
1331 char **ret_nick)
1332 {
1333 char *cp, s = 0, e = 0, *nick;
1334 SilcBool n = FALSE;
1335 int len;
1336
1337 if (!client->internal->params->nickname_format[0]) {
1338 *ret_nick = NULL;
1339 return TRUE;
1340 }
1341
1342 if (!nickname || !nickname[0])
1343 return FALSE;
1344
1345 cp = client->internal->params->nickname_format;
1346 while (cp && *cp) {
1347 if (*cp == '%') {
1348 cp++;
1349 continue;
1350 }
1351
1352 switch(*cp) {
1353 case 'n':
1354 n = TRUE;
1355 break;
1356
1357 case 'h':
1358 case 'H':
1359 case 'a':
1360 break;
1361
1362 default:
1363 /* Get separator character */
1364 if (n)
1365 e = *cp;
1366 else
1367 s = *cp;
1368 break;
1369 }
1370
1371 cp++;
1372 }
1373 if (!n)
1374 return FALSE;
1375
1376 /* Parse the nickname */
1377 nick = nickname;
1378 len = strlen(nick);
1379 if (s)
1380 if (strchr(nickname, s))
1381 nick = strchr(nickname, s) + 1;
1382 if (e)
1383 if (strchr(nick, e))
1384 len = strchr(nick, e) - nick;
1385 if (!len)
1386 return FALSE;
1387
1388 *ret_nick = silc_memdup(nick, len);
1389 if (!(*ret_nick))
1390 return FALSE;
1391
1392 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1393
1394 return TRUE;
1395 }
1396
1397 /************************ Channel Searching Locally *************************/
1398
1399 /* Finds entry for channel by the channel name. Returns the entry or NULL
1400 if the entry was not found. It is found only if the client is joined
1401 to the channel. */
1402
silc_client_get_channel(SilcClient client,SilcClientConnection conn,char * channel)1403 SilcChannelEntry silc_client_get_channel(SilcClient client,
1404 SilcClientConnection conn,
1405 char *channel)
1406 {
1407 SilcList list;
1408 SilcIDCacheEntry id_cache;
1409 SilcChannelEntry entry = NULL;
1410 char chname[256 + 1], server[256 + 1];
1411
1412 if (!client || !conn || !channel)
1413 return NULL;
1414
1415 SILC_LOG_DEBUG(("Find channel %s", channel));
1416
1417 /* Parse server name from channel name */
1418 silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1419
1420 /* Normalize name for search */
1421 channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1422 256, NULL);
1423 if (!channel)
1424 return NULL;
1425
1426 silc_mutex_lock(conn->internal->lock);
1427
1428 if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1429 &list)) {
1430 silc_mutex_unlock(conn->internal->lock);
1431 silc_free(channel);
1432 return NULL;
1433 }
1434
1435 /* If server name was specified with channel name, find the correct
1436 channel entry with the server name. There can only be one channel
1437 with same name on same server. */
1438 silc_list_start(list);
1439 if (server[0]) {
1440 while ((id_cache = silc_list_get(list))) {
1441 entry = id_cache->context;
1442 if (!entry->server[0])
1443 continue;
1444 if (silc_utf8_strcasecmp(entry->server, server))
1445 break;
1446 }
1447 } else {
1448 /* Get first channel without server name specified or one with our
1449 current server connection name */
1450 while ((id_cache = silc_list_get(list))) {
1451 entry = id_cache->context;
1452 if (!entry->server[0])
1453 break;
1454 if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1455 break;
1456 }
1457 }
1458
1459 if (!id_cache) {
1460 silc_mutex_unlock(conn->internal->lock);
1461 silc_free(channel);
1462 return NULL;
1463 }
1464
1465 SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1466 entry->server[0] ? "@" : "", entry->server));
1467
1468 /* Reference */
1469 silc_client_ref_channel(client, conn, entry);
1470 silc_mutex_unlock(conn->internal->lock);
1471
1472 silc_free(channel);
1473
1474 return entry;
1475 }
1476
1477 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1478 if the entry was not found. It is found only if the client is joined
1479 to the channel. */
1480
silc_client_get_channel_by_id(SilcClient client,SilcClientConnection conn,SilcChannelID * channel_id)1481 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1482 SilcClientConnection conn,
1483 SilcChannelID *channel_id)
1484 {
1485 SilcIDCacheEntry id_cache;
1486 SilcChannelEntry entry;
1487
1488 if (!client || !conn || !channel_id)
1489 return NULL;
1490
1491 SILC_LOG_DEBUG(("Find channel by id %s",
1492 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1493
1494 silc_mutex_lock(conn->internal->lock);
1495
1496 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1497 &id_cache)) {
1498 silc_mutex_unlock(conn->internal->lock);
1499 return NULL;
1500 }
1501
1502 SILC_LOG_DEBUG(("Found"));
1503
1504 entry = id_cache->context;
1505
1506 /* Reference */
1507 silc_client_ref_channel(client, conn, entry);
1508 silc_mutex_unlock(conn->internal->lock);
1509
1510 return entry;
1511 }
1512
1513 /********************** Channel Resolving from Server ***********************/
1514
1515 /* Channel resolving context */
1516 typedef struct {
1517 SilcDList channels;
1518 SilcGetChannelCallback completion;
1519 void *context;
1520 } *SilcClientGetChannelInternal;
1521
1522 /* Resolving command callback */
1523
silc_client_get_channel_cb(SilcClient client,SilcClientConnection conn,SilcCommand command,SilcStatus status,SilcStatus error,void * context,va_list ap)1524 static SilcBool silc_client_get_channel_cb(SilcClient client,
1525 SilcClientConnection conn,
1526 SilcCommand command,
1527 SilcStatus status,
1528 SilcStatus error,
1529 void *context,
1530 va_list ap)
1531 {
1532 SilcClientGetChannelInternal i = context;
1533 SilcChannelEntry entry;
1534
1535 if (error != SILC_STATUS_OK) {
1536 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1537 if (i->completion)
1538 i->completion(client, conn, error, NULL, i->context);
1539 goto out;
1540 }
1541
1542 /* Add the returned channel to list */
1543 if (i->completion) {
1544 entry = va_arg(ap, SilcChannelEntry);
1545 silc_client_ref_channel(client, conn, entry);
1546 silc_dlist_add(i->channels, entry);
1547 }
1548
1549 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1550 /* Deliver the channels to the caller */
1551 if (i->completion) {
1552 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1553 silc_dlist_start(i->channels);
1554 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1555 }
1556 goto out;
1557 }
1558
1559 return TRUE;
1560
1561 out:
1562 silc_client_list_free_channels(client, conn, i->channels);
1563 silc_free(i);
1564 return FALSE;
1565 }
1566
1567 /* Resolves channel entry from the server by the channel name. */
1568
silc_client_get_channel_resolve(SilcClient client,SilcClientConnection conn,char * channel_name,SilcGetChannelCallback completion,void * context)1569 void silc_client_get_channel_resolve(SilcClient client,
1570 SilcClientConnection conn,
1571 char *channel_name,
1572 SilcGetChannelCallback completion,
1573 void *context)
1574 {
1575 SilcClientGetChannelInternal i;
1576
1577 if (!client || !conn || !channel_name || !completion)
1578 return;
1579
1580 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1581
1582 i = silc_calloc(1, sizeof(*i));
1583 if (!i)
1584 return;
1585 i->completion = completion;
1586 i->context = context;
1587 i->channels = silc_dlist_init();
1588 if (!i->channels) {
1589 silc_free(i);
1590 return;
1591 }
1592
1593 /* Send the command */
1594 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1595 silc_client_get_channel_cb, i, 1,
1596 3, channel_name, strlen(channel_name))) {
1597 if (completion)
1598 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1599 }
1600 }
1601
1602 /* Resolves channel information from the server by the channel ID. */
1603
1604 SilcUInt16
silc_client_get_channel_by_id_resolve(SilcClient client,SilcClientConnection conn,SilcChannelID * channel_id,SilcGetChannelCallback completion,void * context)1605 silc_client_get_channel_by_id_resolve(SilcClient client,
1606 SilcClientConnection conn,
1607 SilcChannelID *channel_id,
1608 SilcGetChannelCallback completion,
1609 void *context)
1610 {
1611 SilcClientGetChannelInternal i;
1612 SilcBuffer idp;
1613 SilcUInt16 cmd_ident;
1614
1615 if (!client || !conn || !channel_id || !completion)
1616 return 0;
1617
1618 SILC_LOG_DEBUG(("Resolve channel by id %s",
1619 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1620
1621 i = silc_calloc(1, sizeof(*i));
1622 if (!i)
1623 return 0;
1624 i->completion = completion;
1625 i->context = context;
1626 i->channels = silc_dlist_init();
1627 if (!i->channels) {
1628 silc_free(i);
1629 return 0;
1630 }
1631
1632 /* Send the command */
1633 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1634 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1635 silc_client_get_channel_cb, i, 1,
1636 5, silc_buffer_datalen(idp));
1637 silc_buffer_free(idp);
1638 if (!cmd_ident && completion)
1639 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1640
1641 return cmd_ident;
1642 }
1643
1644 /************************* Channel Entry Routines ***************************/
1645
1646 /* Add new channel entry to the ID Cache */
1647
silc_client_add_channel(SilcClient client,SilcClientConnection conn,const char * channel_name,SilcUInt32 mode,SilcChannelID * channel_id)1648 SilcChannelEntry silc_client_add_channel(SilcClient client,
1649 SilcClientConnection conn,
1650 const char *channel_name,
1651 SilcUInt32 mode,
1652 SilcChannelID *channel_id)
1653 {
1654 SilcChannelEntry channel;
1655 char *channel_namec, name[256 + 1];
1656
1657 SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1658
1659 channel = silc_calloc(1, sizeof(*channel));
1660 if (!channel)
1661 return NULL;
1662
1663 silc_rwlock_alloc(&channel->internal.lock);
1664 silc_atomic_init32(&channel->internal.refcnt, 0);
1665 silc_atomic_init32(&channel->internal.deleted, 1);
1666 channel->id = *channel_id;
1667 channel->mode = mode;
1668
1669 silc_parse_userfqdn(channel_name, name, sizeof(name),
1670 channel->server, sizeof(channel->server));
1671 if (client->internal->params->full_channel_names)
1672 channel->channel_name = strdup(channel_name);
1673 else
1674 channel->channel_name = strdup(name);
1675
1676 if (!channel->channel_name) {
1677 silc_rwlock_free(channel->internal.lock);
1678 silc_atomic_uninit32(&channel->internal.refcnt);
1679 silc_atomic_uninit32(&channel->internal.deleted);
1680 silc_free(channel);
1681 return NULL;
1682 }
1683
1684 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1685 NULL, NULL, NULL, TRUE);
1686 if (!channel->user_list) {
1687 silc_rwlock_free(channel->internal.lock);
1688 silc_atomic_uninit32(&channel->internal.refcnt);
1689 silc_atomic_uninit32(&channel->internal.deleted);
1690 silc_free(channel->channel_name);
1691 silc_free(channel);
1692 return NULL;
1693 }
1694
1695 /* Normalize channel name */
1696 channel_namec = silc_channel_name_check(name, strlen(name),
1697 SILC_STRING_UTF8, 256, NULL);
1698 if (!channel_namec) {
1699 silc_rwlock_free(channel->internal.lock);
1700 silc_atomic_uninit32(&channel->internal.refcnt);
1701 silc_atomic_uninit32(&channel->internal.deleted);
1702 silc_free(channel->channel_name);
1703 silc_hash_table_free(channel->user_list);
1704 silc_free(channel);
1705 return NULL;
1706 }
1707
1708 silc_mutex_lock(conn->internal->lock);
1709
1710 /* Add channel to cache, the normalized channel name is saved to cache */
1711 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1712 &channel->id, channel)) {
1713 silc_rwlock_free(channel->internal.lock);
1714 silc_atomic_uninit32(&channel->internal.refcnt);
1715 silc_atomic_uninit32(&channel->internal.deleted);
1716 silc_free(channel_namec);
1717 silc_free(channel->channel_name);
1718 silc_hash_table_free(channel->user_list);
1719 silc_free(channel);
1720 silc_mutex_unlock(conn->internal->lock);
1721 return NULL;
1722 }
1723
1724 silc_mutex_unlock(conn->internal->lock);
1725 silc_client_ref_channel(client, conn, channel);
1726
1727 SILC_LOG_DEBUG(("Added %p", channel));
1728
1729 return channel;
1730 }
1731
1732 /* Removes channel from the cache by the channel entry. */
1733
silc_client_del_channel(SilcClient client,SilcClientConnection conn,SilcChannelEntry channel)1734 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1735 SilcChannelEntry channel)
1736 {
1737 if (!channel)
1738 return FALSE;
1739
1740 SILC_LOG_DEBUG(("Marking channel entry %p deleted", channel));
1741
1742 if (silc_atomic_sub_int32(&channel->internal.deleted, 1) != 0) {
1743 SILC_LOG_DEBUG(("Channel entry %p already marked deleted", channel));
1744 return FALSE;
1745 }
1746
1747 silc_client_unref_channel(client, conn, channel);
1748 return TRUE;
1749 }
1750
1751 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1752 if the ID could not be changed. This handles entry locking internally. */
1753
silc_client_replace_channel_id(SilcClient client,SilcClientConnection conn,SilcChannelEntry channel,SilcChannelID * new_id)1754 SilcBool silc_client_replace_channel_id(SilcClient client,
1755 SilcClientConnection conn,
1756 SilcChannelEntry channel,
1757 SilcChannelID *new_id)
1758 {
1759 SilcBool ret = FALSE;
1760
1761 if (!new_id)
1762 return FALSE;
1763
1764 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1765 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1766 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1767 silc_id_render(new_id, SILC_ID_CHANNEL)));
1768
1769 /* Update the ID */
1770 silc_rwlock_wrlock(channel->internal.lock);
1771 silc_mutex_lock(conn->internal->lock);
1772 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1773 new_id, NULL, FALSE);
1774 silc_mutex_unlock(conn->internal->lock);
1775 silc_rwlock_unlock(channel->internal.lock);
1776
1777 return ret;
1778 }
1779
1780 /* Lock channel */
1781
silc_client_lock_channel(SilcChannelEntry channel_entry)1782 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1783 {
1784 silc_rwlock_rdlock(channel_entry->internal.lock);
1785 }
1786
1787 /* Unlock channel */
1788
silc_client_unlock_channel(SilcChannelEntry channel_entry)1789 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1790 {
1791 silc_rwlock_unlock(channel_entry->internal.lock);
1792 }
1793
1794 /* Take reference of channel entry */
1795
silc_client_ref_channel(SilcClient client,SilcClientConnection conn,SilcChannelEntry channel_entry)1796 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1797 SilcClientConnection conn,
1798 SilcChannelEntry channel_entry)
1799 {
1800 silc_atomic_add_int32(&channel_entry->internal.refcnt, 1);
1801 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1802 silc_atomic_get_int32(&channel_entry->internal.refcnt) - 1,
1803 silc_atomic_get_int32(&channel_entry->internal.refcnt)));
1804 return channel_entry;
1805 }
1806
1807 /* Release reference of channel entry */
1808
silc_client_unref_channel(SilcClient client,SilcClientConnection conn,SilcChannelEntry channel_entry)1809 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1810 SilcChannelEntry channel_entry)
1811 {
1812 SilcIDCacheEntry id_cache;
1813 SilcBool ret = TRUE;
1814 SilcCipher key;
1815 SilcHmac hmac;
1816 char *namec;
1817
1818 if (!channel_entry)
1819 return;
1820
1821 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1822 silc_atomic_get_int32(&channel_entry->internal.refcnt),
1823 silc_atomic_get_int32(&channel_entry->internal.refcnt)
1824 - 1));
1825
1826 if (silc_atomic_sub_int32(&channel_entry->internal.refcnt, 1) > 0)
1827 return;
1828
1829 SILC_LOG_DEBUG(("Deleting channel %p", channel_entry));
1830
1831 silc_mutex_lock(conn->internal->lock);
1832 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel_entry,
1833 &id_cache)) {
1834 namec = id_cache->name;
1835 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1836 channel_entry, NULL);
1837 silc_free(namec);
1838 }
1839 silc_mutex_unlock(conn->internal->lock);
1840
1841 if (!ret)
1842 return;
1843
1844 silc_client_empty_channel(client, conn, channel_entry);
1845 silc_client_del_channel_private_keys(client, conn, channel_entry);
1846 silc_hash_table_free(channel_entry->user_list);
1847 silc_free(channel_entry->channel_name);
1848 silc_free(channel_entry->topic);
1849 if (channel_entry->founder_key)
1850 silc_pkcs_public_key_free(channel_entry->founder_key);
1851 if (channel_entry->internal.send_key)
1852 silc_cipher_free(channel_entry->internal.send_key);
1853 if (channel_entry->internal.receive_key)
1854 silc_cipher_free(channel_entry->internal.receive_key);
1855 if (channel_entry->internal.hmac)
1856 silc_hmac_free(channel_entry->internal.hmac);
1857 if (channel_entry->internal.old_channel_keys) {
1858 silc_dlist_start(channel_entry->internal.old_channel_keys);
1859 while ((key = silc_dlist_get(channel_entry->internal.old_channel_keys)))
1860 silc_cipher_free(key);
1861 silc_dlist_uninit(channel_entry->internal.old_channel_keys);
1862 }
1863 if (channel_entry->internal.old_hmacs) {
1864 silc_dlist_start(channel_entry->internal.old_hmacs);
1865 while ((hmac = silc_dlist_get(channel_entry->internal.old_hmacs)))
1866 silc_hmac_free(hmac);
1867 silc_dlist_uninit(channel_entry->internal.old_hmacs);
1868 }
1869 if (channel_entry->channel_pubkeys)
1870 silc_argument_list_free(channel_entry->channel_pubkeys,
1871 SILC_ARGUMENT_PUBLIC_KEY);
1872 silc_atomic_uninit32(&channel_entry->internal.deleted);
1873 silc_atomic_uninit32(&channel_entry->internal.refcnt);
1874 silc_rwlock_free(channel_entry->internal.lock);
1875 silc_schedule_task_del_by_context(conn->client->schedule, channel_entry);
1876 silc_free(channel_entry);
1877 }
1878
1879 /* Free channel entry list */
1880
silc_client_list_free_channels(SilcClient client,SilcClientConnection conn,SilcDList channel_list)1881 void silc_client_list_free_channels(SilcClient client,
1882 SilcClientConnection conn,
1883 SilcDList channel_list)
1884 {
1885 SilcChannelEntry channel_entry;
1886
1887 if (channel_list) {
1888 silc_dlist_start(channel_list);
1889 while ((channel_entry = silc_dlist_get(channel_list)))
1890 silc_client_unref_channel(client, conn, channel_entry);
1891
1892 silc_dlist_uninit(channel_list);
1893 }
1894 }
1895
1896 /************************* Server Searching Locally *************************/
1897
1898 /* Finds entry for server by the server name. */
1899
silc_client_get_server(SilcClient client,SilcClientConnection conn,char * server_name)1900 SilcServerEntry silc_client_get_server(SilcClient client,
1901 SilcClientConnection conn,
1902 char *server_name)
1903 {
1904 SilcIDCacheEntry id_cache;
1905 SilcServerEntry entry;
1906
1907 if (!client || !conn || !server_name)
1908 return NULL;
1909
1910 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1911
1912 /* Normalize server name for search */
1913 server_name = silc_identifier_check(server_name, strlen(server_name),
1914 SILC_STRING_UTF8, 256, NULL);
1915 if (!server_name)
1916 return NULL;
1917
1918 silc_mutex_lock(conn->internal->lock);
1919
1920 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1921 server_name, &id_cache)) {
1922 silc_free(server_name);
1923 silc_mutex_unlock(conn->internal->lock);
1924 return NULL;
1925 }
1926
1927 SILC_LOG_DEBUG(("Found"));
1928
1929 /* Reference */
1930 entry = id_cache->context;
1931 silc_client_ref_server(client, conn, entry);
1932
1933 silc_mutex_unlock(conn->internal->lock);
1934
1935 silc_free(server_name);
1936
1937 return entry;
1938 }
1939
1940 /* Finds entry for server by the server ID. */
1941
silc_client_get_server_by_id(SilcClient client,SilcClientConnection conn,SilcServerID * server_id)1942 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1943 SilcClientConnection conn,
1944 SilcServerID *server_id)
1945 {
1946 SilcIDCacheEntry id_cache;
1947 SilcServerEntry entry;
1948
1949 if (!client || !conn || !server_id)
1950 return NULL;
1951
1952 SILC_LOG_DEBUG(("Find server by id %s",
1953 silc_id_render(server_id, SILC_ID_SERVER)));
1954
1955 silc_mutex_lock(conn->internal->lock);
1956
1957 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1958 server_id, &id_cache)) {
1959 silc_mutex_unlock(conn->internal->lock);
1960 return NULL;
1961 }
1962
1963 SILC_LOG_DEBUG(("Found"));
1964
1965 /* Reference */
1966 entry = id_cache->context;
1967 silc_client_ref_server(client, conn, entry);
1968
1969 silc_mutex_unlock(conn->internal->lock);
1970
1971 return entry;
1972 }
1973
1974 /*********************** Server Resolving from Server ***********************/
1975
1976 /* Resolving context */
1977 typedef struct {
1978 SilcDList servers;
1979 SilcGetServerCallback completion;
1980 void *context;
1981 } *SilcClientGetServerInternal;
1982
1983 /* Resolving command callback */
1984
silc_client_get_server_cb(SilcClient client,SilcClientConnection conn,SilcCommand command,SilcStatus status,SilcStatus error,void * context,va_list ap)1985 static SilcBool silc_client_get_server_cb(SilcClient client,
1986 SilcClientConnection conn,
1987 SilcCommand command,
1988 SilcStatus status,
1989 SilcStatus error,
1990 void *context,
1991 va_list ap)
1992 {
1993 SilcClientGetServerInternal i = context;
1994 SilcServerEntry server;
1995
1996 if (error != SILC_STATUS_OK) {
1997 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1998 if (i->completion)
1999 i->completion(client, conn, error, NULL, i->context);
2000 goto out;
2001 }
2002
2003 /* Add the returned servers to list */
2004 if (i->completion) {
2005 server = va_arg(ap, SilcServerEntry);
2006 silc_client_ref_server(client, conn, server);
2007 silc_dlist_add(i->servers, server);
2008 server->internal.resolve_cmd_ident = 0;
2009 }
2010
2011 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
2012 /* Deliver the servers to the caller */
2013 if (i->completion) {
2014 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
2015 silc_dlist_start(i->servers);
2016 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
2017 }
2018 goto out;
2019 }
2020
2021 return TRUE;
2022
2023 out:
2024 silc_client_list_free_servers(client, conn, i->servers);
2025 silc_free(i);
2026 return FALSE;
2027 }
2028
2029 /* Resolve server by server ID */
2030
2031 SilcUInt16
silc_client_get_server_by_id_resolve(SilcClient client,SilcClientConnection conn,SilcServerID * server_id,SilcGetServerCallback completion,void * context)2032 silc_client_get_server_by_id_resolve(SilcClient client,
2033 SilcClientConnection conn,
2034 SilcServerID *server_id,
2035 SilcGetServerCallback completion,
2036 void *context)
2037 {
2038 SilcClientGetServerInternal i;
2039 SilcServerEntry server;
2040 SilcBuffer idp;
2041 SilcUInt16 cmd_ident;
2042
2043 if (!client || !conn || !server_id || !completion)
2044 return 0;
2045
2046 SILC_LOG_DEBUG(("Resolve server by id %s",
2047 silc_id_render(server_id, SILC_ID_SERVER)));
2048
2049 i = silc_calloc(1, sizeof(*i));
2050 if (!i)
2051 return 0;
2052 i->completion = completion;
2053 i->context = context;
2054 i->servers = silc_dlist_init();
2055 if (!i->servers) {
2056 silc_free(i);
2057 return 0;
2058 }
2059
2060 /* Attach to resolving, if on going */
2061 server = silc_client_get_server_by_id(client, conn, server_id);
2062 if (server && server->internal.resolve_cmd_ident) {
2063 SILC_LOG_DEBUG(("Attach to existing resolving"));
2064 silc_client_unref_server(client, conn, server);
2065 silc_client_command_pending(conn, SILC_COMMAND_NONE,
2066 server->internal.resolve_cmd_ident,
2067 silc_client_get_server_cb, i);
2068 return server->internal.resolve_cmd_ident;
2069 }
2070
2071 /* Send the command */
2072 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2073 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2074 silc_client_get_server_cb, i, 1,
2075 5, silc_buffer_datalen(idp));
2076 silc_buffer_free(idp);
2077 if (!cmd_ident && completion)
2078 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2079
2080 if (server && cmd_ident)
2081 server->internal.resolve_cmd_ident = cmd_ident;
2082
2083 silc_client_unref_server(client, conn, server);
2084
2085 return cmd_ident;
2086 }
2087
2088 /************************** Server Entry Routines ***************************/
2089
2090 /* Add new server entry */
2091
silc_client_add_server(SilcClient client,SilcClientConnection conn,const char * server_name,const char * server_info,SilcServerID * server_id)2092 SilcServerEntry silc_client_add_server(SilcClient client,
2093 SilcClientConnection conn,
2094 const char *server_name,
2095 const char *server_info,
2096 SilcServerID *server_id)
2097 {
2098 SilcServerEntry server_entry;
2099 char *server_namec = NULL;
2100
2101 if (!server_id)
2102 return NULL;
2103
2104 SILC_LOG_DEBUG(("Adding new server %s", server_name));
2105
2106 server_entry = silc_calloc(1, sizeof(*server_entry));
2107 if (!server_entry)
2108 return NULL;
2109
2110 silc_rwlock_alloc(&server_entry->internal.lock);
2111 silc_atomic_init32(&server_entry->internal.refcnt, 0);
2112 silc_atomic_init32(&server_entry->internal.deleted, 1);
2113 server_entry->id = *server_id;
2114 if (server_name)
2115 server_entry->server_name = strdup(server_name);
2116 if (server_info)
2117 server_entry->server_info = strdup(server_info);
2118
2119 /* Normalize server name */
2120 if (server_name) {
2121 server_namec = silc_identifier_check(server_name, strlen(server_name),
2122 SILC_STRING_UTF8, 256, NULL);
2123 if (!server_namec) {
2124 silc_free(server_entry->server_name);
2125 silc_free(server_entry->server_info);
2126 silc_atomic_uninit32(&server_entry->internal.deleted);
2127 silc_atomic_uninit32(&server_entry->internal.refcnt);
2128 silc_rwlock_free(server_entry->internal.lock);
2129 silc_free(server_entry);
2130 return NULL;
2131 }
2132 }
2133
2134 silc_mutex_lock(conn->internal->lock);
2135
2136 /* Add server to cache */
2137 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2138 &server_entry->id, server_entry)) {
2139 silc_free(server_namec);
2140 silc_free(server_entry->server_name);
2141 silc_free(server_entry->server_info);
2142 silc_atomic_uninit32(&server_entry->internal.deleted);
2143 silc_atomic_uninit32(&server_entry->internal.refcnt);
2144 silc_rwlock_free(server_entry->internal.lock);
2145 silc_free(server_entry);
2146 silc_mutex_unlock(conn->internal->lock);
2147 return NULL;
2148 }
2149
2150 silc_mutex_unlock(conn->internal->lock);
2151 silc_client_ref_server(client, conn, server_entry);
2152
2153 SILC_LOG_DEBUG(("Added %p", server_entry));
2154
2155 return server_entry;
2156 }
2157
2158 /* Removes server from the cache by the server entry. */
2159
silc_client_del_server(SilcClient client,SilcClientConnection conn,SilcServerEntry server)2160 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2161 SilcServerEntry server)
2162 {
2163 if (!server)
2164 return FALSE;
2165
2166 if (silc_atomic_sub_int32(&server->internal.deleted, 1) != 0)
2167 return FALSE;
2168
2169 silc_client_unref_server(client, conn, server);
2170 return TRUE;
2171 }
2172
2173 /* Updates the `server_entry' with the new information sent as argument. */
2174
silc_client_update_server(SilcClient client,SilcClientConnection conn,SilcServerEntry server_entry,const char * server_name,const char * server_info)2175 void silc_client_update_server(SilcClient client,
2176 SilcClientConnection conn,
2177 SilcServerEntry server_entry,
2178 const char *server_name,
2179 const char *server_info)
2180 {
2181 char *server_namec = NULL;
2182
2183 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2184
2185 if (server_name &&
2186 (!server_entry->server_name ||
2187 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2188
2189 server_namec = silc_identifier_check(server_name, strlen(server_name),
2190 SILC_STRING_UTF8, 256, NULL);
2191 if (!server_namec)
2192 return;
2193
2194 silc_free(server_entry->server_name);
2195 server_entry->server_name = strdup(server_name);
2196 if (!server_entry->server_name)
2197 return;
2198
2199 /* Update cache entry */
2200 silc_mutex_lock(conn->internal->lock);
2201 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2202 NULL, server_namec, TRUE);
2203 silc_mutex_unlock(conn->internal->lock);
2204 }
2205
2206 if (server_info &&
2207 (!server_entry->server_info ||
2208 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2209 silc_free(server_entry->server_info);
2210 server_entry->server_info = strdup(server_info);
2211 }
2212 }
2213
2214 /* Lock server */
2215
silc_client_lock_server(SilcServerEntry server_entry)2216 void silc_client_lock_server(SilcServerEntry server_entry)
2217 {
2218 silc_rwlock_rdlock(server_entry->internal.lock);
2219 }
2220
2221 /* Unlock server */
2222
silc_client_unlock_server(SilcServerEntry server_entry)2223 void silc_client_unlock_server(SilcServerEntry server_entry)
2224 {
2225 silc_rwlock_unlock(server_entry->internal.lock);
2226 }
2227
2228 /* Take reference of server entry */
2229
silc_client_ref_server(SilcClient client,SilcClientConnection conn,SilcServerEntry server_entry)2230 SilcServerEntry silc_client_ref_server(SilcClient client,
2231 SilcClientConnection conn,
2232 SilcServerEntry server_entry)
2233 {
2234 silc_atomic_add_int32(&server_entry->internal.refcnt, 1);
2235 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2236 silc_atomic_get_int32(&server_entry->internal.refcnt) - 1,
2237 silc_atomic_get_int32(&server_entry->internal.refcnt)));
2238 return server_entry;
2239 }
2240
2241 /* Release reference of server entry */
2242
silc_client_unref_server(SilcClient client,SilcClientConnection conn,SilcServerEntry server_entry)2243 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2244 SilcServerEntry server_entry)
2245 {
2246 SilcIDCacheEntry id_cache;
2247 char *namec;
2248
2249 if (!server_entry)
2250 return;
2251
2252 if (silc_atomic_sub_int32(&server_entry->internal.refcnt, 1) > 0)
2253 return;
2254
2255 SILC_LOG_DEBUG(("Deleting server %p", server_entry));
2256
2257 silc_mutex_lock(conn->internal->lock);
2258 if (silc_idcache_find_by_context(conn->internal->server_cache, server_entry,
2259 &id_cache)) {
2260 namec = id_cache->name;
2261 silc_idcache_del_by_context(conn->internal->server_cache,
2262 server_entry, NULL);
2263 silc_free(namec);
2264 }
2265 silc_mutex_unlock(conn->internal->lock);
2266
2267 silc_free(server_entry->server_name);
2268 silc_free(server_entry->server_info);
2269 if (server_entry->public_key)
2270 silc_pkcs_public_key_free(server_entry->public_key);
2271 silc_atomic_uninit32(&server_entry->internal.deleted);
2272 silc_atomic_uninit32(&server_entry->internal.refcnt);
2273 silc_rwlock_free(server_entry->internal.lock);
2274 silc_free(server_entry);
2275 }
2276
2277 /* Free server entry list */
2278
silc_client_list_free_servers(SilcClient client,SilcClientConnection conn,SilcDList server_list)2279 void silc_client_list_free_servers(SilcClient client,
2280 SilcClientConnection conn,
2281 SilcDList server_list)
2282 {
2283 SilcServerEntry server_entry;
2284
2285 if (server_list) {
2286 silc_dlist_start(server_list);
2287 while ((server_entry = silc_dlist_get(server_list)))
2288 silc_client_unref_server(client, conn, server_entry);
2289
2290 silc_dlist_uninit(server_list);
2291 }
2292 }
2293