KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sync4j > server > session > ManagementSessionHandler


1 /**
2  * Copyright (C) 2003-2005 Funambol
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10                 serverAuthType = authRequiredFromClient;
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package sync4j.server.session;
21
22 import java.util.*;
23 import java.util.logging.Level JavaDoc;
24 import java.util.logging.Logger JavaDoc;
25
26 import sync4j.framework.config.Configuration;
27 import sync4j.framework.config.ConfigurationConstants;
28 import sync4j.framework.core.*;
29 import sync4j.framework.engine.dm.DeviceDMState;
30 import sync4j.framework.engine.dm.ManagementException;
31 import sync4j.framework.engine.dm.ManagementProcessor;
32 import sync4j.framework.engine.dm.Util;
33 import sync4j.framework.logging.Sync4jLogger;
34 import sync4j.framework.logging.Sync4jLoggerName;
35 import sync4j.framework.protocol.*;
36 import sync4j.framework.security.Officer;
37 import sync4j.framework.security.SecurityConstants;
38 import sync4j.framework.security.Sync4jPrincipal;
39 import sync4j.framework.server.Sync4jDevice;
40 import sync4j.framework.server.SyncTimestamp;
41 import sync4j.framework.server.dm.ProcessorSelector;
42 import sync4j.framework.server.error.InvalidCredentialsException;
43 import sync4j.framework.server.error.ServerException;
44 import sync4j.framework.server.error.ServerFailureException;
45 import sync4j.framework.server.session.ManagementState;
46 import sync4j.framework.server.session.SessionHandler;
47 import sync4j.framework.server.store.NotFoundException;
48 import sync4j.framework.server.store.PersistentStoreException;
49 import sync4j.framework.tools.Base64;
50 import sync4j.framework.tools.SimpleIdGenerator;
51 import sync4j.framework.tools.SizeCalculator;
52
53 import sync4j.server.engine.dm.ManagementEngine;
54
55 /**
56  * This class represents the handler for a SyncML DM session. It coordinates and
57  * handles the packages and messages as dictated by the protocol.
58  * <p>
59  * The entry point is <i>processMessage()</i>, which determines which message is
60  * expected and what processing has to be done (depending on the value of
61  * <i>currentState</i>). If an error accours, the session goes to the state
62  * <i>STATE_ERROR</i>; in this state no other messages but initialization can be
63  * performed.
64  * <p>
65  *
66  * LOG NAME: sync4j.handler
67  *
68  * @see sync4j.framework.engine.SessionHandler
69  *
70  * @author Stefano Fornari @ Funambol
71  * @author Stefano Nichele @ Funambol
72  *
73  * @version $Id: ManagementSessionHandler.java,v 1.3 2005/07/05 10:08:31 nichele Exp $
74  *
75  */

