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