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