76 public class ManagementSessionHandler
77 implements SessionHandler, java.io.Serializable JavaDoc,
78            SecurityConstants, ConfigurationConstants {
79
80     // --------------------------------------------------------------- Constants
81

82     //
83
// NOTE: the following states are in addition to the ones defined in
84
// SessionHandler
85
//
86
public static final int STATE_INITIALIZATION_PROCESSING = 0x0010;
87     public static final int STATE_INITIALIZATION_PROCESSED = 0x0011;
88     public static final int STATE_MANAGEMENT_PROCESSING = 0x0012;
89     public static final int STATE_MANAGEMENT_PROCESSED = 0x0013;
90     public static final int STATE_MANAGEMENT_COMPLETION = 0x0014;
91     public static final int STATE_CLIENT_UNAUTHORIZED = 0x0016;
92
93
94     //
95
// The server has sent a complete package. It is awainting status from the
96
// client on the commands sent in the package. Because the status and results
97
// may be large, such as the result of Get commands, the client may send
98
// multiple message back to the server before completing its response
99
//
100
public static final int STATE_WAITING_MORE_MSG = 0x0015;
101
102     // ------------------------------------------------------- Private Constants
103
private static final String JavaDoc[] MANAGEMENT_COMMANDS
104          = {"Add", "Alert", "Copy", "Delete", "Exec" ,"Get", "Replace", "Atomic", "Sequence"};
105
106      /**
107       * Given a maxMsgSize, only the 85% is used, so in the pipeline manager,
108       * the server has the 15% available
109       */

110      private static final double TOLLERANCE_MAX_MSG_SIZE = 0.85d;
111
112
113     // ------------------------------------------------------------ Private data
114

115     private int currentState = STATE_START;
116
117     /**
118      * Gets the current state
119      *
120      * @return the current state
121      */

122     public int getCurrentState() {
123         return currentState;
124     }
125
126     private long creationTimestamp = -1;
127
128     /**
129      * Gets the creation timestamp of the session
130      *
131      * @return the creation timestamp
132      */

133     public long getCreationTimestamp() {
134         return creationTimestamp;
135     }
136
137     private transient Logger JavaDoc log =
138         Sync4jLogger.getLogger(Sync4jLoggerName.HANDLER);
139
140     private transient ManagementInitialization init = null;
141     private transient ManagementActions actions = null;
142
143     /**
144      * Keeps the state information of this management session
145      */

146     private ManagementState sessionState = null;
147
148     /**
149      * The management processor to be used for this management session
150      */

151     private ManagementProcessor processor = null;
152
153     /**
154      * The server authentication type
155      */

156     private String JavaDoc serverAuthType = null;
157
158     /**
159      * The supported server authentication type
160      */

161     private String JavaDoc supportedAuthType = null;
162
163     /**
164      * The client authentication type
165      */

166     private String JavaDoc clientAuthType = null;
167
168     /**
169      * Sync4j engine object
170      */

171     private ManagementEngine engine = null;
172
173     /**
174      * The message mime type
175      */

176     private String JavaDoc mimeType = null;
177
178     /**
179      * Set message mime type
180      */

181     public void setMimeType(String JavaDoc mimeType) {
182         this.mimeType = mimeType;
183     }
184
185     /**
186      * Indicates if the session is a new session
187      */

188     //
189
// @TODO is this still used
190
//
191
private boolean newSession = true;
192
193     /**
194      * List of command in order to create an answer with the maximum size
195      * available (case of MultiMessage)
196      */

197     private ArrayList addStatus = null;
198     private ArrayList addAbsCmd = null;
199
200     /**
201      * The max msg size
202      */

203     private long maxSizeAvailable = 0;
204
205
206     // -------------------------------------------------------------- Properties
207

208     /**
209      * Returns the management session id
210      *
211      * @return the management session id
212      */

213     public String JavaDoc getSessionId() {
214         return sessionState.getSessionId();
215     }
216
217     /**
218      * Returns the management device
219      *
220      * @return the management device
221      */

222     public Sync4jDevice getDevice() {
223         return sessionState.device;
224     }
225
226
227     /**
228      * The cmdIdGenerator must be reset each time the process
229      * of a message is starting
230      */

231     private void resetIdGenerator() {
232         engine.getCommandIdGenerator().reset();
233     }
234
235     /**
236      * The message id generator (it defaults ro a <i>SimpleIdGenerator</i> instance)
237      */

238     private SimpleIdGenerator msgIdGenerator = new SimpleIdGenerator();
239
240     // ------------------------------------------------------------ Constructors
241

242     /**
243      * Creates a new instance of SimpleSessionHandler
244      */

245     public ManagementSessionHandler() {
246         creationTimestamp = System.currentTimeMillis();
247         init();
248     }
249
250     /**
251      * Creates a new instance of SimpleSessionHandler with a given session id
252      *
253      * @param sessionId session id
254      */

255     public ManagementSessionHandler(String JavaDoc sessionId) {
256         this();
257         sessionState.sessionId = sessionId;
258     }
259
260     // ---------------------------------------------------------- Public methods
261

262     /**
263      * Sets newSession
264      *
265      * @param newSession the new value
266      */

267     public void setNew(boolean newSession) {
268         this.newSession = newSession;
269     }
270
271     /**
272      * Is newSession true?
273      *
274      * @return the newSession value
275      */

276     public boolean isNew() {
277         return this.newSession;
278     }
279
280     /**
281      * Returns true if the sessione has been authenticated.
282      *
283      * @return true if the sessione has been authenticated, false otherwise
284      */

285     public boolean isAuthenticated() {
286         return sessionState.authenticationState == AUTH_AUTHENTICATED;
287     }
288
289     /**
290      * Processes the given message. See the class description for more
291      * information.
292      *
293      * @param message the message to be processed
294      *
295      * @return the response message
296      *
297      * @throws ProtocolException in case of a protocol error
298      * @throws InvalidCredentialsException in case of invalid credentials
299      * @throws ServerFailureException in case of an internal server error
300      */

301     public SyncML processMessage(SyncML message)
302     throws ProtocolException, InvalidCredentialsException, ServerFailureException {
303         String JavaDoc deviceId = null;
304         SyncML response = null;
305
306         //
307
// Reset the cmdIdGenerator has specified in the spec
308
//
309
resetIdGenerator();
310
311         //
312
// Each time a message is received for a particular session adjust the message ID
313
//
314
msgIdGenerator.next();
315
316         //
317
// We maintain the message Id from client
318
//
319
sessionState.lastMsgIdFromClient = message.getSyncHdr().getMsgID();
320
321         //
322
// Initialize the device ID from the client request.
323
//
324
deviceId = message.getSyncHdr().getSource().getLocURI();
325
326         if (log.isLoggable(Level.FINE)) {
327             log.fine("device id: " + deviceId);
328             log.fine("current state: " + getStateName(currentState));
329         }
330         //
331
// Set maximum message size
332
//
333
Meta meta = message.getSyncHdr().getMeta();
334         if (meta != null) {
335             Long JavaDoc maxMsgSize = meta.getMaxMsgSize();
336             if (maxMsgSize != null) {
337                 sessionState.setMaxMsgSize(maxMsgSize.longValue());
338             }
339
340             Long JavaDoc maxObjSize = meta.getMaxObjSize();
341             if (maxObjSize != null) {
342                 sessionState.setMaxObjSize(maxObjSize.longValue());
343             }
344         }
345
346         try {
347             switch (currentState) {
348                 case STATE_ERROR: // in case of error you can start a new initialization
349
case STATE_END:
350                 case STATE_START:
351                 case STATE_CLIENT_UNAUTHORIZED:
352                     sessionState.reset();
353
354                     sessionState.nextTimestamp = new SyncTimestamp(
355                         System.currentTimeMillis()
356                         );
357
358                     //
359
// Read device information. Use this information with care
360
// until the client has been authenticated.
361
//
362
sessionState.device = new Sync4jDevice(deviceId);
363                     engine.readDevice(sessionState.device);
364
365                     sessionState.syncMLVerProto =
366                                 message.getSyncHdr().getVerProto().getVersion();
367                     this.engine.setSyncMLVerProto(sessionState.syncMLVerProto);
368
369                     if (currentState != STATE_CLIENT_UNAUTHORIZED) {
370                         moveTo(STATE_INITIALIZATION_PROCESSING);
371                     }
372                 case STATE_INITIALIZATION_PROCESSING:
373                     //
374
// Skip authentication if already authenticated
375
//
376
if (!isAuthenticated()) {
377
378                         //
379
// Check if the client has sent the credentials with a
380
// authentication type supported by the server
381
//
382
Cred cred = message.getSyncHdr().getCred();
383                         if (!checkAuthType(cred)) {
384                             //
385
// the client uses an authentication type different
386
// from the server authentication type
387
//
388
sessionState.loggedCredential = null;
389                         } else {
390                             login(cred, deviceId);
391
392                             if (isAuthenticated()) {
393                                 try {
394                                     engine.readPrincipal(sessionState.loggedPrincipal);
395                                 } catch (NotFoundException e) {
396                                     if (log.isLoggable(Level.INFO)) {
397                                         log.info("Authenticated principal not found: " + sessionState.loggedPrincipal);
398                                     }
399                                     sessionState.authenticationState = AUTH_INVALID_CREDENTIALS;
400                                     sessionState.loggedCredential = null;
401                                 }
402                             }
403                         }
404                     }
405
406                     boolean clientAuthenticated = isAuthenticated();
407                     boolean chalNotRequired = false;
408
409                     if (!clientAuthenticated && (currentState == STATE_CLIENT_UNAUTHORIZED)) {
410                         //
411
// If the client isn't authenticated for the second time,
412
// no chal must be sent from the server
413
//
414
chalNotRequired = true;
415                     }
416
417                     response = processInitMessage(message, chalNotRequired);
418
419                     if (!clientAuthenticated) {
420                         if (currentState == STATE_CLIENT_UNAUTHORIZED) {
421                             response.setLastMessage(true);
422                             moveTo(STATE_ERROR);
423                         } else {
424                             moveTo(STATE_CLIENT_UNAUTHORIZED);
425                         }
426                         break;
427                     }
428
429                     if (message.getSyncBody().isFinalMsg()) {
430                         moveTo(STATE_INITIALIZATION_PROCESSED);
431                     } else {
432                         break;
433                     }
434
435                 case STATE_INITIALIZATION_PROCESSED:
436                     //
437
// If the client did not authenticate the server, we have to
438
// set the credentials once more if the authentication type
439
// is MD5 or stop the processing otherwise
440
//
441
checkServerAuthentication(message);
442
443                     if ( (sessionState.started == false) ||
444                          (
445                            (sessionState.serverAuthenticationState != AUTH_AUTHENTICATED) &&
446                            (sessionState.serverAuthenticationState != AUTH_ACCEPTED)
447                          )
448                        ) {
449                         init.setFlag(Flags.FLAG_FINAL_MESSAGE);
450
451                         // if addAbsCmd != null means that
452
// the server have already sent to the client the command,
453
// in previous message
454
// but the client not have authenticate the server. So,
455
// re-add the addAbsCmd to the cache. Before re-add the commands,
456
// we checks if there is one command splitted. If happens,
457
// we merge data command with data splitted (splittedData in
458
// sessionState)
459
//
460
if (addAbsCmd != null) {
461                             if (sessionState.getSplittedCommand() != null) {
462                                 if (addAbsCmd.contains(sessionState.getSplittedCommand())) {
463                                     mergeData();
464                                     sessionState.setSplittedCommand(null);
465                                     sessionState.setNextDataToSend(null);
466                                 }
467                             }
468                             sessionState.addCmdOut(0, addAbsCmd);
469                         }
470
471                         // re-set the ManagementInitialization with the clientCommand
472
AbstractCommand[] allClientCommands =
473                             (AbstractCommand[])(message.getSyncBody()).getCommands().toArray(
474                             new AbstractCommand[0]);
475
476                         init.addClientCommand(allClientCommands);
477
478                         response = startManagementSession(message);
479
480                         if (init.isSessionAbortRequired()) {
481                             // session abort
482

483                             if (log.isLoggable(Level.INFO)) {
484                                 log.info("Session aborted by client");
485                             }
486
487                             response.setLastMessage(true);
488                             sessionState.nextTimestamp.end = System.currentTimeMillis();
489                             sessionState.dmstate.state = DeviceDMState.STATE_ABORTED;
490                             endSession();
491                             moveTo(STATE_SESSION_ABORTED);
492                             break;
493                         }
494
495                         sessionState.started = true;
496
497                         if (ProtocolUtil.noMoreResponse(response)) {
498                             moveTo(STATE_MANAGEMENT_COMPLETION);
499                             sessionState.nextTimestamp.end = System.currentTimeMillis();
500                             endSession();
501                             moveTo(STATE_END);
502                         }
503                         break;
504                     } else {
505                         moveTo(STATE_MANAGEMENT_PROCESSING);
506                     }
507
508                 case STATE_MANAGEMENT_PROCESSING:
509
510                     response = processManagementMessage(message);
511
512                     if (actions.isSessionAbortRequired()) {
513                         // session abort
514

515                         if (log.isLoggable(Level.INFO)) {
516                             log.info("Session aborted by client");
517                         }
518
519                         response.setLastMessage(true);
520                         sessionState.nextTimestamp.end = System.currentTimeMillis();
521                         sessionState.dmstate.state = DeviceDMState.STATE_ABORTED;
522                         endSession();
523                         moveTo(STATE_SESSION_ABORTED);
524
525                         break;
526                     }
527
528                     if (ProtocolUtil.noMoreResponse(response)) {
529                         moveTo(STATE_MANAGEMENT_PROCESSED);
530                         moveTo(STATE_MANAGEMENT_COMPLETION);
531                     } else {
532                         break;
533                     }
534
535                 case STATE_MANAGEMENT_COMPLETION:
536                     sessionState.nextTimestamp.end = System.currentTimeMillis();
537
538                     response.setLastMessage(true);
539
540                     endSession();
541
542                     moveTo(STATE_END);
543                     break;
544
545                 default:
546                     endSession();
547                     throw new ProtocolException( "Illegal state: "
548                                                + getStateName(currentState)
549                                                + '('
550                                                + currentState
551                                                + ')'
552                                                );
553             }
554         } catch (ProtocolException e) {
555             if (sessionState.dmstate != null) {
556                 sessionState.dmstate.state = DeviceDMState.STATE_ERROR;
557             }
558             endSession();
559             log.throwing(getClass().getName(), "processMessage", e);
560             moveTo(STATE_ERROR);
561             throw e;
562         } catch (NotFoundException e) {
563             if (sessionState.dmstate != null) {
564                 sessionState.dmstate.state = DeviceDMState.STATE_ERROR;
565             }
566             endSession();
567             log.throwing(getClass().getName(), "processMessage", e);
568             moveTo(STATE_ERROR);
569             throw new InvalidCredentialsException("Invalid credential error", e);
570         } catch (PersistentStoreException e) {
571             if (sessionState.dmstate != null) {
572                 sessionState.dmstate.state = DeviceDMState.STATE_ERROR;
573             }
574             endSession();
575             log.throwing(getClass().getName(), "processMessage", e);
576             moveTo(STATE_ERROR);
577             throw new ServerFailureException("Persistent store error", e);
578         } catch (ManagementException e) {
579             if (sessionState.dmstate != null) {
580                 sessionState.dmstate.state = DeviceDMState.STATE_ERROR;
581             }
582             endSession();
583             log.throwing(getClass().getName(), "processMessage", e);
584             moveTo(STATE_ERROR);
585             throw new ServerFailureException("Management error", e);
586         } catch (Throwable JavaDoc t) {
587             if (sessionState.dmstate != null) {
588                 sessionState.dmstate.state = DeviceDMState.STATE_ERROR;
589             }
590             endSession();
591             log.throwing(getClass().getName(), "processMessage", t);
592             moveTo(STATE_ERROR);
593         }
594
595         List commands = response.getSyncBody().getCommands();
596         ProtocolUtil.updateCmdId(commands);
597         return response;
598     }
599
600     /**
601      * Processes an error condition. This method is called when the error is
602      * is not fatal and is manageable at a protocol/session level. This results
603      * in a well formed SyncML message with an appropriete error code.
604      * <p>
605      * Note that the offending message <i>msg</i> cannot be null, meaning that
606      * at least the incoming message was a SyncML message. In this context,
607      * <i>RepresentationException</i>s are excluded.
608      *
609      * @param the offending message - NOT NULL
610      * @param the exception representing the error condition - NOT NULL
611      *
612      * @throws sync4j.framework.core.Sync4jException in case of unexpected errors
613      *
614      * @return the response message
615      */

616     public SyncML processError(SyncML msg, Throwable JavaDoc error)
617     throws Sync4jException {
618         SyncHdr msgHeader = msg.getSyncHdr();
619
620         int status = StatusCode.SERVER_FAILURE;
621
622         if (error instanceof ServerException) {
623             status = ((ServerException)error).getStatusCode();
624         }
625
626         Item item = new Item(
627             new Target(msgHeader.getSource().getLocURI()),
628             new Source(msgHeader.getTarget().getLocURI()),
629             null ,
630             new ComplexData(error.getMessage()) ,
631             false /* MoreData */
632         );
633
634         Status statusCommand = new Status(
635                 engine.getCommandIdGenerator().next(),
636                 msgHeader.getMsgID() ,
637                 "0" /* command ref */ ,
638                 "SyncHdr" /* see SyncML specs */ ,
639                 new TargetRef(msgHeader.getTarget()) ,
640                 new SourceRef(msgHeader.getSource()) ,
641                 null /* credential */ ,
642                 null /* challenge */ ,
643                 new Data(status) ,
644                 new Item[] { item }
645             );
646
647         String JavaDoc serverURI =
648             Configuration.getConfiguration().getStringValue(CFG_SERVER_URI);
649         SyncHdr syncHeader = new SyncHdr (
650                                     msgHeader.getVerDTD() ,
651                                     msgHeader.getVerProto() ,
652                                     msgHeader.getSessionID() ,
653                                     msgIdGenerator.current() ,
654                                     new Target(msgHeader.getSource().getLocURI()),
655                                     new Source(serverURI) ,
656                                     null /* response URI */ ,
657                                     false ,
658                                     null /* credentials */ ,
659                                     null /* metadata */
660                                );
661
662         SyncBody syncBody = new SyncBody(
663                                 new AbstractCommand[] { statusCommand },
664                                 true /* final */
665                             );
666
667         moveTo(STATE_ERROR);
668
669         return new SyncML(syncHeader, syncBody);
670     }
671
672     /**
673      * Called by the <i>SessionManager</i> when the session is expired.
674      * It logs out the credential and release aquired resources.
675      */

676
677     public void expire() {
678         logout();
679     }
680
681     /**
682      * Called to interrupt the processing in case of errors depending on
683      * extenal causes (i.e. the transport) or in case of session abort.
684      * The current implementation just move the session state to the error state.
685      * <p>
686      *
687      * @param statusCode the error code
688      *
689      * @see sync4j.framework.core.StatusCode for valid status codes
690      *
691      */

692     public void abort(int statusCode) {
693         if (statusCode != StatusCode.SESSION_ABORTED) {
694             moveTo(STATE_ERROR);
695         }
696     }
697
698    /**
699     * Called by the <i>SyncBean</i> when the container release the session.
700     * It commit the change to the DB, logs out the credential and
701     * release aquired resources.
702     */

703     public void endSession() {
704         logout();
705
706         if (sessionState.dmstate != null) {
707
708             switch (sessionState.dmstate.state) {
709
710                 case DeviceDMState.STATE_COMPLETED:
711
712                     engine.deleteDeviceDMState(sessionState.dmstate);
713                     try {
714                         processor.endSession(DeviceDMState.STATE_COMPLETED);
715                     } catch (ManagementException e) {
716                         if (log.isLoggable(Level.SEVERE)) {
717                             log.severe("Error calling endSession: " + e.getMessage());
718                         }
719                         log.throwing("ManagementSessionHandler", "endSession", e);
720                     }
721                     break;
722
723                 case DeviceDMState.STATE_ABORTED:
724
725                     try {
726                         processor.endSession(DeviceDMState.STATE_ABORTED);
727                     } catch (ManagementException e) {
728                         if (log.isLoggable(Level.SEVERE)) {
729                             log.severe("Error calling endSession: " + e.getMessage());
730                         }
731                         log.throwing("ManagementSessionHandler", "endSession", e);
732                     }
733
734                 default:
735                     sessionState.dmstate.end = new Date(sessionState.nextTimestamp.end);
736                     engine.storeDeviceDMState(sessionState.dmstate);
737             }
738
739         }
740     }
741
742     /**
743      * Called to permanently commit the synchronization. It does the following:
744      * <ul>
745      * <li>persists the <i>last</i> timestamp to the database for the sources
746      * successfully synchronized
747      * </ul>
748      */

749     public void commit() {
750         sessionState.dmstate.end = new Date(System.currentTimeMillis());
751
752         if (log.isLoggable(Level.FINE)) {
753             log.fine("Committing state: " + sessionState.dmstate);
754         }
755
756         engine.storeDeviceDMState(sessionState.dmstate);
757     }
758
759     // --------------------------------------------------------- Private methods
760

761     /**
762      * Initializes internal handler state
763      */

764     private void init() {
765         sessionState = new ManagementState();
766
767         engine = new ManagementEngine(Configuration.getConfiguration());
768     }
769
770     /**
771      * Processes the given initialization message.
772      *
773      *
774      * @param message the message to be processed
775      * @param chalNotRequired if a chal with a new next noce is not required
776      * @return the response message
777      *
778      * @throws ProtocolException, ManagementException
779      */

780     private SyncML processInitMessage(SyncML request, boolean chalNotRequired)
781         throws ProtocolException, ManagementException {
782         //
783
// In some circumstances, the device doesn't send the replace command (devInf)
784
// if the server has answered 401 to the first message. The device sends
785
// only the new credential.
786
// If there is the replace command, the SessionHandler caches it.
787
// If there isn't the replace command in the message, but there is a replace command in
788
// cache, the SessionHandler adds it to the message because the ManagementInitialization
789
// wants the replace command.
790
//
791

792         SyncBody syncBody = request.getSyncBody();
793         ArrayList allClientCommands = syncBody.getCommands();
794         List replaceCommands = ProtocolUtil.filterCommands(allClientCommands, new String JavaDoc[] {"Replace"});
795
796         if (replaceCommands.size() == 0) {
797             if (sessionState.devInfReplaceCommand != null) {
798                 allClientCommands.add(sessionState.devInfReplaceCommand);
799                 syncBody.setCommands((AbstractCommand[])allClientCommands.toArray(new AbstractCommand[0]));
800             }
801         } else {
802             sessionState.devInfReplaceCommand = (Replace)replaceCommands.get(0);
803         }
804
805         init = new ManagementInitialization(request.getSyncHdr(),
806                                             request.getSyncBody());
807
808         init.setIdGenerator(engine.getCommandIdGenerator());
809
810         //
811
// Sets the server authentication type so that the server will be able
812
// to accordingly challenge the client
813
//
814
init.setServerAuthType(serverAuthType);
815
816         init.setSupportedAuthType(supportedAuthType);
817
818         init.setClientAuthType(clientAuthType);
819
820         if (request.getSyncBody().isFinalMsg()) {
821             init.setFlag(Flags.FLAG_FINAL_MESSAGE);
822         } else {
823             init.unsetFlag(Flags.FLAG_FINAL_MESSAGE);
824         }
825
826         //
827
// If authentication type is MD5 or HMAC then generate a new NextNonce and store
828
// it into the database
829
//
830
if (clientAuthType != null && !chalNotRequired) {
831             if (clientAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_MD5) ||
832                 clientAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_HMAC)) {
833
834                 NextNonce nonce = ProtocolUtil.generateNextNonce();
835                 sessionState.device.setClientNonce(nonce.getValue());
836                 init.setNextNonce(nonce);
837                 engine.storeDevice(sessionState.device);
838
839             }
840
841         }
842
843         if (!isAuthenticated()) {
844
845             switch (sessionState.authenticationState) {
846                 case AUTH_MISSING_CREDENTIALS:
847                     init.setAuthorizedStatusCode(StatusCode.MISSING_CREDENTIALS);
848
849                     // if missing credential then server returns a chal with the
850
// server auth type. If the server auth type is md5 or hmac then set next nonce
851

852                     if (serverAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_MD5) ||
853                         serverAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_HMAC)) {
854                         if (!chalNotRequired) {
855                             NextNonce nonce = ProtocolUtil.generateNextNonce();
856                             sessionState.device.setClientNonce(nonce.getValue());
857                             init.setNextNonce(nonce);
858                             engine.storeDevice(sessionState.device);
859                         }
860                     }
861
862                     break;
863                 case AUTH_INVALID_CREDENTIALS:
864                     init.setAuthorizedStatusCode(StatusCode.INVALID_CREDENTIALS);
865
866                     // if missing credential then server returns a chal with the
867
// server auth type. If the server auth type is md5 or hmac then set next nonce
868

