More client library rewrites.
[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 - 2004, 2006 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 } 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)SILC_PTR_TO_32(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 #if 0
58   /* We replace the TIMEZONE with valid value here */
59   if (attribute == SILC_ATTRIBUTE_TIMEZONE) {
60     data = (const unsigned char *)silc_get_time(0);
61     data_len = strlen(data);
62     f->buffer = silc_attribute_payload_encode(f->buffer, attribute,
63                                               SILC_ATTRIBUTE_FLAG_VALID,
64                                               (void *)data, data_len);
65     return;
66   }
67 #endif
68
69   f->buffer = silc_attribute_payload_encode_data(f->buffer, attribute,
70                                                  SILC_ATTRIBUTE_FLAG_VALID,
71                                                  data, data_len);
72 }
73
74 /* Process list of attributes.  Returns reply to the requested attributes. */
75
76 SilcBuffer silc_client_attributes_process(SilcClient client,
77                                           SilcClientConnection conn,
78                                           SilcDList attrs)
79 {
80   SilcBuffer buffer = NULL;
81   SilcAttrForeach f;
82   SilcAttribute attribute;
83   SilcAttributePayload attr;
84   SilcAttributeObjPk pk;
85   unsigned char sign[2048 + 1];
86   SilcUInt32 sign_len;
87
88   SILC_LOG_DEBUG(("Process Requested Attributes"));
89
90   /* If nothing is set by application assume that we don't want to use
91      attributes, ignore the request. */
92   if (!conn->internal->attrs)
93     return NULL;
94
95   /* Always put our public key. */
96   pk.type = "silc-rsa";
97   pk.data = silc_pkcs_public_key_encode(conn->public_key, &pk.data_len);
98   buffer = silc_attribute_payload_encode(buffer,
99                                          SILC_ATTRIBUTE_USER_PUBLIC_KEY,
100                                          pk.data ? SILC_ATTRIBUTE_FLAG_VALID :
101                                          SILC_ATTRIBUTE_FLAG_INVALID,
102                                          &pk, sizeof(pk));
103   silc_free(pk.data);
104
105   /* Go through all requested attributes */
106   f.buffer = buffer;
107   silc_dlist_start(attrs);
108   while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
109     /* Put all attributes of this type */
110     attribute = silc_attribute_get_attribute(attr);
111
112     /* Ignore signature since we will compute it later */
113     if (attribute == SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE)
114       continue;
115
116     silc_hash_table_find_foreach(conn->internal->attrs,
117                                  SILC_32_TO_PTR(attribute),
118                                  silc_client_attributes_process_foreach,
119                                  &f);
120   }
121   buffer = f.buffer;
122
123   /* Finally compute the digital signature of all the data we provided. */
124   if (silc_pkcs_sign(conn->private_key, silc_buffer_data(buffer),
125                      silc_buffer_len(buffer), sign, sizeof(sign), &sign_len,
126                      conn->internal->sha1hash)) {
127     pk.type = NULL;
128     pk.data = sign;
129     pk.data_len = sign_len;
130     buffer =
131       silc_attribute_payload_encode(buffer,
132                                     SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE,
133                                     SILC_ATTRIBUTE_FLAG_VALID,
134                                     &pk, sizeof(pk));
135   }
136
137   return buffer;
138 }
139
140 static void silc_client_attribute_destruct(void *key, void *context,
141                                            void *user_context)
142 {
143   silc_attribute_payload_free(context);
144 }
145
146 /* Add new attribute */
147
148 SilcAttributePayload silc_client_attribute_add(SilcClient client,
149                                                SilcClientConnection conn,
150                                                SilcAttribute attribute,
151                                                void *object,
152                                                SilcUInt32 object_size)
153 {
154   SilcAttributePayload attr;
155
156   attr = silc_attribute_payload_alloc(attribute, SILC_ATTRIBUTE_FLAG_VALID,
157                                       object, object_size);
158   if (!attr)
159     return NULL;
160
161   if (!conn->internal->attrs)
162     conn->internal->attrs =
163       silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL,
164                             NULL, silc_client_attribute_destruct,
165                             NULL, TRUE);
166   silc_hash_table_add(conn->internal->attrs,
167                       SILC_32_TO_PTR(attribute), attr);
168   return attr;
169 }
170
171 static void silc_client_attribute_del_foreach(void *key, void *context,
172                                               void *user_context)
173 {
174   SilcClientConnection conn = user_context;
175   SilcAttributePayload attr = context;
176   SilcAttribute attribute;
177   if (!attr)
178     return;
179   attribute = silc_attribute_get_attribute(attr);
180   silc_hash_table_del_by_context(conn->internal->attrs,
181                                  SILC_32_TO_PTR(attribute), attr);
182 }
183
184 /* Delete one attribute */
185
186 SilcBool silc_client_attribute_del(SilcClient client,
187                                SilcClientConnection conn,
188                                SilcAttribute attribute,
189                                SilcAttributePayload attr)
190 {
191   SilcBool ret;
192
193   if (!conn->internal->attrs)
194     return FALSE;
195
196   if (attr) {
197     attribute = silc_attribute_get_attribute(attr);
198     ret = silc_hash_table_del_by_context(conn->internal->attrs,
199                                          SILC_32_TO_PTR(attribute), attr);
200   } else if (attribute) {
201     silc_hash_table_find_foreach(conn->internal->attrs,
202                                  SILC_32_TO_PTR(attribute),
203                                  silc_client_attribute_del_foreach, conn);
204     ret = TRUE;
205   } else{
206     return FALSE;
207   }
208
209   if (ret)
210     if (!silc_hash_table_count(conn->internal->attrs)) {
211       silc_hash_table_free(conn->internal->attrs);
212       conn->internal->attrs = NULL;
213     }
214
215   return ret;
216 }
217
218 /* Return all attributes */
219
220 SilcHashTable silc_client_attributes_get(SilcClient client,
221                                          SilcClientConnection conn)
222 {
223   return conn->internal->attrs;
224 }
225
226 /* Construct a Requested Attributes buffer. If the `attribute' is zero (0)
227    then all attributes are requested.  Additionally `attribute' and
228    all variable arguments can be one requested attribute.  Always set
229    the last requested attribute to zero (0) to complete list of
230    requested attribute. */
231
232 SilcBuffer silc_client_attributes_request(SilcAttribute attribute, ...)
233 {
234   va_list va;
235   SilcBuffer buffer = NULL;
236
237   if (!attribute)
238     return silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
239                                           SILC_ATTRIBUTE_SERVICE,
240                                           SILC_ATTRIBUTE_STATUS_MOOD,
241                                           SILC_ATTRIBUTE_STATUS_FREETEXT,
242                                           SILC_ATTRIBUTE_STATUS_MESSAGE,
243                                           SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
244                                           SILC_ATTRIBUTE_PREFERRED_CONTACT,
245                                           SILC_ATTRIBUTE_TIMEZONE,
246                                           SILC_ATTRIBUTE_GEOLOCATION,
247                                           SILC_ATTRIBUTE_DEVICE_INFO,
248                                           SILC_ATTRIBUTE_USER_PUBLIC_KEY, 0);
249
250   va_start(va, attribute);
251   while (attribute) {
252     buffer = silc_attribute_payload_encode(buffer, attribute, 0, NULL, 0);
253     attribute = (SilcAttribute)va_arg(va, SilcUInt32);
254   }
255   va_end(va);
256
257   return buffer;
258 }