Beta merge 2
[profile/ivi/wrt-plugins-tizen.git] / src / platform / Tizen / Messaging / Sms.cpp
1 /*
2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License. 
15 */
16
17
18 /**
19  *
20  *
21  * @file       Sms.cpp
22  * @author     Pawel Misiak (p.misiak@samsung.com)
23  * @version    0.1
24  * @brief
25  */
26 #include "Sms.h"
27 #include <dpl/log/log.h>
28 #include <Commons/Exception.h>
29 #include <API/Messaging/ReqReceiverMessage.h>
30 #include "Messaging.h"
31 #include "CallbackMgr.h"
32 #include "MsgServiceHandleMgr.h"
33
34 extern "C" {
35 #include <MapiStorage.h>
36 #include <MapiMessage.h>
37 }
38
39 using namespace std;
40 using namespace TizenApis::Api::Messaging;
41
42 namespace {
43 const char* EMPTY_ID = "0";
44 }
45
46 namespace TizenApis {
47 namespace Platform {
48 namespace Messaging {
49
50 Sms::Sms(const string& id) :
51     IMessage(SMS, id),
52     m_messageData(NULL)
53 {
54     LogDebug("enter");
55     LogDebug("m_id=" << getIdRef());
56     LogDebug("m_msgType=" << getMessageType());
57
58     if (getIdRef().empty()) {
59         createNewMessage();
60     } else {
61         readExistingMessage();
62     }
63
64     Touch();
65 }
66
67 Sms::~Sms()
68 {
69     LogDebug("enter");
70
71     if (m_messageData) {
72         // release platform message structure
73         msg_release_message(&m_messageData);
74     }
75 }
76
77 void Sms::update(bool draftsOnly)
78 {
79     LogDebug("updating m_id=" << getIdRef());
80
81     if (!m_messageData) {
82         //error if platform message not exists
83         LogError("message can not be updated");
84         Throw(WrtDeviceApis::Commons::PlatformException);
85     }
86
87     //update all sms data
88     if (!draftsOnly || getCurrentFolder() == Api::Messaging::DRAFTBOX) {
89         updateBody();
90         updateFrom();
91         updateTo();
92         updateSourceAddress();
93     }
94     updateReadStatus();
95 }
96
97 void Sms::readAllData()
98 {
99     readExistingMessage();
100 }
101
102 void Sms::moveToFolder(const FolderType newFolder)
103 {
104     update();
105
106     Try
107     {
108         const MSG_FOLDER_ID_T platfromFolderId =
109             Messaging::convertFolderToPlatform(newFolder);
110
111         //call platform api to move message to other folder
112         if (msg_move_msg_to_folder(MsgGetCommonHandle(), convertId(getId()),
113                                    platfromFolderId) != MSG_SUCCESS) {
114             Throw(WrtDeviceApis::Commons::PlatformException);
115         }
116     }
117
118     Catch(WrtDeviceApis::Commons::PlatformException) {
119         LogError("remove message error");
120         throw;
121     }
122 }
123
124 void Sms::moveToFolder(const std::string& newFolder)
125 {
126     update();
127     //TODO
128 }
129
130 void Sms::copyToFolder(const FolderType newFolder)
131 {
132     update();
133
134     msg_message_t msg = msg_new_message();
135
136     Try
137     {
138         MSG_SENDINGOPT_S option = { false, false, false };
139         option.option.smsSendOpt.bReplyPath = true;
140         // trying to get message from platform
141         if (MSG_SUCCESS ==
142             msg_get_message(MsgGetCommonHandle(), convertId(getIdRef()), msg,
143                             &option)) {
144             const MSG_FOLDER_ID_T platfromFolderId =
145                 Messaging::convertFolderToPlatform(newFolder);
146             // new id will me set (initialize to 0)
147             msg_set_message_id(msg, 0);
148             msg_set_folder_id(msg, platfromFolderId);
149
150             // trying to add message
151             int error = msg_add_message(MsgGetCommonHandle(), msg, &option);
152             if (error != MSG_SUCCESS) {
153                 LogError("msg_add_message failed, error code=" << error);
154                 Throw(WrtDeviceApis::Commons::PlatformException);
155             }
156
157             //releasing platform message structure
158             msg_release_message(&msg);
159         }
160     }
161
162     Catch(WrtDeviceApis::Commons::PlatformException) {
163         LogError("remove message error");
164         if (msg) {
165             //releasing platform message structure
166             msg_release_message(&msg);
167         }
168         throw;
169     }
170 }
171
172 void Sms::copyToFolder(const std::string& newFolder)
173 {
174     update();
175     //TODO
176 }
177
178 int Sms::send()
179 {
180     LogDebug("sending message, id=" << getIdRef());
181
182     //prepare for sending sms
183     update();
184
185     // split the message
186     LogDebug("Start Sending Message...");
187     LogDebug("msgId=" << msg_get_message_id(m_messageData));
188     LogDebug("subject: " << msg_get_subject(m_messageData));
189     LogDebug("recipient count: " << msg_get_address_count(m_messageData));
190
191     SendRequest req;
192
193     for (int i = 0; i < msg_get_address_count(m_messageData); ++i) {
194         if (MSG_RECIPIENTS_TYPE_TO !=
195             msg_get_ith_recipient_type(m_messageData, i)) {
196             continue;
197         }
198         msg_message_t msg = createNewCopyOfPLatformMsg(m_messageData);
199
200         // remove all addresses except ith
201         msg_reset_address(msg);
202         const char* to = msg_get_ith_address(m_messageData, i);
203         msg_add_address(msg, to, MSG_RECIPIENTS_TYPE_TO);
204
205         setMessageStatus(MESSAGE_STATUS_SENDING);
206
207         LogDebug("recipient: " << msg_get_ith_address(msg, 0));
208         LogDebug("body: " << msg_sms_get_message_body(msg));
209
210         req.queue.push(msg);
211     }
212
213     DPL::Mutex::ScopedLock lock(&m_mutex);
214     m_sendRequests.push(req);
215
216     // schedule sending
217     PostEvent(0);
218
219     LogDebug("sending method finished");
220
221     return 0;           //sms handle is -1;
222 }
223
224 void Sms::sendingCallback(MSG_SENT_STATUS_S *sent_status)
225 {
226     LogInfo(
227         "sendingCallback callback received. Req id = " <<
228         sent_status->reqId << " Status = " << (int)sent_status->status <<
229         ", Recipient=" << getRecipient());
230
231     DPL::Mutex::ScopedLock lock(&m_mutex);
232
233     if (sent_status->status != MSG_NETWORK_SEND_SUCCESS) {
234         m_sendRequests.front().failed = true; // TODO mutex
235         //send callback, error for recipient
236         setSendingStatusFailed(getRecipient());
237         setMessageStatus(MESSAGE_STATUS_FAILED);
238     } else {
239         setSendingStatusOk(getRecipient());
240         setMessageStatus(MESSAGE_STATUS_SENT);
241     }
242         
243     msg_release_message(&currentQueue().front());
244     currentQueue().pop();
245
246     //schedule another sub message send
247     PostEvent(0);
248 }
249
250 void Sms::OnEventReceived(const int&)
251 {
252     LogDebug("enter");
253     // send another one
254     sendSubMessage();
255 }
256
257 Api::Messaging::FolderType Sms::toFolder(const std::string &folder)
258 {
259     if (folder == "INBOX") {
260         return INBOX;
261     } else if (folder == "OUTBOX") {
262         return OUTBOX;
263     } else if (folder == "SENTBOX") {
264         return SENTBOX;
265     } else if (folder == "DRAFTBOX") {
266         return DRAFTBOX;
267     }
268     ThrowMsg(WrtDeviceApis::Commons::PlatformException, "Invalid folder");
269 }
270
271 void Sms::sendSubMessage()
272 {
273     LogDebug("enter");
274
275     DPL::Mutex::ScopedLock lock(&m_mutex);
276
277     // if current request queue gets empty, try next one
278     while (!m_sendRequests.empty()) {
279         // send as long as the sending fails until there are no more messages
280         for (MSG_ERROR_T err = MSG_ERR_UNKNOWN;
281              err != MSG_SUCCESS && !currentQueue().empty();
282              ) {
283             err = CallbackMgrSingleton::Instance().registerAndSend(
284                     msg_sms_send_message,
285                     currentQueue().front(),
286                     this);
287             LogInfo("Sending Message (submit request) result:" << err);
288             if (err != MSG_SUCCESS) {
289                 LogError(
290                     "Sending Message (submit request) failed!!! err=" << err);
291                 msg_release_message(&currentQueue().front());
292                 currentQueue().pop();
293                 m_sendRequests.front().failed = true;
294                 //send callback, error for recipient
295                 setSendingStatusFailed(getRecipient());
296             }
297         }
298
299         // empty queue -> notify about results
300         if (currentQueue().empty()) {
301                 /*
302             if (m_sendRequests.front().failed) {
303                 setSendingStatusFailed();
304             } else {
305                 setSendingStatusOk();
306             }
307             */
308             // this request is finished, try next one
309             m_sendRequests.pop();
310             continue;
311         }
312         break;
313     }
314 }
315
316 void Sms::setSendingStatusOk(const string &recipient)
317 {
318     LogDebug("enter, success number is : " << recipient);
319     //success callback should be executed here
320     ReqReceiverMessage *requestReceiver = getRequestReceiver();
321     if (requestReceiver) {
322         EventSendMessagePtr event = getSendMessageEvent();
323         event->setExceptionCode(WrtDeviceApis::Commons::ExceptionCodes::None);
324         event->m_successRecipients.push_back(recipient);
325           //LogDebug("total address size : " << msg_get_address_count(m_messageData) << " Status size : " << event->m_successRecipients.size() + event->m_failRecipients.size());
326         if ( event->m_successRecipients.size() + event->m_failRecipients.size() == msg_get_address_count(m_messageData))
327         {
328                 requestReceiver->WrtDeviceApis::Commons::EventRequestReceiver< EventSendMessage >::ManualAnswer(event);
329         }
330     }
331 }
332
333 void Sms::setSendingStatusFailed(const string &recipient)
334 {
335     LogDebug("enter, fail number is : " << recipient);
336     //error callback should be executed here
337     EventOnSendingFailedEmitterPtr emitter = getEmitter();
338     if (emitter) {
339         EventOnSendingFailedPtr event(new EventOnSendingFailed);
340         event->setError(EventOnSendingFailed::UNKNOWN); // TODO error codes
341         emitter->emit(event);
342     } else {
343         ReqReceiverMessage *requestReceiver = getRequestReceiver();
344         if (requestReceiver) {
345             LogError("calling JS error callback");
346             EventSendMessagePtr event = getSendMessageEvent();
347             event->setExceptionCode(WrtDeviceApis::Commons::ExceptionCodes::UnknownException);
348             event->m_failRecipients.push_back(recipient);
349                  //LogDebug("total address size : " << msg_get_address_count(m_messageData) << " Status size : " << event->m_successRecipients.size() + event->m_failRecipients.size());
350                  if ( event->m_successRecipients.size() + event->m_failRecipients.size() == msg_get_address_count(m_messageData))
351                  {
352                         requestReceiver->WrtDeviceApis::Commons::EventRequestReceiver< EventSendMessage >::ManualAnswer(event);
353                  }
354         }
355     }
356 }
357
358 void Sms::remove()
359 {
360     Try
361     {
362         // delete message from platform
363         if (msg_delete_message(MsgGetCommonHandle(),
364                                convertId(getId())) != MSG_SUCCESS) {
365             LogError("delete message failed");
366             Throw(WrtDeviceApis::Commons::PlatformException);
367         } else {
368             LogDebug("message deleted succesfully");
369         }
370     }
371
372     Catch(WrtDeviceApis::Commons::PlatformException) {
373         LogError("remove message error");
374         throw;
375     }
376 }
377
378 void Sms::createNewMessage()
379 {
380     if (m_messageData) {
381         // release platform message if it was created previously
382         msg_release_message(&m_messageData);
383         m_messageData = NULL;
384     }
385     // create new platform message
386     m_messageData = msg_new_message();
387     Try
388     {
389         MSG_SENDINGOPT_S option = { false, false, false };
390         option.option.smsSendOpt.bReplyPath = true;
391
392         //default message initialization
393         msg_set_message_id(m_messageData, 0); // It should be set 0
394         msg_set_folder_id(m_messageData, MSG_DRAFT_ID);
395         msg_set_message_type(m_messageData, MSG_TYPE_SMS);
396         msg_set_storage_id(m_messageData, MSG_STORAGE_PHONE);
397         msg_set_network_status(m_messageData, MSG_NETWORK_NOT_SEND);
398
399         // update sms data
400         updateBody();
401         updateTo();
402
403         setMessageStatus(Api::Messaging::MESSAGE_STATUS_DRAFT);
404
405         tm dateT = getDateTime();
406         msg_set_time(m_messageData, mktime(&dateT));
407
408           setMessageStatus(Api::Messaging::MESSAGE_STATUS_CREATED);
409         LogDebug("Message created successfully, msgId=" << getId());
410     }
411
412     Catch(WrtDeviceApis::Commons::PlatformException) {
413         LogError("Problem with message creation, cleaning");
414     }
415 }
416
417 void Sms::readExistingMessage()
418 {
419     if (getIdRef().empty() || (EMPTY_ID == getIdRef())) {
420         ThrowMsg(WrtDeviceApis::Commons::PlatformException, "Empty id.");
421     }
422
423     Try {
424         MSG_SENDINGOPT_S option = { false, false, false };
425         option.option.smsSendOpt.bReplyPath = true;
426
427         // release old data
428         msg_release_message(&m_messageData);
429         m_messageData = NULL;
430         // create new platform structure
431         m_messageData = msg_new_message();
432
433         MSG_MESSAGE_ID_T l_id = convertId(getIdRef());
434         LogDebug("reading message id=" << l_id);
435         // trying to get message from platform
436         if (MSG_SUCCESS ==
437             msg_get_message(MsgGetCommonHandle(), l_id, m_messageData,
438                             &option)) {
439             LogDebug("message found with msgId=" << getIdRef());
440
441             // read all mms dat to abstraction layer
442             readRecipientList(m_messageData);
443             readBody(m_messageData);
444             readFolder(m_messageData);
445             readDateTime(m_messageData);
446             readReadStatus(m_messageData);
447             readSize(m_messageData);
448         } else {
449             LogError("message not found with msgId=" << getIdRef());
450             setId("");
451         }
452     }
453     Catch(WrtDeviceApis::Commons::PlatformException) {
454         // nothing to do
455     }
456 }
457
458 void Sms::updateBody()
459 {
460     // check if abstraction body value has been changed
461     if (isBodyValid()) {
462         return;
463     }
464     LogInfo("updating platfoprm body");
465
466     // change body value in platform structure
467     msg_sms_set_message_body(m_messageData, getBodyRef().c_str(),
468                              getBodyRef().length());
469
470     // set flag as true - platform synchronized with abstraction
471     setBodyValidity(true);
472 }
473
474 void Sms::updateTo()
475 {
476     // check if abstraction recipient (to) value has been changed
477     if (getToRecipients().isValid()) {
478         return;
479     }
480     LogInfo("updating platform to");
481
482     // change recipient value in platform structure
483     msg_reset_address(m_messageData);
484     vector<string> to = getToRecipients().getRecipients();
485     LogInfo("updating to");
486     for (size_t i = 0; i < to.size(); i++) {
487         if (i >= MAX_TO_ADDRESS_CNT) {
488             LogError("max number of recipient exceeded");
489             break;
490         }
491         LogDebug("adding to[" << i << "]=" << to[i]);
492         string tmpAddr = to[i];
493         // function for validating phone number
494         if (validatePhoneNumber(tmpAddr)) {
495             // add phone number to platform message struncture
496             if (msg_add_address(m_messageData, tmpAddr.c_str(),
497                                 MSG_RECIPIENTS_TYPE_TO) != MSG_SUCCESS) {
498                 LogError("problem witgh add address");
499                 Throw(WrtDeviceApis::Commons::PlatformException);
500             }
501         } else {
502             //nothing to do if wrong format
503             LogError("wrong phone number format");
504                  
505         }
506     }
507
508     // set flag as true - platform synchronized with abstraction
509     setToValidity(true);
510 }
511
512 void Sms::updateFrom()
513 {
514     // check if abstraction from field value has been changed
515     if (getFromValidity()) {
516         // do not update if not changed
517         return;
518     }
519     LogInfo("updating platform from");
520
521     //TODO
522
523     // set flag as true - platform synchronized with abstraction
524     setFromValidity(true);
525 }
526
527 void Sms::updateSourceAddress()
528 {
529     // check if abstraction source address value has been changed
530     if (getSourceAddressValidity()) {
531         // do not update if not changed
532         return;
533     }
534     LogInfo("updating platform source address");
535
536     //TODO
537
538     // set flag as true - platform synchronized with abstraction
539     setSourceAddressValidity(true);
540 }
541
542 void Sms::updateReadStatus()
543 {
544     LogInfo("updating platform read status: " << isRead());
545     if (isReadStatusValid()) {
546         // do not update if not changed
547         return;
548     }
549
550     if (MSG_SUCCESS != msg_set_read_status(m_messageData, isRead())) {
551         LogError("problem with setting subject");
552         ThrowMsg(WrtDeviceApis::Commons::PlatformException, "Problem with setting subject");
553     }
554     setReadStatusValidity(true);
555 }
556
557 void Sms::updateIsRead()
558 {
559     LogDebug("updating m_id=" << getIdRef());
560
561     if (!m_messageData) {
562         //error if platform message not exists
563         LogError("message can not be updated");
564         Throw(WrtDeviceApis::Commons::PlatformException);
565     }
566         
567     // check if abstraction from m_isReadChange value has been changed
568     if (isReadChangeStatusValid()) {
569         // do not update if not changed
570         return;
571     }
572         
573     Try
574     {
575         if (this->getIdRef().empty()) {
576             LogError("existing msgId is zero, remove msg not done");
577             ThrowMsg(WrtDeviceApis::Commons::PlatformException,
578                     "existing msgId is zero, remove msg not done");
579         }
580
581         if (MSG_SUCCESS !=
582             msg_update_read_status(MsgGetCommonHandle(), convertId(getIdRef()), isReadChangeStatus()))
583         {
584             LogError("Failed to update isRead");
585             ThrowMsg(WrtDeviceApis::Commons::PlatformException, "Failed to update isRead");
586         }
587     }
588     Catch(WrtDeviceApis::Commons::PlatformException) {
589         LogError("platform error occurs");
590     }
591     setisReadChangeStatusValidity(true);
592         
593 }
594
595
596 void Sms::addMessageToDraft()
597 {
598     LogDebug("convert m_id= " << convertId(getIdRef()));        
599
600     //prepare for add sms to draft folder
601     if (!m_messageData) {
602         //error if platform message not exists
603         LogError("message can not be updated");
604         Throw(WrtDeviceApis::Commons::PlatformException);
605     }
606
607     //update all sms data
608     if (getCurrentFolder() == Api::Messaging::DRAFTBOX) {
609         updateBody();
610         updateFrom();
611         updateTo();
612         updateSourceAddress();
613     }
614
615     Try
616     {
617         MSG_SENDINGOPT_S option = { false, false, false };
618         option.option.smsSendOpt.bReplyPath = true;
619         // trying to get message from platform
620
621         const MSG_FOLDER_ID_T platfromFolderId =
622         Messaging::convertFolderToPlatform(DRAFTBOX);
623
624         msg_set_message_id(m_messageData, 0);           
625         msg_set_folder_id(m_messageData, platfromFolderId);
626         msg_set_network_status(m_messageData, MSG_NETWORK_NOT_SEND);
627
628         // trying to add message
629         int ret = msg_add_message(MsgGetCommonHandle(), m_messageData, &option);
630         if (ret < MSG_SUCCESS) {
631             LogError("msg_add_message failed, error code=" << ret);
632             Throw(WrtDeviceApis::Commons::PlatformException);
633         }
634
635         //releasing platform message structure
636         //msg_release_message(&m_messageData);
637                 
638         setFolderType(Api::Messaging::DRAFTBOX);
639         setMessageStatus(Api::Messaging::MESSAGE_STATUS_DRAFT);
640     }
641     Catch(WrtDeviceApis::Commons::PlatformException) {
642         LogError("remove message error");
643         if (m_messageData) {
644             //releasing platform message structure
645             msg_release_message(&m_messageData);
646         }
647         throw;
648     }
649
650 }
651
652
653 void Sms::readRecipientList(msg_message_t& messageData)
654 {
655     LogDebug("Adding recipients to message, count=" <<
656              msg_get_address_count(messageData));
657     // iterate for all address and store in abstraction
658     for (int i = 0; i < msg_get_address_count(messageData); i++) {
659         string tmp = msg_get_ith_address(messageData, i);
660         int type = msg_get_direction_info(messageData);
661
662         if (MSG_DIRECTION_TYPE_MT == type) {
663             if (validatePhoneNumber(tmp)) {
664                 setSourceAddress(tmp);
665                 setFrom(tmp);
666             }
667         } else if (MSG_DIRECTION_TYPE_MO == type) {
668             appendToRecipients(tmp);
669         } else {
670             ThrowMsg(WrtDeviceApis::Commons::PlatformException, "Wrong type of recipient.");
671         }
672     }
673 }
674
675 void Sms::readBody(msg_message_t& messageData)
676 {
677     //set abstraction body value
678     setBody(msg_sms_get_message_body(messageData));
679 }
680
681 void Sms::readFolder(msg_message_t& messageData)
682 {
683     switch (msg_get_folder_id(messageData)) {
684     case MSG_INBOX_ID:
685         setFolderType(Api::Messaging::INBOX);
686         break;
687     case MSG_OUTBOX_ID:
688         setFolderType(Api::Messaging::OUTBOX);
689         break;
690     case MSG_SENTBOX_ID:
691         setFolderType(Api::Messaging::SENTBOX);
692         break;
693     case MSG_DRAFT_ID:
694         setFolderType(Api::Messaging::DRAFTBOX);
695         break;
696     case MSG_SPAMBOX_ID:
697         setFolderType(Api::Messaging::SPAMBOX);
698         break;
699     default:
700         LogError("Wrong folder id");
701         ThrowMsg(WrtDeviceApis::Commons::PlatformException, "Unsupported folder id.");
702     }
703 }
704
705 void Sms::readDateTime(msg_message_t& messageData)
706 {
707     tm* time = localtime(msg_get_time(messageData));
708     if (!time) {
709         LogError("localtime failed");
710         Throw(WrtDeviceApis::Commons::PlatformException);
711     }
712     setDateTime(*time);
713 }
714
715 void Sms::readReadStatus(msg_message_t& messageData)
716 {
717     // get read status
718     setReadStatus(msg_is_read(messageData));
719 }
720
721 void Sms::readSize(msg_message_t& messageData)
722 {
723     setSize(msg_get_message_body_size(messageData));
724 }
725
726 msg_message_t Sms::createNewCopyOfPLatformMsg(const msg_message_t src) const
727 {
728     msg_message_t msg = msg_new_message();
729
730     msg_set_message_id(msg, 0);
731     msg_set_storage_id(msg, msg_get_storage_id(src));
732     msg_set_message_type(msg, msg_get_message_type(src));
733     msg_set_folder_id(msg, msg_get_folder_id(src));
734     for (int i = 0; i < msg_get_address_count(src); i++) {
735         msg_add_address(msg, msg_get_ith_address(src,
736                                                  i),
737                         msg_get_ith_recipient_type(src, i));
738     }
739     msg_sms_set_message_body(msg, msg_sms_get_message_body(
740                                  src), msg_get_message_body_size(src));
741     msg_set_subject(msg, msg_get_subject(src));
742     msg_set_time(msg, *msg_get_time(src));
743     msg_set_network_status(msg, msg_get_network_status(src));
744     msg_set_encode_type(msg, msg_get_encode_type(src));
745     msg_set_read_status(msg, msg_is_read(src));
746     msg_set_protect_status(msg, msg_is_protected(src));
747     msg_set_priority_info(msg, msg_get_priority_info(src));
748     msg_set_direction_info(msg, msg_get_direction_info(src));
749     msg_set_port(msg, msg_get_dest_port(src), msg_get_src_port(src));
750     msg_set_scheduled_time(msg, *msg_get_scheduled_time(src));
751
752     return msg;
753 }
754 }
755 }
756 }