869                     if (serverAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_MD5) ||
870                         serverAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_HMAC)) {
871                         if (!chalNotRequired) {
872                             NextNonce nonce = ProtocolUtil.generateNextNonce();
873                             sessionState.device.setClientNonce(nonce.getValue());
874                             init.setNextNonce(nonce);
875                             engine.storeDevice(sessionState.device);
876                         }
877                     }
878
879                     break;
880                 default:
881                     init.setAuthorizedStatusCode(StatusCode.FORBIDDEN);
882             }
883         } else {
884             if (Constants.AUTH_TYPE_HMAC.equalsIgnoreCase(clientAuthType)) {
885                 //
886
// Using HMAC the server must return 200
887
//
888
init.setAuthorizedStatusCode(StatusCode.OK);
889             } else {
890                 init.setAuthorizedStatusCode(StatusCode.AUTHENTICATION_ACCEPTED);
891             }
892             //
893
// The alert could be issued in any of the initialization messages
894
//
895
Alert alert = init.getClientAlert();
896
897             if (alert != null) {
898                 sessionState.type = alert.getData();
899             }
900         }
901
902         //
903
// Set server credentials if required
904
//
905
if (sessionState.serverAuthenticationState != AUTH_ACCEPTED) {
906             init.setServerCredentials(
907                 engine.getServerCredentials(getChal(request), sessionState.device)
908                 );
909         }
910         if (chalNotRequired) {
911             init.setFlag(Flags.FLAG_CHAL_NOT_REQUIRED);
912         } else {
913             init.unsetFlag(Flags.FLAG_CHAL_NOT_REQUIRED);
914         }
915         SyncML response = init.getResponse(msgIdGenerator.current());
916
917         return response;
918     }
919
920     /**
921      * Processes the given management message.
922      *
923      * @param syncRequest the message to be processed
924      *
925      * @return the response message
926      *
927      * @throws ProtocolException
928      */

