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