Add Camera Module
[apps/native/position-finder-server.git] / src / webutil.c
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Contact: Jin Yoon <jinny.yoon@samsung.com>
5  *          Geunsun Lee <gs86.lee@samsung.com>
6  *          Eunyoung Lee <ey928.lee@samsung.com>
7  *          Junkyu Han <junkyu.han@samsung.com>
8  *          Jeonghoon Park <jh1979.park@samsung.com>
9  *
10  * Licensed under the Flora License, Version 1.1 (the License);
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://floralicense.org/license/
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an AS IS BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22
23 #include <stdbool.h>
24 #include <curl/curl.h>
25 #include <glib.h>
26 #include <json-glib/json-glib.h>
27 #include "log.h"
28 #include "webutil.h"
29
30 #define URI_PATH_LEN 64
31 #define REQ_CON_TIMEOUT 5L
32 #define REQ_TIMEOUT 7L
33
34 typedef struct _wu_json_handle {
35         JsonBuilder *builder;
36         bool is_begin;
37         bool is_end;
38 } wu_json_handle;
39
40 static wu_json_handle Json_h = {NULL, false, false};
41
42 static size_t _post_response_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
43 {
44         size_t res_size = 0;
45
46         res_size = size*nmemb;
47
48         if (res_size > 0)
49                 _I("POST response : %s", ptr);
50         /* What should we do here, if response body has negative message? */
51
52         return res_size;
53 }
54
55 static size_t _get_response_write_callback(void *ptr, size_t size, size_t nmemb, void *data)
56 {
57         size_t res_size = 0;
58         char **received = (char **)data;
59
60         res_size = size*nmemb;
61
62         if (received && res_size > 0)
63                 *received = strndup((char *)ptr, size*nmemb);
64         else
65                 _E("fail to get response [res size : %d]", res_size);
66
67         return res_size;
68 }
69
70 static int __curl_debug(CURL *handle, curl_infotype type,
71         char *data, size_t size, void *userptr)
72 {
73         const char *prefix = NULL;
74         char *message = NULL;
75
76         switch (type) {
77         case CURLINFO_END:
78                 return 0;
79         case CURLINFO_TEXT:
80                 _D("== text Info: %s", data);
81                 return 0;
82         case CURLINFO_HEADER_OUT:
83                 prefix = "=> Send header:";
84                 break;
85         case CURLINFO_DATA_OUT:
86                 prefix = "=> Send data:";
87                 break;
88         case CURLINFO_SSL_DATA_OUT:
89                 prefix = "=> Send SSL data:";
90                 break;
91         case CURLINFO_HEADER_IN:
92                 prefix = "<= Recv header:";
93                 break;
94         case CURLINFO_DATA_IN:
95                 prefix = "<= Recv data:";
96                 break;
97         case CURLINFO_SSL_DATA_IN:
98                 prefix = "<= Recv SSL data:";
99                 break;
100         }
101         message = g_strndup(data, size);
102         _D("%s %s", prefix, message);
103         g_free(message);
104         return 0;
105 }
106
107 static const char *_get_time_str(void)
108 {
109         struct timeval val;
110         struct tm *ptm;
111         static char res_time[40] = {0, };
112
113         gettimeofday(&val, NULL);
114         ptm = localtime(&val.tv_sec);
115
116         // format : YY-MM-DD_hh:mm:ss:uuuuuu
117         snprintf(res_time, sizeof(res_time), "%04d-%02d-%02d_%02d:%02d:%02d:%06ld"
118                 , ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday
119                 , ptm->tm_hour, ptm->tm_min, ptm->tm_sec
120                 , val.tv_usec);
121
122         return (const char *)res_time;
123 }
124
125
126 int web_util_noti_init(void)
127 {
128         int ret = 0;
129         CURLcode result;
130         result = curl_global_init(CURL_GLOBAL_DEFAULT);
131         if (result != CURLE_OK) {
132                 _E("curl_global_init() failed: %s",
133                         curl_easy_strerror(result));
134                 ret = -1;
135         }
136         return ret;
137 }
138
139 void web_util_noti_fini(void)
140 {
141         curl_global_cleanup();
142         return;
143 }
144
145 int web_util_noti_post_image_data(const char *url, const char *device_id,
146         const void *image_data, unsigned int image_size)
147 {
148         int ret = 0;
149         CURL *curl = NULL;
150         CURLcode response = CURLE_OK;
151         struct curl_httppost *formpost = NULL;
152         struct curl_httppost *lastptr = NULL;
153         char *filename = NULL;
154         char *post_url = NULL;
155
156         retv_if(url == NULL, -1);
157         retv_if(device_id == NULL, -1);
158         retv_if(image_data == NULL, -1);
159         retv_if(image_size == 0, -1);
160
161         curl = curl_easy_init();
162
163         if (!curl) {
164                 _E("fail to init curl");
165                 return -1;
166         }
167
168         filename = g_strdup_printf("%s_%s.jpg", device_id, _get_time_str());
169         post_url = g_strdup_printf("%s?id=%s", url, device_id);
170         _D("FileName: [%s], PostUrl: [%s]", filename, post_url);
171
172         curl_formadd(&formpost, &lastptr,
173                 CURLFORM_COPYNAME, "content-type:",
174                 CURLFORM_COPYCONTENTS, "multipart/form-data",
175                 CURLFORM_END);
176
177         curl_formadd(&formpost, &lastptr,
178                 CURLFORM_COPYNAME, "imageFile",
179                 CURLFORM_BUFFER, filename,
180                 CURLFORM_BUFFERPTR, image_data,
181                 CURLFORM_BUFFERLENGTH, image_size,
182                 CURLFORM_END);
183
184         curl_easy_setopt(curl, CURLOPT_URL, post_url);
185         curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
186
187         /* if CURLOPT_VERBOSE is enabled, __curl_debug() function will be called */
188         // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
189         curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, __curl_debug);
190
191         // curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, REQ_CON_TIMEOUT);
192         // curl_easy_setopt(curl, CURLOPT_TIMEOUT, REQ_TIMEOUT);
193
194         response = curl_easy_perform(curl);
195
196         if (response != CURLE_OK) {
197                 _E("curl_easy_perform() failed: %s",
198                         curl_easy_strerror(response));
199                 ret = -1;
200         }
201
202         curl_easy_cleanup(curl);
203         curl_formfree(formpost);
204         g_free(post_url);
205         g_free(filename);
206
207         return ret;
208 }
209
210 int web_util_noti_post(const char *resource, const char *json_data)
211 {
212         int ret = 0;
213         CURL *curl = NULL;
214         CURLcode response = CURLE_OK;
215         struct curl_slist *headers = NULL;
216
217         retv_if(resource == NULL, -1);
218         retv_if(json_data == NULL, -1);
219
220         _I("server : %s", resource);
221         _I("json_data : %s", json_data);
222
223         curl = curl_easy_init();
224
225         if (!curl) {
226                 _E("fail to init curl");
227                 return -1;
228         }
229
230         headers = curl_slist_append(headers, "Accept: application/json");
231         headers = curl_slist_append(headers, "Content-Type: application/json");
232
233         curl_easy_setopt(curl, CURLOPT_URL, resource);
234         curl_easy_setopt(curl, CURLOPT_POST, 1L);
235         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
236         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);
237         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _post_response_write_callback);
238         curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, REQ_CON_TIMEOUT);
239         curl_easy_setopt(curl, CURLOPT_TIMEOUT, REQ_TIMEOUT);
240
241         response = curl_easy_perform(curl);
242
243         if (response != CURLE_OK) {
244                 _E("curl_easy_perform() failed: %s",
245                         curl_easy_strerror(response));
246                 /* What should we do here, if response is kind of errors? */
247                 ret = -1;
248         }
249
250         curl_slist_free_all(headers);
251         curl_easy_cleanup(curl);
252
253         return ret;
254 }
255
256 int web_util_noti_get(const char *resource, char **res)
257 {
258         int ret = 0;
259         CURL *curl = NULL;
260         CURLcode response = CURLE_OK;
261
262         retv_if(resource == NULL, -1);
263
264         _I("GET to [%s]", resource);
265
266         curl = curl_easy_init();
267
268         if (!curl) {
269                 _E("fail to init curl");
270                 return -1;
271         }
272
273         curl_easy_setopt(curl, CURLOPT_URL, resource);
274         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _get_response_write_callback);
275         curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)res);
276         curl_easy_setopt(curl, CURLOPT_USERAGENT, "tizen-iot-agent/1.0");
277         curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, REQ_CON_TIMEOUT);
278
279         response = curl_easy_perform(curl);
280
281         if (response != CURLE_OK) {
282                 _E("curl_easy_perform() failed: %s",
283                 curl_easy_strerror(response));
284                 /* What should we do here, if response is kind of errors? */
285                 ret = -1;
286         }
287
288         curl_easy_cleanup(curl);
289
290         return ret;
291 }
292
293 int web_util_json_init(void)
294 {
295         if (Json_h.builder)
296                 g_object_unref(Json_h.builder);
297
298         Json_h.is_begin = false;
299         Json_h.is_end = false;
300         Json_h.builder = json_builder_new();
301         retv_if(Json_h.builder == NULL, -1);
302
303         return 0;
304 }
305
306 int web_util_json_fini(void)
307 {
308
309         if (Json_h.builder) {
310                 g_object_unref(Json_h.builder);
311                 Json_h.builder = NULL;
312         }
313
314         Json_h.is_begin = false;
315         Json_h.is_end = false;
316
317         return 0;
318 }
319
320 int web_util_json_begin(void)
321 {
322         retv_if(Json_h.builder == NULL, -1);
323         retv_if(Json_h.is_begin == true, -1);
324         retv_if(Json_h.is_end == true, -1);
325
326         Json_h.is_begin = true;
327
328         json_builder_begin_object(Json_h.builder);
329
330         return 0;
331 }
332
333 int web_util_json_end(void)
334 {
335         retv_if(Json_h.builder == NULL, -1);
336         retv_if(Json_h.is_begin == false, -1);
337         retv_if(Json_h.is_end == true, -1);
338
339         json_builder_end_object(Json_h.builder);
340         Json_h.is_end = true;
341
342         return 0;
343 }
344
345 int web_util_json_add_int(const char* key, long long int value)
346 {
347         retv_if(!key, -1);
348
349         if (Json_h.builder == NULL) {
350                 _E("Handle for json is not initialized, call web_util_json_init() first");
351                 return -1;
352         }
353
354         if (Json_h.is_begin == false) {
355                 _E("json object has not begun, call web_util_json_begin() first");
356                 return -1;
357         }
358
359         if (Json_h.is_end == true) {
360                 _E("json object has already ended, call web_util_json_begin() first");
361                 return -1;
362         }
363
364         json_builder_set_member_name(Json_h.builder, key);
365         json_builder_add_int_value(Json_h.builder, value);
366
367         return 0;
368 }
369
370 int web_util_json_add_double(const char* key, double value)
371 {
372         retv_if(!key, -1);
373
374         if (Json_h.builder == NULL) {
375                 _E("Handle for json is not initialized, call web_util_json_init() first");
376                 return -1;
377         }
378
379         if (Json_h.is_begin == false) {
380                 _E("json object has not begun, call web_util_json_begin() first");
381                 return -1;
382         }
383
384         if (Json_h.is_end == true) {
385                 _E("json object has already ended, call web_util_json_begin() first");
386                 return -1;
387         }
388
389         json_builder_set_member_name(Json_h.builder, key);
390         json_builder_add_double_value(Json_h.builder, value);
391
392         return 0;
393 }
394
395 int web_util_json_add_boolean(const char* key, bool value)
396 {
397         retv_if(!key, -1);
398
399         if (Json_h.builder == NULL) {
400                 _E("Handle for json is not initialized, call web_util_json_init() first");
401                 return -1;
402         }
403
404         if (Json_h.is_begin == false) {
405                 _E("json object has not begun, call web_util_json_begin() first");
406                 return -1;
407         }
408
409         if (Json_h.is_end == true) {
410                 _E("json object has already ended, call web_util_json_begin() first");
411                 return -1;
412         }
413
414         json_builder_set_member_name(Json_h.builder, key);
415         json_builder_add_boolean_value(Json_h.builder, value);
416
417         return 0;
418 }
419
420 int web_util_json_add_string(const char* key, const char *value)
421 {
422         retv_if(!key, -1);
423
424         if (Json_h.builder == NULL) {
425                 _E("Handle for json is not initialized, call web_util_json_init() first");
426                 return -1;
427         }
428
429         if (Json_h.is_begin == false) {
430                 _E("json object has not begun, call web_util_json_begin() first");
431                 return -1;
432         }
433
434         if (Json_h.is_end == true) {
435                 _E("json object has already ended, call web_util_json_begin() first");
436                 return -1;
437         }
438
439         json_builder_set_member_name(Json_h.builder, key);
440         json_builder_add_string_value(Json_h.builder, value);
441
442         return 0;
443 }
444
445 int web_util_json_data_array_begin(void)
446 {
447         int ret = 0;
448         retv_if(Json_h.builder == NULL, -1);
449
450         ret = web_util_json_begin();
451         retv_if(ret, -1);
452
453         json_builder_set_member_name(Json_h.builder, "SensorDataList");
454         json_builder_begin_array(Json_h.builder);
455
456         return 0;
457 }
458
459 int web_util_json_data_array_end(void)
460 {
461         retv_if(Json_h.builder == NULL, -1);
462         retv_if(Json_h.is_begin == false, -1);
463         retv_if(Json_h.is_end == true, -1);
464
465         json_builder_end_array(Json_h.builder);
466         web_util_json_end();
467
468         return 0;
469 }
470
471 int web_util_json_add_sensor_data(const char* sensorpi_id, web_util_sensor_data_s *sensor_data)
472 {
473         const char n_id[] = "SensorPiID";
474         const char n_motion[] = "Motion";
475         const char n_flame[] = "Flame";
476         const char n_hum[] = "Humidity";
477         const char n_temp[] = "Temperature";
478         const char n_vib[] = "Vibration";
479         const char n_co2[] = "CO2";
480         const char n_sound[] = "SoundLevel";
481         const char n_tilt[] = "Tilt";
482         const char n_light[] = "Light";
483         const char n_collision[] = "Collision";
484         const char n_obstacle[] = "Obstacle";
485         const char n_distance[] = "Distance";
486         const char n_rain[] = "Rain";
487         const char n_touch[] = "Touch";
488         const char n_gas[] = "Gas";
489         const char n_e_sensor[] = "SensorEnabled";
490         const char n_hash[] = "Hash";
491         const char n_ip[] = "SensorPiIP";
492
493         retv_if(!sensorpi_id, -1);
494         retv_if(Json_h.builder == NULL, -1);
495         retv_if(Json_h.is_begin == false, -1);
496         retv_if(Json_h.is_end == true, -1);
497         retv_if(sensor_data == NULL, -1);
498
499         /* JSON structure :
500         {
501                 SensorPiID: string,
502                 SensorPiIP: string,
503                 Motion: boolean,
504                 Flame: boolean,
505                 Humidity: double,
506                 Temperature: double,
507                 Vibration: boolean,
508                 CO2: double,
509                 SoundLevel: int,
510                 Tilt: int,
511                 Light: int,
512                 Collision: int,
513                 Obstacle: int,
514                 Distance: double,
515                 Rain: int,
516                 Touch: int,
517                 Gas: int,
518                 SensorEnabled: [Motion, ],
519                 Hash: string,
520         }
521         */
522
523         json_builder_begin_object(Json_h.builder);
524
525         json_builder_set_member_name(Json_h.builder, n_id);
526         json_builder_add_string_value(Json_h.builder, sensorpi_id);
527
528         if (sensor_data->ip_addr) {
529                 json_builder_set_member_name(Json_h.builder, n_ip);
530                 json_builder_add_string_value(Json_h.builder, sensor_data->ip_addr);
531         }
532
533         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_MOTION) {
534                 json_builder_set_member_name(Json_h.builder, n_motion);
535                 json_builder_add_int_value(Json_h.builder, sensor_data->motion);
536         }
537
538         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_FLAME) {
539                 json_builder_set_member_name(Json_h.builder, n_flame);
540                 json_builder_add_int_value(Json_h.builder, sensor_data->flame);
541         }
542
543         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_HUMIDITY) {
544                 json_builder_set_member_name(Json_h.builder, n_hum);
545                 json_builder_add_double_value(Json_h.builder, sensor_data->humidity);
546         }
547
548         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_TEMPERATURE) {
549                 json_builder_set_member_name(Json_h.builder, n_temp);
550                 json_builder_add_double_value(Json_h.builder, sensor_data->temperature);
551         }
552
553         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_VIB) {
554                 json_builder_set_member_name(Json_h.builder, n_vib);
555                 json_builder_add_int_value(Json_h.builder, sensor_data->virbration);
556         }
557
558         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_CO2) {
559                 json_builder_set_member_name(Json_h.builder, n_co2);
560                 json_builder_add_double_value(Json_h.builder, sensor_data->co2);
561         }
562
563         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_SOUND) {
564                 json_builder_set_member_name(Json_h.builder, n_sound);
565                 json_builder_add_int_value(Json_h.builder, sensor_data->soundlevel);
566         }
567
568         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_TILT) {
569                 json_builder_set_member_name(Json_h.builder, n_tilt);
570                 json_builder_add_int_value(Json_h.builder, sensor_data->tilt);
571         }
572
573         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_LIGHT) {
574                 json_builder_set_member_name(Json_h.builder, n_light);
575                 json_builder_add_int_value(Json_h.builder, sensor_data->light);
576         }
577
578         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_COLLISION) {
579                 json_builder_set_member_name(Json_h.builder, n_collision);
580                 json_builder_add_int_value(Json_h.builder, sensor_data->collision);
581         }
582
583         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_OBSTACLE) {
584                 json_builder_set_member_name(Json_h.builder, n_obstacle);
585                 json_builder_add_int_value(Json_h.builder, sensor_data->obstacle);
586         }
587
588         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_ULTRASONIC_DISTANCE) {
589                 json_builder_set_member_name(Json_h.builder, n_distance);
590                 json_builder_add_double_value(Json_h.builder, sensor_data->distance);
591         }
592
593         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_RAIN) {
594                 json_builder_set_member_name(Json_h.builder, n_rain);
595                 json_builder_add_int_value(Json_h.builder, sensor_data->rain);
596         }
597
598         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_TOUCH) {
599                 json_builder_set_member_name(Json_h.builder, n_touch);
600                 json_builder_add_int_value(Json_h.builder, sensor_data->touch);
601         }
602
603         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_GAS) {
604                 json_builder_set_member_name(Json_h.builder, n_gas);
605                 json_builder_add_int_value(Json_h.builder, sensor_data->gas);
606         }
607
608         json_builder_set_member_name(Json_h.builder, n_e_sensor);
609         json_builder_begin_array(Json_h.builder);
610
611         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_MOTION)
612                 json_builder_add_string_value(Json_h.builder, n_motion);
613
614         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_FLAME)
615                 json_builder_add_string_value(Json_h.builder, n_flame);
616
617         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_HUMIDITY)
618                 json_builder_add_string_value(Json_h.builder, n_hum);
619
620         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_TEMPERATURE)
621                 json_builder_add_string_value(Json_h.builder, n_temp);
622
623         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_VIB)
624                 json_builder_add_string_value(Json_h.builder, n_vib);
625
626         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_CO2)
627                 json_builder_add_string_value(Json_h.builder, n_co2);
628
629         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_SOUND)
630                 json_builder_add_string_value(Json_h.builder, n_sound);
631
632         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_TILT)
633                 json_builder_add_string_value(Json_h.builder, n_tilt);
634
635         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_LIGHT)
636                 json_builder_add_string_value(Json_h.builder, n_light);
637
638         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_COLLISION)
639                 json_builder_add_string_value(Json_h.builder, n_collision);
640
641         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_OBSTACLE)
642                 json_builder_add_string_value(Json_h.builder, n_obstacle);
643
644         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_ULTRASONIC_DISTANCE)
645                 json_builder_add_string_value(Json_h.builder, n_distance);
646
647         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_RAIN)
648                 json_builder_add_string_value(Json_h.builder, n_rain);
649
650         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_TOUCH)
651                 json_builder_add_string_value(Json_h.builder, n_touch);
652
653         if (sensor_data->enabled_sensor & WEB_UTIL_SENSOR_GAS)
654                 json_builder_add_string_value(Json_h.builder, n_gas);
655
656         json_builder_end_array(Json_h.builder);
657
658         if (sensor_data->hash) {
659                 json_builder_set_member_name(Json_h.builder, n_hash);
660                 json_builder_add_string_value(Json_h.builder, sensor_data->hash);
661         }
662
663         json_builder_end_object(Json_h.builder);
664
665         return 0;
666 }
667
668 char *web_util_get_json_string(void)
669 {
670         JsonGenerator *gen = NULL;
671         JsonNode *root = NULL;
672         char *str = NULL;
673
674         retv_if(Json_h.builder == NULL, NULL);
675         retv_if(Json_h.is_begin == false, NULL);
676         retv_if(Json_h.is_end == false, NULL);
677
678         root = json_builder_get_root(Json_h.builder);
679         retv_if(root == NULL, NULL);
680
681         gen = json_generator_new();
682         goto_if(gen == NULL, error_release_all);
683         json_generator_set_root(gen, root);
684
685         str = json_generator_to_data(gen, NULL);
686
687         return str;
688
689 error_release_all:
690         if (root)
691                 json_node_free(root);
692
693         return NULL;
694 }
695