929     private SyncML processManagementMessage(SyncML request) throws ProtocolException,
930         ManagementException {
931
932         //
933
// If the client sent a MD5/HMAC Chal, store the server next nonce
934
//
935
storeServerNonce(ProtocolUtil.getStatusChal(request));
936
937         actions = new ManagementActions(request.getSyncHdr(),
938                                         request.getSyncBody());
939
940         actions.setClientAuthType(clientAuthType);
941         actions.setIdGenerator(engine.getCommandIdGenerator());
942
943         //
944
// If the server uses the HMAC and the client not sends the chal with the next nonce or
945
// if the client uses the HMAC but the value received is not correct, the server abort the session
946
//
947
boolean serverAbortRequired = false;
948
949         if (Constants.AUTH_TYPE_HMAC.equalsIgnoreCase(serverAuthType)) {
950             //
951
// Checks if the client has sent the next nonce
952
//
953
Chal chal = getMessageChal(request);
954             if (chal == null) {
955                 //
956
// The server must abort the session (see OMA-SyncML-DMSecurity)
957
//
958
if (log.isLoggable(Level.FINEST)) {
959                     log.finest("Server abort the session because the client has not sent the next nonce");
960                 }
961
962                 serverAbortRequired = true;
963             }
964         }
965
966         //
967
// If the client uses the HMAC the server must check again the credential
968
// and generate new nonce
969
//
970
Cred clientCred = null;
971         if (Constants.AUTH_TYPE_HMAC.equalsIgnoreCase(clientAuthType)) {
972             //
973
// Generate new nonce
974
//
975
NextNonce nonce = ProtocolUtil.generateNextNonce();
976             sessionState.device.setClientNonce(nonce.getValue());
977             actions.setNextNonce(nonce);
978             engine.storeDevice(sessionState.device);
979
980             //
981
// Checks if the credential is valid
982
//
983
clientCred = request.getSyncHdr().getCred();
984             Officer officer = engine.getOfficer();
985
986             if (!officer.authenticate(clientCred)) {
987
988                 if (log.isLoggable(Level.FINEST)) {
989                     log.finest("Server abort the session because the credential sent by the client is not valid");
990                 }
991
992                 serverAbortRequired = true;
993             }
994         }
995
996
997         if (serverAbortRequired) {
998             //
999
// We must abort the session
1000
//
1001
//
1002
// Set server credentials if required
1003
//
1004
if (sessionState.serverAuthenticationState != AUTH_ACCEPTED) {
1005                actions.setServerCredentials(
1006                    engine.getServerCredentials(getChal(request), sessionState.device)
1007                    );
1008            }
1009            actions.setFlag(Flags.FLAG_SERVER_ABORT_REQUIRED);
1010            actions.setFlag(Flags.FLAG_FINAL_MESSAGE);
1011
1012            sessionState.nextTimestamp.end = System.currentTimeMillis();
1013            sessionState.dmstate.state = DeviceDMState.STATE_ABORTED;
1014            endSession();
1015            moveTo(STATE_SESSION_ABORTED);
1016            SyncML response = actions.getResponse(msgIdGenerator.current());
1017            response.setLastMessage(true);
1018            return response;
1019
1020        }
1021
1022        boolean alertCode1222sentFromTheClient = false;
1023        boolean alertCode1222requiredFromTheClient = false;
1024
1025        //
1026
// Caches the status/results of the client
1027
//
1028
//
1029
List commandToCache = ProtocolUtil.filterCommands(
1030                                   request.getSyncBody().getCommands(),
1031                                   new String JavaDoc[]{ Status.COMMAND_NAME, Results.COMMAND_NAME }
1032                                   );
1033
1034        checkForReceivedLargeObject(commandToCache);
1035
1036        sessionState.addClientCommands((AbstractCommand[])commandToCache.toArray(new AbstractCommand[0]));
1037
1038        //
1039
// If the request is not final and there isn't an alert with code 1222, the server must answer
1040
// with a 1222 alert code without new commands and caches, if there is, large object
1041
//
1042
if (!request.getSyncBody().isFinalMsg()) {
1043
1044            //
1045
// Check if in the request there is a 1222 alert code. If there is it
1046
// then we check if there is a large object to send
1047
//
1048
//
1049
Alert alert = ProtocolUtil.searchAlertCommand(request.getSyncBody(), AlertCode.MORE_DATA);
1050            if (alert != null) {
1051                //
1052
// The client wants more data
1053
//
1054
alertCode1222sentFromTheClient = true;
1055            } else {
1056                //
1057
// The client has not sent all status/results
1058
//
1059
actions.setFlag(Flags.FLAG_MORE_MSG_REQUIRED);
1060                alertCode1222requiredFromTheClient = true;
1061
1062                Item itemWithLargeObject = ProtocolUtil.getLargeObject(commandToCache);
1063
1064                if (itemWithLargeObject != null) {
1065                    sessionState.setReceivedLargeObject(itemWithLargeObject.getData().getData());
1066                    Long JavaDoc size = sync4j.framework.core.Util.getItemSize(itemWithLargeObject);
1067                    if (size != null) {
1068                        sessionState.setSizeOfReceivedLargeObject(size);
1069                    } else {
1070                        if (sessionState.getSizeOfReceivedLargeObject() == null) {
1071                            // Sets size to -1 so the ManagementActions knows when
1072
// a large object is sent from the client without the meta size
1073
sessionState.setSizeOfReceivedLargeObject(new Long JavaDoc(-1));
1074                        }
1075                    }
1076                } else {
1077                    //
1078
// If the request is final, the large object in cache not must
1079
// be more used
1080
//
1081
sessionState.setReceivedLargeObject(null);
1082                    sessionState.setSizeOfReceivedLargeObject(null);
1083                }
1084            }
1085
1086        } else {
1087            actions.setFlag(Flags.FLAG_FINAL_MESSAGE);
1088            //
1089
// Set the clientCommands with the client commands in cache
1090
//
1091
actions.setClientCommands(sessionState.getClientCommands());
1092            //
1093
// If the cache is empty we call the setOperationResults with all results
1094
//
1095
if (sessionState.getCmdOut() == null || sessionState.getCmdOut().size() == 0) {
1096                //
1097
// Gets the client Status and Results commands and merge them in
1098
// in a corresponding array of ManagementOperationResult objects.
1099
// The so returned array can then be passed to the management
1100
// processor.
1101
//
1102
processor.setOperationResults(
1103                    Util.operationResults(
1104                    actions.getClientCommands(), String.valueOf(StatusCode.CHUNKED_ITEM_ACCEPTED))
1105                );
1106                sessionState.clearClientCommands();
1107            }
1108
1109            // If the client sent a final message, the previous data is removed from the
1110
// sessionState because it is already merge with new data
1111
sessionState.setReceivedLargeObject(null);
1112        }
1113
1114        actions.setFlag(Flags.FLAG_ALL_RESPONSES_REQUIRED);
1115
1116        //
1117
// If the session is aborted or the server is awaiting for more msg or the
1118
// client is awaiting for more data, no commands must be added to the response
1119
//
1120
if (!actions.isSessionAbortRequired() && !alertCode1222requiredFromTheClient && !alertCode1222sentFromTheClient) {
1121            //
1122
// Checks if there are command in cache
1123
//
1124
LinkedList commandInCache = sessionState.getCmdOut();
1125            if (log.isLoggable(Level.FINER)) {
1126                log.finer("command in cache: " + commandInCache.size());
1127            }
1128            if (commandInCache.size() != 0) {
1129                AbstractCommand[] newCommands = (AbstractCommand[])commandInCache.toArray(new
1130                    AbstractCommand[] {});
1131                actions.setManagementCommands(newCommands);
1132                //
1133
// remove the commands from the cache
1134
//
1135
sessionState.removeCmdOut(commandInCache);
1136                if (log.isLoggable(Level.FINER)) {
1137                    log.finer("Num. of managementCommands in actions: " +
1138                              actions.getManagementCommands().length);
1139                }
1140            } else {
1141                //
1142
// Gets the next available operations from the processor
1143
//
1144
if (log.isLoggable(Level.FINER)) {
1145                    log.finer("Call getNextOperations for new operation");
1146                }
1147
1148                actions.setManagementCommands(
1149                    Util.managementOperations2commands(
1150                    processor.getNextOperations(),
1151                    engine.getCommandIdGenerator()
1152                    )
1153                    );
1154            }
1155        } else if (alertCode1222sentFromTheClient) {
1156            //
1157
// We must check if there is previous command splitted on more
1158
// message (large object)
1159
//
1160
AbstractCommand previousCommand = sessionState.getSplittedCommand();
1161
1162            if (previousCommand == null) {
1163                throw new ProtocolException("No more data to send");
1164            }
1165            //
1166
// If previousCmd is not null then it is a ItemizedCommand
1167
// (only ItemizedCommand are splittabled)
1168
// with only one item
1169
//
1170
Item item = (Item)((ItemizedCommand)previousCommand).getItems().get(0);
1171            item.getData().setData(sessionState.getNextDataToSend());
1172
1173            //
1174
// The dimension of the data is already sent to the client in the previous message.
1175
// Then remove meta size information from the item
1176
//
1177
Meta meta = item.getMeta();
1178            if (meta != null) {
1179                meta.setSize(null);
1180            }
1181
1182            // Sets more data to false
1183
item.setMoreData(Boolean.FALSE);
1184
1185            LinkedList commandInCache = sessionState.getCmdOut();
1186
1187            //
1188
// Put the command in cache so tha actions manage all command
1189
//
1190
commandInCache.addFirst(previousCommand);
1191
1192            AbstractCommand[] newCommands = (AbstractCommand[])commandInCache.toArray(new
1193                AbstractCommand[] {});
1194            actions.setManagementCommands(newCommands);
1195        }
1196
1197        //
1198
// Set server credentials if required
1199
//
1200
if (sessionState.serverAuthenticationState != AUTH_ACCEPTED) {
1201            actions.setServerCredentials(
1202                engine.getServerCredentials(getChal(request), sessionState.device)
1203                );
1204        }
1205
1206        actions.setMimeType(mimeType);
1207        SyncML response = actions.getResponse(msgIdGenerator.current());
1208
1209        if (alertCode1222requiredFromTheClient) {
1210            //
1211
// Returns the response without checks the dimension because the
1212
// response must contain only status for header and alert code 1222
1213
//
1214
return response;
1215        }
1216
1217        //
1218
// Cache the commands to send in the next message
1219
//
1220
clearCache();
1221        cacheCommands(response);
1222
1223        if (log.isLoggable(Level.FINEST)) {
1224            log.finest("Hypothetical response: " + sync4j.framework.core.Util.toXML(response));
1225        }
1226        //
1227
// Calculate size of response message
1228
//
1229
SyncHdr syncHdr = response.getSyncHdr();
1230        SyncBody syncBody = response.getSyncBody();
1231        long sizeSyncHdr = 0, sizeSyncBody = 0;
1232
1233        /**
1234         * The test case suite currently send only ds mime type, then we must check
1235         * also MIMETYPE_SYNCMLDS_WBXML and MIMETYPE_SYNCMLDS_XML
1236         */

1237        if (Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType) ||
1238            Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType)) {
1239            sizeSyncHdr = SizeCalculator.getWBXMLSize(syncHdr);
1240            sizeSyncBody = SizeCalculator.getWBXMLSize(syncBody);
1241        } else if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
1242                   Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
1243            sizeSyncHdr = SizeCalculator.getXMLSize(syncHdr);
1244            sizeSyncBody = SizeCalculator.getXMLSize(syncBody);
1245        }
1246
1247        if (log.isLoggable(Level.FINER)) {
1248            log.finer("maxMsgSize: " + sessionState.getMaxMsgSize());
1249            log.finer("sizeSyncHdr: " + sizeSyncHdr);
1250            log.finer("sizeSyncBody: " + sizeSyncBody);
1251        }
1252
1253        sessionState.setOverheadHdr(sizeSyncHdr);
1254        maxSizeAvailable = (int)(sessionState.getMaxMsgSize() * TOLLERANCE_MAX_MSG_SIZE);
1255
1256        //
1257
// If the dimension of the response is < maxSizeAvailable or maxSizeAvailable is = 0
1258
// and there aren't the status/alerts/cmds cached then returns the response.
1259
// Else caches the commands of the response and re-create it.
1260
//
1261
if ( (maxSizeAvailable >= sizeSyncHdr + sizeSyncBody ||
1262              maxSizeAvailable == 0) &&
1263            sessionState.getStatusCmdOut().size() == 0 &&
1264            sessionState.getAlertCmdOut().size() == 0 &&
1265            sessionState.getCmdOut().size() == 0
1266            ) {
1267            return response;
1268        }
1269
1270        //
1271
// The size of the answer is greater then the allowed MaxMsgSize
1272
// Calculate size of the single commands of the response.
1273
// Create one answer of the allowed dimension.
1274
//
1275
try {
1276            response = createNextMsg(response);
1277        } catch (Sync4jException e) {
1278            throw new ProtocolException(e);
1279        }
1280
1281        return response;
1282    }
1283
1284    /**
1285     * Makes a state transition. Very simple implementation at the moment: it
1286     * changes the value of <i>currentState</i> to the given value.
1287     *
1288     * @param state the new state
1289     */

