res_pjsip: AMI commands and events.
[asterisk/asterisk.git] / res / res_pjsip_exten_state.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Kevin Harwell <kharwell@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*** MODULEINFO
20         <depend>pjproject</depend>
21         <depend>res_pjsip</depend>
22         <depend>res_pjsip_pubsub</depend>
23         <support_level>core</support_level>
24  ***/
25
26 #include "asterisk.h"
27
28 #include <pjsip.h>
29 #include <pjsip_simple.h>
30 #include <pjlib.h>
31
32 #include "asterisk/res_pjsip.h"
33 #include "asterisk/res_pjsip_pubsub.h"
34 #include "asterisk/res_pjsip_exten_state.h"
35 #include "asterisk/module.h"
36 #include "asterisk/logger.h"
37 #include "asterisk/astobj2.h"
38 #include "asterisk/sorcery.h"
39 #include "asterisk/app.h"
40
41 #define BODY_SIZE 1024
42 #define EVENT_TYPE_SIZE 50
43
44 AST_RWLIST_HEAD_STATIC(providers, ast_sip_exten_state_provider);
45
46 /*!
47  * \internal
48  * \brief Find a provider based on the given accept body type.
49  */
50 static struct ast_sip_exten_state_provider *provider_by_type(const char *type)
51 {
52         struct ast_sip_exten_state_provider *i;
53         SCOPED_LOCK(lock, &providers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
54         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&providers, i, next) {
55                 if (!strcmp(i->body_type, type)) {
56                         return i;
57                 }
58         }
59         AST_RWLIST_TRAVERSE_SAFE_END;
60         return NULL;
61 }
62
63 /*!
64  * \internal
65  * \brief Find a provider based on the given accept body types.
66  */
67 static struct ast_sip_exten_state_provider *provider_by_types(const char *event_name,
68                                                               char **types, int count)
69 {
70         int i;
71         struct ast_sip_exten_state_provider *res;
72         for (i = 0; i < count; ++i) {
73                 if ((res = provider_by_type(types[i])) &&
74                     !strcmp(event_name, res->event_name)) {
75                         return res;
76                 }
77         }
78         return NULL;
79 }
80
81 /*!
82  * \brief A subscription for extension state
83  *
84  * This structure acts as the owner for the underlying SIP subscription. It
85  * also keeps a pointer to an associated "provider" so when a state changes
86  * a notify data creator is quickly accessible.
87  */
88 struct exten_state_subscription {
89         /*! Watcher id when registering for extension state changes */
90         int id;
91         /*! The SIP subscription */
92         struct ast_sip_subscription *sip_sub;
93         /*! The name of the event the subscribed to */
94         char event_name[EVENT_TYPE_SIZE];
95         /*! The number of body types */
96         int body_types_count;
97         /*! The subscription body types */
98         char **body_types;
99         /*! Context in which subscription looks for updates */
100         char context[AST_MAX_CONTEXT];
101         /*! Extension within the context to receive updates from */
102         char exten[AST_MAX_EXTENSION];
103         /*! The last known extension state */
104         enum ast_extension_states last_exten_state;
105 };
106
107 static void exten_state_subscription_destructor(void *obj)
108 {
109         struct exten_state_subscription *sub = obj;
110         int i;
111
112         for (i = 0; i < sub->body_types_count; ++i) {
113                 ast_free(sub->body_types[i]);
114         }
115
116         ast_free(sub->body_types);
117         ao2_cleanup(sub->sip_sub);
118 }
119
120 /*!
121  * \internal
122  * \brief Copies the body types the message wishes to subscribe to.
123  */
124 static void copy_body_types(pjsip_rx_data *rdata,
125                             struct exten_state_subscription *exten_state_sub)
126 {
127         int i;
128         pjsip_accept_hdr *hdr = (pjsip_accept_hdr*)
129                 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
130
131         exten_state_sub->body_types_count = hdr->count;
132         exten_state_sub->body_types = ast_malloc(hdr->count * sizeof(char*));
133
134         for (i = 0; i < hdr->count; ++i) {
135                 exten_state_sub->body_types[i] =
136                         ast_malloc(hdr->values[i].slen * sizeof(char*) + 1);
137
138                 ast_copy_string(exten_state_sub->body_types[i],
139                                 pj_strbuf(&hdr->values[i]), hdr->values[i].slen + 1);
140         }
141 }
142
143 /*!
144  * \internal
145  * \brief Initialize the last extension state to something outside
146  * its usual states.
147  */
148 #define INITIAL_LAST_EXTEN_STATE -3
149
150 /*!
151  * \internal
152  * \brief Allocates an exten_state_subscription object.
153  *
154  * Creates the underlying SIP subscription for the given request. First makes
155  * sure that there are registered handler and provider objects available.
156  */
157 static struct exten_state_subscription *exten_state_subscription_alloc(
158         struct ast_sip_endpoint *endpoint, enum ast_sip_subscription_role role, pjsip_rx_data *rdata)
159 {
160         static const pj_str_t event_name = { "Event", 5 };
161         pjsip_event_hdr *hdr = (pjsip_event_hdr*)pjsip_msg_find_hdr_by_name(
162                 rdata->msg_info.msg, &event_name, NULL);
163
164         struct ast_sip_exten_state_provider *provider;
165         RAII_VAR(struct exten_state_subscription *, exten_state_sub,
166                  ao2_alloc(sizeof(*exten_state_sub), exten_state_subscription_destructor), ao2_cleanup);
167
168         if (!exten_state_sub) {
169                 return NULL;
170         }
171
172         ast_copy_pj_str(exten_state_sub->event_name, &hdr->event_type,
173                         sizeof(exten_state_sub->event_name));
174
175         copy_body_types(rdata, exten_state_sub);
176         if (!(provider = provider_by_types(exten_state_sub->event_name,
177                                            exten_state_sub->body_types,
178                                            exten_state_sub->body_types_count))) {
179                 ast_log(LOG_WARNING, "Unable to locate subscription handler\n");
180                 return NULL;
181         }
182
183         if (!(exten_state_sub->sip_sub = ast_sip_create_subscription(
184                       provider->handler, role, endpoint, rdata))) {
185                 ast_log(LOG_WARNING, "Unable to create SIP subscription for endpoint %s\n",
186                         ast_sorcery_object_get_id(endpoint));
187                 return NULL;
188         }
189
190         exten_state_sub->last_exten_state = INITIAL_LAST_EXTEN_STATE;
191
192         ao2_ref(exten_state_sub, +1);
193         return exten_state_sub;
194 }
195
196 /*!
197  * \internal
198  * \brief Create and send a NOTIFY request to the subscriber.
199  */
200 static void create_send_notify(struct exten_state_subscription *exten_state_sub, const char *reason,
201                                pjsip_evsub_state evsub_state, struct ast_sip_exten_state_data *exten_state_data)
202 {
203         RAII_VAR(struct ast_str *, body_text, ast_str_create(BODY_SIZE), ast_free_ptr);
204         pj_str_t reason_str;
205         const pj_str_t *reason_str_ptr = NULL;
206         pjsip_tx_data *tdata;
207         pjsip_dialog *dlg;
208         char local[PJSIP_MAX_URL_SIZE], remote[PJSIP_MAX_URL_SIZE];
209         struct ast_sip_body body;
210
211         struct ast_sip_exten_state_provider *provider = provider_by_types(
212                 exten_state_sub->event_name, exten_state_sub->body_types,
213                 exten_state_sub->body_types_count);
214
215         if (!provider) {
216                 ast_log(LOG_ERROR, "Unable to locate provider for subscription\n");
217                 return;
218         }
219
220         body.type = provider->type;
221         body.subtype = provider->subtype;
222
223         dlg = ast_sip_subscription_get_dlg(exten_state_sub->sip_sub);
224         ast_copy_pj_str(local, &dlg->local.info_str, sizeof(local));
225         ast_copy_pj_str(remote, &dlg->remote.info_str, sizeof(remote));
226
227         if (provider->create_body(exten_state_data, local, remote, &body_text)) {
228                 ast_log(LOG_ERROR, "Unable to create body on NOTIFY request\n");
229                 return;
230         }
231
232         body.body_text = ast_str_buffer(body_text);
233
234         if (reason) {
235                 pj_cstr(&reason_str, reason);
236                 reason_str_ptr = &reason_str;
237         }
238
239         if (pjsip_evsub_notify(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub),
240                               evsub_state, NULL, reason_str_ptr, &tdata) != PJ_SUCCESS) {
241                 ast_log(LOG_WARNING, "Unable to create NOTIFY request\n");
242                 return;
243         }
244
245         if (ast_sip_add_body(tdata, &body)) {
246                 ast_log(LOG_WARNING, "Unable to add body to NOTIFY request\n");
247                 pjsip_tx_data_dec_ref(tdata);
248                 return;
249         }
250
251         if (ast_sip_subscription_send_request(exten_state_sub->sip_sub, tdata) != PJ_SUCCESS) {
252                 ast_log(LOG_WARNING, "Unable to send NOTIFY request\n");
253         }
254 }
255
256 /*!
257  * \internal
258  * \brief Get device state information and send notification to the subscriber.
259  */
260 static void send_notify(struct exten_state_subscription *exten_state_sub, const char *reason,
261         pjsip_evsub_state evsub_state)
262 {
263         RAII_VAR(struct ao2_container*, info, NULL, ao2_cleanup);
264         char *subtype = NULL, *message = NULL;
265
266         struct ast_sip_exten_state_data exten_state_data = {
267                 .exten = exten_state_sub->exten,
268                 .presence_state = ast_hint_presence_state(NULL, exten_state_sub->context,
269                                                           exten_state_sub->exten, &subtype, &message),
270         };
271
272         if ((exten_state_data.exten_state = ast_extension_state_extended(
273                      NULL, exten_state_sub->context, exten_state_sub->exten, &info)) < 0) {
274
275                 ast_log(LOG_WARNING, "Unable to get device hint/info for extension %s\n",
276                         exten_state_sub->exten);
277                 return;
278         }
279
280         exten_state_data.device_state_info = info;
281         create_send_notify(exten_state_sub, reason, evsub_state, &exten_state_data);
282 }
283
284 struct notify_task_data {
285         struct ast_sip_exten_state_data exten_state_data;
286         struct exten_state_subscription *exten_state_sub;
287         pjsip_evsub_state evsub_state;
288 };
289
290 static void notify_task_data_destructor(void *obj)
291 {
292         struct notify_task_data *task_data = obj;
293
294         ao2_ref(task_data->exten_state_sub, -1);
295         ao2_cleanup(task_data->exten_state_data.device_state_info);
296 }
297
298 static struct notify_task_data *alloc_notify_task_data(char *exten, struct exten_state_subscription *exten_state_sub,
299                                                        struct ast_state_cb_info *info)
300 {
301         struct notify_task_data *task_data =
302                 ao2_alloc(sizeof(*task_data), notify_task_data_destructor);
303
304         if (!task_data) {
305                 ast_log(LOG_WARNING, "Unable to create notify task data\n");
306                 return NULL;
307         }
308
309         task_data->evsub_state = PJSIP_EVSUB_STATE_ACTIVE;
310         task_data->exten_state_sub = exten_state_sub;
311         task_data->exten_state_sub->last_exten_state = info->exten_state;
312         ao2_ref(task_data->exten_state_sub, +1);
313
314         task_data->exten_state_data.exten = exten_state_sub->exten;
315         task_data->exten_state_data.exten_state = info->exten_state;
316         task_data->exten_state_data.presence_state = info->presence_state;
317         task_data->exten_state_data.device_state_info = info->device_state_info;
318
319         if (task_data->exten_state_data.device_state_info) {
320                 ao2_ref(task_data->exten_state_data.device_state_info, +1);
321         }
322
323         if ((info->exten_state == AST_EXTENSION_DEACTIVATED) ||
324             (info->exten_state == AST_EXTENSION_REMOVED)) {
325                 task_data->evsub_state = PJSIP_EVSUB_STATE_TERMINATED;
326                 ast_log(LOG_WARNING, "Watcher for hint %s %s\n", exten, info->exten_state
327                          == AST_EXTENSION_REMOVED ? "removed" : "deactivated");
328         }
329
330         return task_data;
331 }
332
333 static int notify_task(void *obj)
334 {
335         RAII_VAR(struct notify_task_data *, task_data, obj, ao2_cleanup);
336
337         create_send_notify(task_data->exten_state_sub, task_data->evsub_state ==
338                            PJSIP_EVSUB_STATE_TERMINATED ? "noresource" : NULL,
339                            task_data->evsub_state, &task_data->exten_state_data);
340         return 0;
341 }
342
343 /*!
344  * \internal
345  * \brief Callback for exten/device state changes.
346  *
347  * Upon state change, send the appropriate notification to the subscriber.
348  */
349 static int state_changed(char *context, char *exten,
350                          struct ast_state_cb_info *info, void *data)
351 {
352         struct notify_task_data *task_data;
353         struct exten_state_subscription *exten_state_sub = data;
354
355         if (exten_state_sub->last_exten_state == info->exten_state) {
356                 return 0;
357         }
358
359         if (!(task_data = alloc_notify_task_data(exten, exten_state_sub, info))) {
360                 return -1;
361         }
362
363         /* safe to push this async since we copy the data from info and
364            add a ref for the device state info */
365         if (ast_sip_push_task(ast_sip_subscription_get_serializer(task_data->exten_state_sub->sip_sub),
366                               notify_task, task_data)) {
367                 ao2_cleanup(task_data);
368                 return -1;
369         }
370         return 0;
371 }
372
373 static void state_changed_destroy(int id, void *data)
374 {
375         struct exten_state_subscription *exten_state_sub = data;
376         ao2_cleanup(exten_state_sub);
377 }
378
379 static struct ast_datastore_info ds_info = { };
380 static const char ds_name[] = "exten state datastore";
381
382 /*!
383  * \internal
384  * \brief Add a datastore for exten exten_state_subscription.
385  *
386  * Adds the exten_state_subscription wrapper object to a datastore so it can be retrieved
387  * later based upon its association with the ast_sip_subscription.
388  */
389 static int add_datastore(struct exten_state_subscription *exten_state_sub)
390 {
391         RAII_VAR(struct ast_datastore *, datastore,
392                  ast_sip_subscription_alloc_datastore(&ds_info, ds_name), ao2_cleanup);
393
394         if (!datastore) {
395                 return -1;
396         }
397
398         datastore->data = exten_state_sub;
399         ast_sip_subscription_add_datastore(exten_state_sub->sip_sub, datastore);
400         ao2_ref(exten_state_sub, +1);
401         return 0;
402 }
403
404 /*!
405  * \internal
406  * \brief Get the exten_state_subscription object associated with the given
407  * ast_sip_subscription in the datastore.
408  */
409 static struct exten_state_subscription *get_exten_state_sub(
410         struct ast_sip_subscription *sub)
411 {
412         RAII_VAR(struct ast_datastore *, datastore,
413                  ast_sip_subscription_get_datastore(sub, ds_name), ao2_cleanup);
414
415         return datastore ? datastore->data : NULL;
416 }
417
418 static void subscription_shutdown(struct ast_sip_subscription *sub)
419 {
420         struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub);
421
422         if (!exten_state_sub) {
423                 return;
424         }
425
426         ast_extension_state_del(exten_state_sub->id, state_changed);
427         ast_sip_subscription_remove_datastore(exten_state_sub->sip_sub, ds_name);
428         /* remove data store reference */
429         ao2_cleanup(exten_state_sub);
430 }
431
432 static struct ast_sip_subscription *new_subscribe(struct ast_sip_endpoint *endpoint,
433                                                   pjsip_rx_data *rdata)
434 {
435         pjsip_uri *uri = rdata->msg_info.msg->line.req.uri;
436         pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(uri);
437         RAII_VAR(struct exten_state_subscription *, exten_state_sub, NULL, ao2_cleanup);
438
439         if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
440                 ast_log(LOG_WARNING, "Attempt to SUBSCRIBE to a non-SIP URI\n");
441                 return NULL;
442         }
443
444         if (!(exten_state_sub = exten_state_subscription_alloc(endpoint, AST_SIP_NOTIFIER, rdata))) {
445                 return NULL;
446         }
447
448         ast_copy_string(exten_state_sub->context, endpoint->context, sizeof(exten_state_sub->context));
449         ast_copy_pj_str(exten_state_sub->exten, &sip_uri->user, sizeof(exten_state_sub->exten));
450
451         if ((exten_state_sub->id = ast_extension_state_add_destroy_extended(
452                      exten_state_sub->context, exten_state_sub->exten,
453                      state_changed, state_changed_destroy, exten_state_sub)) < 0) {
454                 ast_log(LOG_WARNING, "Unable to subscribe endpoint '%s' to extension '%s@%s'\n",
455                         ast_sorcery_object_get_id(endpoint), exten_state_sub->exten,
456                         exten_state_sub->context);
457                 pjsip_evsub_terminate(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), PJ_FALSE);
458                 return NULL;
459         }
460
461         /* bump the ref since ast_extension_state_add holds a reference */
462         ao2_ref(exten_state_sub, +1);
463
464         if (add_datastore(exten_state_sub)) {
465                 ast_log(LOG_WARNING, "Unable to add to subscription datastore.\n");
466                 pjsip_evsub_terminate(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), PJ_FALSE);
467                 return NULL;
468         }
469
470         if (pjsip_evsub_accept(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub),
471                                rdata, 200, NULL) != PJ_SUCCESS) {
472                 ast_log(LOG_WARNING, "Unable to accept the incoming extension state subscription.\n");
473                 pjsip_evsub_terminate(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), PJ_FALSE);
474                 return NULL;
475         }
476
477         send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_ACTIVE);
478         return exten_state_sub->sip_sub;
479 }
480
481 static void resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata,
482                         struct ast_sip_subscription_response_data *response_data)
483 {
484         struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub);
485
486         if (!exten_state_sub) {
487                 return;
488         }
489
490         send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_ACTIVE);
491 }
492
493 static void subscription_timeout(struct ast_sip_subscription *sub)
494 {
495         struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub);
496
497         if (!exten_state_sub) {
498                 return;
499         }
500
501         ast_verbose(VERBOSE_PREFIX_3 "Subscription has timed out.\n");
502         send_notify(exten_state_sub, "timeout", PJSIP_EVSUB_STATE_TERMINATED);
503 }
504
505 static void subscription_terminated(struct ast_sip_subscription *sub,
506                                     pjsip_rx_data *rdata)
507 {
508         struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub);
509
510         if (!exten_state_sub) {
511                 return;
512         }
513
514         ast_verbose(VERBOSE_PREFIX_3 "Subscription has been terminated.\n");
515         send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_TERMINATED);
516 }
517
518 static void to_ami(struct ast_sip_subscription *sub,
519                    struct ast_str **buf)
520 {
521         struct exten_state_subscription *exten_state_sub =
522                 get_exten_state_sub(sub);
523
524         ast_str_append(buf, 0, "SubscriptionType: extension_state\r\n"
525                        "Extension: %s\r\nExtensionStates: %s\r\n",
526                        exten_state_sub->exten, ast_extension_state2str(
527                                exten_state_sub->last_exten_state));
528 }
529
530 #define DEFAULT_PRESENCE_BODY "application/pidf+xml"
531
532 /*!
533  * \internal
534  * \brief Create and register a subscription handler.
535  *
536  * Creates a subscription handler that can be registered with the pub/sub
537  * framework for the given event_name and accept value.
538  */
539 static struct ast_sip_subscription_handler *create_and_register_handler(
540         const char *event_name, const char *accept)
541 {
542         struct ast_sip_subscription_handler *handler =
543                 ao2_alloc(sizeof(*handler), NULL);
544
545         if (!handler) {
546                 return NULL;
547         }
548
549         handler->event_name = event_name;
550         handler->accept[0] = accept;
551         if (!strcmp(accept, DEFAULT_PRESENCE_BODY)) {
552                 handler->handles_default_accept = 1;
553         }
554
555         handler->subscription_shutdown = subscription_shutdown;
556         handler->new_subscribe = new_subscribe;
557         handler->resubscribe = resubscribe;
558         handler->subscription_timeout = subscription_timeout;
559         handler->subscription_terminated = subscription_terminated;
560         handler->to_ami = to_ami;
561
562         if (ast_sip_register_subscription_handler(handler)) {
563                 ast_log(LOG_WARNING, "Unable to register subscription handler %s\n",
564                         handler->event_name);
565                 ao2_cleanup(handler);
566                 return NULL;
567         }
568
569         return handler;
570 }
571
572 int ast_sip_register_exten_state_provider(struct ast_sip_exten_state_provider *obj)
573 {
574         if (ast_strlen_zero(obj->type)) {
575                 ast_log(LOG_WARNING, "Type not specified on provider for event %s\n",
576                         obj->event_name);
577                 return -1;
578         }
579
580         if (ast_strlen_zero(obj->subtype)) {
581                 ast_log(LOG_WARNING, "Subtype not specified on provider for event %s\n",
582                         obj->event_name);
583                 return -1;
584         }
585
586         if (!obj->create_body) {
587                 ast_log(LOG_WARNING, "Body handler not specified on provide for event %s\n",
588                     obj->event_name);
589                 return -1;
590         }
591
592         if (!(obj->handler = create_and_register_handler(obj->event_name, obj->body_type))) {
593                 ast_log(LOG_WARNING, "Handler could not be registered for provider event %s\n",
594                     obj->event_name);
595                 return -1;
596         }
597
598         /* scope to avoid mix declarations */
599         {
600                 SCOPED_LOCK(lock, &providers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
601                 AST_RWLIST_INSERT_TAIL(&providers, obj, next);
602                 ast_module_ref(ast_module_info->self);
603         }
604
605         return 0;
606 }
607
608 void ast_sip_unregister_exten_state_provider(struct ast_sip_exten_state_provider *obj)
609 {
610         struct ast_sip_exten_state_provider *i;
611         SCOPED_LOCK(lock, &providers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
612         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&providers, i, next) {
613                 if (i == obj) {
614                         ast_sip_unregister_subscription_handler(i->handler);
615                         ao2_cleanup(i->handler);
616                         AST_RWLIST_REMOVE_CURRENT(next);
617                         ast_module_unref(ast_module_info->self);
618                         break;
619                 }
620         }
621         AST_RWLIST_TRAVERSE_SAFE_END;
622 }
623
624 static int load_module(void)
625 {
626         return AST_MODULE_LOAD_SUCCESS;
627 }
628
629 static int unload_module(void)
630 {
631         return 0;
632 }
633
634 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State Notifications",
635                 .load = load_module,
636                 .unload = unload_module,
637                 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
638 );