Imported Upstream version 0.8~alpha1
[platform/upstream/syncevolution.git] / src / client-api / src / c++ / common / spds / SyncMLProcessor.cpp
1 /*
2  * Funambol is a mobile platform developed by Funambol, Inc. 
3  * Copyright (C) 2003 - 2007 Funambol, Inc.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Affero General Public License version 3 as published by
7  * the Free Software Foundation with the addition of the following permission 
8  * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
9  * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE 
10  * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
11  * 
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  * 
17  * You should have received a copy of the GNU Affero General Public License 
18  * along with this program; if not, see http://www.gnu.org/licenses or write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20  * MA 02110-1301 USA.
21  * 
22  * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite 
23  * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
24  * 
25  * The interactive user interfaces in modified source and object code versions
26  * of this program must display Appropriate Legal Notices, as required under
27  * Section 5 of the GNU Affero General Public License version 3.
28  * 
29  * In accordance with Section 7(b) of the GNU Affero General Public License
30  * version 3, these Appropriate Legal Notices must retain the display of the
31  * "Powered by Funambol" logo. If the display of the logo is not reasonably 
32  * feasible for technical reasons, the Appropriate Legal Notices must display
33  * the words "Powered by Funambol".
34  */
35
36 #include <stdlib.h>
37
38 #include "base/Log.h"  // TBR
39 #include "base/util/ArrayList.h"
40 #include "base/util/utils.h"
41 #include "spds/constants.h"
42 #include "spds/SyncMLProcessor.h"
43 #include "spds/spdsutils.h"
44
45 #include "event/FireEvent.h"
46 #include "base/globalsdef.h"
47
48 USE_NAMESPACE
49
50 /*
51  * This class is responsible for the processing of the incoming messages.
52  */
53 /*
54  * Constructor
55  */
56 SyncMLProcessor::SyncMLProcessor() : XMLProcessor() {
57 }
58
59 SyncML* SyncMLProcessor::processMsg(char* msg) {
60     SyncML* syncml      = Parser::getSyncML(msg);
61     return syncml;
62 }
63
64
65 int SyncMLProcessor::processSyncHdrStatus(SyncML* syncml) {
66     int ret = getStatusCode(syncml->getSyncBody(), NULL, SYNC_HDR);
67
68     // Fire Sync Status Event: syncHdr status from server
69     fireSyncStatusEvent(SYNC_HDR, ret, NULL, NULL, NULL , SERVER_STATUS);
70
71     return ret;
72 }
73
74 int SyncMLProcessor::processAlertStatus(SyncSource& source, SyncML* syncml, ArrayList* alerts) {
75
76     int ret = -1;
77     const char* name = NULL;
78     Status* s     = NULL;
79     Data* data    = NULL;
80     SourceRef* sourceRef    = NULL;
81
82     if (alerts->size()) {
83         ArrayList* list = syncml->getSyncBody()->getCommands();
84
85         for (int i = 0; i < list->size(); i++) {
86             // is returned the pointer to the element not a new element
87             name = ((AbstractCommand*)(list->get(i)))->getName();
88             if (name && strcmp(name, STATUS) == 0) {
89                 s = (Status*)list->get(i);
90                 if (strcmp(s->getCmd(), ALERT) == 0) {
91                     sourceRef = (SourceRef*)(s->getSourceRef()->get(0));
92
93                     if (sourceRef) {
94                         if (strcmp(_wcc(source.getName()),
95                                     sourceRef->getValue()) == 0) {
96                             ret = getAlertStatusCode(s, _wcc(source.getName()));
97                             break;
98                         }
99                     } else {
100                         // Server did not include <SourceRef>, which
101                         // is a violation of the standard for commands
102                         // which were sent with <SourceRef>. Happens
103                         // with Synthesis server if authentication
104                         // failed, in which case we can simply ignore
105                         // it.
106                     }
107                 }
108             }
109         }
110     }
111
112     // Fire a syncStatus event: Alert status from server
113     fireSyncStatusEvent(ALERT, ret, source.getConfig().getName(), source.getConfig().getURI(), NULL, SERVER_STATUS);
114
115     return ret;
116
117 }
118
119
120 /*
121  * Processes the initialization response. Returns 0 in case of success, an
122  * error code in case of error.
123  *
124  * @param msg the response from the server
125  */
126 int SyncMLProcessor::processServerAlert(SyncSource& source, SyncML* syncml) {
127
128     int ret             = -1;
129     int iterator        = 0;
130     AbstractCommand* a  = NULL;
131     Item* item          = NULL;
132     bool found          = false;
133
134     ret = 0;
135     do {
136         a = getCommand(syncml->getSyncBody(), ALERT, iterator);
137         if (a == NULL) {
138             // This happens with the Synthesis server's reply:
139             // instead of sending SyncBody/Alert we get SyncBody/Put
140             // with device infos and a SyncBody/Get requesting our own
141             // device infos. Ignoring the request is not correct, but
142             // allows synchronization to proceed and complete eventually
143             // without any further errors. For that to work we must not
144             // set lastErrorCode here, as it will be checked at the end of
145             // the sync.
146             //
147             // lastErrorCode = ERR_REPRESENTATION;
148             // sprintf(lastErrorMsg, "SyncBody/Alert not found!");
149             goto finally;
150         }
151         Alert* alert = (Alert*)a;
152         Item* item = NULL;
153         ArrayList* itemList = alert->getItems();
154
155         for (int i = 0; i < itemList->size(); i++) {
156             item = (Item*)getArrayElement(itemList, i);
157             const char *locURI = ((Target*)item->getTarget())->getLocURI();
158             if (strcmp( locURI, _wcc(source.getName()) ) == 0) {
159                 if (alert->getData() == NULL) {
160                     //lastErrorCode = ERR_REPRESENTATION;
161                     //sprintf(lastErrorMsg, "SyncBody/Alert/Data not found!");
162                     setError(ERR_REPRESENTATION, "SyncBody/Alert/Data not found!");
163                     goto finally;
164                 }
165
166                 source.setSyncMode((SyncMode)alert->getData());
167                 ret = 0;
168                 found = true;
169                 break;
170             }
171         }
172         iterator++;
173         if (found)
174             break;
175
176     } while(a);
177
178 finally:
179
180     return ret;
181 }
182
183
184 char** SyncMLProcessor::getSortedSourcesFromServer(SyncML* syncml, int sourcesNumber) {
185
186     char** sourceList = new char*[sourcesNumber+1];
187     int iterator        = 0;
188     AbstractCommand* a  = NULL;
189     Item* item          = NULL;
190
191     do {
192         a = getCommand(syncml->getSyncBody(), ALERT, iterator);
193         if (a == NULL) {
194             goto finally;
195         }
196         Alert* alert = (Alert*)a;
197         Item* item = NULL;
198         ArrayList* itemList = alert->getItems();
199
200         for (int i = 0; i < itemList->size(); i++) {
201             item = (Item*)getArrayElement(itemList, i);
202             const char *locURI = ((Target*)item->getTarget())->getLocURI();
203             sourceList[iterator] = stringdup(locURI);
204         }
205         iterator++;
206
207     } while(a);
208
209 finally:
210     sourceList[iterator] = NULL;
211     return sourceList;
212 }
213
214
215
216
217 int SyncMLProcessor::processItemStatus(SyncSource& source, SyncBody* syncBody) {
218
219     ArrayList* items = NULL;
220     Item* item       = NULL;
221     SourceRef* sourceRef = NULL;
222     Status* s = NULL;
223     const char* name = NULL;
224     Data* data = NULL;
225     int ret = 0;
226
227     ArrayList* list = getCommands(syncBody, STATUS);
228
229     for (int i = 0; i < list->size(); i++) {
230         s = (Status*)list->get(i);
231         name = s->getCmd();
232         data = s->getData();
233         if (strcmp(name, SYNC) == 0){
234             char *srcname = toMultibyte(source.getName());
235             int alertStatus = getAlertStatusCode(s, srcname);
236             delete [] srcname;
237
238             /*
239             * Try to find if the server send a message together the error code if any
240             * The items in the status message should be always one...
241             */
242             char *statusMessage = NULL;
243             items = s->getItems();
244                         for (int k = 0; k < items->size(); k++) {
245                 item = (Item*)items->get(k);
246                 if (item) {
247                     ComplexData* cd = item->getData();
248                     if (cd) {
249                         statusMessage = stringdup(cd->getData());
250                     }
251                 }
252             }
253             // Fire Sync Status Event: sync status from server
254             fireSyncStatusEvent(SYNC, s->getStatusCode(), source.getConfig().getName(), source.getConfig().getURI(), NULL, SERVER_STATUS);
255
256             if(alertStatus < 0 || alertStatus >=300){
257                 if (statusMessage) {
258                     //strcpy(lastErrorMsg, statusMessage);
259                     setError( alertStatus, statusMessage);
260                 } else {
261                     //strcpy(lastErrorMsg, "Error in sync status sent by server.");
262                     setError( alertStatus, "Error in sync status sent by server.");
263                 }
264                 if ((ret = alertStatus) < 0)
265                     LOG.error("processItemStatus: status not found in SYNC");
266                 else
267                     LOG.error("processItemStatus: server sent status %d in SYNC", alertStatus);
268                 break;
269             }
270             if (statusMessage) {
271                 delete [] statusMessage;
272             }
273         }
274
275         else if (strcmp(name, ADD) == 0 ||
276             strcmp(name, REPLACE) == 0 ||
277             strcmp(name, DEL) == 0) {
278
279             int k;
280
281             items = s->getItems();
282             long val = strtol(data->getData() , NULL, 10);
283             for (k = 0; k < items->size(); k++) {
284                 item = (Item*)items->get(k);
285                 if (item) {
286                     Source* itemSource = item->getSource();
287                     if (itemSource) {
288                         WCHAR *uri = toWideChar(itemSource->getLocURI());
289
290                         ComplexData* cd = item->getData();
291                         WCHAR *statusMessage = NULL;
292                         if (cd) {
293                             statusMessage = toWideChar(cd->getData());
294                         }
295
296                         // Fire Sync Status Event: item status from server
297                         fireSyncStatusEvent(s->getCmd(), s->getStatusCode(), source.getConfig().getName(), source.getConfig().getURI(), uri, SERVER_STATUS);
298                         // Update SyncReport
299                         source.getReport()->addItem(SERVER, s->getCmd(), uri, s->getStatusCode(), statusMessage);
300
301                         source.setItemStatus(uri, val, name);
302                         delete [] uri;
303                         if (statusMessage)
304                             delete [] statusMessage;
305                     } else {
306                         // the item might consist of additional information, as in:
307                         // <SourceRef>pas-id-44B544A600000092</SourceRef>
308                         // <Data>200</Data>
309                         // <Item><Data>Conflict resolved by server</Data></Item>
310                     }
311                 }
312             }
313             items = s->getSourceRef();
314             for (k = 0; k < items->size(); k++) {
315                 sourceRef = (SourceRef*)items->get(k);
316                 if (sourceRef) {
317                     WCHAR *srcref = toWideChar(sourceRef->getValue());
318                                 // Fire Sync Status Event: item status from server
319                     fireSyncStatusEvent(s->getCmd(), s->getStatusCode(), source.getConfig().getName(), source.getConfig().getURI(), srcref, SERVER_STATUS);
320                     // Update SyncReport
321                     source.getReport()->addItem(SERVER, s->getCmd(), srcref, s->getStatusCode(), NULL);
322
323                     source.setItemStatus(srcref, val, name);
324                     delete [] srcref;
325                 }
326             }
327         }
328     }
329
330     //deleteArrayList(&list);
331     if (list){
332         delete list;
333         list = NULL;
334     }
335     return ret;
336 }
337
338 /*
339  * Processes the initialization response. Returns 0 in case of success, an
340  * error code in case of error.
341  *
342  * @param msg the response from the server
343  */
344
345 Sync* SyncMLProcessor::processSyncResponse(SyncSource& source, SyncML* syncml) {
346
347     int iterator = 0, ret = 0;
348
349     AbstractCommand* a  = NULL;
350     Sync* sync          = NULL;
351
352     ret = getStatusCode(syncml->getSyncBody(), &source, SYNC_HDR);
353     if ((ret < 200) || (ret > 299)) {
354         goto finally;
355     }
356
357     while((a = getCommand(syncml->getSyncBody(), SYNC, iterator)) != NULL){
358         sync = (Sync*)a;
359         const char *locuri = ((Target*)(sync->getTarget()))->getLocURI();
360         if (strcmp(locuri, _wcc(source.getName())) == 0) {
361
362             //
363             // To handle the NumberOfChanges. The default is -1 that means the server doesn't send
364             // any tag <NumberOfChanges>. Whit value >= 0 the value is correct
365             //
366
367             long noc = sync->getNumberOfChanges();
368             fireSyncSourceEvent(source.getConfig().getURI(), source.getConfig().getName(), source.getSyncMode(), noc, SYNC_SOURCE_TOTAL_SERVER_ITEMS);
369
370             break;
371         }
372         sync = NULL;
373         iterator++;
374     }
375
376 finally:
377
378     return sync;
379
380 }
381
382 /*
383  * Processes the map message response. Returns 0 in case of success, an
384  * error code in case of error.
385  * It feeds the given source with the server side modifications
386  *
387  * @param source the source
388  * @param msg the response from the server
389  */
390 int SyncMLProcessor::processMapResponse(SyncSource& source, SyncBody* syncBody) {
391     int ret = -1;
392
393     //
394     // for now it is always ok
395     //
396     //
397     // First of all check the status for the SyncHead
398     //
399     // TBD
400     ret = getStatusCode(syncBody, &source, SYNC_HDR);
401
402     // Fire Sync Status Event: map status from server (TBD)
403     //fireSyncStatusEvent(MAP, ret, source.getConfig().getURI(), NULL, SERVER_STATUS);
404
405     if ((ret < 200) || (ret >299)) {
406         goto finally;
407     }
408
409     ret = 0;
410
411 finally:
412
413     return ret;
414 }
415
416 /*
417  * Returns the SyncHeader/RespURI element of the given message. If the element is not
418  * found it returns NULL. The returned respURI is allocated with the new operator
419  * and must be discarded with delete by the caller.
420  *
421  * @param SyncHdr - the SyncHdr object - NOT NULL
422  */
423 const char* SyncMLProcessor::getRespURI(SyncHdr* syncHdr) {
424
425     char* respURI = NULL;
426
427     if (syncHdr == NULL) {
428         goto finally;
429     }
430     respURI = stringdup(syncHdr->getRespURI());
431
432 finally:
433
434     return respURI;
435 }
436
437
438 Chal* SyncMLProcessor::getChal(SyncBody* syncBody) {
439
440     ArrayList* list = syncBody->getCommands();
441     const char* name = NULL;
442     Status* s     = NULL;
443     Chal* chal    = NULL;
444
445     for (int i = 0; i < list->size(); i++) {
446         name = ((AbstractCommand*)(list->get(i)))->getName();    // is returned the pointer to the element not a new element
447         if (name && strcmp(name, STATUS) == 0) {
448             s = (Status*)list->get(i);
449             if (strcmp(s->getCmd(), SYNC_HDR) == 0) {
450                 if (strcmp(s->getCmdRef(), "0") != 0) {
451
452                     //sprintf(lastErrorMsg, "Status/CmdRef either not found or not referring to SyncHeader!");
453                     //lastErrorCode = ERR_REPRESENTATION;
454                     setError(ERR_REPRESENTATION, "Status/CmdRef either not found or not referring to SyncHeader!"); 
455                     goto finally;
456                 }
457
458                 chal = s->getChal();
459                 if (chal == NULL) {
460                     //
461                     // no chal found
462                     //
463                     goto finally;
464                 }
465                 break;
466             }
467         }
468     }
469
470 finally:
471
472     return chal;
473 }
474
475 /*
476 * Return an array list of commands of the given command name. It return an ArrayList that have to be
477 * discarded by the caller
478 */
479 ArrayList* SyncMLProcessor::getCommands(SyncBody* syncBody, const char*commandName) {
480
481     ArrayList* ret = new ArrayList();
482     AbstractCommand* a = NULL;
483
484     for (int i = 0; i < syncBody->getCommands()->size(); i++) {
485         a = getCommand(syncBody, commandName, i);
486         if (a)
487             ret->add(*a);
488     }
489     return ret;
490 }
491
492 // ------------------------------------------------------------- Private methods
493
494 /*
495 * To get a generic array element. It returns the <index> arrayElement it founds.
496 * 0-based.
497 */
498 ArrayElement* SyncMLProcessor::getArrayElement(ArrayList* list, int index) {
499
500     if (list == NULL)
501         return NULL;
502
503     ArrayElement* a     = NULL;
504     int count           = 0;
505     for (int i = 0; i < list->size(); i++) {
506             if (count == index) {
507                 a = list->get(i);
508                 break;
509             }
510             ++ count;
511     }
512     return a;
513 }
514
515 /*
516 * Return the index number of occurrence of this command. If doesn't exists return NULL;
517 * The first command has number 0.
518 */
519 AbstractCommand* SyncMLProcessor::getCommand(SyncBody* syncBody, const char*commandName, int index) {
520
521     int iterator = 0, found = 0;
522     ArrayList* list     = syncBody->getCommands();
523     int l = list->size();
524     AbstractCommand* a  = NULL;
525     const char* name = NULL;
526     do {
527         a = (AbstractCommand*)getArrayElement(list, iterator);
528         if (a) {
529             name = a->getName();    // is returned the pointer to the element not a new element
530             if (name && strcmp(name, commandName) == 0) {
531                 if (found == index)
532                     break;
533                 else
534                     found++;
535             }
536         }
537         ++iterator;
538     } while(a);
539
540     return a;
541 }
542
543
544 int SyncMLProcessor::getStatusCode(SyncBody* syncBody, SyncSource* source, const char*commandName) {
545     int ret = -1;
546
547     ArrayList* list = syncBody->getCommands();
548     const char* name = NULL;
549     Status* s     = NULL;
550     Data* data    = NULL;
551
552     for (int i = 0; i < list->size(); i++) {
553         name = ((AbstractCommand*)(list->get(i)))->getName();    // is returned the pointer to the element not a new element
554         if (name && strcmp(name, STATUS) == 0) {
555             s = (Status*)list->get(i);
556             if (strcmp(s->getCmd(), commandName) == 0) {
557                 if (strcmp(commandName, SYNC_HDR) == 0) {
558                     ret = getSyncHeaderStatusCode(s);
559                 } else if (strcmp(commandName, ALERT) == 0) {
560                     ret = getAlertStatusCode(s, (char*)source->getName());
561                 }
562                 break;
563             }
564         }
565     }
566
567     if (ret == -1) {
568         //sprintf(lastErrorMsg, "Error reading status code of command '%s'", commandName);
569         //lastErrorCode = ERR_REPRESENTATION;
570         setErrorF(ERR_REPRESENTATION, "Error reading status code of command '%s'", commandName);
571     }
572     return ret;
573
574 }
575
576 /*
577  * Returns the status code for the SyncHeader command included
578  * in the message sent by the client.
579  *
580  * @param syncBody - the SyncBody content
581  */
582 int SyncMLProcessor::getSyncHeaderStatusCode(Status* s) {
583
584     int ret = -1;
585     Data* data    = NULL;
586
587     if (s == NULL)
588         goto finally;
589
590     if (strcmp(s->getCmdRef(), "0") != 0) {
591
592         //sprintf(lastErrorMsg, "Status/CmdRef either not found or not referring to SyncHeader!");
593         //lastErrorCode = ERR_REPRESENTATION;
594         setError(ERR_REPRESENTATION, "Status/CmdRef either not found or not referring to SyncHeader!");
595         goto finally;
596     }
597
598     data = s->getData();
599     if (data->getData() == NULL) {
600          //
601         // It should not happen
602         //
603         //sprintf(lastErrorMsg, "Status/Data not found!");
604         //lastErrorCode = ERR_REPRESENTATION;
605         setError(ERR_REPRESENTATION, "Status/Data not found!");
606         goto finally;
607     }
608     ret = strtol(data->getData() , NULL, 10);
609
610
611 finally:
612
613     return ret;
614 }
615
616 /*
617  * Returns the status code for the Alert relative to the given source.
618  *
619  * @param syncBody - the SyncBody content
620  * @param sourceName - the name of the source
621  */
622
623 int SyncMLProcessor::getAlertStatusCode(Status* s, const char* sourceName) {
624     int ret = -1;
625     if (s == NULL) {
626         return ret;
627     }
628
629     Data* data = NULL;
630     ArrayList* sourceRefs = s->getSourceRef();
631
632     if (strcmp(((SourceRef*)(sourceRefs->get(0)))->getValue(), sourceName) == 0) {
633         data = s->getData();
634         if (data->getData() == NULL) {
635             //
636             // It should not happen
637             //
638             //sprintf(lastErrorMsg, "Status/Data not found!");
639             //lastErrorCode = ERR_REPRESENTATION;
640             setError(ERR_REPRESENTATION, "Status/Data not found!");
641             return ret;
642         }
643         ret = strtol(data->getData(), NULL, 10);
644     }
645
646     return ret;
647 }
648
649 /*
650  * Processes the initialization response. Returns 0 in case of success, an
651  * error code in case of error.
652  *
653  * @param msg the response from the server
654  */
655
656 Sync* SyncMLProcessor::getSyncResponse(SyncML* syncml, int index) {
657
658     AbstractCommand* a  = NULL;
659     Sync* sync          = NULL;
660
661     a = getCommand(syncml->getSyncBody(), SYNC, index);
662     sync = (Sync*)a;
663
664     return sync;
665
666 }
667