1290    private void moveTo(int state) {
1291        if (log.isLoggable(Level.FINE)) {
1292            log.fine("moving to state " + getStateName(state));
1293        }
1294        currentState = state;
1295    }
1296
1297
1298    /**
1299     * Checks that the credentials of the given message are allowed to start a
1300     * session.
1301     *
1302     * @param credential the message
1303     * @param deviceId the deviceId
1304     */

1305    private boolean login(Cred credential, String JavaDoc deviceId) {
1306        //
1307
// May be the credential is already logged in...
1308
//
1309
logout();
1310
1311        //
1312
// If the credential is not specified, create a new "guest" credential
1313
// but only if the property isGuestEnabled is true
1314
//
1315
if (credential == null) {
1316            sessionState.authenticationState = AUTH_MISSING_CREDENTIALS;
1317
1318            return false;
1319        }
1320
1321        clientAuthType = credential.getType();
1322        Sync4jPrincipal p = Sync4jPrincipal.fromCredential(credential.getData(),
1323                                                           credential.getType(),
1324                                                           deviceId );
1325        //
1326
// If the client authentication type is MD5 or HMAC then set the server nonce
1327
// value into credential
1328
//
1329
if (clientAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_MD5) ||
1330            clientAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_HMAC)) {
1331
1332            NextNonce nonce = new NextNonce(sessionState.device.getClientNonce());
1333
1334            Authentication auth = credential.getAuthentication();
1335            auth.setNextNonce(nonce);
1336            auth.setDeviceId(deviceId);
1337        }
1338
1339        try {
1340            Officer officer = engine.getOfficer();
1341            if (officer.authenticate(credential)) {
1342                //
1343
// if authType == MD5, the officer sets in the Authentication the
1344
// correct username, then sets it in the principal
1345
//
1346
if (clientAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_MD5) ||
1347                    clientAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_HMAC)) {
1348                    p.setUsername(credential.getAuthentication().getUsername());
1349                }
1350
1351                if (officer.authorize(p, RESOURCE_MANAGEMENT)) {
1352
1353                    sessionState.loggedCredential = credential;
1354                    sessionState.loggedPrincipal = p ;
1355
1356                    sessionState.authenticationState = AUTH_AUTHENTICATED;
1357
1358                    return true;
1359                }
1360            }
1361        } catch (Exception JavaDoc e) {
1362            if (log.isLoggable(Level.SEVERE)) {
1363                log.severe("Unable to login due to the error: " + e.getMessage());
1364            }
1365            log.throwing(getClass().getName(), "login", e);
1366        }
1367
1368        return false;
1369    }
1370
1371    /**
1372     * Logs out the logged in credential
1373     */

1374    private void logout() {
1375        sessionState.authenticationState = AUTH_INVALID_CREDENTIALS;
1376        sessionState.loggedCredential = null;
1377        sessionState.loggedPrincipal = null;
1378    }
1379
1380    /**
1381     * Starts a new management session, choosing the right managment processor.
1382     *
1383     * @param requestMessage the request
1384     *
1385     * @return the SyncML message to send back to the client containing the
1386     * management commands (if there is any) as obtained by the
1387     * management processor
1388     *
1389     * @throws ProtocolException in the case the message could not meet SyncML specs
1390     * @throws ManagementException if any other error occurs
1391     */

1392    private SyncML startManagementSession(SyncML requestMessage)
1393        throws ProtocolException, ManagementException {
1394
1395        //
1396
// If the client sent a MD5/HMAC Chal, store the server next nonce
1397
//
1398
storeServerNonce(ProtocolUtil.getStatusChal(requestMessage));
1399
1400        String JavaDoc sessionId = requestMessage.getSyncHdr().getSessionID().getSessionID();
1401
1402        //
1403
// Gets the processor selector and retrieve the processor to be used
1404
// for this management session
1405
//
1406
Configuration c = Configuration.getConfiguration();
1407
1408        //
1409
// Try to associate this session to an existing state (by session id or
1410
// device id). See the design document for details.
1411
//
1412
sessionState.dmstate = new DeviceDMState(init.getDevInfo().getDevId());
1413        sessionState.dmstate.mssid = init.getSessionId();
1414        if (!engine.resolveDMState(sessionState.dmstate, sessionState.device)) {
1415            //
1416
// This is new DM session for which the server has no information
1417
//
1418
sessionState.dmstate.id = null;
1419            sessionState.dmstate.mssid = init.getSessionId();
1420        }
1421
1422        //
1423
// If addAbsCmd is != null means that the processor already has been called.
1424
// Then cache contains the AbstractCommand to send
1425
//
1426
if (addAbsCmd == null) {
1427            ProcessorSelector selector =
1428                (ProcessorSelector)c.getBeanInstance(CFG_SERVER_DM_SELECTOR);
1429
1430            processor = selector.getProcessor(sessionState.dmstate, init.getDevInfo());
1431
1432            if (processor == null) {
1433                throw new ManagementException("No management processor could be selected!");
1434            }
1435
1436                        if (sessionState.dmstate.state == DeviceDMState.STATE_NOTIFIED) {
1437                //
1438
// Change the state to IN_PROGRESS
1439
//
1440
sessionState.dmstate.state = DeviceDMState.STATE_IN_PROGRESS;
1441            }
1442        }
1443
1444        //
1445
// The alert could be issued in any of the initialization messages
1446
//
1447
Alert alert = init.getClientAlert();
1448
1449        if (alert != null) {
1450            sessionState.type = alert.getData();
1451        }
1452
1453        //
1454
// If addAbsCmd is != null means that the processor already has been called.
1455
// Then cache contains the AbstractCommand to send
1456
//
1457
if (addAbsCmd == null) {
1458            sessionState.dmstate.start = new Date(System.currentTimeMillis());
1459            //
1460
// Perform session initialization
1461
//
1462
processor.beginSession(
1463                sessionId,
1464                sessionState.loggedPrincipal,
1465                sessionState.type,
1466                init.getDevInfo(),
1467                sessionState.dmstate
1468                );
1469
1470            if (!init.isSessionAbortRequired()) {
1471                //
1472
// Get the next available operations
1473
//
1474
init.setManagementCommands(
1475                    Util.managementOperations2commands(
1476                    processor.getNextOperations(),
1477                    engine.getCommandIdGenerator()
1478                    )
1479                    );
1480            }
1481        }
1482
1483        // re-set the request because the init put in the response a reference to the request (MsgRef)
1484
init.setRequest(requestMessage);
1485        if (requestMessage.getSyncBody().isFinalMsg()) {
1486            init.setFlag(Flags.FLAG_FINAL_MESSAGE);
1487        } else {
1488            init.unsetFlag(Flags.FLAG_FINAL_MESSAGE);
1489        }
1490
1491        SyncML response = init.getResponse(msgIdGenerator.current());
1492
1493        //
1494
// Calculate size of response message
1495
//
1496
SyncHdr syncHdr = response.getSyncHdr();
1497        SyncBody syncBody = response.getSyncBody();
1498        long sizeSyncHdr = 0, sizeSyncBody = 0;
1499
1500        /**
1501         * The test case suite currently send only ds mime type, then we must check
1502         * also MIMETYPE_SYNCMLDS_WBXML and MIMETYPE_SYNCMLDS_XML
1503         */

1504        if (Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType) ||
1505            Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType)) {
1506            sizeSyncHdr = SizeCalculator.getWBXMLSize(syncHdr);
1507            sizeSyncBody = SizeCalculator.getWBXMLSize(syncBody);
1508        } else if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
1509                   Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
1510            sizeSyncHdr = SizeCalculator.getXMLSize(syncHdr);
1511            sizeSyncBody = SizeCalculator.getXMLSize(syncBody);
1512        }
1513
1514        if (log.isLoggable(Level.FINER)) {
1515            log.finer("maxMsgSize: " + sessionState.getMaxMsgSize());
1516            log.finer("sizeSyncHdr: " + sizeSyncHdr);
1517            log.finer("sizeSyncBody: " + sizeSyncBody);
1518        }
1519
1520        sessionState.setOverheadHdr(sizeSyncHdr);
1521        maxSizeAvailable = sessionState.getMaxMsgSize();
1522
1523        //
1524
// Check if the client MaxMsgSize is greater then the server
1525
// minmsgsize
1526
//
1527
checkMaxMsgSize(response);
1528
1529        //
1530
// If the dimension of the response is < maxSizeAvailable or maxSizeAvailable is = 0
1531
// and there aren't the status/alerts/cmds cached then returns the response.
1532
// Else caches the commands of the response and re-create it.
1533
//
1534
if ( (maxSizeAvailable >= sizeSyncHdr + sizeSyncBody ||
1535              maxSizeAvailable == 0) &&
1536            sessionState.getStatusCmdOut().size() == 0 &&
1537            sessionState.getAlertCmdOut().size() == 0 &&
1538            sessionState.getCmdOut().size() == 0
1539            ) {
1540           return response;
1541        }
1542
1543        clearCache();
1544        //
1545
// Cache the alert/status/commands to send in the next package
1546
//
1547
cacheCommands(response);
1548
1549        if (log.isLoggable(Level.FINEST)) {
1550            log.finest("Hypothetical response: " + sync4j.framework.core.Util.toXML(response));
1551        }
1552
1553        //
1554
// The size of the answer is greater then the allowed MaxMsgSize
1555
// Calculate size of the single commands of the response.
1556
// Create one answer of the allowed dimension.
1557
//
1558
try {
1559            response = createNextMsg(response);
1560        } catch (Sync4jException e) {
1561            throw new ProtocolException(e);
1562        }
1563
1564        return response;
1565
1566    }
1567
1568    /**
1569     * Check if the client authentication type is supported from the officer
1570     *
1571     * @param cred the client credential
1572     *
1573     * @return true if the client authentication type is supported, false otherwise
1574     */

