Fix crash when invalid parameter given
[platform/core/api/capability-manager.git] / src / client.cc
1 // Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by a apache 2.0 license that can be
3 // found in the LICENSE file.
4
5 #include <aul_svc.h>
6 #include <bundle.h>
7 #include <bundle_internal.h>
8 #include <glib.h>
9
10 #include <cstring>
11 #include <map>
12 #include <memory>
13 #include <string>
14 #include <vector>
15
16 #include "include/capability_manager.h"
17 #include "src/dbus.h"
18 #include "src/sql_connection.h"
19 #include "src/sql_statement.h"
20 #include "src/sqlite_connection.h"
21 #include "src/utils/logging.h"
22
23 #define API __attribute__((visibility("default")))
24
25 namespace {
26
27 const char kDBPath[] = "/run/capmgr/capmgr.db";
28
29 }  // namespace
30
31 struct capmgr_device_s {
32   std::string device_id;
33   std::string model_name;
34   std::string device_name;
35   std::string platform_ver;
36   std::string profile;
37   std::string sw_ver;
38 };
39
40 struct capmgr_app_control_s {
41   capmgr_device_h device;
42   bundle* b;
43 };
44
45 struct capmgr_application_info_s {
46   std::string appid;
47   std::string pkgid;
48   std::string label;
49   std::string version;
50   capmgr_device_h device;
51 };
52
53 API int capmgr_device_foreach_devices(capmgr_device_foreach_cb cb,
54     void* user_data) {
55   if (!cb)
56     return CAPMGR_ERROR_INVALID_PARAMETER;
57
58   std::unique_ptr<capmgr::SQLConnection> sql_conn(
59       new capmgr::SQLiteConnection(kDBPath, true));
60
61   const char kQueryForeachDevices[] =
62       "SELECT device_id, model_name, device_name, platform_ver,"
63       "  profile, sw_ver "
64       "FROM devices";
65   std::shared_ptr<capmgr::SQLStatement> stmt = sql_conn->PrepareStatement(
66       kQueryForeachDevices);
67   if (!stmt)
68     return CAPMGR_ERROR_IO_ERROR;
69
70   while (stmt->Step() == capmgr::SQLStatement::StepResult::ROW) {
71     struct capmgr_device_s dev;
72     int idx = 0;
73     dev.device_id = stmt->GetColumnString(idx++);
74     dev.model_name = stmt->GetColumnString(idx++);
75     dev.device_name = stmt->GetColumnString(idx++);
76     dev.platform_ver = stmt->GetColumnString(idx++);
77     dev.profile = stmt->GetColumnString(idx++);
78     dev.sw_ver = stmt->GetColumnString(idx++);
79     if (cb(&dev, user_data))
80       break;
81   }
82
83   return CAPMGR_ERROR_NONE;
84 }
85
86 API int capmgr_device_clone(const capmgr_device_h device,
87     capmgr_device_h* device_clone) {
88   if (!device || !device_clone)
89     return CAPMGR_ERROR_INVALID_PARAMETER;
90
91   try {
92     struct capmgr_device_s* clone = new struct capmgr_device_s();
93
94     clone->device_id = device->device_id;
95     clone->model_name = device->model_name;
96     clone->device_name = device->device_name;
97     clone->platform_ver = device->platform_ver;
98     clone->profile = device->profile;
99     clone->sw_ver = device->sw_ver;
100
101     *device_clone = clone;
102   } catch (const std::bad_alloc& e) {
103     LOG(ERROR) << e.what();
104     return CAPMGR_ERROR_OUT_OF_MEMORY;
105   }
106
107   return CAPMGR_ERROR_NONE;
108 }
109
110 API int capmgr_device_destroy(capmgr_device_h device) {
111   if (!device)
112     return CAPMGR_ERROR_INVALID_PARAMETER;
113
114   delete device;
115
116   return CAPMGR_ERROR_NONE;
117 }
118
119 API int capmgr_device_get_device_id(capmgr_device_h device,
120     char** device_id) {
121   if (!device || !device_id)
122     return CAPMGR_ERROR_INVALID_PARAMETER;
123
124   *device_id = strdup(device->device_id.c_str());
125   if (*device_id == nullptr)
126     return CAPMGR_ERROR_OUT_OF_MEMORY;
127
128   return CAPMGR_ERROR_NONE;
129 }
130
131 API int capmgr_device_get_model_name(capmgr_device_h device,
132     char** model_name) {
133   if (!device || !model_name)
134     return CAPMGR_ERROR_INVALID_PARAMETER;
135
136   *model_name = strdup(device->model_name.c_str());
137   if (*model_name == nullptr)
138     return CAPMGR_ERROR_OUT_OF_MEMORY;
139
140   return CAPMGR_ERROR_NONE;
141 }
142
143 API int capmgr_device_get_device_name(capmgr_device_h device,
144     char** device_name) {
145   if (!device || !device_name)
146     return CAPMGR_ERROR_INVALID_PARAMETER;
147
148   *device_name = strdup(device->device_name.c_str());
149   if (*device_name == nullptr)
150     return CAPMGR_ERROR_OUT_OF_MEMORY;
151
152   return CAPMGR_ERROR_NONE;
153 }
154
155 API int capmgr_device_get_platform_ver(capmgr_device_h device,
156     char** platform_ver) {
157   if (!device || !platform_ver)
158     return CAPMGR_ERROR_INVALID_PARAMETER;
159
160   *platform_ver = strdup(device->platform_ver.c_str());
161   if (*platform_ver == nullptr)
162     return CAPMGR_ERROR_OUT_OF_MEMORY;
163
164   return CAPMGR_ERROR_NONE;
165 }
166
167 API int capmgr_device_get_profile(capmgr_device_h device, char** profile) {
168   if (!device || !profile)
169     return CAPMGR_ERROR_INVALID_PARAMETER;
170
171   *profile = strdup(device->profile.c_str());
172   if (*profile == nullptr)
173     return CAPMGR_ERROR_OUT_OF_MEMORY;
174
175   return CAPMGR_ERROR_NONE;
176 }
177
178 API int capmgr_device_get_sw_ver(capmgr_device_h device,
179     char** sw_ver) {
180   if (!device || !sw_ver)
181     return CAPMGR_ERROR_INVALID_PARAMETER;
182
183   *sw_ver = strdup(device->sw_ver.c_str());
184   if (*sw_ver == nullptr)
185     return CAPMGR_ERROR_OUT_OF_MEMORY;
186
187   return CAPMGR_ERROR_NONE;
188 }
189
190 API int capmgr_app_control_create(capmgr_app_control_h* app_control) {
191   if (!app_control)
192     return CAPMGR_ERROR_INVALID_PARAMETER;
193
194   try {
195     struct capmgr_app_control_s* control = new struct capmgr_app_control_s();
196     control->b = bundle_create();
197     if (!control->b) {
198       delete control;
199       return CAPMGR_ERROR_OUT_OF_MEMORY;
200     }
201     int r = aul_svc_set_operation(control->b, AUL_SVC_OPERATION_DEFAULT);
202     if (r != AUL_SVC_RET_OK) {
203       bundle_free(control->b);
204       delete control;
205       return CAPMGR_ERROR_OUT_OF_MEMORY;
206     }
207     *app_control = control;
208   } catch (const std::bad_alloc& e) {
209     LOG(ERROR) << e.what();
210     return CAPMGR_ERROR_OUT_OF_MEMORY;
211   }
212
213   return CAPMGR_ERROR_NONE;
214 }
215
216 API int capmgr_app_control_clone(const capmgr_app_control_h app_control,
217     capmgr_app_control_h* app_control_clone) {
218   if (!app_control || !app_control_clone)
219     return CAPMGR_ERROR_INVALID_PARAMETER;
220
221   try {
222     struct capmgr_app_control_s* clone = new struct capmgr_app_control_s();
223
224     int ret = capmgr_device_clone(app_control->device, &clone->device);
225     if (ret != CAPMGR_ERROR_NONE) {
226       delete clone;
227       return CAPMGR_ERROR_OUT_OF_MEMORY;
228     }
229
230     clone->b = bundle_dup(app_control->b);
231     if (!clone->b) {
232       capmgr_app_control_destroy(clone);
233       return CAPMGR_ERROR_OUT_OF_MEMORY;
234     }
235
236     *app_control_clone = clone;
237   } catch (const std::bad_alloc& e) {
238     LOG(ERROR) << e.what();
239     return CAPMGR_ERROR_OUT_OF_MEMORY;
240   }
241
242   return CAPMGR_ERROR_NONE;
243 }
244
245 API int capmgr_app_control_destroy(capmgr_app_control_h app_control) {
246   if (!app_control)
247     return CAPMGR_ERROR_INVALID_PARAMETER;
248
249   capmgr_device_destroy(app_control->device);
250   bundle_free(app_control->b);
251   delete app_control;
252
253   return CAPMGR_ERROR_NONE;
254 }
255
256 API int capmgr_app_control_get_device(capmgr_app_control_h app_control,
257     capmgr_device_h* device) {
258   if (!app_control || !device)
259     return CAPMGR_ERROR_INVALID_PARAMETER;
260
261   int ret = capmgr_device_clone(app_control->device, device);
262   if (ret != CAPMGR_ERROR_NONE)
263     return ret;
264
265   return CAPMGR_ERROR_NONE;
266 }
267
268 API int capmgr_app_control_get_operation(capmgr_app_control_h app_control,
269     char** operation) {
270   if (!app_control || !operation)
271     return CAPMGR_ERROR_INVALID_PARAMETER;
272
273   const char* val = aul_svc_get_operation(app_control->b);
274   if (!val)
275     return CAPMGR_ERROR_INVALID_PARAMETER;
276
277   *operation = strdup(val);
278   if (*operation == nullptr)
279     return CAPMGR_ERROR_OUT_OF_MEMORY;
280
281   return CAPMGR_ERROR_NONE;
282 }
283
284 API int capmgr_app_control_get_uri(capmgr_app_control_h app_control,
285     char** uri) {
286   if (!app_control || !uri)
287     return CAPMGR_ERROR_INVALID_PARAMETER;
288
289   const char* val = aul_svc_get_uri(app_control->b);
290   if (!val)
291     return CAPMGR_ERROR_INVALID_PARAMETER;
292
293   *uri = strdup(val);
294   if (*uri == nullptr)
295     return CAPMGR_ERROR_OUT_OF_MEMORY;
296
297   return CAPMGR_ERROR_NONE;
298 }
299
300 API int capmgr_app_control_get_mime(capmgr_app_control_h app_control,
301     char** mime) {
302   if (!app_control || !mime)
303     return CAPMGR_ERROR_INVALID_PARAMETER;
304
305   const char* val = aul_svc_get_mime(app_control->b);
306   if (!val)
307     return CAPMGR_ERROR_INVALID_PARAMETER;
308
309   *mime = strdup(val);
310   if (*mime == nullptr)
311     return CAPMGR_ERROR_OUT_OF_MEMORY;
312
313   return CAPMGR_ERROR_NONE;
314 }
315
316 API int capmgr_app_control_get_appid(capmgr_app_control_h app_control,
317     char** appid) {
318   if (!app_control || !appid)
319     return CAPMGR_ERROR_INVALID_PARAMETER;
320
321   const char* val = aul_svc_get_appid(app_control->b);
322   if (!val)
323     return CAPMGR_ERROR_INVALID_PARAMETER;
324
325   *appid = strdup(val);
326   if (*appid == nullptr)
327     return CAPMGR_ERROR_OUT_OF_MEMORY;
328
329   return CAPMGR_ERROR_NONE;
330 }
331
332 API int capmgr_app_control_get_extra_data(capmgr_app_control_h app_control,
333     const char* key, char** value) {
334   if (!app_control || !key || !value)
335     return CAPMGR_ERROR_INVALID_PARAMETER;
336
337   // TODO(jeremy.jang): handle reserved key
338
339   const char* val = aul_svc_get_data(app_control->b, key);
340   if (!val) {
341     LOG(ERROR) << "There is no extra data of key(" << key << ")";
342     return CAPMGR_ERROR_INVALID_PARAMETER;
343   }
344
345   *value = strdup(val);
346   if (*value == nullptr)
347     return CAPMGR_ERROR_OUT_OF_MEMORY;
348
349   return CAPMGR_ERROR_NONE;
350 }
351
352 API int capmgr_app_control_set_device(capmgr_app_control_h app_control,
353     const capmgr_device_h device) {
354   if (!app_control || !device)
355     return CAPMGR_ERROR_INVALID_PARAMETER;
356
357   if (app_control->device)
358     capmgr_device_destroy(app_control->device);
359
360   int ret = capmgr_device_clone(device, &app_control->device);
361   if (ret != CAPMGR_ERROR_NONE)
362     return CAPMGR_ERROR_OUT_OF_MEMORY;
363
364   return CAPMGR_ERROR_NONE;
365 }
366
367 API int capmgr_app_control_set_operation(capmgr_app_control_h app_control,
368     const char* operation) {
369   if (!app_control || !operation)
370     return CAPMGR_ERROR_INVALID_PARAMETER;
371
372   int r = aul_svc_set_operation(app_control->b, operation);
373   if (r != AUL_SVC_RET_OK)
374     return CAPMGR_ERROR_OUT_OF_MEMORY;
375
376   return CAPMGR_ERROR_NONE;
377 }
378
379 API int capmgr_app_control_set_uri(capmgr_app_control_h app_control,
380     const char* uri) {
381   if (!app_control || !uri)
382     return CAPMGR_ERROR_INVALID_PARAMETER;
383
384   int r = aul_svc_set_uri(app_control->b, uri);
385   if (r != AUL_SVC_RET_OK)
386     return CAPMGR_ERROR_OUT_OF_MEMORY;
387
388   return CAPMGR_ERROR_NONE;
389 }
390
391 API int capmgr_app_control_set_mime(capmgr_app_control_h app_control,
392     const char* mime) {
393   if (!app_control || !mime)
394     return CAPMGR_ERROR_INVALID_PARAMETER;
395
396   int r = aul_svc_set_mime(app_control->b, mime);
397   if (r != AUL_SVC_RET_OK)
398     return CAPMGR_ERROR_OUT_OF_MEMORY;
399
400   return CAPMGR_ERROR_NONE;
401 }
402
403 API int capmgr_app_control_set_appid(capmgr_app_control_h app_control,
404     const char* appid) {
405   if (!app_control || !appid)
406     return CAPMGR_ERROR_INVALID_PARAMETER;
407
408   int r = aul_svc_set_appid(app_control->b, appid);
409   if (r != AUL_SVC_RET_OK)
410     return CAPMGR_ERROR_OUT_OF_MEMORY;
411
412   return CAPMGR_ERROR_NONE;
413 }
414
415 API int capmgr_app_control_add_extra_data(capmgr_app_control_h app_control,
416     const char* key, const char* value) {
417   if (!app_control || !key || !value)
418     return CAPMGR_ERROR_INVALID_PARAMETER;
419
420   // TODO(jeremy.jang): handle reserved key
421
422   int r = aul_svc_add_data(app_control->b, key, value);
423   if (r != AUL_SVC_RET_OK)
424     return CAPMGR_ERROR_OUT_OF_MEMORY;
425
426   return CAPMGR_ERROR_NONE;
427 }
428
429 API int capmgr_app_control_remove_extra_data(capmgr_app_control_h app_control,
430     const char* key) {
431   if (!app_control || !key)
432     return CAPMGR_ERROR_INVALID_PARAMETER;
433
434   // TODO(jeremy.jang): handle reserved key
435
436   int r = bundle_del(app_control->b, key);
437   if (r != BUNDLE_ERROR_NONE)
438     return CAPMGR_ERROR_INVALID_PARAMETER;
439
440   return CAPMGR_ERROR_NONE;
441 }
442
443 struct cbdata {
444   capmgr_app_control_h request;
445   capmgr_app_control_reply_cb cb;
446   void* user_data;
447 };
448
449 void capmgr_dbus_callback(GVariant* result, void* user_data) {
450   LOG(DEBUG) << "Dbus callback for client called";
451
452   // TODO(jeremy.jang): some data maybe returned
453   GVariantIter* iter;
454   guchar* data;
455   guint len;
456   g_variant_get(result, "(ayu)", &iter, &len);
457   if (!iter)
458     LOG(ERROR) << "Some error occurred";
459
460   data = reinterpret_cast<guchar*>(g_try_malloc(len));
461   if (!data) {
462     LOG(ERROR) << "Out of memory";
463     return;
464   }
465
466   for (unsigned int i = 0; i < len; i++) {
467     if (!g_variant_iter_loop(iter, "y", &data[i])) {
468       LOG(ERROR) << "Failed to get data from GVariant!";
469       break;
470     }
471   }
472   g_variant_iter_free(iter);
473
474   struct cbdata* cbdata = reinterpret_cast<struct cbdata*>(user_data);
475   struct capmgr_app_control_s* reply = new struct capmgr_app_control_s();
476   reply->b = bundle_decode(data, len);
477   if (!reply->b) {
478     LOG(ERROR) << "Invalid bundle data!";
479     capmgr_app_control_destroy(cbdata->request);
480     delete cbdata;
481     return;
482   }
483   reply->device = cbdata->request->device;
484
485   // TODO(jeremy.jang): need to receive aul_svc result code
486   cbdata->cb(cbdata->request, reply, CAPMGR_APP_CONTROL_RESULT_OK,
487       cbdata->user_data);
488
489   capmgr_app_control_destroy(cbdata->request);
490   delete cbdata;
491 }
492
493 API int capmgr_app_control_send(capmgr_app_control_h app_control,
494     capmgr_app_control_reply_cb cb, void* user_data) {
495   if (!app_control || !app_control->device || !cb)
496     return CAPMGR_ERROR_INVALID_PARAMETER;
497
498   bundle_raw* raw;
499   int len;
500   int r = bundle_encode(app_control->b, &raw, &len);
501   if (r != BUNDLE_ERROR_NONE) {
502     LOG(ERROR) << "Failed to encode bundle: " << r;
503     return CAPMGR_ERROR_INVALID_PARAMETER;
504   }
505
506   // send app_control
507   GVariantBuilder* array_builder = g_variant_builder_new(G_VARIANT_TYPE("ay"));
508   for (int i = 0; i < len; i++)
509     g_variant_builder_add(array_builder, "y", raw[i]);
510   GVariant* gv = g_variant_new("(sayu)",
511       app_control->device->device_id.c_str(), array_builder, len);
512   g_variant_builder_unref(array_builder);
513   bundle_free_encoded_rawdata(&raw);
514   if (!gv) {
515     LOG(ERROR) << "Failed to create GVariant";
516     return CAPMGR_ERROR_INVALID_PARAMETER;
517   }
518   g_variant_ref_sink(gv);
519
520   capmgr_app_control_h clone;
521   r = capmgr_app_control_clone(app_control, &clone);
522   if (r != CAPMGR_ERROR_NONE) {
523     LOG(ERROR) << "Failed to clone app control request: " << r;
524     return r;
525   }
526
527   struct cbdata* cbdata = new struct cbdata();
528   cbdata->request = clone;
529   cbdata->cb = cb;
530   cbdata->user_data = user_data;
531
532   if (!capmgr::ProxyCallAsync("SendRemoteAppControl", gv, capmgr_dbus_callback,
533         cbdata)) {
534     LOG(ERROR) << "Failed to dbus method call";
535     delete cbdata;
536     capmgr_app_control_destroy(clone);
537     g_variant_unref(gv);
538     // errcode?
539     return CAPMGR_ERROR_INVALID_PARAMETER;
540   }
541
542   g_variant_unref(gv);
543
544   return CAPMGR_ERROR_NONE;
545 }
546
547 API int capmgr_application_info_clone(
548     const capmgr_application_info_h remote_app_info,
549     capmgr_application_info_h* remote_app_info_clone) {
550   if (!remote_app_info || !remote_app_info_clone)
551     return CAPMGR_ERROR_INVALID_PARAMETER;
552
553   struct capmgr_application_info_s* clone;
554   try {
555     clone = new struct capmgr_application_info_s();
556   } catch (const std::bad_alloc& e) {
557     LOG(ERROR) << e.what();
558     return CAPMGR_ERROR_OUT_OF_MEMORY;
559   }
560
561   clone->appid = remote_app_info->appid;
562   clone->pkgid = remote_app_info->pkgid;
563   clone->label = remote_app_info->label;
564   clone->version = remote_app_info->version;
565
566   int ret = capmgr_device_clone(remote_app_info->device, &(clone->device));
567   if (ret != CAPMGR_ERROR_NONE) {
568     LOG(ERROR) << "Failed to clone capmgr device";
569     delete clone;
570     return ret;
571   }
572
573   *remote_app_info_clone = clone;
574
575   return CAPMGR_ERROR_NONE;
576 }
577
578 API int capmgr_application_info_destroy(
579     capmgr_application_info_h remote_app_info) {
580   if (!remote_app_info)
581     return CAPMGR_ERROR_INVALID_PARAMETER;
582
583   capmgr_device_destroy(remote_app_info->device);
584   delete remote_app_info;
585
586   return CAPMGR_ERROR_NONE;
587 }
588
589 API int capmgr_application_info_get_appid(
590     capmgr_application_info_h remote_app_info, char** appid) {
591   if (!remote_app_info || !appid || remote_app_info->appid.empty())
592     return CAPMGR_ERROR_INVALID_PARAMETER;
593
594   *appid = strdup(remote_app_info->appid.c_str());
595   if (*appid == nullptr)
596     return CAPMGR_ERROR_OUT_OF_MEMORY;
597
598   return CAPMGR_ERROR_NONE;
599 }
600
601 API int capmgr_application_info_get_pkgid(
602     capmgr_application_info_h remote_app_info, char** pkgid) {
603   if (!remote_app_info || !pkgid || remote_app_info->pkgid.empty())
604     return CAPMGR_ERROR_INVALID_PARAMETER;
605
606   *pkgid = strdup(remote_app_info->pkgid.c_str());
607   if (*pkgid == nullptr)
608     return CAPMGR_ERROR_OUT_OF_MEMORY;
609
610   return CAPMGR_ERROR_NONE;
611 }
612
613 API int capmgr_application_info_get_label(
614     capmgr_application_info_h remote_app_info, char** label) {
615   if (!remote_app_info || !label || remote_app_info->label.empty())
616     return CAPMGR_ERROR_INVALID_PARAMETER;
617
618   *label = strdup(remote_app_info->label.c_str());
619   if (*label == nullptr)
620     return CAPMGR_ERROR_OUT_OF_MEMORY;
621
622   return CAPMGR_ERROR_NONE;
623 }
624
625 API int capmgr_application_info_get_version(
626     capmgr_application_info_h remote_app_info, char** version) {
627   if (!remote_app_info || !version || remote_app_info->version.empty())
628     return CAPMGR_ERROR_INVALID_PARAMETER;
629
630   *version = strdup(remote_app_info->version.c_str());
631   if (*version == nullptr)
632     return CAPMGR_ERROR_OUT_OF_MEMORY;
633
634   return CAPMGR_ERROR_NONE;
635 }
636
637 API int capmgr_application_info_get_device(
638     capmgr_application_info_h remote_app_info,
639     capmgr_device_h* device) {
640   if (!remote_app_info || !device)
641     return CAPMGR_ERROR_INVALID_PARAMETER;
642
643   int ret = capmgr_device_clone(remote_app_info->device, device);
644   if (ret != CAPMGR_ERROR_NONE)
645     return ret;
646
647   return CAPMGR_ERROR_NONE;
648 }
649
650 API int capmgr_application_info_foreach_applications(
651     const capmgr_device_h device,
652     capmgr_application_info_foreach_app_cb cb,
653     void* user_data) {
654   if (!device || !cb)
655     return CAPMGR_ERROR_INVALID_PARAMETER;
656
657   std::unique_ptr<capmgr::SQLConnection> sql_conn(
658       new capmgr::SQLiteConnection(kDBPath, true));
659
660   const char kQueryForeachPackages[] =
661       "SELECT appid, pkgid, label, version FROM remote_app_info WHERE "
662       "device_id = ?";
663
664   std::shared_ptr<capmgr::SQLStatement> stmt = sql_conn->PrepareStatement(
665       kQueryForeachPackages);
666   if (!stmt)
667     return CAPMGR_ERROR_IO_ERROR;
668
669   if (stmt->BindString(1, device->device_id)) {
670     while (stmt->Step() == capmgr::SQLStatement::StepResult::ROW) {
671       struct capmgr_application_info_s info;
672       int idx = 0;
673       info.appid = stmt->GetColumnString(idx++);
674       info.pkgid = stmt->GetColumnString(idx++);
675       info.label = stmt->GetColumnString(idx++);
676       info.version = stmt->GetColumnString(idx++);
677       info.device = device;
678       if (cb(&info, user_data))
679         break;
680     }
681   }
682
683   return CAPMGR_ERROR_NONE;
684 }