Added support for saving requested attributes.
[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 && silc_vcard_decode(tmp, tmp_len, &vcard))
271       silc_client_attribute_add(silc_client, conn,
272                                 SILC_ATTRIBUTE_USER_INFO, (void *)&vcard,
273                                 sizeof(vcard));
274     silc_vcard_free(&vcard);
275     silc_free(tmp);
276   }
277
278   sv = settings_get_str("attr_services");
279   if (sv && *sv) {
280     /* Put SERVICE */
281     silc_client_attribute_del(silc_client, conn,
282                               SILC_ATTRIBUTE_SERVICE, NULL);
283     list = g_strsplit(sv, " ", -1);
284     for (entry = list; *entry != NULL; entry++) {
285       if (!strchr(*entry, ':'))
286         continue;
287       tmp = strchr(*entry, ':') + 1;
288       if (!tmp || !(*tmp))
289         continue;
290       service.port = atoi(tmp);
291       *strchr(*entry, ':') = '\0';
292       silc_strncat(service.address, sizeof(service.address), *entry,
293                    strlen(*entry));
294       service.status = TRUE;
295       silc_client_attribute_add(silc_client, conn,
296                                 SILC_ATTRIBUTE_SERVICE, &service,
297                                 sizeof(service));
298     }
299     g_strfreev(list);
300   }
301
302   sv = settings_get_str("attr_status_mood");
303   if (sv && *sv) {
304     /* Put STATUS_MOOD */
305     silc_client_attribute_del(silc_client, conn,
306                               SILC_ATTRIBUTE_STATUS_MOOD, NULL);
307     mask = 0;
308     list = g_strsplit(sv, " ", -1);
309     for (entry = list; *entry != NULL; entry++) {
310       if (!strcasecmp(*entry, "NORMAL"))
311         mask |= SILC_ATTRIBUTE_MOOD_NORMAL;
312       if (!strcasecmp(*entry, "HAPPY"))
313         mask |= SILC_ATTRIBUTE_MOOD_HAPPY;
314       if (!strcasecmp(*entry, "SAD"))
315         mask |= SILC_ATTRIBUTE_MOOD_SAD;
316       if (!strcasecmp(*entry, "ANGRY"))
317         mask |= SILC_ATTRIBUTE_MOOD_ANGRY;
318       if (!strcasecmp(*entry, "JEALOUS"))
319         mask |= SILC_ATTRIBUTE_MOOD_JEALOUS;
320       if (!strcasecmp(*entry, "ASHAMED"))
321         mask |= SILC_ATTRIBUTE_MOOD_ASHAMED;
322       if (!strcasecmp(*entry, "INVINCIBLE"))
323         mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE;
324       if (!strcasecmp(*entry, "INLOVE"))
325         mask |= SILC_ATTRIBUTE_MOOD_INLOVE;
326       if (!strcasecmp(*entry, "SLEEPY"))
327         mask |= SILC_ATTRIBUTE_MOOD_SLEEPY;
328       if (!strcasecmp(*entry, "BORED"))
329         mask |= SILC_ATTRIBUTE_MOOD_BORED;
330       if (!strcasecmp(*entry, "EXCITED"))
331         mask |= SILC_ATTRIBUTE_MOOD_EXCITED;
332       if (!strcasecmp(*entry, "ANXIOUS"))
333         mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS;
334       silc_client_attribute_add(silc_client, conn,
335                                 SILC_ATTRIBUTE_STATUS_MOOD, (void *)mask,
336                                 sizeof(SilcUInt32));
337     }
338     g_strfreev(list);
339   }
340
341   sv = settings_get_str("attr_status_text");
342   if (sv && *sv) {
343     /* Put STATUS_TEXT */
344     silc_client_attribute_del(silc_client, conn,
345                               SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
346     silc_client_attribute_add(silc_client, conn,
347                               SILC_ATTRIBUTE_STATUS_FREETEXT, (void *)sv,
348                               strlen(sv));
349   }
350
351   sv = settings_get_str("attr_status_message");
352   if (sv && *sv) {
353     /* Put STATUS_MESSAGE */
354     silc_client_attribute_del(silc_client, conn,
355                               SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
356     tmp = silc_file_readfile(sv, &tmp_len);
357     if (tmp) {
358       mime.mime = (const unsigned char *)tmp;
359       mime.mime_len = tmp_len;
360       silc_client_attribute_add(silc_client, conn,
361                                 SILC_ATTRIBUTE_STATUS_MESSAGE, &mime,
362                                 sizeof(mime));
363     }
364     silc_free(tmp);
365   }
366
367   sv = settings_get_str("attr_preferred_language");
368   if (sv && *sv) {
369     /* Put PREFERRED_LANGUAGE */
370     silc_client_attribute_del(silc_client, conn,
371                               SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
372     list = g_strsplit(sv, " ", -1);
373     for (entry = list; *entry != NULL; entry++) {
374       silc_client_attribute_add(silc_client, conn,
375                                 SILC_ATTRIBUTE_PREFERRED_LANGUAGE, *entry,
376                                 strlen(*entry));
377     }
378     g_strfreev(list);
379   }
380
381   sv = settings_get_str("attr_preferred_contact");
382   if (sv && *sv) {
383     /* Put PREFERRED_CONTACT */
384     silc_client_attribute_del(silc_client, conn,
385                               SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
386     mask = 0;
387     list = g_strsplit(sv, " ", -1);
388     for (entry = list; *entry != NULL; entry++) {
389       if (!strcasecmp(*entry, "NONE"))
390         mask = 0;
391       if (!strcasecmp(*entry, "EMAIL"))
392         mask |= SILC_ATTRIBUTE_CONTACT_EMAIL;
393       if (!strcasecmp(*entry, "CALL"))
394         mask |= SILC_ATTRIBUTE_CONTACT_CALL;
395       if (!strcasecmp(*entry, "PAGE"))
396         mask |= SILC_ATTRIBUTE_CONTACT_PAGE;
397       if (!strcasecmp(*entry, "SMS"))
398         mask |= SILC_ATTRIBUTE_CONTACT_SMS;
399       if (!strcasecmp(*entry, "MMS"))
400         mask |= SILC_ATTRIBUTE_CONTACT_MMS;
401       if (!strcasecmp(*entry, "CHAT"))
402         mask |= SILC_ATTRIBUTE_CONTACT_CHAT;
403       silc_client_attribute_add(silc_client, conn,
404                                 SILC_ATTRIBUTE_PREFERRED_CONTACT, (void *)mask,
405                                 sizeof(SilcUInt32));
406     }
407     g_strfreev(list);
408   }
409
410   /* Put TIMEZONE */
411   allowed = settings_get_bool("attr_timezone");
412   silc_client_attribute_del(silc_client, conn,
413                             SILC_ATTRIBUTE_TIMEZONE, NULL);
414   if (allowed)
415     silc_client_attribute_add(silc_client, conn,
416                               SILC_ATTRIBUTE_TIMEZONE, "foo", 3);
417
418   sv = settings_get_str("attr_geolocation");
419   if (sv && *sv) {
420     /* Put GEOLOCATION */
421     silc_client_attribute_del(silc_client, conn,
422                               SILC_ATTRIBUTE_GEOLOCATION, NULL);
423     list = g_strsplit(sv, ":", -1);
424     for (entry = list; *entry != NULL; entry++) {
425       if (!geo.longitude) {
426         geo.longitude = *entry;
427         continue;
428       }
429       if (!geo.latitude) {
430         geo.latitude = *entry;
431         continue;
432       }
433       if (!geo.altitude) {
434         geo.altitude = *entry;
435         continue;
436       }
437       if (!geo.accuracy) {
438         geo.accuracy = *entry;
439         continue;
440       }
441     }
442     silc_client_attribute_add(silc_client, conn,
443                               SILC_ATTRIBUTE_GEOLOCATION, &geo,
444                               sizeof(geo));
445     g_strfreev(list);
446   }
447
448   sv = settings_get_str("attr_device_info");
449   if (sv && *sv) {
450     /* Put DEVICE_INFO */
451     silc_client_attribute_del(silc_client, conn,
452                               SILC_ATTRIBUTE_DEVICE_INFO, NULL);
453     allowed = FALSE;
454     list = g_strsplit(sv, ":", -1);
455     for (entry = list; *entry != NULL; entry++) {
456       if (!allowed) {
457         allowed = TRUE;
458         if (!strcasecmp(*entry, "COMPUTER"))
459           dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
460         if (!strcasecmp(*entry, "MOBILE_PHONE"))
461           dev.type = SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE;
462         if (!strcasecmp(sv, "PDA"))
463           dev.type = SILC_ATTRIBUTE_DEVICE_PDA;
464         if (!strcasecmp(sv, "TERMINAL"))
465           dev.type = SILC_ATTRIBUTE_DEVICE_TERMINAL;
466         continue;
467       }
468       if (!dev.manufacturer) {
469         dev.manufacturer = *entry;
470         continue;
471       }
472       if (!dev.version) {
473         dev.version = *entry;
474         continue;
475       }
476       if (!dev.model) {
477         dev.model = *entry;
478         continue;
479       }
480       if (!dev.language) {
481         dev.language = *entry;
482         continue;
483       }
484     }
485     silc_client_attribute_add(silc_client, conn,
486                               SILC_ATTRIBUTE_DEVICE_INFO, &dev,
487                               sizeof(dev));
488     g_strfreev(list);
489   }
490
491   sv = settings_get_str("attr_public_keys");
492   if (sv && *sv) {
493     /* Put USER_PUBLIC_KEY */
494     silc_client_attribute_del(silc_client, conn,
495                               SILC_ATTRIBUTE_USER_PUBLIC_KEY, NULL);
496     list = g_strsplit(sv, " ", -1);
497     for (entry = list; *entry != NULL; entry++) {
498       if (!strncasecmp(*entry, "silc-rsa:", 8)) {
499         tmp = silc_file_readfile((*entry) + 8, &tmp_len);
500         if (tmp) {
501           pk.type = "silc-rsa";
502           pk.data = tmp;
503           pk.data_len = tmp_len;
504           silc_client_attribute_add(silc_client, conn,
505                                     SILC_ATTRIBUTE_USER_PUBLIC_KEY, &pk,
506                                     sizeof(pk));
507         }
508         silc_free(tmp);
509       } else {
510         silc_say_error("Unsupported public key type '%s'", *entry);
511       }
512     }
513     g_strfreev(list);
514   }
515 }
516
517 typedef struct {
518   SilcClient client;
519   SILC_SERVER_REC *server;
520   char *name;
521   SilcAttributeObjPk userpk;
522   SilcVCardStruct vcard;
523   SilcAttributeObjMime message;
524   SilcAttributeObjMime extension;
525 } *AttrVerify;
526
527 void silc_query_attributes_print(SILC_SERVER_REC *server,
528                                  SilcClient client,
529                                  SilcClientConnection conn,
530                                  SilcDList attrs,
531                                  SilcClientEntry client_entry)
532 {
533   SilcAttributePayload attr;
534   SilcAttribute attribute;
535   char tmp[512];
536   SilcAttributeObjPk serverpk, usersign, serversign;
537   AttrVerify verify;
538
539   printformat_module("fe-common/silc", server, NULL,
540                      MSGLEVEL_CRAP, SILCTXT_ATTR_HEADER);
541
542   memset(&serverpk, 0, sizeof(serverpk));
543   memset(&usersign, 0, sizeof(usersign));
544   memset(&serversign, 0, sizeof(serversign));
545
546   verify = silc_calloc(1, sizeof(*verify));
547   if (!verify)
548     return;
549   verify->client = client;
550   verify->server = server;
551   verify->name = strdup(client_entry->nickname);
552
553   silc_dlist_start(attrs);
554   while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
555     attribute = silc_attribute_get_attribute(attr);
556     memset(tmp, 0, sizeof(tmp));
557
558     switch (attribute) {
559
560     case SILC_ATTRIBUTE_USER_INFO:
561       {
562         if (!silc_attribute_get_object(attr, (void *)&verify->vcard,
563                                        sizeof(verify->vcard)))
564           continue;
565         printformat_module("fe-common/silc", server, NULL,
566                            MSGLEVEL_CRAP, SILCTXT_ATTR_VCARD_FILE,
567                            "present");
568       }
569       break;
570
571     case SILC_ATTRIBUTE_SERVICE:
572       {
573         SilcAttributeObjService service;
574         if (!silc_attribute_get_object(attr, (void *)&service,
575                                        sizeof(service)))
576           continue;
577         snprintf(tmp, sizeof(tmp) - 1, "%s:%d (logged %s)",
578                  service.address, (unsigned int)service.port,
579                  service.status ? "in" : "out");
580         printformat_module("fe-common/silc", server, NULL,
581                            MSGLEVEL_CRAP, SILCTXT_ATTR_SERVICES, tmp);
582       }
583       break;
584
585     case SILC_ATTRIBUTE_STATUS_MOOD:
586       {
587         SilcUInt32 mask;
588         if (!silc_attribute_get_object(attr, (void *)&mask, sizeof(mask)))
589           continue;
590         if (!mask)
591           silc_strncat(tmp, sizeof(tmp), "NORMAL ", strlen(" NORMAL"));
592         if (mask & SILC_ATTRIBUTE_MOOD_HAPPY)
593           silc_strncat(tmp, sizeof(tmp), "HAPPY ", strlen(" HAPPY"));
594         if (mask & SILC_ATTRIBUTE_MOOD_SAD)
595           silc_strncat(tmp, sizeof(tmp), "SAD ", strlen(" SAD"));
596         if (mask & SILC_ATTRIBUTE_MOOD_ANGRY)
597           silc_strncat(tmp, sizeof(tmp), "ANGRY ", strlen(" ANGRY"));
598         if (mask & SILC_ATTRIBUTE_MOOD_JEALOUS)
599           silc_strncat(tmp, sizeof(tmp), "JEALOUS ", strlen(" JEALOUS"));
600         if (mask & SILC_ATTRIBUTE_MOOD_ASHAMED)
601           silc_strncat(tmp, sizeof(tmp), "ASHAMED ", strlen(" ASHAMED"));
602         if (mask & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
603           silc_strncat(tmp, sizeof(tmp), "INVINCIBLE ", strlen(" INVINCIBLE"));
604         if (mask & SILC_ATTRIBUTE_MOOD_INLOVE)
605           silc_strncat(tmp, sizeof(tmp), "INLOVE ", strlen(" INLOVE"));
606         if (mask & SILC_ATTRIBUTE_MOOD_SLEEPY)
607           silc_strncat(tmp, sizeof(tmp), "SLEEPY ", strlen(" SLEEPY"));
608         if (mask & SILC_ATTRIBUTE_MOOD_BORED)
609           silc_strncat(tmp, sizeof(tmp), "BORED ", strlen(" BORED"));
610         if (mask & SILC_ATTRIBUTE_MOOD_EXCITED)
611           silc_strncat(tmp, sizeof(tmp), "EXCITED ", strlen(" EXCITED"));
612         if (mask & SILC_ATTRIBUTE_MOOD_ANXIOUS)
613           silc_strncat(tmp, sizeof(tmp), "ANXIOUS ", strlen(" ANXIOUS"));
614         printformat_module("fe-common/silc", server, NULL,
615                            MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MOOD, tmp);
616       }
617       break;
618
619     case SILC_ATTRIBUTE_STATUS_FREETEXT:
620       {
621         if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
622           continue;
623         printformat_module("fe-common/silc", server, NULL,
624                            MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_TEXT, tmp);
625       }
626       break;
627
628     case SILC_ATTRIBUTE_STATUS_MESSAGE:
629       {
630         if (!silc_attribute_get_object(attr, (void *)&verify->message,
631                                        sizeof(verify->message)))
632           continue;
633         printformat_module("fe-common/silc", server, NULL,
634                            MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MESSAGE,
635                            "present");
636       }
637       break;
638
639     case SILC_ATTRIBUTE_PREFERRED_LANGUAGE:
640       {
641         if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
642           continue;
643         printformat_module("fe-common/silc", server, NULL,
644                            MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_LANGUAGE,
645                            tmp);
646       }
647       break;
648
649     case SILC_ATTRIBUTE_PREFERRED_CONTACT:
650       {
651         SilcUInt32 mask;
652         if (!silc_attribute_get_object(attr, (void *)&mask, sizeof(mask)))
653           continue;
654         if (!mask)
655           silc_strncat(tmp, sizeof(tmp), "NONE ", strlen(" NONE"));
656         if (mask & SILC_ATTRIBUTE_CONTACT_CHAT)
657           silc_strncat(tmp, sizeof(tmp), "CHAT ", strlen(" CHAT"));
658         if (mask & SILC_ATTRIBUTE_CONTACT_EMAIL)
659           silc_strncat(tmp, sizeof(tmp), "EMAIL ", strlen(" EMAIL"));
660         if (mask & SILC_ATTRIBUTE_CONTACT_CALL)
661           silc_strncat(tmp, sizeof(tmp), "CALL ", strlen(" CALL"));
662         if (mask & SILC_ATTRIBUTE_CONTACT_PAGE)
663           silc_strncat(tmp, sizeof(tmp), "PAGE ", strlen(" PAGE"));
664         if (mask & SILC_ATTRIBUTE_CONTACT_SMS)
665           silc_strncat(tmp, sizeof(tmp), "SMS ", strlen(" SMS"));
666         if (mask & SILC_ATTRIBUTE_CONTACT_MMS)
667           silc_strncat(tmp, sizeof(tmp), "MMS ", strlen(" MMS"));
668         printformat_module("fe-common/silc", server, NULL,
669                            MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_CONTACT, tmp);
670       }
671       break;
672
673     case SILC_ATTRIBUTE_TIMEZONE:
674       {
675         if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
676           continue;
677         printformat_module("fe-common/silc", server, NULL,
678                            MSGLEVEL_CRAP, SILCTXT_ATTR_TIMEZONE, tmp);
679       }
680       break;
681
682     case SILC_ATTRIBUTE_EXTENSION:
683       {
684         if (!silc_attribute_get_object(attr, (void *)&verify->extension,
685                                        sizeof(verify->extension)))
686           continue;
687         printformat_module("fe-common/silc", server, NULL,
688                            MSGLEVEL_CRAP, SILCTXT_ATTR_EXTENSION,
689                            "present");
690       }
691       break;
692
693     case SILC_ATTRIBUTE_GEOLOCATION:
694       {
695         SilcAttributeObjGeo geo;
696         memset(&geo, 0, sizeof(geo));
697         if (!silc_attribute_get_object(attr, (void *)&geo, sizeof(geo)))
698           continue;
699         snprintf(tmp, sizeof(tmp) - 1, "%s:%s:%s:%s",
700                  geo.longitude ? geo.longitude : "",
701                  geo.latitude ? geo.latitude : "",
702                  geo.altitude ? geo.altitude : "",
703                  geo.accuracy ? geo.accuracy : "");
704         printformat_module("fe-common/silc", server, NULL,
705                            MSGLEVEL_CRAP, SILCTXT_ATTR_GEOLOCATION, tmp);
706       }
707       break;
708
709     case SILC_ATTRIBUTE_DEVICE_INFO:
710       {
711         SilcAttributeObjDevice dev;
712         memset(&dev, 0, sizeof(dev));
713         if (!silc_attribute_get_object(attr, (void *)&dev, sizeof(dev)))
714           continue;
715         snprintf(tmp, sizeof(tmp) - 1, "%s:%s:%s:%s:%s",
716                  (dev.type == SILC_ATTRIBUTE_DEVICE_COMPUTER ? "COMPUTER" :
717                   dev.type == SILC_ATTRIBUTE_DEVICE_PDA ? "PDA" :
718                   dev.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE ?
719                   "MOBILE PHONE" :
720                   dev.type == SILC_ATTRIBUTE_DEVICE_TERMINAL ? "TERMINAL" :
721                   ""),
722                  dev.manufacturer ? dev.manufacturer : "",
723                  dev.version ? dev.version : "",
724                  dev.model ? dev.model: "",
725                  dev.language ? dev.language : "");
726         printformat_module("fe-common/silc", server, NULL,
727                            MSGLEVEL_CRAP, SILCTXT_ATTR_DEVICE_INFO, tmp);
728       }
729       break;
730
731     case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
732       {
733         if (verify->userpk.type)
734           continue;
735         if (!silc_attribute_get_object(attr, (void *)&verify->userpk,
736                                        sizeof(verify->userpk)))
737           continue;
738       }
739       break;
740
741     case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
742       {
743         if (serverpk.type)
744           continue;
745         if (!silc_attribute_get_object(attr, (void *)&serverpk,
746                                        sizeof(serverpk)))
747           continue;
748       }
749       break;
750
751     case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
752       {
753         if (usersign.data)
754           continue;
755         if (!silc_attribute_get_object(attr, (void *)&usersign,
756                                        sizeof(usersign)))
757           continue;
758       }
759       break;
760
761     case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
762       {
763         if (serversign.data)
764           continue;
765         if (!silc_attribute_get_object(attr, (void *)&serversign,
766                                        sizeof(serversign)))
767           continue;
768       }
769       break;
770
771     default:
772       break;
773     }
774   }
775
776   /* Handle the signature verifications and public key verifying here */
777
778   if (usersign.data && !strcmp(verify->userpk.type, "silc-rsa")) {
779     /* Verify the signature now */
780     SilcPublicKey public_key;
781     SilcPKCS pkcs;
782     unsigned char *verifyd;
783     SilcUInt32 verify_len;
784
785     if (silc_pkcs_public_key_decode(verify->userpk.data,
786                                     verify->userpk.data_len,
787                                     &public_key)) {
788       silc_pkcs_alloc("rsa", &pkcs);
789       verifyd = silc_attribute_get_verify_data(attrs, FALSE, &verify_len);
790       if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)){
791         if (silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
792                                        usersign.data,
793                                        usersign.data_len,
794                                        verifyd, verify_len)) {
795           printformat_module("fe-common/silc", server, NULL,
796                              MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_VERIFIED);
797         } else {
798           printformat_module("fe-common/silc", server, NULL,
799                              MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_FAILED);
800         }
801       }
802
803       silc_pkcs_public_key_free(public_key);
804       silc_free(verifyd);
805     }
806   } else {
807     printformat_module("fe-common/silc", server, NULL,
808                        MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_NOT_PRESENT);
809   }
810
811   if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
812     /* Verify the signature now */
813     SilcPublicKey public_key;
814     SilcPKCS pkcs;
815     unsigned char *verifyd;
816     SilcUInt32 verify_len;
817
818     if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
819                                     &public_key)) {
820       silc_pkcs_alloc("rsa", &pkcs);
821       verifyd = silc_attribute_get_verify_data(attrs, TRUE, &verify_len);
822       if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
823         if (silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
824                                        serversign.data,
825                                        serversign.data_len,
826                                        verifyd, verify_len)) {
827           printformat_module("fe-common/silc", server, NULL,
828                              MSGLEVEL_CRAP, SILCTXT_ATTR_SERVER_SIGN_VERIFIED);
829         } else {
830           printformat_module("fe-common/silc", server, NULL,
831                              MSGLEVEL_CRAP, SILCTXT_ATTR_SERVER_SIGN_FAILED);
832         }
833       }
834
835       silc_pkcs_public_key_free(public_key);
836       silc_free(verifyd);
837     }
838   }
839
840   if (!verify->userpk.type || !usersign.data)
841     printformat_module("fe-common/silc", server, NULL,
842                        MSGLEVEL_CRAP, SILCTXT_ATTR_FOOTER);
843
844   silc_verify_public_key(client, conn, SILC_SOCKET_TYPE_CLIENT,
845                          verify->userpk.data, verify->userpk.data_len, 
846                          SILC_SKE_PK_TYPE_SILC,
847                          silc_query_attributes_print_final, verify);
848 }
849
850 static void silc_query_attributes_print_final(bool success, void *context)
851 {
852   AttrVerify verify = context;
853   SilcClient client = verify->client;
854   SILC_SERVER_REC *server = verify->server;
855   char *format = NULL;
856   unsigned char filename[256], *fingerprint = NULL, *tmp;
857   struct stat st;
858   int i;
859
860   if (success) {
861     printformat_module("fe-common/silc", NULL, NULL,
862                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, "user",
863                        verify->name);
864   } else {
865     printformat_module("fe-common/silc", NULL, NULL,
866                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED, "user",
867                        verify->name);
868   }
869
870   printformat_module("fe-common/silc", server, NULL,
871                      MSGLEVEL_CRAP, SILCTXT_ATTR_FOOTER);
872
873   /* Replace all whitespaces with `_'. */
874   fingerprint = silc_hash_fingerprint(client->sha1hash,
875                                       verify->userpk.data,
876                                       verify->userpk.data_len);
877   for (i = 0; i < strlen(fingerprint); i++)
878     if (fingerprint[i] == ' ')
879       fingerprint[i] = '_';
880   
881   /* Filename for dir */
882   tmp = fingerprint + strlen(fingerprint) - 9;
883   snprintf(filename, sizeof(filename) - 1, "%s/friends/%s", 
884            get_irssi_dir(), tmp);
885   silc_free(fingerprint);
886
887   if ((stat(filename, &st)) == -1) {
888     /* Ask to accept save requested attributes */
889     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
890                              SILCTXT_ATTR_SAVE);
891     keyboard_entry_redirect((SIGNAL_FUNC)silc_query_attributes_accept,
892                             format, 0, verify);
893   } else {
894     /* Save new data to existing directory */
895     silc_query_attributes_accept("Y", verify);
896   }
897
898   g_free(format);
899 }
900
901 static void silc_query_attributes_accept(const char *line, void *context)
902 {
903   AttrVerify verify = context;
904   SilcClient client = verify->client;
905   SILC_SERVER_REC *server = verify->server;
906   struct stat st;
907   struct passwd *pw;
908   unsigned char filename[256], filename2[256], *fingerprint = NULL, *tmp;
909   SilcUInt32 len;
910   int i;
911
912   if (line[0] == 'Y' || line[0] == 'y') {
913     /* Save the attributes */
914     memset(filename, 0, sizeof(filename));
915     memset(filename2, 0, sizeof(filename2));
916
917     pw = getpwuid(getuid());
918     if (!pw)
919       goto out;
920
921     /* Replace all whitespaces with `_'. */
922     fingerprint = silc_hash_fingerprint(client->sha1hash,
923                                         verify->userpk.data,
924                                         verify->userpk.data_len);
925     for (i = 0; i < strlen(fingerprint); i++)
926       if (fingerprint[i] == ' ')
927         fingerprint[i] = '_';
928
929     /* Filename for dir */
930     tmp = fingerprint + strlen(fingerprint) - 9;
931     snprintf(filename, sizeof(filename) - 1, "%s/friends/%s", 
932              get_irssi_dir(), tmp);
933
934     /* Create dir if it doesn't exist */
935     if ((stat(filename, &st)) == -1) {
936       /* If dir doesn't exist */
937       if (errno == ENOENT) {
938         if (pw->pw_uid == geteuid()) {
939           if ((mkdir(filename, 0755)) == -1) {
940             silc_say_error("Couldn't create `%s' directory",
941                            filename);
942             goto out;
943           }
944         } else {
945           silc_say_error("Couldn't create `%s' directory due to a "
946                          "wrong uid!", filename);
947           goto out;
948         }
949       } else {
950         silc_say_error("%s", strerror(errno));
951         goto out;
952       }
953     }
954
955     /* Save the stuff to the directory */
956
957     /* Save VCard */
958     snprintf(filename2, sizeof(filename2) - 1, "%s/vcard", filename);
959     if (verify->vcard.full_name) {
960       tmp = silc_vcard_encode(&verify->vcard, &len);
961       silc_file_writefile(filename2, tmp, len);
962       silc_free(tmp);
963     }
964
965     /* Save public key */
966     memset(filename2, 0, sizeof(filename2));
967     snprintf(filename2, sizeof(filename2) - 1, "%s/clientkey_%s.pub",
968              filename, fingerprint);
969     silc_pkcs_save_public_key_data(filename2, verify->userpk.data,
970                                    verify->userpk.data_len,
971                                    SILC_PKCS_FILE_PEM);
972
973     /* Save extension data */
974     if (verify->extension.mime) {
975       memset(filename2, 0, sizeof(filename2));
976       snprintf(filename2, sizeof(filename2) - 1, "%s/extension.mime",
977                filename);
978       silc_file_writefile(filename2, verify->extension.mime,
979                           verify->extension.mime_len);
980     }
981
982     /* Save MIME message data */
983     if (verify->extension.mime) {
984       memset(filename2, 0, sizeof(filename2));
985       snprintf(filename2, sizeof(filename2) - 1, "%s/status_message.mime",
986                filename);
987       silc_file_writefile(filename2, verify->message.mime,
988                           verify->message.mime_len);
989     }
990
991     printformat_module("fe-common/silc", server, NULL,
992                        MSGLEVEL_CRAP, SILCTXT_ATTR_SAVED, filename);
993   }
994
995  out:
996   silc_free(fingerprint);
997   silc_free(verify->name);
998   silc_vcard_free(&verify->vcard);
999   silc_free(verify);
1000 }