1575    private boolean checkAuthType(Cred cred) {
1576        Officer officer = engine.getOfficer();
1577
1578        serverAuthType = officer.getAuthType();
1579        supportedAuthType = officer.getSupportedAuthType();
1580
1581        if (cred == null) {
1582            if (officer.isGuestEnabled()) {
1583                sessionState.authenticationState = sessionState.AUTH_AUTHENTICATED;
1584            } else {
1585                sessionState.authenticationState = sessionState.AUTH_MISSING_CREDENTIALS;
1586            }
1587            return officer.isGuestEnabled();
1588        }
1589
1590        String JavaDoc clientAuthType = cred.getType();
1591
1592        if (supportedAuthType.indexOf(clientAuthType) != -1) {
1593            return true;
1594        }
1595
1596        sessionState.authenticationState = sessionState.AUTH_INVALID_CREDENTIALS;
1597
1598        return false;
1599    }
1600
1601    /**
1602     * Checks if the client expects the server to send its credentials. If so,
1603     * checkServerAuth returns the client Chal command.
1604     *
1605     * @param message the SyncML object
1606     *
1607     * @return the client Chal command or null if the client did not
1608     * challenge the server
1609     */

1610    private Chal getMessageChal(SyncML message) {
1611        Status[] statusCmds =
1612            (Status[])ProtocolUtil.filterCommands(
1613                                        message.getSyncBody().getCommands(),
1614                                        new String JavaDoc[]{ Status.COMMAND_NAME }
1615                                        ).toArray(new Status[0]);
1616
1617        for (int i = 0; statusCmds != null && i < statusCmds.length; i++) {
1618            if ("0".equals(statusCmds[i].getCmdRef()) &&
1619                "SyncHdr".equals(statusCmds[i].getCmd())) {
1620
1621                return statusCmds[i].getChal();
1622            }
1623        }
1624
1625        return null;
1626    }
1627
1628    /**
1629     * Extracts or create a Chal object that represents how server credentials
1630     * should be created.<br>
1631     * If the client challenged the server, we must use the requested auth
1632     * mechanism, otherwise, if the server is not logged, we use the default server authentication scheme.
1633     *
1634     * @param msg the request message
1635     *
1636     * @return a Chal object representing how server credentials should be
1637     * formatted. <code>null</code> if the client not requires the
1638     * authentication and if serverAuthType is set to Constants.AUTH_TYPE_NONE
1639     */

1640    private Chal getChal(final SyncML msg) {
1641        Chal chal = getMessageChal(msg);
1642
1643
1644        if (chal == null) {
1645          //
1646
// client not challenged the server
1647
//
1648
if (sessionState.serverAuthenticationState != AUTH_ACCEPTED) {
1649            //
1650
// the server is not logged
1651
//
1652
if (serverAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_NONE)) {
1653              return null;
1654            }
1655
1656            Meta meta = new Meta();
1657            meta.setType(serverAuthType);
1658            meta.setFormat(Constants.FORMAT_CLEAR);
1659            meta.setNextNonce(new NextNonce(sessionState.device.getServerNonce()));
1660            chal = new Chal(meta);
1661
1662          }
1663        } else {
1664            if (log.isLoggable(Level.FINE)) {
1665                log.fine("Challenged server authentication with scheme " + chal.getType());
1666            }
1667        }
1668
1669        return chal;
1670    }
1671
1672    /**
1673     * Checks if the server authentication succedeed. It throws a
1674     * ProtocolException if not.
1675     *
1676     * @param msg the SyncML message to check
1677     *
1678     * @return true if the server should retry authentication, false otherwise
1679     *
1680     * @throw ProcotolException if server authentication did not succeed
1681     */

1682    private void checkServerAuthentication(SyncML msg) throws ProtocolException {
1683        int headerStatusCode = ProtocolUtil.getHeaderStatusCode(msg);
1684        if (headerStatusCode == -1) {
1685            return;
1686        }
1687
1688        if ( (headerStatusCode == StatusCode.INVALID_CREDENTIALS ||
1689              headerStatusCode == StatusCode.MISSING_CREDENTIALS)) {
1690
1691            /**
1692             * The server isn't authorized.
1693             * If server has used basic authentication and the client has required
1694             * basic authentication means that the server is unable to authenticate
1695             * to the client.
1696             * If the client has required a authentication type different from that used
1697             * from the server, then set the credential for the server to the new authentication
1698             * type.
1699             * If the authentication type used from the server is equal to that required
1700             * from the client (different from basic), the server retries with the same
1701             * authentication type (the client could have sent a new nonce)
1702             */

1703
1704            String JavaDoc authRequiredFromClient = null;
1705            Chal chal = getChal(msg);
1706
1707            if (chal != null) {
1708                authRequiredFromClient = chal.getType();
1709            }
1710
1711            if (serverAuthType.equalsIgnoreCase(Constants.AUTH_TYPE_BASIC) &&
1712                authRequiredFromClient.equalsIgnoreCase(Constants.AUTH_TYPE_BASIC)) {
1713
1714                throw new ProtocolException("Unable to authenticate to the client");
1715
1716            } else if (!authRequiredFromClient.equalsIgnoreCase(serverAuthType)) {
1717                serverAuthType = authRequiredFromClient;
1718
1719                init.setServerCredentials(
1720                    engine.getServerCredentials(chal, sessionState.device)
1721                    );
1722                sessionState.serverAuthenticationState = AUTH_RETRY_1;
1723
1724            } else if (authRequiredFromClient.equalsIgnoreCase(serverAuthType) &&
1725                       sessionState.serverAuthenticationState == AUTH_UNAUTHENTICATED) {
1726                init.setServerCredentials(
1727                    engine.getServerCredentials(chal, sessionState.device)
1728                );
1729                sessionState.serverAuthenticationState = AUTH_RETRY_1;
1730            } else {
1731                throw new ProtocolException("Unable to authenticate to the client");
1732            }
1733
1734            return;
1735        } else if (headerStatusCode == StatusCode.AUTHENTICATION_ACCEPTED) {
1736            //
1737
// Authenticated with code 212
1738
//
1739
if (log.isLoggable(Level.FINE)) {
1740                log.fine("Server logged (code 212)");
1741            }
1742            sessionState.serverAuthenticationState = AUTH_ACCEPTED;
1743        } else {
1744            //
1745
// Authenticated with code 200
1746
//
1747
if (log.isLoggable(Level.FINE)) {
1748                log.fine("Server auhenticated (code 200)");
1749            }
1750            sessionState.serverAuthenticationState = AUTH_AUTHENTICATED;
1751        }
1752    }
1753
1754    /**
1755     * Stores the server next nonce in both the database and the managementState
1756     * device member if the chal type is MD5/HMAC. If chal is null, nothing is saved.
1757     *
1758     * @parameter chal the Chal object; if null, nothing is saved
1759     */

1760    private void storeServerNonce(Chal chal) {
1761        if (chal == null) {
1762            return;
1763        }
1764
1765        if (Constants.AUTH_TYPE_MD5.equals(chal.getType()) ||
1766            Constants.AUTH_TYPE_HMAC.equals(chal.getType())) {
1767
1768            NextNonce nonce = chal.getNextNonce();
1769            if (nonce != null) {
1770                sessionState.device.setServerNonce(Base64.decode(nonce.getValue()));
1771                engine.storeDevice(sessionState.device);
1772            }
1773        }
1774    }
1775
1776    /**
1777     * Returns a String representation of the given state
1778     *
1779     * @param state the state code
1780     *
1781     * @return a String representation of the given state
1782     */

1783    private String JavaDoc getStateName(int state) {
1784        String JavaDoc stateName = "STATE_UNKNOWN";
1785
1786        switch (state) {
1787            case STATE_START : stateName = "STATE_START" ; break;
1788            case STATE_END : stateName = "STATE_END" ; break;
1789            case STATE_ERROR : stateName = "STATE_ERROR" ; break;
1790            case STATE_INITIALIZATION_PROCESSING : stateName = "STATE_INITIALIZATION_PROCESSING" ; break;
1791            case STATE_INITIALIZATION_PROCESSED : stateName = "STATE_INITIALIZATION_PROCESSED" ; break;
1792            case STATE_MANAGEMENT_PROCESSING : stateName = "STATE_MANAGEMENT_PROCESSING" ; break;
1793            case STATE_MANAGEMENT_PROCESSED : stateName = "STATE_MANAGEMENT_PROCESSED" ; break;
1794            case STATE_MANAGEMENT_COMPLETION : stateName = "STATE_MANAGEMENT_COMPLETION" ; break;
1795            case STATE_SESSION_ABORTED : stateName = "STATE_SESSION_ABORTED" ; break;
1796
1797            default : stateName = "STATE_UNKNOWN" ; break;
1798        }
1799
1800        return stateName;
1801    }
1802
1803    /**
1804     * Clear the cache
1805     */

1806    private void clearCache() {
1807        sessionState.getAlertCmdOut().clear();
1808        sessionState.getStatusCmdOut().clear();
1809        sessionState.getCmdOut().clear();
1810    }
1811
1812    /**
1813     * Cache the commands to send in the next message
1814     */

1815    private void cacheCommands(SyncML response) {
1816        //
1817
// Filter Status commands
1818
//
1819
List statusCmdOut = ProtocolUtil.filterCommands(
1820            response.getSyncBody().getCommands(),
1821            new String JavaDoc[] {Status.COMMAND_NAME}
1822            );
1823        //
1824
// Sort Status for CmdRef
1825
//
1826
List listStatus = Arrays.asList(ProtocolUtil.sortStatusCommand(
1827            statusCmdOut.toArray(new Status[0]))
1828                                      );
1829        //
1830
// Cache Status commands
1831
//
1832
sessionState.addStatusCmdOut(listStatus);
1833
1834        Status statusSyncHdr = ProtocolUtil.filterStatus(
1835            (Status[])listStatus.toArray(new Status[0]),
1836            Status.class,
1837            "SyncHdr"
1838        );
1839
1840        /**
1841         * The test case suite currently send only ds mime type, then we must check
1842         * also MIMETYPE_SYNCMLDS_WBXML and MIMETYPE_SYNCMLDS_XML
1843         */

1844        if (Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType) ||
1845            Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType)) {
1846            sessionState.setSizeStatusSyncHdr(SizeCalculator.getWBXMLSize(statusSyncHdr));
1847        } else if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
1848                   Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
1849            sessionState.setSizeStatusSyncHdr(SizeCalculator.getXMLSize(statusSyncHdr));
1850        }
1851
1852        //
1853
// Cache Status for SyncHdr to use (in order to create the asnwer) when
1854
// the server isn't in the state "STATE_SYNCHRONIZATION_PROCESSING"
1855
//
1856
sessionState.setStatusSyncHdr(statusSyncHdr);
1857
1858        //
1859
// Remove status of the SyncHdr
1860
//
1861
ArrayList removeStatus = new ArrayList();
1862        removeStatus.add(statusSyncHdr);
1863        sessionState.removeStatusCmdOut(removeStatus);
1864
1865        //
1866
// Filter AbstractCommand commands
1867
// (in particular the Sync, Results and Get commands)
1868
//
1869
List cmdOut = ProtocolUtil.filterCommands(
1870            response.getSyncBody().getCommands(),
1871            MANAGEMENT_COMMANDS
1872            );
1873
1874        //
1875
// Sort AbstractCommand for CmdID
1876
//
1877
List listCmd = Arrays.asList(ProtocolUtil.sortAbstractCommand(
1878            cmdOut.toArray(new AbstractCommand[0]))
1879                                     );
1880        //
1881
// Cache commands
1882
//
1883
sessionState.addCmdOut(new LinkedList(listCmd));
1884    }
1885
1886    /**
1887     * Create the next Initialization message with commands presents into queues
1888     *
1889     * @param syncML the hypothetical answer
1890     *
1891     * @return the server response
1892     */

