Fixed some bugs in Requested Attributes support.
[silc.git] / lib / silcclient / client_attrs.c
1 /*
2
3   client_attrs.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 /* $Id$ */
20
21 #include "silcincludes.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 typedef struct {
26   SilcBuffer buffer;
27 } SilcAttrForeach;
28
29 /* Add one attribute that was found from hash table */
30
31 static void silc_client_attributes_process_foreach(void *key, void *context,
32                                                    void *user_context)
33 {
34   SilcAttribute attribute = (SilcAttribute)(SilcUInt32)key;
35   SilcAttributePayload attr = context;
36   SilcAttrForeach *f = user_context;
37   const unsigned char *data;
38   SilcUInt32 data_len;
39
40   if (!context) {
41     SILC_LOG_DEBUG(("Attribute %d was not set", attribute));
42
43     /* USER_PUBLIC_KEY we have set earlier */
44     if (attribute == SILC_ATTRIBUTE_USER_PUBLIC_KEY)
45       return;
46
47     /* The requested attribute was not found */
48     f->buffer = silc_attribute_payload_encode(f->buffer, attribute,
49                                               SILC_ATTRIBUTE_FLAG_INVALID,
50                                               NULL, 0);
51     return;
52   }
53
54   SILC_LOG_DEBUG(("Attribute %d found", attribute));
55   data = silc_attribute_get_data(attr, &data_len);
56
57   /* We replace the TIMEZONE with valid value here */
58   if (attribute == SILC_ATTRIBUTE_TIMEZONE) {
59     data = (const unsigned char *)silc_get_time(0);
60     data_len = strlen(data);
61     f->buffer = silc_attribute_payload_encode(f->buffer, attribute,
62                                               SILC_ATTRIBUTE_FLAG_VALID,
63                                               (void *)data, data_len);
64     return;
65   }
66
67   f->buffer = silc_attribute_payload_encode_data(f->buffer, attribute,
68                                                  SILC_ATTRIBUTE_FLAG_VALID,
69                                                  data, data_len);
70 }
71
72 /* Process list of attributes.  Returns reply to the requested attributes. */
73
74 SilcBuffer silc_client_attributes_process(SilcClient client,
75                                           SilcSocketConnection sock,
76                                           SilcDList attrs)
77 {
78   SilcClientConnection conn = sock->user_data;
79   SilcBuffer buffer = NULL;
80   SilcAttrForeach f;
81   SilcAttribute attribute;
82   SilcAttributePayload attr;
83   SilcAttributeObjPk pk;
84   unsigned char sign[2048];
85   SilcUInt32 sign_len;
86
87   SILC_LOG_DEBUG(("Process Requested Attributes"));
88
89   /* If nothing is set by application assume that we don't want to use
90      attributes, ignore the request. */
91   if (!conn->attrs)
92     return NULL;
93
94   /* Always put our public key. */
95   pk.type = "silc-rsa";
96   pk.data = silc_pkcs_public_key_encode(client->public_key, &pk.data_len);
97   buffer = silc_attribute_payload_encode(buffer,
98                                          SILC_ATTRIBUTE_USER_PUBLIC_KEY,
99                                          pk.data ? SILC_ATTRIBUTE_FLAG_VALID :
100                                          SILC_ATTRIBUTE_FLAG_INVALID,
101                                          &pk, sizeof(pk));
102   silc_free(pk.data);
103
104   /* Go through all requested attributes */
105   f.buffer = buffer;
106   silc_dlist_start(attrs);
107   while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
108     /* Put all attributes of this type */
109     attribute = silc_attribute_get_attribute(attr);
110
111     /* Ignore signature since we will compute it later */
112     if (attribute == SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE)
113       continue;
114
115     silc_hash_table_find_foreach(conn->attrs, (void *)(SilcUInt32)attribute,
116                                  silc_client_attributes_process_foreach,
117                                  &f);
118   }
119   buffer = f.buffer;
120
121   /* Finally compute the digital signature of all the data we provided. */
122   if (silc_pkcs_sign_with_hash(client->pkcs, client->sha1hash,
123                                buffer->data, buffer->len,
124                                sign, &sign_len)) {
125     pk.type = NULL;
126     pk.data = sign;
127     pk.data_len = sign_len;
128     buffer =
129       silc_attribute_payload_encode(buffer,
130                                     SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE,
131                                     SILC_ATTRIBUTE_FLAG_VALID,
132                                     &pk, sizeof(pk));
133   }
134
135   return buffer;
136 }
137
138 static void silc_client_attribute_destruct(void *key, void *context,
139                                            void *user_context)
140 {
141   silc_attribute_payload_free(context);
142 }
143
144 /* Add new attribute */
145
146 SilcAttributePayload silc_client_attribute_add(SilcClient client,
147                                                SilcClientConnection conn,
148                                                SilcAttribute attribute,
149                                                void *object,
150                                                SilcUInt32 object_size)
151 {
152   SilcAttributePayload attr;
153
154   attr = silc_attribute_payload_alloc(attribute, SILC_ATTRIBUTE_FLAG_VALID,
155                                       object, object_size);
156   if (!attr)
157     return NULL;
158
159   if (!conn->attrs)
160     conn->attrs = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL,
161                                         NULL, silc_client_attribute_destruct,
162                                         NULL, TRUE);
163   silc_hash_table_add(conn->attrs, (void *)(SilcUInt32)attribute, attr);
164   return attr;
165 }
166
167 static void silc_client_attribute_del_foreach(void *key, void *context,
168                                               void *user_context)
169 {
170   SilcClientConnection conn = user_context;
171   SilcAttributePayload attr = context;
172   SilcAttribute attribute;
173   if (!attr)
174     return;
175   attribute = silc_attribute_get_attribute(attr);
176   silc_hash_table_del_by_context(conn->attrs,
177                                  (void *)(SilcUInt32)attribute, attr);
178 }
179
180 /* Delete one attribute */
181
182 bool silc_client_attribute_del(SilcClient client,
183                                SilcClientConnection conn,
184                                SilcAttribute attribute,
185                                SilcAttributePayload attr)
186 {
187   bool ret;
188
189   if (!conn->attrs)
190     return FALSE;
191
192   if (attr) {
193     attribute = silc_attribute_get_attribute(attr);
194     ret = silc_hash_table_del_by_context(conn->attrs,
195                                          (void *)(SilcUInt32)attribute, attr);
196   } else if (attribute) {
197     silc_hash_table_find_foreach(conn->attrs, (void *)(SilcUInt32)attribute,
198                                  silc_client_attribute_del_foreach, conn);
199     ret = TRUE;
200   } else{
201     return FALSE;
202   }
203
204   if (ret)
205     if (!silc_hash_table_count(conn->attrs)) {
206       silc_hash_table_free(conn->attrs);
207       conn->attrs = NULL;
208     }
209
210   return ret;
211 }
212
213 /* Return all attributes */
214
215 const SilcHashTable silc_client_attributes_get(SilcClient client,
216                                                SilcClientConnection conn)
217 {
218   return (const SilcHashTable)conn->attrs;
219 }
220
221 /* Construct a Requested Attributes buffer. If the `attribute' is zero (0)
222    then all attributes are requested.  Additionally `attribute' and
223    all variable arguments can be one requested attribute.  Always set
224    the last requested attribute to zero (0) to complete list of
225    requested attribute. */
226
227 SilcBuffer silc_client_attributes_request(SilcAttribute attribute, ...)
228 {
229   va_list va;
230   SilcBuffer buffer = NULL;
231
232   if (!attribute)
233     return silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
234                                           SILC_ATTRIBUTE_SERVICE,
235                                           SILC_ATTRIBUTE_STATUS_MOOD,
236                                           SILC_ATTRIBUTE_STATUS_FREETEXT,
237                                           SILC_ATTRIBUTE_STATUS_MESSAGE,
238                                           SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
239                                           SILC_ATTRIBUTE_PREFERRED_CONTACT,
240                                           SILC_ATTRIBUTE_TIMEZONE,
241                                           SILC_ATTRIBUTE_GEOLOCATION,
242                                           SILC_ATTRIBUTE_DEVICE_INFO,
243                                           SILC_ATTRIBUTE_USER_PUBLIC_KEY, 0);
244
245   va_start(va, attribute);
246   while (attribute) {
247     buffer = silc_attribute_payload_encode(buffer, attribute, 0, NULL, 0);
248     attribute = (SilcAttribute)va_arg(va, SilcUInt32);
249   }
250   va_end(va);
251
252   return buffer;
253 }