Corrected use of silc_file_readfile.
[silc.git] / apps / irssi / src / silc / core / silc-queries.c
1 /*
2
3   silc-queries.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "module.h"
21 #include "signals.h"
22 #include "misc.h"
23 #include "silc-queries.h"
24 #include "settings.h"
25 #include "levels.h"
26 #include "modules.h"
27 #include "commands.h"
28 #include "misc.h"
29
30 #include "fe-common/core/printtext.h"
31 #include "fe-common/core/fe-channels.h"
32 #include "fe-common/core/keyboard.h"
33 #include "fe-common/silc/module-formats.h"
34
35 static void silc_query_attributes_print_final(bool success, void *context);
36 static void silc_query_attributes_accept(const char *line, void *context);
37
38 QUERY_REC *silc_query_create(const char *server_tag,
39                              const char *nick, int automatic)
40 {
41   QUERY_REC *rec;
42
43   g_return_val_if_fail(nick != NULL, NULL);
44
45   rec = g_new0(QUERY_REC, 1);
46   rec->chat_type = SILC_PROTOCOL;
47   rec->name = g_strdup(nick);
48   rec->server_tag = g_strdup(server_tag);
49   query_init(rec, automatic);
50   return rec;
51 }
52
53 void silc_queries_init(void)
54 {
55 }
56
57 void silc_queries_deinit(void)
58 {
59 }
60
61 /* ATTR command */
62
63 void command_attr(const char *data, SILC_SERVER_REC *server,
64                   WI_ITEM_REC *item)
65 {
66   char *tmp;
67   unsigned char **argv;
68   SilcUInt32 argc;
69   SilcUInt32 *argv_lens, *argv_types;
70   const char *sv;
71   bool allowed;
72
73   /* Now parse all arguments */
74   tmp = g_strconcat("ATTR", " ", data, NULL);
75   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 3);
76   g_free(tmp);
77
78   if (argc == 1) {
79     /* Show all attributes */
80     printformat_module("fe-common/silc", server, NULL,
81                        MSGLEVEL_CRAP, SILCTXT_ATTR_HEADER);
82
83     allowed = settings_get_bool("attr_allow");
84     printformat_module("fe-common/silc", server, NULL,
85                        MSGLEVEL_CRAP, SILCTXT_ATTR_ALLOW,
86                        allowed ? "Yes" : "No");
87
88     sv = settings_get_str("attr_vcard");
89     if (sv && *sv)
90       printformat_module("fe-common/silc", server, NULL,
91                          MSGLEVEL_CRAP, SILCTXT_ATTR_VCARD_FILE, sv);
92
93     sv = settings_get_str("attr_services");
94     if (sv && *sv)
95       printformat_module("fe-common/silc", server, NULL,
96                          MSGLEVEL_CRAP, SILCTXT_ATTR_SERVICES, sv);
97
98     sv = settings_get_str("attr_status_mood");
99     if (sv && *sv)
100       printformat_module("fe-common/silc", server, NULL,
101                          MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MOOD, sv);
102
103     sv = settings_get_str("attr_status_text");
104     if (sv && *sv)
105       printformat_module("fe-common/silc", server, NULL,
106                          MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_TEXT, sv);
107
108     sv = settings_get_str("attr_status_message");
109     if (sv && *sv)
110       printformat_module("fe-common/silc", server, NULL,
111                          MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MESSAGE_FILE,
112                          sv);
113
114     sv = settings_get_str("attr_preferred_language");
115     if (sv && *sv)
116       printformat_module("fe-common/silc", server, NULL,
117                          MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_LANGUAGE,
118                          sv);
119
120     sv = settings_get_str("attr_preferred_contact");
121     if (sv && *sv)
122       printformat_module("fe-common/silc", server, NULL,
123                          MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_CONTACT,
124                          sv);
125
126     sv = settings_get_str("attr_geolocation");
127     if (sv && *sv)
128       printformat_module("fe-common/silc", server, NULL,
129                          MSGLEVEL_CRAP, SILCTXT_ATTR_GEOLOCATION,
130                          sv);
131
132     sv = settings_get_str("attr_device_info");
133     if (sv && *sv)
134       printformat_module("fe-common/silc", server, NULL,
135                          MSGLEVEL_CRAP, SILCTXT_ATTR_DEVICE_INFO,
136                          sv);
137
138     sv = settings_get_str("attr_public_keys");
139     if (sv && *sv)
140       printformat_module("fe-common/silc", server, NULL,
141                          MSGLEVEL_CRAP, SILCTXT_ATTR_PUBLIC_KEYS,
142                          sv);
143
144     allowed = settings_get_bool("attr_timezone");
145     printformat_module("fe-common/silc", server, NULL,
146                        MSGLEVEL_CRAP, SILCTXT_ATTR_TIMEZONE_ALLOW,
147                        allowed ? "Yes" : "No");
148
149     printformat_module("fe-common/silc", server, NULL,
150                        MSGLEVEL_CRAP, SILCTXT_ATTR_FOOTER);
151     return;
152   }
153
154   if (argc < 3)
155     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
156
157   if (!strcasecmp(argv[1], "-del")) {
158     /* Delete attribute */
159     if (!strcasecmp(argv[1], "vcard")) {
160       silc_client_attribute_del(silc_client, server->conn,
161                                 SILC_ATTRIBUTE_USER_INFO, NULL);
162       settings_set_str("attr_vcard", "");
163     } else if (!strcasecmp(argv[1], "services")) {
164       silc_client_attribute_del(silc_client, server->conn,
165                                 SILC_ATTRIBUTE_SERVICE, NULL);
166       settings_set_str("attr_services", argv[2]);
167     } else if (!strcasecmp(argv[1], "status_mood")) {
168       silc_client_attribute_del(silc_client, server->conn,
169                                 SILC_ATTRIBUTE_STATUS_MOOD, NULL);
170       settings_set_str("attr_status_mood", "");
171     } else if (!strcasecmp(argv[1], "status_text")) {
172       silc_client_attribute_del(silc_client, server->conn,
173                                 SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
174       settings_set_str("attr_status_text", "");
175     } else if (!strcasecmp(argv[1], "status_message")) {
176       silc_client_attribute_del(silc_client, server->conn,
177                                 SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
178       settings_set_str("attr_status_message", "");
179     } else if (!strcasecmp(argv[1], "preferred_langauge")) {
180       silc_client_attribute_del(silc_client, server->conn,
181                                 SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
182       settings_set_str("attr_preferred_language", "");
183     } else if (!strcasecmp(argv[1], "preferred_contact")) {
184       silc_client_attribute_del(silc_client, server->conn,
185                                 SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
186       settings_set_str("attr_preferred_contact", "");
187     } else if (!strcasecmp(argv[1], "timezone")) {
188       return;
189     } else if (!strcasecmp(argv[1], "geolocation")) {
190       silc_client_attribute_del(silc_client, server->conn,
191                                 SILC_ATTRIBUTE_GEOLOCATION, NULL);
192       settings_set_str("attr_geolocation", "");
193     } else if (!strcasecmp(argv[1], "device_info")) {
194       silc_client_attribute_del(silc_client, server->conn,
195                                 SILC_ATTRIBUTE_DEVICE_INFO, NULL);
196       settings_set_str("attr_device_info", "");
197     } else if (!strcasecmp(argv[1], "public_keys")) {
198       silc_client_attribute_del(silc_client, server->conn,
199                                 SILC_ATTRIBUTE_USER_PUBLIC_KEY, NULL);
200       settings_set_str("attr_public_keys", "");
201     } else {
202       cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
203     }
204     return;
205   }
206
207   /* Add new attribute */
208   if (!strcasecmp(argv[1], "allow")) {
209     allowed = !strcasecmp(argv[2], "ON") || !strcasecmp(argv[2], "YES");
210     settings_set_bool("attr_allow", allowed);
211   } else if (!strcasecmp(argv[1], "vcard")) {
212     settings_set_str("attr_vcard", argv[2]);
213   } else if (!strcasecmp(argv[1], "services")) {
214     settings_set_str("attr_services", argv[2]);
215   } else if (!strcasecmp(argv[1], "status_mood")) {
216     settings_set_str("attr_status_mood", argv[2]);
217   } else if (!strcasecmp(argv[1], "status_text")) {
218     settings_set_str("attr_status_text", argv[2]);
219   } else if (!strcasecmp(argv[1], "status_message")) {
220     settings_set_str("attr_status_message", argv[2]);
221   } else if (!strcasecmp(argv[1], "preferred_langauge")) {
222     settings_set_str("attr_preferred_language", argv[2]);
223   } else if (!strcasecmp(argv[1], "preferred_contact")) {
224     settings_set_str("attr_preferred_contact", argv[2]);
225   } else if (!strcasecmp(argv[1], "timezone")) {
226     allowed = !strcasecmp(argv[2], "ON") || !strcasecmp(argv[2], "YES");
227     settings_set_bool("attr_timezone", allowed);
228   } else if (!strcasecmp(argv[1], "geolocation")) {
229     settings_set_str("attr_geolocation", argv[2]);
230   } else if (!strcasecmp(argv[1], "device_info")) {
231     settings_set_str("attr_device_info", argv[2]);
232   } else if (!strcasecmp(argv[1], "public_keys")) {
233     settings_set_str("attr_public_keys", argv[2]);
234   } else {
235     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
236   }
237
238   silc_query_attributes_default(silc_client, server->conn);
239 }
240
241 /* Put default attributes to client library */
242
243 void silc_query_attributes_default(SilcClient client,
244                                    SilcClientConnection conn)
245 {
246   char *tmp, **list, **entry;
247   const char *sv;
248   SilcUInt32 tmp_len, mask;
249   SilcAttributeObjService service;
250   SilcAttributeObjMime mime;
251   SilcAttributeObjGeo geo;
252   SilcAttributeObjDevice dev;
253   SilcAttributeObjPk pk;
254   SilcVCardStruct vcard;
255   bool allowed;
256
257   memset(&service, 0, sizeof(service));
258   memset(&mime, 0, sizeof(mime));
259   memset(&geo, 0, sizeof(geo));
260   memset(&dev, 0, sizeof(dev));
261   memset(&pk, 0, sizeof(pk));
262   memset(&vcard, 0, sizeof(vcard));
263
264   sv = settings_get_str("attr_vcard");
265   if (sv && *sv) {
266     /* Put USER_INFO */
267     silc_client_attribute_del(silc_client, conn,
268                               SILC_ATTRIBUTE_USER_INFO, NULL);
269     tmp = silc_file_readfile(sv, &tmp_len);
270     if (tmp) {
271       tmp[tmp_len] = 0;
272       if (silc_vcard_decode(tmp, tmp_len, &vcard))
273         silc_client_attribute_add(silc_client, conn,
274                                   SILC_ATTRIBUTE_USER_INFO, (void *)&vcard,
275                                   sizeof(vcard));
276     }
277     silc_vcard_free(&vcard);
278     silc_free(tmp);
279   }
280
281   sv = settings_get_str("attr_services");
282   if (sv && *sv) {
283     /* Put SERVICE */
284     silc_client_attribute_del(silc_client, conn,
285                               SILC_ATTRIBUTE_SERVICE, NULL);
286     list = g_strsplit(sv, " ", -1);
287     for (entry = list; *entry != NULL; entry++) {
288       if (!strchr(*entry, ':'))
289         continue;
290       tmp = strchr(*entry, ':') + 1;
291       if (!tmp || !(*tmp))
292         continue;
293       service.port = atoi(tmp);
294       *strchr(*entry, ':') = '\0';
295       silc_strncat(service.address, sizeof(service.address), *entry,
296                    strlen(*entry));
297       service.status = TRUE;
298       silc_client_attribute_add(silc_client, conn,
299                                 SILC_ATTRIBUTE_SERVICE, &service,
300                                 sizeof(service));
301     }
302     g_strfreev(list);
303   }
304
305   sv = settings_get_str("attr_status_mood");
306   if (sv && *sv) {
307     /* Put STATUS_MOOD */
308     silc_client_attribute_del(silc_client, conn,
309                               SILC_ATTRIBUTE_STATUS_MOOD, NULL);
310     mask = 0;
311     list = g_strsplit(sv, " ", -1);
312     for (entry = list; *entry != NULL; entry++) {
313       if (!strcasecmp(*entry, "NORMAL"))
314         mask |= SILC_ATTRIBUTE_MOOD_NORMAL;
315       if (!strcasecmp(*entry, "HAPPY"))
316         mask |= SILC_ATTRIBUTE_MOOD_HAPPY;
317       if (!strcasecmp(*entry, "SAD"))
318         mask |= SILC_ATTRIBUTE_MOOD_SAD;
319       if (!strcasecmp(*entry, "ANGRY"))
320         mask |= SILC_ATTRIBUTE_MOOD_ANGRY;
321       if (!strcasecmp(*entry, "JEALOUS"))
322         mask |= SILC_ATTRIBUTE_MOOD_JEALOUS;
323       if (!strcasecmp(*entry, "ASHAMED"))
324         mask |= SILC_ATTRIBUTE_MOOD_ASHAMED;
325       if (!strcasecmp(*entry, "INVINCIBLE"))
326         mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE;
327       if (!strcasecmp(*entry, "INLOVE"))
328         mask |= SILC_ATTRIBUTE_MOOD_INLOVE;
329       if (!strcasecmp(*entry, "SLEEPY"))
330         mask |= SILC_ATTRIBUTE_MOOD_SLEEPY;
331       if (!strcasecmp(*entry, "BORED"))
332         mask |= SILC_ATTRIBUTE_MOOD_BORED;
333       if (!strcasecmp(*entry, "EXCITED"))
334         mask |= SILC_ATTRIBUTE_MOOD_EXCITED;
335       if (!strcasecmp(*entry, "ANXIOUS"))
336         mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS;
337       silc_client_attribute_add(silc_client, conn,
338                                 SILC_ATTRIBUTE_STATUS_MOOD, (void *)mask,
339                                 sizeof(SilcUInt32));
340     }
341     g_strfreev(list);
342   }
343
344   sv = settings_get_str("attr_status_text");
345   if (sv && *sv) {
346     /* Put STATUS_TEXT */
347     silc_client_attribute_del(silc_client, conn,
348                               SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
349     silc_client_attribute_add(silc_client, conn,
350                               SILC_ATTRIBUTE_STATUS_FREETEXT, (void *)sv,
351                               strlen(sv));
352   }
353
354   sv = settings_get_str("attr_status_message");
355   if (sv && *sv) {
356     /* Put STATUS_MESSAGE */
357     silc_client_attribute_del(silc_client, conn,
358                               SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
359     tmp = silc_file_readfile(sv, &tmp_len);
360     if (tmp) {
361       tmp[tmp_len] = 0;
362       mime.mime = (const unsigned char *)tmp;
363       mime.mime_len = tmp_len;
364       silc_client_attribute_add(silc_client, conn,
365                                 SILC_ATTRIBUTE_STATUS_MESSAGE, &mime,
366                                 sizeof(mime));
367     }
368     silc_free(tmp);
369   }
370
371   sv = settings_get_str("attr_preferred_language");
372   if (sv && *sv) {
373     /* Put PREFERRED_LANGUAGE */
374     silc_client_attribute_del(silc_client, conn,
375                               SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
376     list = g_strsplit(sv, " ", -1);
377     for (entry = list; *entry != NULL; entry++) {
378       silc_client_attribute_add(silc_client, conn,
379                                 SILC_ATTRIBUTE_PREFERRED_LANGUAGE, *entry,
380                                 strlen(*entry));
381     }
382     g_strfreev(list);
383   }
384
385   sv = settings_get_str("attr_preferred_contact");
386   if (sv && *sv) {
387     /* Put PREFERRED_CONTACT */
388     silc_client_attribute_del(silc_client, conn,
389                               SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
390     mask = 0;
391     list = g_strsplit(sv, " ", -1);
392     for (entry = list; *entry != NULL; entry++) {
393       if (!strcasecmp(*entry, "NONE"))
394         mask = 0;
395       if (!strcasecmp(*entry, "EMAIL"))
396         mask |= SILC_ATTRIBUTE_CONTACT_EMAIL;
397       if (!strcasecmp(*entry, "CALL"))
398         mask |= SILC_ATTRIBUTE_CONTACT_CALL;
399       if (!strcasecmp(*entry, "PAGE"))
400         mask |= SILC_ATTRIBUTE_CONTACT_PAGE;
401       if (!strcasecmp(*entry, "SMS"))
402         mask |= SILC_ATTRIBUTE_CONTACT_SMS;
403       if (!strcasecmp(*entry, "MMS"))
404         mask |= SILC_ATTRIBUTE_CONTACT_MMS;
405       if (!strcasecmp(*entry, "CHAT"))
406         mask |= SILC_ATTRIBUTE_CONTACT_CHAT;
407       silc_client_attribute_add(silc_client, conn,
408                                 SILC_ATTRIBUTE_PREFERRED_CONTACT, (void *)mask,
409                                 sizeof(SilcUInt32));
410     }
411     g_strfreev(list);
412   }
413
414   /* Put TIMEZONE */
415   allowed = settings_get_bool("attr_timezone");
416   silc_client_attribute_del(silc_client, conn,
417                             SILC_ATTRIBUTE_TIMEZONE, NULL);
418   if (allowed)
419     silc_client_attribute_add(silc_client, conn,
420                               SILC_ATTRIBUTE_TIMEZONE, "foo", 3);
421
422   sv = settings_get_str("attr_geolocation");
423   if (sv && *sv) {
424     /* Put GEOLOCATION */
425     silc_client_attribute_del(silc_client, conn,
426                               SILC_ATTRIBUTE_GEOLOCATION, NULL);
427     list = g_strsplit(sv, ":", -1);
428     for (entry = list; *entry != NULL; entry++) {
429       if (!geo.longitude) {
430         geo.longitude = *entry;
431         continue;
432       }
433       if (!geo.latitude) {
434         geo.latitude = *entry;
435         continue;
436       }
437       if (!geo.altitude) {
438         geo.altitude = *entry;
439         continue;
440       }
441       if (!geo.accuracy) {
442         geo.accuracy = *entry;
443         continue;
444       }
445     }
446     silc_client_attribute_add(silc_client, conn,
447                               SILC_ATTRIBUTE_GEOLOCATION, &geo,
448                               sizeof(geo));
449     g_strfreev(list);
450   }
451
452   sv = settings_get_str("attr_device_info");
453   if (sv && *sv) {
454     /* Put DEVICE_INFO */
455     silc_client_attribute_del(silc_client, conn,
456                               SILC_ATTRIBUTE_DEVICE_INFO, NULL);
457     allowed = FALSE;
458     list = g_strsplit(sv, ":", -1);
459     for (entry = list; *entry != NULL; entry++) {
460       if (!allowed) {
461         allowed = TRUE;
462         if (!strcasecmp(*entry, "COMPUTER"))
463           dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
464         if (!strcasecmp(*entry, "MOBILE_PHONE"))
465           dev.type = SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE;
466         if (!strcasecmp(sv, "PDA"))
467           dev.type = SILC_ATTRIBUTE_DEVICE_PDA;
468         if (!strcasecmp(sv, "TERMINAL"))
469           dev.type = SILC_ATTRIBUTE_DEVICE_TERMINAL;
470         continue;
471       }
472       if (!dev.manufacturer) {
473         dev.manufacturer = *entry;
474         continue;
475       }
476       if (!dev.version) {
477         dev.version = *entry;
478         continue;
479       }
480       if (!dev.model) {
481         dev.model = *entry;
482         continue;
483       }
484       if (!dev.language) {
485         dev.language = *entry;
486         continue;
487       }
488     }
489     silc_client_attribute_add(silc_client, conn,
490                               SILC_ATTRIBUTE_DEVICE_INFO, &dev,
491                               sizeof(dev));
492     g_strfreev(list);
493   }
494
495   sv = settings_get_str("attr_public_keys");
496   if (sv && *sv) {
497     /* Put USER_PUBLIC_KEY */
498     silc_client_attribute_del(silc_client, conn,
499                               SILC_ATTRIBUTE_USER_PUBLIC_KEY, NULL);
500     list = g_strsplit(sv, " ", -1);
501     for (entry = list; *entry != NULL; entry++) {
502       if (!strncasecmp(*entry, "silc-rsa:", 8)) {
503         tmp = silc_file_readfile((*entry) + 8, &tmp_len);
504         if (tmp) {
505           tmp[tmp_len] = 0;
506           pk.type = "silc-rsa";
507           pk.data = tmp;
508           pk.data_len = tmp_len;
509           silc_client_attribute_add(silc_client, conn,
510                                     SILC_ATTRIBUTE_USER_PUBLIC_KEY, &pk,
511                                     sizeof(pk));
512         }
513         silc_free(tmp);
514       } else {
515         silc_say_error("Unsupported public key type '%s'", *entry);
516       }
517     }
518     g_strfreev(list);
519   }
520 }
521
522 typedef struct {
523   SilcClient client;
524   SILC_SERVER_REC *server;
525   char *name;
526   SilcAttributeObjPk userpk;
527   SilcVCardStruct vcard;
528   SilcAttributeObjMime message;
529   SilcAttributeObjMime extension;
530   bool nopk;
531 } *AttrVerify;
532
533 void silc_query_attributes_print(SILC_SERVER_REC *server,
534                                  SilcClient client,
535                                  SilcClientConnection conn,
536                                  SilcDList attrs,
537                                  SilcClientEntry client_entry)
538 {
539   SilcAttributePayload attr;
540   SilcAttribute attribute;
541   char tmp[512];
542   SilcAttributeObjPk serverpk, usersign, serversign;
543   AttrVerify verify;
544
545   printformat_module("fe-common/silc", server, NULL,
546                      MSGLEVEL_CRAP, SILCTXT_ATTR_HEADER);
547
548   memset(&serverpk, 0, sizeof(serverpk));
549   memset(&usersign, 0, sizeof(usersign));
550   memset(&serversign, 0, sizeof(serversign));
551
552   verify = silc_calloc(1, sizeof(*verify));
553   if (!verify)
554     return;
555   verify->client = client;
556   verify->server = server;
557   verify->name = strdup(client_entry->nickname);
558
559   silc_dlist_start(attrs);
560   while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
561     attribute = silc_attribute_get_attribute(attr);
562     memset(tmp, 0, sizeof(tmp));
563
564     switch (attribute) {
565
566     case SILC_ATTRIBUTE_USER_INFO:
567       {
568         if (!silc_attribute_get_object(attr, (void *)&verify->vcard,
569                                        sizeof(verify->vcard)))
570           continue;
571         printformat_module("fe-common/silc", server, NULL,
572                            MSGLEVEL_CRAP, SILCTXT_ATTR_VCARD_FILE,
573                            "present");
574       }
575       break;
576
577     case SILC_ATTRIBUTE_SERVICE:
578       {
579         SilcAttributeObjService service;
580         memset(&service, 0, sizeof(service));
581         if (!silc_attribute_get_object(attr, (void *)&service,
582                                        sizeof(service)))
583           continue;
584         snprintf(tmp, sizeof(tmp) - 1, "%s:%d (logged %s)",
585                  service.address, (unsigned int)service.port,
586                  service.status ? "in" : "out");
587         printformat_module("fe-common/silc", server, NULL,
588                            MSGLEVEL_CRAP, SILCTXT_ATTR_SERVICES, tmp);
589       }
590       break;
591
592     case SILC_ATTRIBUTE_STATUS_MOOD:
593       {
594         SilcUInt32 mask;
595         if (!silc_attribute_get_object(attr, (void *)&mask, sizeof(mask)))
596           continue;
597         if (!mask)
598           silc_strncat(tmp, sizeof(tmp), "NORMAL ", strlen(" NORMAL"));
599         if (mask & SILC_ATTRIBUTE_MOOD_HAPPY)
600           silc_strncat(tmp, sizeof(tmp), "HAPPY ", strlen(" HAPPY"));
601         if (mask & SILC_ATTRIBUTE_MOOD_SAD)
602           silc_strncat(tmp, sizeof(tmp), "SAD ", strlen(" SAD"));
603         if (mask & SILC_ATTRIBUTE_MOOD_ANGRY)
604           silc_strncat(tmp, sizeof(tmp), "ANGRY ", strlen(" ANGRY"));
605         if (mask & SILC_ATTRIBUTE_MOOD_JEALOUS)
606           silc_strncat(tmp, sizeof(tmp), "JEALOUS ", strlen(" JEALOUS"));
607         if (mask & SILC_ATTRIBUTE_MOOD_ASHAMED)
608           silc_strncat(tmp, sizeof(tmp), "ASHAMED ", strlen(" ASHAMED"));
609         if (mask & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
610           silc_strncat(tmp, sizeof(tmp), "INVINCIBLE ", strlen(" INVINCIBLE"));
611         if (mask & SILC_ATTRIBUTE_MOOD_INLOVE)
612           silc_strncat(tmp, sizeof(tmp), "INLOVE ", strlen(" INLOVE"));
613         if (mask & SILC_ATTRIBUTE_MOOD_SLEEPY)
614           silc_strncat(tmp, sizeof(tmp), "SLEEPY ", strlen(" SLEEPY"));
615         if (mask & SILC_ATTRIBUTE_MOOD_BORED)
616           silc_strncat(tmp, sizeof(tmp), "BORED ", strlen(" BORED"));
617         if (mask & SILC_ATTRIBUTE_MOOD_EXCITED)
618           silc_strncat(tmp, sizeof(tmp), "EXCITED ", strlen(" EXCITED"));
619         if (mask & SILC_ATTRIBUTE_MOOD_ANXIOUS)
620           silc_strncat(tmp, sizeof(tmp), "ANXIOUS ", strlen(" ANXIOUS"));
621         printformat_module("fe-common/silc", server, NULL,
622                            MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MOOD, tmp);
623       }
624       break;
625
626     case SILC_ATTRIBUTE_STATUS_FREETEXT:
627       {
628         if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
629           continue;
630         printformat_module("fe-common/silc", server, NULL,
631                            MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_TEXT, tmp);
632       }
633       break;
634
635     case SILC_ATTRIBUTE_STATUS_MESSAGE:
636       {
637         if (!silc_attribute_get_object(attr, (void *)&verify->message,
638                                        sizeof(verify->message)))
639           continue;
640         printformat_module("fe-common/silc", server, NULL,
641                            MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MESSAGE,
642                            "present");
643       }
644       break;
645
646     case SILC_ATTRIBUTE_PREFERRED_LANGUAGE:
647       {
648         if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
649           continue;
650         printformat_module("fe-common/silc", server, NULL,
651                            MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_LANGUAGE,
652                            tmp);
653       }
654       break;
655
656     case SILC_ATTRIBUTE_PREFERRED_CONTACT:
657       {
658         SilcUInt32 mask;
659         if (!silc_attribute_get_object(attr, (void *)&mask, sizeof(mask)))
660           continue;
661         if (!mask)
662           silc_strncat(tmp, sizeof(tmp), "NONE ", strlen(" NONE"));
663         if (mask & SILC_ATTRIBUTE_CONTACT_CHAT)
664           silc_strncat(tmp, sizeof(tmp), "CHAT ", strlen(" CHAT"));
665         if (mask & SILC_ATTRIBUTE_CONTACT_EMAIL)
666           silc_strncat(tmp, sizeof(tmp), "EMAIL ", strlen(" EMAIL"));
667         if (mask & SILC_ATTRIBUTE_CONTACT_CALL)
668           silc_strncat(tmp, sizeof(tmp), "CALL ", strlen(" CALL"));
669         if (mask & SILC_ATTRIBUTE_CONTACT_PAGE)
670           silc_strncat(tmp, sizeof(tmp), "PAGE ", strlen(" PAGE"));
671         if (mask & SILC_ATTRIBUTE_CONTACT_SMS)
672           silc_strncat(tmp, sizeof(tmp), "SMS ", strlen(" SMS"));
673         if (mask & SILC_ATTRIBUTE_CONTACT_MMS)
674           silc_strncat(tmp, sizeof(tmp), "MMS ", strlen(" MMS"));
675         printformat_module("fe-common/silc", server, NULL,
676                            MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_CONTACT, tmp);
677       }
678       break;
679
680     case SILC_ATTRIBUTE_TIMEZONE:
681       {
682         if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
683           continue;
684         printformat_module("fe-common/silc", server, NULL,
685                            MSGLEVEL_CRAP, SILCTXT_ATTR_TIMEZONE, tmp);
686       }
687       break;
688
689     case SILC_ATTRIBUTE_EXTENSION:
690       {
691         if (!silc_attribute_get_object(attr, (void *)&verify->extension,
692                                        sizeof(verify->extension)))
693           continue;
694         printformat_module("fe-common/silc", server, NULL,
695                            MSGLEVEL_CRAP, SILCTXT_ATTR_EXTENSION,
696                            "present");
697       }
698       break;
699
700     case SILC_ATTRIBUTE_GEOLOCATION:
701       {
702         SilcAttributeObjGeo geo;
703         memset(&geo, 0, sizeof(geo));
704         if (!silc_attribute_get_object(attr, (void *)&geo, sizeof(geo)))
705           continue;
706         snprintf(tmp, sizeof(tmp) - 1, "%s:%s:%s:%s",
707                  geo.longitude ? geo.longitude : "",
708                  geo.latitude ? geo.latitude : "",
709                  geo.altitude ? geo.altitude : "",
710                  geo.accuracy ? geo.accuracy : "");
711         printformat_module("fe-common/silc", server, NULL,
712                            MSGLEVEL_CRAP, SILCTXT_ATTR_GEOLOCATION, tmp);
713       }
714       break;
715
716     case SILC_ATTRIBUTE_DEVICE_INFO:
717       {
718         SilcAttributeObjDevice dev;
719         memset(&dev, 0, sizeof(dev));
720         if (!silc_attribute_get_object(attr, (void *)&dev, sizeof(dev)))
721           continue;
722         snprintf(tmp, sizeof(tmp) - 1, "%s:%s:%s:%s:%s",
723                  (dev.type == SILC_ATTRIBUTE_DEVICE_COMPUTER ? "COMPUTER" :
724                   dev.type == SILC_ATTRIBUTE_DEVICE_PDA ? "PDA" :
725                   dev.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE ?
726                   "MOBILE PHONE" :
727                   dev.type == SILC_ATTRIBUTE_DEVICE_TERMINAL ? "TERMINAL" :
728                   ""),
729                  dev.manufacturer ? dev.manufacturer : "",
730                  dev.version ? dev.version : "",
731                  dev.model ? dev.model: "",
732                  dev.language ? dev.language : "");
733         printformat_module("fe-common/silc", server, NULL,
734                            MSGLEVEL_CRAP, SILCTXT_ATTR_DEVICE_INFO, tmp);
735       }
736       break;
737
738     case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
739       {
740         if (verify->userpk.type)
741           continue;
742         if (!silc_attribute_get_object(attr, (void *)&verify->userpk,
743                                        sizeof(verify->userpk)))
744           continue;
745       }
746       break;
747
748     case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
749       {
750         if (serverpk.type)
751           continue;
752         if (!silc_attribute_get_object(attr, (void *)&serverpk,
753                                        sizeof(serverpk)))
754           continue;
755       }
756       break;
757
758     case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
759       {
760         if (usersign.data)
761           continue;
762         if (!silc_attribute_get_object(attr, (void *)&usersign,
763                                        sizeof(usersign)))
764           continue;
765       }
766       break;
767
768     case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
769       {
770         if (serversign.data)
771           continue;
772         if (!silc_attribute_get_object(attr, (void *)&serversign,
773                                        sizeof(serversign)))
774           continue;
775       }
776       break;
777
778     default:
779       break;
780     }
781   }
782
783   /* Handle the signature verifications and public key verifying here */
784
785   if (usersign.data && !strcmp(verify->userpk.type, "silc-rsa")) {
786     /* Verify the signature now */
787     SilcPublicKey public_key;
788     SilcPKCS pkcs;
789     unsigned char *verifyd;
790     SilcUInt32 verify_len;
791
792     if (silc_pkcs_public_key_decode(verify->userpk.data,
793                                     verify->userpk.data_len,
794                                     &public_key)) {
795       silc_pkcs_alloc("rsa", &pkcs);
796       verifyd = silc_attribute_get_verify_data(attrs, FALSE, &verify_len);
797       if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)){
798         if (silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
799                                        usersign.data,
800                                        usersign.data_len,
801                                        verifyd, verify_len)) {
802           printformat_module("fe-common/silc", server, NULL,
803                              MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_VERIFIED);
804         } else {
805           printformat_module("fe-common/silc", server, NULL,
806                              MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_FAILED);
807         }
808       }
809
810       silc_pkcs_public_key_free(public_key);
811       silc_free(verifyd);
812     }
813   } else {
814     printformat_module("fe-common/silc", server, NULL,
815                        MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_NOT_PRESENT);
816   }
817
818   if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
819     /* Verify the signature now */
820     SilcPublicKey public_key;
821     SilcPKCS pkcs;
822     unsigned char *verifyd;
823     SilcUInt32 verify_len;
824
825     if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
826                                     &public_key)) {
827       silc_pkcs_alloc("rsa", &pkcs);
828       verifyd = silc_attribute_get_verify_data(attrs, TRUE, &verify_len);
829       if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
830         if (silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
831                                        serversign.data,
832                                        serversign.data_len,
833                                        verifyd, verify_len)) {
834           printformat_module("fe-common/silc", server, NULL,
835                              MSGLEVEL_CRAP, SILCTXT_ATTR_SERVER_SIGN_VERIFIED);
836         } else {
837           printformat_module("fe-common/silc", server, NULL,
838                              MSGLEVEL_CRAP, SILCTXT_ATTR_SERVER_SIGN_FAILED);
839         }
840       }
841
842       silc_pkcs_public_key_free(public_key);
843       silc_free(verifyd);
844     }
845   }
846
847   if (verify->userpk.data) {
848     silc_verify_public_key(client, conn, SILC_SOCKET_TYPE_CLIENT,
849                            verify->userpk.data, verify->userpk.data_len, 
850                            SILC_SKE_PK_TYPE_SILC,
851                            silc_query_attributes_print_final, verify);
852   } else {
853     verify->nopk = TRUE;
854     silc_query_attributes_print_final(FALSE, verify);
855   }
856 }
857
858 static void silc_query_attributes_print_final(bool success, void *context)
859 {
860   AttrVerify verify = context;
861   SilcClient client = verify->client;
862   SILC_SERVER_REC *server = verify->server;
863   char *format = NULL;
864   unsigned char filename[256], *fingerprint = NULL, *tmp;
865   struct stat st;
866   int i;
867
868   if (!verify->nopk) {
869     if (success) {
870       printformat_module("fe-common/silc", NULL, NULL,
871                          MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, "user",
872                          verify->name);
873     } else {
874       printformat_module("fe-common/silc", NULL, NULL,
875                          MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED, "user",
876                          verify->name);
877     }
878   }
879
880   printformat_module("fe-common/silc", server, NULL,
881                      MSGLEVEL_CRAP, SILCTXT_ATTR_FOOTER);
882
883   /* Replace all whitespaces with `_'. */
884   fingerprint = silc_hash_fingerprint(client->sha1hash,
885                                       verify->userpk.data,
886                                       verify->userpk.data_len);
887   for (i = 0; i < strlen(fingerprint); i++)
888     if (fingerprint[i] == ' ')
889       fingerprint[i] = '_';
890   
891   /* Filename for dir */
892   tmp = fingerprint + strlen(fingerprint) - 9;
893   snprintf(filename, sizeof(filename) - 1, "%s/friends/%s", 
894            get_irssi_dir(), tmp);
895   silc_free(fingerprint);
896
897   if ((stat(filename, &st)) == -1) {
898     /* Ask to accept save requested attributes */
899     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
900                              SILCTXT_ATTR_SAVE);
901     keyboard_entry_redirect((SIGNAL_FUNC)silc_query_attributes_accept,
902                             format, 0, verify);
903   } else {
904     /* Save new data to existing directory */
905     silc_query_attributes_accept("Y", verify);
906   }
907
908   g_free(format);
909 }
910
911 static void silc_query_attributes_accept(const char *line, void *context)
912 {
913   AttrVerify verify = context;
914   SilcClient client = verify->client;
915   SILC_SERVER_REC *server = verify->server;
916   struct stat st;
917   struct passwd *pw;
918   unsigned char filename[256], filename2[256], *fingerprint = NULL, *tmp;
919   SilcUInt32 len;
920   int i;
921
922   if (line[0] == 'Y' || line[0] == 'y') {
923     /* Save the attributes */
924     memset(filename, 0, sizeof(filename));
925     memset(filename2, 0, sizeof(filename2));
926
927     pw = getpwuid(getuid());
928     if (!pw)
929       goto out;
930
931     /* Replace all whitespaces with `_'. */
932     fingerprint = silc_hash_fingerprint(client->sha1hash,
933                                         verify->userpk.data,
934                                         verify->userpk.data_len);
935     for (i = 0; i < strlen(fingerprint); i++)
936       if (fingerprint[i] == ' ')
937         fingerprint[i] = '_';
938
939     /* Filename for dir */
940     tmp = fingerprint + strlen(fingerprint) - 9;
941     snprintf(filename, sizeof(filename) - 1, "%s/friends/%s", 
942              get_irssi_dir(), tmp);
943
944     /* Create dir if it doesn't exist */
945     if ((stat(filename, &st)) == -1) {
946       /* If dir doesn't exist */
947       if (errno == ENOENT) {
948         if (pw->pw_uid == geteuid()) {
949           if ((mkdir(filename, 0755)) == -1) {
950             silc_say_error("Couldn't create `%s' directory",
951                            filename);
952             goto out;
953           }
954         } else {
955           silc_say_error("Couldn't create `%s' directory due to a "
956                          "wrong uid!", filename);
957           goto out;
958         }
959       } else {
960         silc_say_error("%s", strerror(errno));
961         goto out;
962       }
963     }
964
965     /* Save the stuff to the directory */
966
967     /* Save VCard */
968     snprintf(filename2, sizeof(filename2) - 1, "%s/vcard", filename);
969     if (verify->vcard.full_name) {
970       tmp = silc_vcard_encode(&verify->vcard, &len);
971       silc_file_writefile(filename2, tmp, len);
972       silc_free(tmp);
973     }
974
975     /* Save public key */
976     memset(filename2, 0, sizeof(filename2));
977     snprintf(filename2, sizeof(filename2) - 1, "%s/clientkey_%s.pub",
978              filename, fingerprint);
979     silc_pkcs_save_public_key_data(filename2, verify->userpk.data,
980                                    verify->userpk.data_len,
981                                    SILC_PKCS_FILE_PEM);
982
983     /* Save extension data */
984     if (verify->extension.mime) {
985       memset(filename2, 0, sizeof(filename2));
986       snprintf(filename2, sizeof(filename2) - 1, "%s/extension.mime",
987                filename);
988       silc_file_writefile(filename2, verify->extension.mime,
989                           verify->extension.mime_len);
990     }
991
992     /* Save MIME message data */
993     if (verify->extension.mime) {
994       memset(filename2, 0, sizeof(filename2));
995       snprintf(filename2, sizeof(filename2) - 1, "%s/status_message.mime",
996                filename);
997       silc_file_writefile(filename2, verify->message.mime,
998                           verify->message.mime_len);
999     }
1000
1001     printformat_module("fe-common/silc", server, NULL,
1002                        MSGLEVEL_CRAP, SILCTXT_ATTR_SAVED, filename);
1003   }
1004
1005  out:
1006   silc_free(fingerprint);
1007   silc_free(verify->name);
1008   silc_vcard_free(&verify->vcard);
1009   silc_free(verify);
1010 }