1893    private SyncML createNextMsg(SyncML syncML) throws Sync4jException {
1894
1895        if (log.isLoggable(Level.FINEST)) {
1896            log.finest("Create Next Message");
1897        }
1898
1899        long sizeSyncHdr = sessionState.getOverheadHdr();
1900        long sizeStatusSyncHdr = sessionState.getSizeStatusSyncHdr();
1901
1902        maxSizeAvailable = sessionState.getMaxMsgSize() -
1903                           sizeSyncHdr -
1904                           sizeStatusSyncHdr ;
1905
1906
1907        if (Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType) ||
1908            Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType)) {
1909            maxSizeAvailable -= SizeCalculator.getWBXMLOverheadSyncML();
1910            maxSizeAvailable -= SizeCalculator.getWBXMLOverheadSyncBody();
1911        } else if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
1912                   Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
1913            maxSizeAvailable -= SizeCalculator.getXMLOverheadSyncML();
1914            maxSizeAvailable -= SizeCalculator.getXMLOverheadSyncBody();
1915        }
1916
1917        //
1918
// Checks status
1919
//
1920
if (log.isLoggable(Level.FINEST)) {
1921            log.finest("How many Status can I include into the response?");
1922        }
1923        howManyStatus(sessionState.getStatusCmdOut());
1924
1925        sessionState.removeStatusCmdOut(addStatus);
1926
1927        //
1928
// Checks AbstractCommand
1929
//
1930
if (log.isLoggable(Level.FINEST)) {
1931            log.finest("How many AbstractCommand can I include into the response?");
1932        }
1933        howManyAbstractCommand();
1934
1935        sessionState.removeCmdOut(addAbsCmd);
1936
1937        int size = addStatus.size() + addAbsCmd.size();
1938        ArrayList commandList = new ArrayList(size);
1939        commandList.addAll(addStatus);
1940        commandList.addAll(addAbsCmd);
1941
1942        AbstractCommand[] absCommands =
1943            (AbstractCommand[])commandList.toArray(new AbstractCommand[size]);
1944
1945        // if there is a data splitted in session, the message is not final
1946
boolean isFinal = (sessionState.getNextDataToSend() == null);
1947        SyncBody responseBody = new SyncBody(absCommands, isFinal);
1948
1949        if (log.isLoggable(Level.FINEST)) {
1950            log.finest("status in cache after creation: " + sessionState.getStatusCmdOut().size());
1951            log.finest("alert in cache after creation: " + sessionState.getAlertCmdOut().size());
1952            log.finest("command in cache after creation: " + sessionState.getCmdOut().size());
1953        }
1954
1955        SyncML newResponse = new SyncML(syncML.getSyncHdr(), responseBody);
1956
1957        if (log.isLoggable(Level.FINEST)) {
1958            if (Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType) ||
1959                Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType)) {
1960                size = (int) SizeCalculator.getWBXMLSize(newResponse);
1961            } else if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
1962                       Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
1963                size = (int) SizeCalculator.getXMLSize(newResponse);
1964            }
1965            log.finest("Response size: " + size);
1966        }
1967
1968        return newResponse;
1969    }
1970
1971    /**
1972     * Calculates how many Status can be sent
1973     */

1974    private void howManyStatus(List allStatus) {
1975        addStatus = new ArrayList();
1976        addStatus.add(sessionState.getStatusSyncHdr());
1977
1978        //
1979
// Number of Status to insert into response
1980
//
1981
int x = 0;
1982        Status status = null;
1983        long size = 0;
1984        for (int i = 0; allStatus != null && i < allStatus.size(); i++) {
1985            status = (Status)allStatus.get(i);
1986            //
1987
// Calculate size Status
1988
//
1989
size = 0;
1990            if (Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType) ||
1991                Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType)) {
1992                size = SizeCalculator.getWBXMLSize(status);
1993            } else if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
1994                       Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
1995                size = SizeCalculator.getXMLSize(status);
1996            }
1997
1998            // If there is space, we add the state
1999
if (maxSizeAvailable - size >= 0) {
2000                addStatus.add( (Status)allStatus.get(i));
2001                maxSizeAvailable -= size;
2002                x++;
2003            } else {
2004                break;
2005            }
2006        }
2007
2008        if (log.isLoggable(Level.FINEST)) {
2009            log.finest("Number of Status inserted: " + x);
2010        }
2011    }
2012
2013
2014    /**
2015     * Calculates how many AbstractCommand can be sent.
2016     */

2017    private void howManyAbstractCommand() {
2018        addAbsCmd = new ArrayList();
2019        boolean isCmdWithLargeObject =false;
2020
2021        //
2022
// Number of AbstractCommand to insert into response
2023
//
2024
int x = 0;
2025        LinkedList allCmd = sessionState.getCmdOut();
2026
2027        if (maxSizeAvailable > 0 &&
2028            sessionState.getStatusCmdOut().size() == 0 &&
2029            sessionState.getAlertCmdOut().size() == 0) {
2030
2031            long size = 0;
2032            AbstractCommand cmd = null;
2033            for (int i = 0; allCmd != null && i < allCmd.size(); i++) {
2034                size = 0;
2035                cmd = (AbstractCommand)allCmd.get(i);
2036
2037                if (Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType) ||
2038                    Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType)) {
2039                    size = SizeCalculator.getCommandWBXMLSize(cmd);
2040                } else if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
2041                           Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
2042                    size = SizeCalculator.getCommandXMLSize(cmd);
2043                }
2044
2045                // If there is space, we add the abstract command
2046
if (maxSizeAvailable - size >= 0) {
2047                    addAbsCmd.add( cmd );
2048                    maxSizeAvailable -= size;
2049                    if (cmd == sessionState.getSplittedCommand()) {
2050                        //
2051
// I have added a previous splitted cmd.
2052
//
2053
sessionState.setSplittedCommand(null);
2054                        sessionState.setNextDataToSend(null);
2055                    }
2056                    x++;
2057                } else {
2058
2059                    isCmdWithLargeObject = checkForSplitData(cmd, maxSizeAvailable);
2060
2061                    //
2062
// If cmd is the first command that we try to add and there isn't
2063
// sufficiently space then we try to split the data in more message (large object).
2064
// If large object isn't permitted (because the dimension is greater of the maxObjectSize)
2065
// we log the info and remove the command from the list
2066
// because this command not will be never in the list
2067
//
2068
if (i == 0) {
2069
2070                        if (!isCmdWithLargeObject) {
2071                            if (log.isLoggable(Level.INFO)) {
2072                                log.info("The command " + cmd + " is too large (" + size +
2073                                         " bytes)");
2074                            }
2075                            sessionState.removeCmdOut(cmd);
2076                        } else {
2077                            addAbsCmd.add(cmd);
2078                            x++;
2079                            if (sessionState.getNextDataToSend() == null) {
2080                                //
2081
// All data is sent. Try to add more commands
2082
//
2083
continue;
2084                            } else {
2085                                break;
2086                            }
2087                        }
2088                    } else {
2089                        if (!isCmdWithLargeObject) {
2090                            //
2091
// This isn't first command and isn't a command with
2092
// large object, then break the process
2093
//
2094
break;
2095                        } else {
2096                            //
2097
// This isn't first command but is a command with
2098
// large object, then add the command and break the process
2099
//
2100
addAbsCmd.add(cmd);
2101                            x++;
2102                            if (sessionState.getNextDataToSend() == null) {
2103                                //
2104
// All data is sent. Try to add more commands
2105
//
2106
continue;
2107                            } else {
2108                                break;
2109                            }
2110                        }
2111                    }
2112                    break;
2113                }
2114            }
2115        }
2116        if (log.isLoggable(Level.FINEST)) {
2117            log.finest("Number of AbstractCommand inserted: " + x);
2118        }
2119
2120    }
2121
2122    /**
2123     * Checks if the given command is splittable with the given sizeAvailable.
2124     *
2125     * @param cmd AbstractCommand
2126     * @param sizeAvailable long
2127     * @return boolean
2128     */

