eb4c2acbc3131a354a92cb1b111c06ab0eb89a52
[strongswan.git] / src / libimcv / plugins / imv_os / imv_os_database.c
1 /*
2 * Copyright (C) 2012 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "imv_os_database.h"
17
18 #include <utils/debug.h>
19
20 #include <string.h>
21
22 typedef struct private_imv_os_database_t private_imv_os_database_t;
23
24 /**
25 * Private data of a imv_os_database_t object.
26 *
27 */
28 struct private_imv_os_database_t {
29
30 /**
31 * Public imv_os_database_t interface.
32 */
33 imv_os_database_t public;
34
35 /**
36 * database instance
37 */
38 database_t *db;
39
40 };
41
42 METHOD(imv_os_database_t, check_packages, status_t,
43 private_imv_os_database_t *this, imv_os_state_t *state,
44 enumerator_t *package_enumerator)
45 {
46 char *product, *package, *release, *cur_release;
47 u_char *pos;
48 chunk_t os_name, os_version, name, version;
49 os_type_t os_type;
50 size_t os_version_len;
51 os_package_state_t package_state;
52 int pid, gid;
53 int count = 0, count_ok = 0, count_no_match = 0, count_blacklist = 0;
54 enumerator_t *e;
55 status_t status = SUCCESS;
56 bool found, match;
57
58 state->get_info(state, &os_type, &os_name, &os_version);
59
60 if (os_type == OS_TYPE_ANDROID)
61 {
62 /*no package dependency on Android version */
63 product = strdup(enum_to_name(os_type_names, os_type));
64 }
65 else
66 {
67 /* remove appended platform info */
68 pos = memchr(os_version.ptr, ' ', os_version.len);
69 os_version_len = pos ? (pos - os_version.ptr) : os_version.len;
70 product = malloc(os_name.len + 1 + os_version_len + 1);
71 sprintf(product, "%.*s %.*s", os_name.len, os_name.ptr,
72 os_version_len, os_version.ptr);
73 }
74 DBG1(DBG_IMV, "processing installed '%s' packages", product);
75
76 /* Get primary key of product */
77 e = this->db->query(this->db,
78 "SELECT id FROM products WHERE name = ?",
79 DB_TEXT, product, DB_INT);
80 if (!e)
81 {
82 free(product);
83 return FAILED;
84 }
85 if (!e->enumerate(e, &pid))
86 {
87 e->destroy(e);
88 free(product);
89 return NOT_FOUND;
90 }
91 e->destroy(e);
92
93 while (package_enumerator->enumerate(package_enumerator, &name, &version))
94 {
95 /* Convert package name chunk to a string */
96 package = strndup(name.ptr, name.len);
97 count++;
98
99 /* Get primary key of package */
100 e = this->db->query(this->db,
101 "SELECT id FROM packages WHERE name = ?",
102 DB_TEXT, package, DB_INT);
103 if (!e)
104 {
105 free(product);
106 free(package);
107 return FAILED;
108 }
109 if (!e->enumerate(e, &gid))
110 {
111 /* package not present in database for any product - skip */
112 if (os_type == OS_TYPE_ANDROID)
113 {
114 DBG2(DBG_IMV, "package '%s' (%.*s) not found",
115 package, version.len, version.ptr);
116 }
117 free(package);
118 e->destroy(e);
119 continue;
120 }
121 e->destroy(e);
122
123 /* Convert package version chunk to a string */
124 release = strndup(version.ptr, version.len);
125
126 /* Enumerate over all acceptable versions */
127 e = this->db->query(this->db,
128 "SELECT release, security FROM versions "
129 "WHERE product = ? AND package = ?",
130 DB_INT, pid, DB_INT, gid, DB_TEXT, DB_INT);
131 if (!e)
132 {
133 free(product);
134 free(package);
135 free(release);
136 return FAILED;
137 }
138 found = FALSE;
139 match = FALSE;
140
141 while (e->enumerate(e, &cur_release, &package_state))
142 {
143 found = TRUE;
144 if (streq(release, cur_release) || streq("*", cur_release))
145 {
146 match = TRUE;
147 break;
148 }
149 }
150 e->destroy(e);
151
152 if (found)
153 {
154 if (match)
155 {
156 if (package_state == OS_PACKAGE_STATE_BLACKLIST)
157 {
158 DBG2(DBG_IMV, "package '%s' (%s) is blacklisted",
159 package, release);
160 count_blacklist++;
161 state->add_bad_package(state, package, package_state);
162 }
163 else
164 {
165 DBG2(DBG_IMV, "package '%s' (%s)%N is ok", package, release,
166 os_package_state_names, package_state);
167 count_ok++;
168 }
169 }
170 else
171 {
172 DBG1(DBG_IMV, "package '%s' (%s) no match", package, release);
173 count_no_match++;
174 state->add_bad_package(state, package, package_state);
175 }
176 }
177 else
178 {
179 /* package not present in database for this product - skip */
180 }
181 free(package);
182 free(release);
183 }
184 free(product);
185 state->set_count(state, count, count_no_match, count_blacklist, count_ok);
186
187 return status;
188 }
189
190 METHOD(imv_os_database_t, get_device_id, int,
191 private_imv_os_database_t *this, chunk_t value)
192 {
193 enumerator_t *e;
194 int id;
195
196 /* get primary key of device ID */
197 e = this->db->query(this->db, "SELECT id FROM devices WHERE value = ?",
198 DB_BLOB, value, DB_INT);
199 if (!e)
200 {
201 return 0;
202 }
203 if (e->enumerate(e, &id))
204 {
205 /* device ID already exists in database - return primary key */
206 e->destroy(e);
207 return id;
208 }
209
210 /* register new device ID in database and return primary key */
211 return (this->db->execute(this->db, &id,
212 "INSERT INTO devices (value) VALUES (?)", DB_BLOB, value) == 1) ?
213 id : 0;
214 }
215
216 METHOD(imv_os_database_t, set_device_info, void,
217 private_imv_os_database_t *this, int device_id, char *os_info,
218 int count, int count_update, int count_blacklist, u_int flags)
219 {
220 enumerator_t *e;
221 time_t last_time;
222 int pid = 0, last_pid = 0, last_count_update = 0, last_count_blacklist = 0;
223 u_int last_flags;
224 bool found = FALSE;
225
226 /* get primary key of OS info string if it exists */
227 e = this->db->query(this->db,
228 "SELECT id FROM products WHERE name = ?", DB_TEXT, os_info,
229 DB_INT);
230 if (e)
231 {
232 e->enumerate(e, &pid);
233 e->destroy(e);
234 }
235
236 /* if OS ifo string has not been found - register it */
237 if (!pid)
238 {
239 this->db->execute(this->db, &pid,
240 "INSERT INTO products (name) VALUES (?)", DB_TEXT, os_info);
241 }
242
243 /* get latest device info record if it exists */
244 e = this->db->query(this->db,
245 "SELECT time, product, count_update, count_blacklist, flags "
246 "FROM device_infos WHERE device = ? ORDER BY time DESC",
247 DB_INT, device_id, DB_UINT, DB_INT, DB_INT, DB_INT, DB_UINT);
248 if (e)
249 {
250 found = e->enumerate(e, &last_time, &last_pid, &last_count_update,
251 &last_count_blacklist, &last_flags);
252 e->destroy(e);
253 }
254 if (found && !last_count_update && !last_count_blacklist && !last_flags &&
255 pid == last_pid)
256 {
257 /* update device info */
258 this->db->execute(this->db, NULL,
259 "UPDATE device_infos SET time = ?, count = ?, count_update = ?, "
260 "count_blacklist = ?, flags = ? WHERE device = ? AND time = ?",
261 DB_UINT, time(NULL), DB_INT, count, DB_INT, count_update,
262 DB_INT, count_blacklist, DB_UINT, flags,
263 DB_INT, device_id, DB_UINT, last_time);
264 }
265 else
266 {
267 /* insert device info */
268 this->db->execute(this->db, NULL,
269 "INSERT INTO device_infos (device, time, product, count, "
270 "count_update, count_blacklist, flags) VALUES (?, ?, ?, ?, ?, ?, ?)",
271 DB_INT, device_id, DB_UINT, time(NULL), DB_INT, pid,
272 DB_INT, count, DB_INT, count_update, DB_INT, count_blacklist,
273 DB_UINT, flags);
274 }
275 }
276
277 METHOD(imv_os_database_t, destroy, void,
278 private_imv_os_database_t *this)
279 {
280 this->db->destroy(this->db);
281 free(this);
282 }
283
284 /**
285 * See header
286 */
287 imv_os_database_t *imv_os_database_create(char *uri)
288 {
289 private_imv_os_database_t *this;
290
291 INIT(this,
292 .public = {
293 .check_packages = _check_packages,
294 .get_device_id = _get_device_id,
295 .set_device_info = _set_device_info,
296 .destroy = _destroy,
297 },
298 .db = lib->db->create(lib->db, uri),
299 );
300
301 if (!this->db)
302 {
303 DBG1(DBG_IMV,
304 "failed to connect to OS database '%s'", uri);
305 free(this);
306 return NULL;
307 }
308
309 return &this->public;
310 }
311