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