2129    private boolean checkForSplitData(AbstractCommand cmd, long sizeAvailable) {
2130        Item itemToSplit = null;
2131        //
2132
// If cmd contains no items or more than 1 items, no large object are permitted
2133
//
2134
if (cmd instanceof ItemizedCommand) {
2135            if (((ItemizedCommand)cmd).getItems().size() != 1) {
2136                if (log.isLoggable(Level.FINER)) {
2137                    log.finer("Command with more items isn't splittable");
2138                }
2139                return false;
2140            } else {
2141                itemToSplit = (Item)((ItemizedCommand)cmd).getItems().get(0);
2142            }
2143        } else {
2144            if (log.isLoggable(Level.FINER)) {
2145                log.finer("Command isn't a ItemizedCommand then it isn't splittable");
2146            }
2147            return false;
2148        }
2149
2150        Data data = itemToSplit.getData();
2151        if (data == null) {
2152            return false;
2153        }
2154        String JavaDoc dataValue = data.getData();
2155        if (dataValue == null) {
2156            return false;
2157        }
2158
2159        Object JavaDoc dataToSplit = dataValue;
2160        boolean isBinaryData = false;
2161        int lengthDataToSplit = 0;
2162
2163        Meta meta = itemToSplit.getMeta();
2164        if (Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType) ||
2165            Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType)) {
2166
2167            if (meta != null) {
2168                String JavaDoc format = meta.getFormat();
2169                if (format != null && format.equalsIgnoreCase("b64")) {
2170                    isBinaryData = true;
2171                    dataToSplit = Base64.decode( ( (String JavaDoc)dataToSplit).getBytes());
2172                    lengthDataToSplit = ( (byte[])dataToSplit).length;
2173                }
2174
2175            }
2176        }
2177
2178        if (!isBinaryData) {
2179            lengthDataToSplit = ((String JavaDoc)dataToSplit).length();
2180        }
2181
2182        long maxObjSize = sessionState.getMaxObjSize();
2183        if (maxObjSize != 0 && lengthDataToSplit > maxObjSize) {
2184            if (log.isLoggable(Level.FINER)) {
2185                log.finer("The dimension of the data is greater of the maxObjSize");
2186            }
2187            // data too large
2188
return false;
2189        }
2190        //
2191
// try to split the cmd
2192
//
2193
if (sessionState.getNextDataToSend() == dataValue) {
2194            //
2195
// the item already has been splitted,
2196
// then no meta size is set
2197
//
2198
} else {
2199            //
2200
// We add data lenght information on the item
2201
//
2202
if (meta == null) {
2203                meta = new Meta();
2204                itemToSplit.setMeta(meta);
2205            }
2206
2207            meta.setSize(new Long JavaDoc(lengthDataToSplit));
2208        }
2209
2210        int sizeAvailableForData = calculateDataSizeAvailable(sizeAvailable, cmd, itemToSplit);
2211
2212        if (sizeAvailableForData <= 0) {
2213            //
2214
// No space for data
2215
//
2216
return false;
2217        }
2218
2219        sessionState.setSplittedCommand(cmd);
2220
2221        Object JavaDoc newData = null;
2222        int lengthNewData = 0;
2223
2224        if (isBinaryData) {
2225            if (sizeAvailableForData > lengthDataToSplit) {
2226                // send all
2227
sizeAvailableForData = lengthDataToSplit;
2228                newData = dataToSplit;
2229            } else {
2230                newData = new byte[sizeAvailableForData];
2231                System.arraycopy(dataToSplit, 0, newData, 0, sizeAvailableForData);
2232            }
2233            lengthNewData = sizeAvailableForData;
2234        } else {
2235            newData = ((String JavaDoc)dataToSplit).substring(0, sizeAvailableForData);
2236            lengthNewData = ((String JavaDoc)newData).length();
2237        }
2238
2239        if (isBinaryData) {
2240            newData = new String JavaDoc(Base64.encode((byte[])newData));
2241        }
2242        itemToSplit.getData().setData((String JavaDoc)newData);
2243
2244        if (sizeAvailableForData >= lengthDataToSplit) {
2245            // all data is sent
2246
sessionState.setNextDataToSend(null);
2247            itemToSplit.setMoreData(Boolean.FALSE);
2248        } else {
2249            //
2250
// We must save the data not sent
2251
//
2252
String JavaDoc dataNotSent = null;
2253            if (isBinaryData) {
2254                byte[] byteNotSent = new byte[lengthDataToSplit - sizeAvailableForData];
2255                System.arraycopy(dataToSplit, sizeAvailableForData, byteNotSent, 0, lengthDataToSplit - sizeAvailableForData);
2256                dataNotSent = new String JavaDoc(Base64.encode((byte[])byteNotSent));
2257            } else {
2258                dataNotSent = ((String JavaDoc)dataToSplit).substring(sizeAvailableForData);
2259
2260            }
2261            itemToSplit.setMoreData(Boolean.TRUE);
2262            sessionState.setNextDataToSend(dataNotSent);
2263        }
2264
2265        return true;
2266    }
2267
2268    /**
2269     * Returns the space available for data.
2270     *
2271     * @param commandSizeAvailable long
2272     * @param cmd AbstractCommand
2273     * @param item Item
2274     * @return int
2275     */

2276    private int calculateDataSizeAvailable(long commandSizeAvailable, AbstractCommand cmd, Item item) {
2277        //
2278
// Caches the original data
2279
//
2280
ComplexData itemData = item.getData();
2281        item.setData(new ComplexData(""));
2282
2283        long commandSize = -1;
2284
2285        if (Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType) ||
2286            Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType)) {
2287            commandSize = SizeCalculator.getCommandWBXMLSize(cmd);
2288        } else if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
2289                   Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
2290            commandSize = SizeCalculator.getCommandXMLSize(cmd);
2291        }
2292
2293        int sizeAvailableForMoreData = (int)(commandSizeAvailable - commandSize);
2294        //
2295
// Re-set the original data
2296
//
2297
item.setData(itemData);
2298        return sizeAvailableForMoreData;
2299    }
2300
2301    /**
2302     * Checks if in the given list of commands there is data to add with
2303     * the previous data. The command with the data to add is the first in the list.
2304     * If there is a command, merges the previous data with the data contained in
2305     * the command.
2306     * @param clientCommands the command list to check
2307     * @throws ProtocolException if the list not contains a valid Results
2308     */

2309    private void checkForReceivedLargeObject(List clientCommands) throws ProtocolException {
2310        String JavaDoc previousReceivedLargeObject = sessionState.getReceivedLargeObject();
2311
2312        if (previousReceivedLargeObject == null) {
2313            return;
2314        }
2315
2316        //
2317
// There is a large object in cache. The first Results command
2318
// contains data to add to previousReceivedLargeObject
2319
//
2320
ArrayList results = ProtocolUtil.filterCommands((AbstractCommand[])(clientCommands.toArray(new AbstractCommand[0])), Results.class);
2321        if (results.size() == 0) {
2322            throw new ProtocolException("Error awaiting more data from the client - No Results in the request");
2323        }
2324
2325        //
2326
// If there is previousReceivedLargeObject, the first command in the next
2327
// message contains the data to add with the previous data
2328
//
2329
Results result = (Results)results.get(0);
2330        Item item = (Item)result.getItems().get(0);
2331
2332        boolean isReceivedItemWithBinaryData = sync4j.framework.core.Util.isItemWithBinaryData(item);
2333
2334        String JavaDoc receivedData = item.getData().getData();
2335
2336        if (isReceivedItemWithBinaryData) {
2337            //
2338
// If mime type is xml, binary data are managed as string
2339
//
2340
if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
2341                Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
2342                isReceivedItemWithBinaryData = false;
2343            }
2344        }
2345
2346        if (isReceivedItemWithBinaryData) {
2347            byte[] previousData = Base64.decode(previousReceivedLargeObject.getBytes());
2348            byte[] bReceivedData = Base64.decode(receivedData.getBytes());
2349            byte[] newData = new byte[previousData.length + bReceivedData.length];
2350            System.arraycopy(previousData, 0, newData, 0, previousData.length);
2351            System.arraycopy(bReceivedData, 0, newData, previousData.length, bReceivedData.length);
2352            item.getData().setData(new String JavaDoc(Base64.encode(newData)));
2353        } else {
2354            String JavaDoc newData = previousReceivedLargeObject + receivedData;
2355            item.getData().setData(newData);
2356        }
2357        //
2358
// re-add the size of the data in the item because the client sends the size
2359
// only in the first message. So, the ManagementAction can checks the correct
2360
// dimension
2361
//
2362
Meta meta = item.getMeta();
2363        if (meta == null) {
2364            meta = new Meta();
2365            item.setMeta(meta);
2366        }
2367        meta.setSize(sessionState.getSizeOfReceivedLargeObject());
2368    }
2369
2370    /**
2371     * Merge the data contained in the command splitted with the data still
2372     * to send. It is use for rebuild the data in case in which the client
2373     * sends a 401 or 407 code
2374     */

2375    private void mergeData() {
2376        AbstractCommand cmd = sessionState.getSplittedCommand();
2377        //
2378
// Only itemizedCommand with only one item are splittable
2379
//
2380
Item itemSplitted = (Item)(((ItemizedCommand)cmd).getItems()).get(0);
2381        boolean isBinary = sync4j.framework.core.Util.isItemWithBinaryData(itemSplitted);
2382
2383        if (isBinary) {
2384            //
2385
// If mime type is xml, binary data are managed as string
2386
//
2387
if (Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType) ||
2388                Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType)) {
2389                isBinary = false;
2390            }
2391        }
2392
2393        if (isBinary) {
2394            byte[] previousData = Base64.decode(itemSplitted.getData().getData().getBytes());
2395            byte[] nextData = Base64.decode(sessionState.getNextDataToSend().getBytes());
2396            byte[] newData = new byte[previousData.length + nextData.length];
2397            System.arraycopy(previousData, 0, newData, 0, previousData.length);
2398            System.arraycopy(nextData, 0, newData, previousData.length, nextData.length);
2399            itemSplitted.getData().setData(new String JavaDoc(Base64.encode(newData)));
2400        } else {
2401            String JavaDoc previousData = itemSplitted.getData().getData();
2402            String JavaDoc nextData = sessionState.getNextDataToSend();
2403            String JavaDoc newData = previousData + nextData;
2404            itemSplitted.getData().setData(newData);
2405        }
2406    }
2407
2408    /*
2409     * Check if the client MaxMsgSize is greater than server minmsgsize.
2410     * If the client MaxMsgSize is smaller than minmsgsize then change
2411     * the status code of SyncHdr into StatusCode.SYNCHRONIZATION_FAILED.
2412     *
2413     * @param response the SyncML response message
2414     */

2415    private void checkMaxMsgSize(SyncML response) {
2416        if (log.isLoggable(Level.FINEST)) {
2417            log.finest("Check if the MaxMsgSize is larger of the minimal " +
2418                       "size of the messages of the server");
2419        }
2420        long minMsgSize = 0;
2421        if (sessionState.getMaxMsgSize() != 0) {
2422            if (Constants.MIMETYPE_SYNCMLDS_WBXML.equals(mimeType) ||
2423                Constants.MIMETYPE_SYNCMLDM_WBXML.equals(mimeType)) {
2424                minMsgSize = Long.parseLong(engine.getConfiguration().getStringValue(engine.CFG_MIN_MSGSIZE_WBXML));
2425            } else if (Constants.MIMETYPE_SYNCMLDS_XML.equals(mimeType) ||
2426                       Constants.MIMETYPE_SYNCMLDM_XML.equals(mimeType)) {
2427                minMsgSize = Long.parseLong(engine.getConfiguration().getStringValue(engine.CFG_MIN_MSGSIZE_XML));
2428            }
2429            if (sessionState.getMaxMsgSize() < minMsgSize) {
2430                Status statusHdr = (Status)response.getSyncBody().getCommands().get(0);
2431                statusHdr.setData(new Data(StatusCode.COMMAND_FAILED));
2432
2433                if (log.isLoggable(Level.INFO)) {
2434                    log.info("The MaxMsgSize is smaller than minimum size " +
2435                             "that the server response could have!");
2436                    log.info("The server will not answer to some message " +
2437                             "of the client.");
2438                }
2439            }
2440        }
2441    }
2442}
2443
Popular Tags