4 * Author: Beat Forster (bfo@synthesis.ch)
6 * Server Alerted Notification
9 * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
15 128 bit 64 bit + n char
16 +------------+--------------------+--------------------+
18 | digest | notification-hdr | notification-body |
20 +------------+--------------------+--------------------+
24 -------------------- ------ \ \
27 +---------+---------+-----------+--------+---------+--------+--------+ +----------+
28 | version | ui-mode | initiator | future | session | server | server | | usage |
29 | | | | use | id | ident | ident | | specific |
30 | | | | | | length | | | |
31 +---------+---------+-----------+--------+---------+--------+--------+ +----------+
32 10 bit 2 bit 1 bit 27 bit 16 bit 8 bit n char
35 H = MD5 hashing function
37 digest= H(B64(H(server-identifier:password)):nonce:B64(H(notification)))
42 +-------+--------+--------+--------+----------+
43 | num | future | sync 1 | sync N | vendor |
44 | syncs | use | | | specific |
46 +-------+--------+--------+--------+----------+
47 4 bit 4 bit / \ n char
52 +------+--------+---------+--------+--------+
53 | sync | future | content | server | server |
54 | type | use | type | URI | URI |
56 +------+--------+---------+--------+--------+
57 4 bit 4 bit 24 bit 8 bit n char
66 #include "prefix_file.h"
67 #include "sync_include.h"
69 #include "sysync_md5.h"
70 #include "sysync_b64.h"
72 #ifndef WITHOUT_SAN_1_1
73 #include "sysync_utils.h"
77 const uInt16 SyncML12 = 12; // currently supported SyncML version
79 #pragma options align= packed // allow direct mapping of the structure
82 using namespace sysync;
86 // ---- structure definition ---------------------------------------------
87 #define BpB 8 // bits per byte
88 #define NBits 7 // bytes of the notification-hdr bits
89 #define BBits 4 // bytes of the notification-body bits
91 #define DB_Full 420 // memory full error
92 #define DB_Error 510 // general DB error
97 uInt8 bitField[ NBits ]; // version, ui-mode, initiator, future use, sesion id
103 uInt8 bitField[ BBits ]; // sync type, future use, content type
109 // ---- defined locally for the moment to avoid dependencies ----
110 // - sysync_free replaced by free
112 // MD5 and B64 given string
113 static void MD5B64_Local(const char *aString, sInt32 aLen, string &aMD5B64)
115 // determine input length
116 if (aLen<=0) aLen=strlen(aString);
118 md5::SYSYNC_MD5_CTX context;
120 md5::Init (&context);
121 md5::Update (&context, (const uInt8 *)aString,aLen);
122 md5::Final (digest, &context);
123 // b64 encode the MD5 digest
125 char *b64md5=b64::encode(digest,16,&b64md5len);
127 aMD5B64.assign(b64md5,b64md5len);
129 /*sysync_*/free(b64md5); // return buffer allocated by b64::encode
134 // ---- constructor/destructor -------------------------------------------
135 SanPackage::SanPackage() // constructor
138 CreateEmptyNotificationBody();
140 memset( &fDigest, 0, DigestSize );
142 fUI_Mode = UI_not_specified;
143 fInitiator = Initiator_Server;
151 SanPackage::~SanPackage() // destructor
154 ReleaseNotificationBody();
159 // ---- digest creation --------------------------------------------------
160 TDigestField SanPackage::H( string s )
165 md5::SYSYNC_MD5_CTX context;
166 md5::Init ( &context );
167 md5::Update ( &context, (const uInt8 *)s.c_str(), s.length() );
168 md5::Final( df.b, &context );
173 string SanPackage::B64_H( string s1, string s2 )
175 if (!s2.empty()) s1+= ":" + s2;
176 MD5B64_Local( s1.c_str(), s1.size(), s1 );
181 string SanPackage::B64_H_Notification( void* san, size_t sanSize )
184 const char* v= (const char*)san + DigestSize;
185 size_t nfySize= sanSize - DigestSize;
186 MD5B64_Local( v, nfySize, s );
192 /*! Prepare the SAN record */
193 void SanPackage::PreparePackage( string aB64_H_srvID_pwd,
195 uInt16 aProtocolVersion,
197 Initiator aInitiator,
201 fB64_H_srvID_pwd= aB64_H_srvID_pwd;
203 fProtocolVersion= aProtocolVersion;
205 fInitiator = aInitiator;
206 fSessionID = aSessionID;
211 // if only hashes are available
212 TSyError SanPackage::CreateDigest( const char* b64_h_serverID_password,
214 void* san, size_t sanSize )
216 string s= b64_h_serverID_password;
218 for (int i= 0; i<DigestSize; i++) { // special case for empty digest
219 fDigest.b[ i ]= 0x00;
224 s+= B64_H_Notification( san,sanSize );
232 TSyError SanPackage::CreateDigest( const char* aServerID,
233 const char* aPassword,
235 void* san, size_t sanSize )
237 return CreateDigest( B64_H( aServerID,aPassword ).c_str(),
244 bool SanPackage::DigestOK( void* san )
246 TDigestField* sanD= (TDigestField*)san;
248 for (int i= 0; i<DigestSize; i++) {
249 if (fDigest.b[ i ]!=sanD->b[ i ]) return false;
257 // ---- bit operations ---------------------------------------------------
258 void SanPackage::AddBits( void* ptr, int pos, int n, uInt32 value )
262 if (lim>BpB*NBits) return; // check if within the field
263 while (lim>BpB) { b++; lim-= BpB; }
266 for (i=0; i<n; i++) {
267 uInt8 db= 1<<(BpB-lim);
269 if ((value % 2)==1) *b|= db; // add bit
270 else *b&= ~db; // remove bit
274 if (lim==0) { lim= BpB; b--; }
279 uInt32 SanPackage::GetBits( void* ptr, int pos, int n )
285 if (lim>BpB*NBits) return 0; // check if within the field
286 while (lim>BpB) { b++; lim-= BpB; }
289 for (i=0; i<n; i++) {
290 uInt8 db= 1<<(BpB-lim);
292 if ((*b & db)!=0) value|= (1<<n); // check bit and add it to <value>
296 if (lim==0) { lim= BpB; b--; }
304 // ---- notification body generation -------------------------------------
305 void SanPackage::CreateEmptyNotificationBody()
307 ReleaseNotificationBody();
309 fEmpty= 0x00; // no sync fields = ALL data stores concerned
311 fBodySize= sizeof(fEmpty);
313 } // CreateEmptyNotificationBody
317 TSyError SanPackage::AddSync( int syncType, uInt32 contentType,
318 const char* serverURI )
320 int len= strlen(serverURI);
321 int nLen= BBits + 1 + len; // length of the new part
322 int newLen= fBodySize + nLen; // total length of the new block
324 void* fb= malloc( newLen ); // allocate it
325 memcpy( fb, fBody,fBodySize ); // copy existing structure to beginning
328 b+= fBodySize; // get a pointer to the new part
330 ReleaseNotificationBody(); // release the old structure
331 fNSync++; // adapt number of available parts
332 fBody = fb; // now the new bigger structure is assigned
335 // fill in new counter value
336 AddBits( fBody, 0, 4, fNSync ); // number of sync datastores
337 AddBits( fBody, 4, 4, 0 ); // future use
339 // fill in contents of the nth structure
340 TBody* tb= (TBody*)b;
341 AddBits( tb->bitField, 0, 4, syncType-200 ); // the sync type 206..210
342 AddBits( tb->bitField, 4, 4, 0 ); // future use
343 AddBits( tb->bitField, 8,24, contentType ); // the content tye
344 tb->serverURI_len= len;
346 byte* pp= (byte*)(tb+1); // = right after TBody
347 memcpy( (void*) pp, (void*)serverURI, len );
353 void SanPackage::ReleaseNotificationBody()
356 fBody!=&fEmpty) { free( fBody ); fBody= NULL; }
357 } // ReleaseNotificationBody
360 #ifndef WITHOUT_SAN_1_1
361 // general callback entry for all others
362 static Ret_t univ( ... )
364 //printf( "callback\n" );
369 static Ret_t startM( InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent )
371 cAppCharP Sy= "SyncML/";
372 size_t n = strlen(Sy);
374 SanPackage* a= (SanPackage*)userData;
378 uInt16 major=0,minor=0;
380 cAppCharP verP = smlPCDataToCharP(pContent->proto);
381 if (strucmp(verP,Sy,n)==0) {
382 n+=StrToUShort(verP+n,major);
385 StrToUShort(verP+n,minor);
390 smlPCDataToLong( pContent->sessionID, sessionID );
392 string srvID= smlSrcTargLocURIToCharP(pContent->source);
394 a->PreparePackage( mup, nonce, 10*major+minor, UI_not_specified, Initiator_Server, sessionID, srvID );
395 a->CreateEmptyNotificationBody();
400 static Ret_t alertM( InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent )
402 SanPackage* a= (SanPackage*)userData;
405 smlPCDataToLong( pContent->data, syncType );
406 uInt32 contentType= 0; // always 0
408 SmlItemListPtr_t el= pContent->itemList;
410 while (true) { // can be a chained list of elements
411 string locURI= smlSrcTargLocURIToCharP(el->item->source);
412 a->AddSync( syncType, contentType, locURI.c_str() ); // for each element add one
414 if (el->next==NULL) break;
418 //printf( "alert\n" );
423 static Ret_t endM( InstanceID_t id, VoidPtr_t userData, Boolean_t final )
430 // Callback record, most of the routines are not used
431 static const SmlCallbacks_t mySmlCallbacks = {
432 /* message callbacks */
433 startM, // smlStartMessageCallback,
434 endM, // smlEndMessageCallback,
435 /* grouping commands */
436 (smlStartSyncFunc) univ, // smlStartSyncCallback,
437 (smlEndSyncFunc) univ, // smlEndSyncCallback,
438 #ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
439 univ, // smlStartAtomicCallback,
440 univ, // smlEndAtomicCallback,
442 #ifdef SEQUENCE_RECEIVE
443 univ, // smlStartSequenceCallback,
444 univ, // smlEndSequenceCallback,
447 (smlAddCmdFunc) univ, // smlAddCmdCallback,
448 alertM, // smlAlertCmdCallback,
449 (smlDeleteCmdFunc)univ, // smlDeleteCmdCallback,
450 (smlGetCmdFunc) univ, // smlGetCmdCallback,
451 (smlPutCmdFunc) univ, // smlPutCmdCallback,
453 (smlMapCmdFunc) univ, // smlMapCmdCallback,
455 #ifdef RESULT_RECEIVE
456 (smlResultsCmdFunc)univ, // smlResultsCmdCallback,
458 (smlStatusCmdFunc) univ, // smlStatusCmdCallback,
459 (smlReplaceCmdFunc)univ, // smlReplaceCmdCallback,
461 #ifdef COPY_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
462 univ, // smlCopyCmdCallback,
465 univ, // smlExecCmdCallback,
467 #ifdef SEARCH_RECEIVE
468 univ, // smlSearchCmdCallback,
470 smlMoveCmdFunc(univ), // smlMoveCmdCallback,
471 /* Other Callbacks */
472 smlHandleErrorFunc (univ), // smlHandleErrorCallback,
473 smlTransmitChunkFunc(univ) // smlTransmitChunkCallback
474 }; /* sml_callbacks struct */
478 // Try to convert a 1.1 message
479 // - if successful, fill in values into 1.2 fields
480 // - if not successful, interpret it as 1.2 structure
481 TSyError SanPackage::Check_11( void* san, size_t sanSize )
484 SmlCallbacks_t scb= mySmlCallbacks;
485 SmlInstanceOptions_t sIOpts;
491 // struct assignment / 1k buffer
492 sIOpts.encoding = SML_WBXML; // it is always WBXML
493 sIOpts.workspaceSize = 1024; // should be always sufficient
494 sIOpts.maxOutgoingSize= 0; // disabled for now
496 err= smlInitInstance( &scb, &sIOpts, this, &id ); if (err) return err;
499 err= smlLockWriteBuffer ( id, &wPos, &freeSize ); if (err) break;
501 memcpy( wPos, san,sanSize ); // now we have a new internal copy
502 err= smlUnlockWriteBuffer( id, sanSize ); if (err) break;
503 err= smlProcessData ( id, SML_ALL_COMMANDS ); if (err) break;
506 cer= smlTerminateInstance( id ); if (!err) err= cer;
510 #endif // WITHOUT_SAN_1_1
513 TSyError SanPackage::PassSan( void* san, size_t sanSize )
516 bool use_as_12= true;
519 //printf( "here we will have the potential 1.1 -> 1.2 conversion\n" );
521 #ifndef WITHOUT_SAN_1_1
522 err= Check_11 ( san,sanSize );
523 if (!err) err= GetPackage( san,sanSize );
524 //use_as_12= err==SML_ERR_XLT_INCOMP_WBXML_VERS;
526 //printf( "err=%d\n", err );
532 fSan= malloc( sanSize );
535 memcpy( fSan, san,sanSize ); // now we have a new internal copy
544 TSyError SanPackage::GetSanSize( void* san, size_t &sanSize )
546 TPackage* tp= (TPackage*)san;
549 byte* b= (byte*)(tp+1);
552 b+= tp->serverID_len;
554 int nth= GetBits( b, 0,4 ); // first not valid = the end
556 b++; // start of 1st element
563 if (b > (byte*)san+sanSize && sanSize>0) return DB_Forbidden;
564 v= b + tb->serverURI_len;
565 if (b > (byte*)san+sanSize && sanSize>0) return DB_Forbidden;
571 b+= tb->serverURI_len; // finally the serverURI length
573 size_t rslt= b - (byte*)san;
574 if (sanSize>0 && sanSize<rslt) return DB_Forbidden;
581 // ---- notification body parsing ----------------------------------------
582 TSyError SanPackage::GetNthSync( int nth,
587 syncType = 0; // set default values
591 TPackage* tp= (TPackage*)fSan;
594 fDigest = tp->digest;
595 fProtocolVersion= GetBits( tp->bitField, 0,10 );
596 fUI_Mode = (UI_Mode)GetBits( tp->bitField, 10, 2 );
597 fInitiator = (Initiator)GetBits( tp->bitField, 12, 1 );
598 fSessionID = GetBits( tp->bitField, 40,16 );
600 // that's the joke, it's no longer forbidden !
601 //if (fProtocolVersion!=SyncML12) return DB_Forbidden;
603 byte* b= (byte*)(tp+1);
606 fServerID.assign( (const char*)b,(unsigned int)tp->serverID_len );
607 b+= tp->serverID_len;
609 fNSync= GetBits( b, 0,4 );
611 if (nth==0) return LOCERR_OK;
612 if (nth<1 || nth>fNSync ) return DB_NotFound;
614 b++; // start of 1st element
621 if (b > (byte*)fSan+fSanSize) return DB_Forbidden; // no access behind the message
622 v= b + tb->serverURI_len;
623 if (v > (byte*)fSan+fSanSize) return DB_Forbidden; // no access behind the message
629 syncType = 200 + GetBits( tb->bitField, 0, 4 );
630 contentType= GetBits( tb->bitField, 8,24 );
632 serverURI.assign( (const char*)b,(unsigned int)tb->serverURI_len );
638 TSyError SanPackage::GetHeader()
640 int syncType; // these 3 variables are not really used
644 return GetNthSync( 0, syncType,contentType,serverURI );
649 // ---- package generation -----------------------------------------------
650 TSyError SanPackage::GetPackage( void* &san, size_t &sanSize,
651 void* vendorSpecific,
652 size_t vendorSpecificSize )
654 ReleasePackage(); // remove a previous one
656 byte len = (byte)fServerID.length(); // calulate the full size
657 sanSize= sizeof(TPackage) + len + fBodySize + vendorSpecificSize;
658 //size_t nfySize= sanSize - DigestSize;
659 fSan = malloc( sanSize );
661 TPackage* tp= (TPackage*)fSan;
663 // -------------------
664 AddBits( tp->bitField, 0,10, fProtocolVersion );
665 AddBits( tp->bitField, 10, 2, fUI_Mode );
666 AddBits( tp->bitField, 12, 1, fInitiator );
667 AddBits( tp->bitField, 13,27, 0 ); // future use, must be "0"
668 AddBits( tp->bitField, 40,16, fSessionID );
669 tp->serverID_len= len;
671 // copy <fServerID> string at the end of TPackage struct
672 byte* pp= (byte*)(tp+1); // = right after TPackage
673 memcpy( (void*) pp, (void*)fServerID.c_str(), len );
674 memcpy( (void*)(pp+len), fBody, fBodySize );
676 if (vendorSpecific!=NULL &&
677 vendorSpecificSize>0)
678 memcpy( (void*)(pp+len+fBodySize), vendorSpecific,vendorSpecificSize );
680 CreateDigest( fB64_H_srvID_pwd.c_str(), fNonce.c_str(), san,sanSize );
688 void SanPackage::ReleasePackage() {
689 if (fSan!=NULL) { free( fSan ); fSan= NULL; }
693 } // namespace sysync