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
78 const uInt16 SyncML11 = 11; // currently supported SyncML version
79 const uInt16 SyncML10 = 10; // currently supported SyncML version
81 #pragma options align= packed // allow direct mapping of the structure
84 using namespace sysync;
88 // ---- structure definition ---------------------------------------------
89 #define BpB 8 // bits per byte
90 #define NBits 7 // bytes of the notification-hdr bits
91 #define BBits 4 // bytes of the notification-body bits
93 #define DB_Full 420 // memory full error
94 #define DB_Error 510 // general DB error
99 uInt8 bitField[ NBits ]; // version, ui-mode, initiator, future use, sesion id
105 uInt8 bitField[ BBits ]; // sync type, future use, content type
111 // ---- defined locally for the moment to avoid dependencies ----
113 // MD5 and B64 given string
114 static void MD5B64_Local(const char *aString, sInt32 aLen, string &aMD5B64)
116 // determine input length
117 if (aLen<=0) aLen=strlen(aString);
119 md5::SYSYNC_MD5_CTX context;
121 md5::Init (&context);
122 md5::Update (&context, (const uInt8 *)aString,aLen);
123 md5::Final (digest, &context);
124 // b64 encode the MD5 digest
126 char *b64md5=b64::encode(digest,16,&b64md5len);
128 aMD5B64.assign(b64md5,b64md5len);
130 b64::free(b64md5); // return buffer allocated by b64::encode
135 // ---- constructor/destructor -------------------------------------------
136 SanPackage::SanPackage() // constructor
139 CreateEmptyNotificationBody();
141 memset( &fDigest, 0, DigestSize );
143 fUI_Mode = UI_not_specified;
144 fInitiator = Initiator_Server;
152 SanPackage::~SanPackage() // destructor
155 ReleaseNotificationBody();
160 // ---- digest creation --------------------------------------------------
161 TDigestField SanPackage::H( string s )
166 md5::SYSYNC_MD5_CTX context;
167 md5::Init ( &context );
168 md5::Update ( &context, (const uInt8 *)s.c_str(), s.length() );
169 md5::Final( df.b, &context );
174 string SanPackage::B64_H( string s1, string s2 )
176 if (!s2.empty()) s1+= ":" + s2;
177 MD5B64_Local( s1.c_str(), s1.size(), s1 );
182 string SanPackage::B64_H_Notification( void* san, size_t sanSize )
185 const char* v= (const char*)san + DigestSize;
186 size_t nfySize= sanSize - DigestSize;
187 MD5B64_Local( v, nfySize, s );
193 /*! Prepare the SAN record */
194 void SanPackage::PreparePackage( string aB64_H_srvID_pwd,
196 uInt16 aProtocolVersion,
198 Initiator aInitiator,
202 fB64_H_srvID_pwd= aB64_H_srvID_pwd;
204 fProtocolVersion= aProtocolVersion;
206 fInitiator = aInitiator;
207 fSessionID = aSessionID;
212 // if only hashes are available
213 TSyError SanPackage::CreateDigest( const char* b64_h_serverID_password,
215 void* san, size_t sanSize )
217 string s= b64_h_serverID_password;
219 for (int i= 0; i<DigestSize; i++) { // special case for empty digest
220 fDigest.b[ i ]= 0x00;
225 s+= B64_H_Notification( san,sanSize );
233 TSyError SanPackage::CreateDigest( const char* aServerID,
234 const char* aPassword,
236 void* san, size_t sanSize )
238 return CreateDigest( B64_H( aServerID,aPassword ).c_str(),
245 bool SanPackage::DigestOK( void* san )
247 TDigestField* sanD= (TDigestField*)san;
249 for (int i= 0; i<DigestSize; i++) {
250 if (fDigest.b[ i ]!=sanD->b[ i ]) return false;
258 // ---- bit operations ---------------------------------------------------
259 void SanPackage::AddBits( void* ptr, int pos, int n, uInt32 value )
263 if (lim>BpB*NBits) return; // check if within the field
264 while (lim>BpB) { b++; lim-= BpB; }
267 for (i=0; i<n; i++) {
268 uInt8 db= 1<<(BpB-lim);
270 if ((value % 2)==1) *b|= db; // add bit
271 else *b&= ~db; // remove bit
275 if (lim==0) { lim= BpB; b--; }
280 uInt32 SanPackage::GetBits( void* ptr, int pos, int n )
286 if (lim>BpB*NBits) return 0; // check if within the field
287 while (lim>BpB) { b++; lim-= BpB; }
290 for (i=0; i<n; i++) {
291 uInt8 db= 1<<(BpB-lim);
293 if ((*b & db)!=0) value|= (1<<n); // check bit and add it to <value>
297 if (lim==0) { lim= BpB; b--; }
305 // ---- notification body generation -------------------------------------
306 void SanPackage::CreateEmptyNotificationBody()
308 ReleaseNotificationBody();
310 fEmpty= 0x00; // no sync fields = ALL data stores concerned
312 fBodySize= sizeof(fEmpty);
314 } // CreateEmptyNotificationBody
318 TSyError SanPackage::AddSync( int syncType, uInt32 contentType,
319 const char* serverURI )
321 int len= strlen(serverURI);
322 int nLen= BBits + 1 + len; // length of the new part
323 int newLen= fBodySize + nLen; // total length of the new block
325 void* fb= malloc( newLen ); // allocate it
326 memcpy( fb, fBody,fBodySize ); // copy existing structure to beginning
329 b+= fBodySize; // get a pointer to the new part
331 ReleaseNotificationBody(); // release the old structure
332 fNSync++; // adapt number of available parts
333 fBody = fb; // now the new bigger structure is assigned
336 // fill in new counter value
337 AddBits( fBody, 0, 4, fNSync ); // number of sync datastores
338 AddBits( fBody, 4, 4, 0 ); // future use
340 // fill in contents of the nth structure
341 TBody* tb= (TBody*)b;
342 AddBits( tb->bitField, 0, 4, syncType-200 ); // the sync type 206..210
343 AddBits( tb->bitField, 4, 4, 0 ); // future use
344 AddBits( tb->bitField, 8,24, contentType ); // the content tye
345 tb->serverURI_len= len;
347 byte* pp= (byte*)(tb+1); // = right after TBody
348 memcpy( (void*) pp, (void*)serverURI, len );
354 void SanPackage::ReleaseNotificationBody()
357 fBody!=&fEmpty) { free( fBody ); fBody= NULL; }
358 } // ReleaseNotificationBody
361 #ifndef WITHOUT_SAN_1_1
362 // general callback entry for all others
363 static Ret_t univ( ... )
365 //printf( "callback\n" );
370 static Ret_t startM( InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent )
372 cAppCharP Sy= "SyncML/";
373 size_t n = strlen(Sy);
375 SanPackage* a= (SanPackage*)userData;
379 uInt16 major=0,minor=0;
381 cAppCharP verP = smlPCDataToCharP(pContent->proto);
382 if (strucmp(verP,Sy,n)==0) {
383 n+=StrToUShort(verP+n,major);
386 StrToUShort(verP+n,minor);
391 smlPCDataToLong( pContent->sessionID, sessionID );
393 string srvID= smlSrcTargLocURIToCharP(pContent->source);
395 a->PreparePackage( mup, nonce, 10*major+minor, UI_not_specified, Initiator_Server, sessionID, srvID );
396 a->CreateEmptyNotificationBody();
401 static Ret_t alertM( InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent )
403 SanPackage* a= (SanPackage*)userData;
406 smlPCDataToLong( pContent->data, syncType );
407 uInt32 contentType= 0; // always 0
409 SmlItemListPtr_t el= pContent->itemList;
411 while (true) { // can be a chained list of elements
412 string locURI= smlSrcTargLocURIToCharP(el->item->source);
413 a->AddSync( syncType, contentType, locURI.c_str() ); // for each element add one
415 if (el->next==NULL) break;
419 //printf( "alert\n" );
424 static Ret_t endM( InstanceID_t id, VoidPtr_t userData, Boolean_t final )
431 // Callback record, most of the routines are not used
432 static const SmlCallbacks_t mySmlCallbacks = {
433 /* message callbacks */
434 startM, // smlStartMessageCallback,
435 endM, // smlEndMessageCallback,
436 /* grouping commands */
437 (smlStartSyncFunc) univ, // smlStartSyncCallback,
438 (smlEndSyncFunc) univ, // smlEndSyncCallback,
439 #ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
440 univ, // smlStartAtomicCallback,
441 univ, // smlEndAtomicCallback,
443 #ifdef SEQUENCE_RECEIVE
444 univ, // smlStartSequenceCallback,
445 univ, // smlEndSequenceCallback,
448 (smlAddCmdFunc) univ, // smlAddCmdCallback,
449 alertM, // smlAlertCmdCallback,
450 (smlDeleteCmdFunc)univ, // smlDeleteCmdCallback,
451 (smlGetCmdFunc) univ, // smlGetCmdCallback,
452 (smlPutCmdFunc) univ, // smlPutCmdCallback,
454 (smlMapCmdFunc) univ, // smlMapCmdCallback,
456 #ifdef RESULT_RECEIVE
457 (smlResultsCmdFunc)univ, // smlResultsCmdCallback,
459 (smlStatusCmdFunc) univ, // smlStatusCmdCallback,
460 (smlReplaceCmdFunc)univ, // smlReplaceCmdCallback,
462 #ifdef COPY_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
463 univ, // smlCopyCmdCallback,
466 univ, // smlExecCmdCallback,
468 #ifdef SEARCH_RECEIVE
469 univ, // smlSearchCmdCallback,
471 smlMoveCmdFunc(univ), // smlMoveCmdCallback,
472 /* Other Callbacks */
473 smlHandleErrorFunc (univ), // smlHandleErrorCallback,
474 smlTransmitChunkFunc(univ) // smlTransmitChunkCallback
475 }; /* sml_callbacks struct */
479 // Try to convert a 1.1 message
480 // - if successful, fill in values into 1.2 fields
481 // - if not successful, interpret it as 1.2 structure
482 TSyError SanPackage::Check_11( void* san, size_t sanSize )
485 SmlCallbacks_t scb= mySmlCallbacks;
486 SmlInstanceOptions_t sIOpts;
492 // struct assignment / 1k buffer
493 sIOpts.encoding = SML_WBXML; // it is always WBXML
494 sIOpts.workspaceSize = 1024*30; // should be always sufficient
495 sIOpts.maxOutgoingSize= 0; // disabled for now
497 err= smlInitInstance( &scb, &sIOpts, this, &id ); if (err) return err;
500 err= smlLockWriteBuffer ( id, &wPos, &freeSize ); if (err) break;
502 memcpy( wPos, san,sanSize ); // now we have a new internal copy
503 err= smlUnlockWriteBuffer( id, sanSize ); if (err) break;
504 err= smlProcessData ( id, SML_ALL_COMMANDS ); if (err) break;
507 cer= smlTerminateInstance( id ); if (!err) err= cer;
511 #endif // WITHOUT_SAN_1_1
514 TSyError SanPackage::PassSan( void* san, size_t sanSize, int mode)
516 TSyError err = LOCERR_OK;
517 bool use_as_12= true;
520 //printf( "here we will have the potential 1.1 -> 1.2 conversion\n" );
522 #ifndef WITHOUT_SAN_1_1
523 if (mode == 0 || mode == 1) {
524 err= Check_11 ( san,sanSize );
525 if (!err) err= GetPackage( san,sanSize );
526 //use_as_12= err==SML_ERR_XLT_INCOMP_WBXML_VERS;
528 //printf( "err=%d\n", err );
532 if (use_as_12 && mode !=1) {
535 fSan= malloc( sanSize );
538 memcpy( fSan, san,sanSize ); // now we have a new internal copy
547 TSyError SanPackage::GetSanSize( void* san, size_t &sanSize )
549 TPackage* tp= (TPackage*)san;
552 byte* b= (byte*)(tp+1);
555 b+= tp->serverID_len;
557 int nth= GetBits( b, 0,4 ); // first not valid = the end
559 b++; // start of 1st element
566 if (b > (byte*)san+sanSize && sanSize>0) return DB_Forbidden;
567 v= b + tb->serverURI_len;
568 if (b > (byte*)san+sanSize && sanSize>0) return DB_Forbidden;
574 b+= tb->serverURI_len; // finally the serverURI length
576 size_t rslt= b - (byte*)san;
577 if (sanSize>0 && sanSize<rslt) return DB_Forbidden;
584 // ---- notification body parsing ----------------------------------------
585 TSyError SanPackage::GetNthSync( int nth,
590 syncType = 0; // set default values
594 TPackage* tp= (TPackage*)fSan;
597 fDigest = tp->digest;
598 fProtocolVersion= GetBits( tp->bitField, 0,10 );
599 fUI_Mode = (UI_Mode)GetBits( tp->bitField, 10, 2 );
600 fInitiator = (Initiator)GetBits( tp->bitField, 12, 1 );
601 fSessionID = GetBits( tp->bitField, 40,16 );
603 /*If the version does not match, this should be an invalid SAN message*/
604 if (fProtocolVersion!=SyncML12 &&
605 fProtocolVersion!=SyncML11 &&
606 fProtocolVersion!=SyncML10) return DB_Forbidden;
608 byte* b= (byte*)(tp+1);
611 fServerID.assign( (const char*)b,(unsigned int)tp->serverID_len );
612 b+= tp->serverID_len;
614 fNSync= GetBits( b, 0,4 );
616 if (nth==0) return LOCERR_OK;
617 if (nth<1 || nth>fNSync ) return DB_NotFound;
619 b++; // start of 1st element
626 if (b > (byte*)fSan+fSanSize) return DB_Forbidden; // no access behind the message
627 v= b + tb->serverURI_len;
628 if (v > (byte*)fSan+fSanSize) return DB_Forbidden; // no access behind the message
634 syncType = 200 + GetBits( tb->bitField, 0, 4 );
635 contentType= GetBits( tb->bitField, 8,24 );
637 serverURI.assign( (const char*)b,(unsigned int)tb->serverURI_len );
643 TSyError SanPackage::GetHeader()
645 int syncType; // these 3 variables are not really used
649 return GetNthSync( 0, syncType,contentType,serverURI );
654 // ---- package generation -----------------------------------------------
655 TSyError SanPackage::GetPackage( void* &san, size_t &sanSize,
656 void* vendorSpecific,
657 size_t vendorSpecificSize )
659 ReleasePackage(); // remove a previous one
661 byte len = (byte)fServerID.length(); // calulate the full size
662 sanSize= sizeof(TPackage) + len + fBodySize + vendorSpecificSize;
663 //size_t nfySize= sanSize - DigestSize;
664 fSan = malloc( sanSize );
666 TPackage* tp= (TPackage*)fSan;
668 // -------------------
669 AddBits( tp->bitField, 0,10, fProtocolVersion );
670 AddBits( tp->bitField, 10, 2, fUI_Mode );
671 AddBits( tp->bitField, 12, 1, fInitiator );
672 AddBits( tp->bitField, 13,27, 0 ); // future use, must be "0"
673 AddBits( tp->bitField, 40,16, fSessionID );
674 tp->serverID_len= len;
676 // copy <fServerID> string at the end of TPackage struct
677 byte* pp= (byte*)(tp+1); // = right after TPackage
678 memcpy( (void*) pp, (void*)fServerID.c_str(), len );
679 memcpy( (void*)(pp+len), fBody, fBodySize );
681 if (vendorSpecific!=NULL &&
682 vendorSpecificSize>0)
683 memcpy( (void*)(pp+len+fBodySize), vendorSpecific,vendorSpecificSize );
685 CreateDigest( fB64_H_srvID_pwd.c_str(), fNonce.c_str(), san,sanSize );
693 void SanPackage::ReleasePackage() {
694 if (fSan!=NULL) { free( fSan ); fSan= NULL; }
697 #ifndef WITHOUT_SAN_1_1
699 const char * const SyncMLVerProtoNames[] =
707 const char *const SyncMLVerDTDNames[] =
715 const SmlVersion_t SmlVersionCodes[] =
723 TSyError SanPackage::GetPackageLegacy( void* &san,
725 const vector<pair <string, string> >& sources,
729 ReleasePackage(); // remove a previous one
731 SmlCallbacks_t scb= mySmlCallbacks;
732 SmlInstanceOptions_t sIOpts;
736 // struct assignment / 1k buffer
737 sIOpts.encoding = wbxml ? SML_WBXML : SML_XML;
738 sIOpts.workspaceSize = 1024; // should be always sufficient
739 sIOpts.maxOutgoingSize= 0; // disabled for now
741 err= smlInitInstance( &scb, &sIOpts, this, &id ); if (err) return err;
743 SmlSyncHdrPtr_t headerP = NULL;
744 SmlAlertPtr_t alertP = NULL;
749 headerP = SML_NEW (SmlSyncHdr_t);
750 headerP->elementType = SML_PE_HEADER;
751 if (fProtocolVersion != 10 && fProtocolVersion != 11){
756 int version = fProtocolVersion - 10 + 1;
757 headerP->version = newPCDataString (SyncMLVerDTDNames[version]);
758 headerP->proto = newPCDataString (SyncMLVerProtoNames[version]);
759 headerP->sessionID = newPCDataLong (fSessionID);
760 headerP->msgID = newPCDataString ("1");
761 headerP->target = newLocation ("/", "");
762 headerP->source = newLocation (fServerID.c_str(), "");
763 headerP->respURI = NULL;
764 headerP->meta = NULL;
766 //TODO generate the cred element for authentication
767 headerP->cred = NULL;
770 err = smlStartMessageExt (id, headerP, SmlVersionCodes[version]); if (err) break;
771 //create Alert Commands
772 //internal Alert element
773 alertP = SML_NEW (SmlAlert_t);
774 alertP->elementType = SML_PE_ALERT;
775 alertP->cmdID = newPCDataLong(1);
777 alertP->data = newPCDataLong (alertCode);
779 alertP->itemList = NULL;
782 //for each source, add a item
783 for (unsigned int num =0; num < sources.size(); num++) {
784 SmlItemPtr_t alertItemP = newItem();
785 alertItemP->source = newOptLocation (sources[num].second.c_str());
786 alertItemP->meta = newMetaType (sources[num].first.c_str());
787 addItemToList (alertItemP, &alertP->itemList);
790 err = smlAlertCmd (id, alertP); if (err) break;
791 err = smlEndMessage (id, true); if (err) break;
794 err = smlLockReadBuffer (id, (MemPtr_t *) &buf, (MemSize_t *)&sanSize); if (err) break;
795 fSan = malloc( sanSize ); if (!fSan) {err = DB_Full; break;}
797 memcpy (san, buf, sanSize);
798 err = smlUnlockReadBuffer (id, sanSize); if (err) break;
801 smlFreeProtoElement (headerP);
805 smlFreeProtoElement (alertP);
812 smlFreeProtoElement (headerP);
815 smlFreeProtoElement (alertP);
819 err = smlTerminateInstance( id );
824 } // namespace sysync