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