#include "dmap_parser.h" #include #include #include #include #define DMAP_STRINGIFY_(x) #x #define DMAP_STRINGIFY(x) DMAP_STRINGIFY_(x) typedef enum { DMAP_UNKNOWN, DMAP_UINT, DMAP_INT, DMAP_STR, DMAP_DATA, DMAP_DATE, DMAP_VERS, DMAP_DICT, DMAP_ITEM } DMAP_TYPE; typedef struct { /** * The four-character code used in the encoded message. */ const char *code; /** * The type of data associated with the content code. */ DMAP_TYPE type; /** * For listings, the type of their listing item children. * * Listing items (mlit) can be of any type, and as with other content codes * their type information is not encoded in the message. Parsers must * determine the type of the listing items based on their parent context. */ DMAP_TYPE list_item_type; /** * A human-readable name for the content code. */ const char *name; } dmap_field; static const dmap_field dmap_fields[] = { { "abal", DMAP_DICT, DMAP_STR, "daap.browsealbumlisting" }, { "abar", DMAP_DICT, DMAP_STR, "daap.browseartistlisting" }, { "abcp", DMAP_DICT, DMAP_STR, "daap.browsecomposerlisting" }, { "abgn", DMAP_DICT, DMAP_STR, "daap.browsegenrelisting" }, { "abpl", DMAP_UINT, 0, "daap.baseplaylist" }, { "abro", DMAP_DICT, 0, "daap.databasebrowse" }, { "adbs", DMAP_DICT, 0, "daap.databasesongs" }, { "aeAD", DMAP_DICT, 0, "com.apple.itunes.adam-ids-array" }, { "aeAI", DMAP_UINT, 0, "com.apple.itunes.itms-artistid" }, { "aeCD", DMAP_DATA, 0, "com.apple.itunes.flat-chapter-data" }, { "aeCF", DMAP_UINT, 0, "com.apple.itunes.cloud-flavor-id" }, { "aeCI", DMAP_UINT, 0, "com.apple.itunes.itms-composerid" }, { "aeCK", DMAP_UINT, 0, "com.apple.itunes.cloud-library-kind" }, { "aeCM", DMAP_UINT, 0, "com.apple.itunes.cloud-match-type" }, { "aeCR", DMAP_STR, 0, "com.apple.itunes.content-rating" } , { "aeCS", DMAP_UINT, 0, "com.apple.itunes.artworkchecksum" }, { "aeCU", DMAP_UINT, 0, "com.apple.itunes.cloud-user-id" }, { "aeCd", DMAP_UINT, 0, "com.apple.itunes.cloud-id" }, { "aeDE", DMAP_STR, 0, "com.apple.itunes.longest-content-description" }, { "aeDL", DMAP_UINT, 0, "com.apple.itunes.drm-downloader-user-id" }, { "aeDP", DMAP_UINT, 0, "com.apple.itunes.drm-platform-id" }, { "aeDR", DMAP_UINT, 0, "com.apple.itunes.drm-user-id" }, { "aeDV", DMAP_UINT, 0, "com.apple.itunes.drm-versions" }, { "aeEN", DMAP_STR, 0, "com.apple.itunes.episode-num-str" }, { "aeES", DMAP_UINT, 0, "com.apple.itunes.episode-sort" }, { "aeFA", DMAP_UINT, 0, "com.apple.itunes.drm-family-id" }, { "aeGD", DMAP_UINT, 0, "com.apple.itunes.gapless-enc-dr" } , { "aeGE", DMAP_UINT, 0, "com.apple.itunes.gapless-enc-del" }, { "aeGH", DMAP_UINT, 0, "com.apple.itunes.gapless-heur" }, { "aeGI", DMAP_UINT, 0, "com.apple.itunes.itms-genreid" }, { "aeGR", DMAP_UINT, 0, "com.apple.itunes.gapless-resy" }, { "aeGU", DMAP_UINT, 0, "com.apple.itunes.gapless-dur" }, { "aeGs", DMAP_UINT, 0, "com.apple.itunes.can-be-genius-seed" }, { "aeHC", DMAP_UINT, 0, "com.apple.itunes.has-chapter-data" }, { "aeHD", DMAP_UINT, 0, "com.apple.itunes.is-hd-video" }, { "aeHV", DMAP_UINT, 0, "com.apple.itunes.has-video" }, { "aeK1", DMAP_UINT, 0, "com.apple.itunes.drm-key1-id" }, { "aeK2", DMAP_UINT, 0, "com.apple.itunes.drm-key2-id" }, { "aeMC", DMAP_UINT, 0, "com.apple.itunes.playlist-contains-media-type-count" }, { "aeMK", DMAP_UINT, 0, "com.apple.itunes.mediakind" }, { "aeMX", DMAP_STR, 0, "com.apple.itunes.movie-info-xml" }, { "aeMk", DMAP_UINT, 0, "com.apple.itunes.extended-media-kind" }, { "aeND", DMAP_UINT, 0, "com.apple.itunes.non-drm-user-id" }, { "aeNN", DMAP_STR, 0, "com.apple.itunes.network-name" }, { "aeNV", DMAP_UINT, 0, "com.apple.itunes.norm-volume" }, { "aePC", DMAP_UINT, 0, "com.apple.itunes.is-podcast" }, { "aePI", DMAP_UINT, 0, "com.apple.itunes.itms-playlistid" }, { "aePP", DMAP_UINT, 0, "com.apple.itunes.is-podcast-playlist" }, { "aePS", DMAP_UINT, 0, "com.apple.itunes.special-playlist" }, { "aeRD", DMAP_UINT, 0, "com.apple.itunes.rental-duration" }, { "aeRP", DMAP_UINT, 0, "com.apple.itunes.rental-pb-start" }, { "aeRS", DMAP_UINT, 0, "com.apple.itunes.rental-start" }, { "aeRU", DMAP_UINT, 0, "com.apple.itunes.rental-pb-duration" }, { "aeRf", DMAP_UINT, 0, "com.apple.itunes.is-featured" }, { "aeSE", DMAP_UINT, 0, "com.apple.itunes.store-pers-id" }, { "aeSF", DMAP_UINT, 0, "com.apple.itunes.itms-storefrontid" }, { "aeSG", DMAP_UINT, 0, "com.apple.itunes.saved-genius" }, { "aeSI", DMAP_UINT, 0, "com.apple.itunes.itms-songid" }, { "aeSN", DMAP_STR, 0, "com.apple.itunes.series-name" }, { "aeSP", DMAP_UINT, 0, "com.apple.itunes.smart-playlist" }, { "aeSU", DMAP_UINT, 0, "com.apple.itunes.season-num" }, { "aeSV", DMAP_VERS, 0, "com.apple.itunes.music-sharing-version" }, { "aeXD", DMAP_STR, 0, "com.apple.itunes.xid" }, { "aecp", DMAP_STR, 0, "com.apple.itunes.collection-description" }, { "aels", DMAP_UINT, 0, "com.apple.itunes.liked-state" }, { "aemi", DMAP_DICT, 0, "com.apple.itunes.media-kind-listing-item" }, { "aeml", DMAP_DICT, 0, "com.apple.itunes.media-kind-listing" }, { "agac", DMAP_UINT, 0, "daap.groupalbumcount" }, { "agma", DMAP_UINT, 0, "daap.groupmatchedqueryalbumcount" }, { "agmi", DMAP_UINT, 0, "daap.groupmatchedqueryitemcount" }, { "agrp", DMAP_STR, 0, "daap.songgrouping" }, { "ajAE", DMAP_UINT, 0, "com.apple.itunes.store.ams-episode-type" }, { "ajAS", DMAP_UINT, 0, "com.apple.itunes.store.ams-episode-sort-order" }, { "ajAT", DMAP_UINT, 0, "com.apple.itunes.store.ams-show-type" }, { "ajAV", DMAP_UINT, 0, "com.apple.itunes.store.is-ams-video" }, { "ajal", DMAP_UINT, 0, "com.apple.itunes.store.album-liked-state" }, { "ajcA", DMAP_UINT, 0, "com.apple.itunes.store.show-composer-as-artist" }, { "ajca", DMAP_UINT, 0, "com.apple.itunes.store.show-composer-as-artist" }, { "ajuw", DMAP_UINT, 0, "com.apple.itunes.store.use-work-name-as-display-name" }, { "amvc", DMAP_UINT, 0, "daap.songmovementcount" }, { "amvm", DMAP_STR, 0, "daap.songmovementname" }, { "amvn", DMAP_UINT, 0, "daap.songmovementnumber" }, { "aply", DMAP_DICT, 0, "daap.databaseplaylists" }, { "aprm", DMAP_UINT, 0, "daap.playlistrepeatmode" }, { "apro", DMAP_VERS, 0, "daap.protocolversion" }, { "apsm", DMAP_UINT, 0, "daap.playlistshufflemode" }, { "apso", DMAP_DICT, 0, "daap.playlistsongs" }, { "arif", DMAP_DICT, 0, "daap.resolveinfo" }, { "arsv", DMAP_DICT, 0, "daap.resolve" }, { "asaa", DMAP_STR, 0, "daap.songalbumartist" }, { "asac", DMAP_UINT, 0, "daap.songartworkcount" }, { "asai", DMAP_UINT, 0, "daap.songalbumid" }, { "asal", DMAP_STR, 0, "daap.songalbum" }, { "asar", DMAP_STR, 0, "daap.songartist" }, { "asas", DMAP_UINT, 0, "daap.songalbumuserratingstatus" }, { "asbk", DMAP_UINT, 0, "daap.bookmarkable" }, { "asbo", DMAP_UINT, 0, "daap.songbookmark" }, { "asbr", DMAP_UINT, 0, "daap.songbitrate" }, { "asbt", DMAP_UINT, 0, "daap.songbeatsperminute" }, { "ascd", DMAP_UINT, 0, "daap.songcodectype" }, { "ascm", DMAP_STR, 0, "daap.songcomment" }, { "ascn", DMAP_STR, 0, "daap.songcontentdescription" }, { "asco", DMAP_UINT, 0, "daap.songcompilation" }, { "ascp", DMAP_STR, 0, "daap.songcomposer" }, { "ascr", DMAP_UINT, 0, "daap.songcontentrating" }, { "ascs", DMAP_UINT, 0, "daap.songcodecsubtype" }, { "asct", DMAP_STR, 0, "daap.songcategory" }, { "asda", DMAP_DATE, 0, "daap.songdateadded" }, { "asdb", DMAP_UINT, 0, "daap.songdisabled" }, { "asdc", DMAP_UINT, 0, "daap.songdisccount" }, { "asdk", DMAP_UINT, 0, "daap.songdatakind" }, { "asdm", DMAP_DATE, 0, "daap.songdatemodified" }, { "asdn", DMAP_UINT, 0, "daap.songdiscnumber" }, { "asdp", DMAP_DATE, 0, "daap.songdatepurchased" }, { "asdr", DMAP_DATE, 0, "daap.songdatereleased" }, { "asdt", DMAP_STR, 0, "daap.songdescription" }, { "ased", DMAP_UINT, 0, "daap.songextradata" }, { "aseq", DMAP_STR, 0, "daap.songeqpreset" }, { "ases", DMAP_UINT, 0, "daap.songexcludefromshuffle" }, { "asfm", DMAP_STR, 0, "daap.songformat" }, { "asgn", DMAP_STR, 0, "daap.songgenre" }, { "asgp", DMAP_UINT, 0, "daap.songgapless" }, { "asgr", DMAP_UINT, 0, "daap.supportsgroups" }, { "ashp", DMAP_UINT, 0, "daap.songhasbeenplayed" }, { "askd", DMAP_DATE, 0, "daap.songlastskipdate" }, { "askp", DMAP_UINT, 0, "daap.songuserskipcount" }, { "asky", DMAP_STR, 0, "daap.songkeywords" }, { "aslc", DMAP_STR, 0, "daap.songlongcontentdescription" }, { "aslr", DMAP_UINT, 0, "daap.songalbumuserrating" }, { "asls", DMAP_UINT, 0, "daap.songlongsize" }, { "aspc", DMAP_UINT, 0, "daap.songuserplaycount" }, { "aspl", DMAP_DATE, 0, "daap.songdateplayed" }, { "aspu", DMAP_STR, 0, "daap.songpodcasturl" }, { "asri", DMAP_UINT, 0, "daap.songartistid" }, { "asrs", DMAP_UINT, 0, "daap.songuserratingstatus" }, { "asrv", DMAP_INT, 0, "daap.songrelativevolume" }, { "assa", DMAP_STR, 0, "daap.sortartist" }, { "assc", DMAP_STR, 0, "daap.sortcomposer" }, { "assl", DMAP_STR, 0, "daap.sortalbumartist" }, { "assn", DMAP_STR, 0, "daap.sortname" }, { "assp", DMAP_UINT, 0, "daap.songstoptime" }, { "assr", DMAP_UINT, 0, "daap.songsamplerate" }, { "asss", DMAP_STR, 0, "daap.sortseriesname" }, { "asst", DMAP_UINT, 0, "daap.songstarttime" }, { "assu", DMAP_STR, 0, "daap.sortalbum" }, { "assz", DMAP_UINT, 0, "daap.songsize" }, { "astc", DMAP_UINT, 0, "daap.songtrackcount" }, { "astm", DMAP_UINT, 0, "daap.songtime" }, { "astn", DMAP_UINT, 0, "daap.songtracknumber" }, { "asul", DMAP_STR, 0, "daap.songdataurl" }, { "asur", DMAP_UINT, 0, "daap.songuserrating" }, { "asvc", DMAP_UINT, 0, "daap.songprimaryvideocodec" }, { "asyr", DMAP_UINT, 0, "daap.songyear" }, { "ated", DMAP_UINT, 0, "daap.supportsextradata" }, { "avdb", DMAP_DICT, 0, "daap.serverdatabases" }, { "awrk", DMAP_STR, 0, "daap.songwork" }, { "caar", DMAP_UINT, 0, "dacp.availablerepeatstates" }, { "caas", DMAP_UINT, 0, "dacp.availableshufflestates" }, { "caci", DMAP_DICT, 0, "caci" }, { "cafe", DMAP_UINT, 0, "dacp.fullscreenenabled" }, { "cafs", DMAP_UINT, 0, "dacp.fullscreen" }, { "caia", DMAP_UINT, 0, "dacp.isactive" }, { "cana", DMAP_STR, 0, "dacp.nowplayingartist" }, { "cang", DMAP_STR, 0, "dacp.nowplayinggenre" }, { "canl", DMAP_STR, 0, "dacp.nowplayingalbum" }, { "cann", DMAP_STR, 0, "dacp.nowplayingname" }, { "canp", DMAP_UINT, 0, "dacp.nowplayingids" }, { "cant", DMAP_UINT, 0, "dacp.nowplayingtime" }, { "capr", DMAP_VERS, 0, "dacp.protocolversion" }, { "caps", DMAP_UINT, 0, "dacp.playerstate" }, { "carp", DMAP_UINT, 0, "dacp.repeatstate" }, { "cash", DMAP_UINT, 0, "dacp.shufflestate" }, { "casp", DMAP_DICT, 0, "dacp.speakers" }, { "cast", DMAP_UINT, 0, "dacp.songtime" }, { "cavc", DMAP_UINT, 0, "dacp.volumecontrollable" }, { "cave", DMAP_UINT, 0, "dacp.visualizerenabled" }, { "cavs", DMAP_UINT, 0, "dacp.visualizer" }, { "ceJC", DMAP_UINT, 0, "com.apple.itunes.jukebox-client-vote" }, { "ceJI", DMAP_UINT, 0, "com.apple.itunes.jukebox-current" }, { "ceJS", DMAP_UINT, 0, "com.apple.itunes.jukebox-score" }, { "ceJV", DMAP_UINT, 0, "com.apple.itunes.jukebox-vote" }, { "ceQR", DMAP_DICT, 0, "com.apple.itunes.playqueue-contents-response" }, { "ceQa", DMAP_STR, 0, "com.apple.itunes.playqueue-album" }, { "ceQg", DMAP_STR, 0, "com.apple.itunes.playqueue-genre" }, { "ceQn", DMAP_STR, 0, "com.apple.itunes.playqueue-name" }, { "ceQr", DMAP_STR, 0, "com.apple.itunes.playqueue-artist" }, { "cmgt", DMAP_DICT, 0, "dmcp.getpropertyresponse" }, { "cmmk", DMAP_UINT, 0, "dmcp.mediakind" }, { "cmpr", DMAP_VERS, 0, "dmcp.protocolversion" }, { "cmsr", DMAP_UINT, 0, "dmcp.serverrevision" }, { "cmst", DMAP_DICT, 0, "dmcp.playstatus" }, { "cmvo", DMAP_UINT, 0, "dmcp.volume" }, { "f\215ch", DMAP_UINT, 0, "dmap.haschildcontainers" }, { "ipsa", DMAP_DICT, 0, "dpap.iphotoslideshowadvancedoptions" }, { "ipsl", DMAP_DICT, 0, "dpap.iphotoslideshowoptions" }, { "mbcl", DMAP_DICT, 0, "dmap.bag" }, { "mccr", DMAP_DICT, 0, "dmap.contentcodesresponse" }, { "mcna", DMAP_STR, 0, "dmap.contentcodesname" }, { "mcnm", DMAP_UINT, 0, "dmap.contentcodesnumber" }, { "mcon", DMAP_DICT, 0, "dmap.container" }, { "mctc", DMAP_UINT, 0, "dmap.containercount" }, { "mcti", DMAP_UINT, 0, "dmap.containeritemid" }, { "mcty", DMAP_UINT, 0, "dmap.contentcodestype" }, { "mdbk", DMAP_UINT, 0, "dmap.databasekind" }, { "mdcl", DMAP_DICT, 0, "dmap.dictionary" }, { "mdst", DMAP_UINT, 0, "dmap.downloadstatus" }, { "meds", DMAP_UINT, 0, "dmap.editcommandssupported" }, { "meia", DMAP_UINT, 0, "dmap.itemdateadded" }, { "meip", DMAP_UINT, 0, "dmap.itemdateplayed" }, { "mext", DMAP_UINT, 0, "dmap.objectextradata" }, { "miid", DMAP_UINT, 0, "dmap.itemid" }, { "mikd", DMAP_UINT, 0, "dmap.itemkind" }, { "mimc", DMAP_UINT, 0, "dmap.itemcount" }, { "minm", DMAP_STR, 0, "dmap.itemname" }, { "mlcl", DMAP_DICT, DMAP_DICT, "dmap.listing" }, { "mlid", DMAP_UINT, 0, "dmap.sessionid" }, { "mlit", DMAP_ITEM, 0, "dmap.listingitem" }, { "mlog", DMAP_DICT, 0, "dmap.loginresponse" }, { "mpco", DMAP_UINT, 0, "dmap.parentcontainerid" }, { "mper", DMAP_UINT, 0, "dmap.persistentid" }, { "mpro", DMAP_VERS, 0, "dmap.protocolversion" }, { "mrco", DMAP_UINT, 0, "dmap.returnedcount" }, { "mrpr", DMAP_UINT, 0, "dmap.remotepersistentid" }, { "msal", DMAP_UINT, 0, "dmap.supportsautologout" }, { "msas", DMAP_UINT, 0, "dmap.authenticationschemes" }, { "msau", DMAP_UINT, 0, "dmap.authenticationmethod" }, { "msbr", DMAP_UINT, 0, "dmap.supportsbrowse" }, { "msdc", DMAP_UINT, 0, "dmap.databasescount" }, { "msex", DMAP_UINT, 0, "dmap.supportsextensions" }, { "msix", DMAP_UINT, 0, "dmap.supportsindex" }, { "mslr", DMAP_UINT, 0, "dmap.loginrequired" }, { "msma", DMAP_UINT, 0, "dmap.machineaddress" }, { "msml", DMAP_DICT, 0, "msml" }, { "mspi", DMAP_UINT, 0, "dmap.supportspersistentids" }, { "msqy", DMAP_UINT, 0, "dmap.supportsquery" }, { "msrs", DMAP_UINT, 0, "dmap.supportsresolve" }, { "msrv", DMAP_DICT, 0, "dmap.serverinforesponse" }, { "mstc", DMAP_DATE, 0, "dmap.utctime" }, { "mstm", DMAP_UINT, 0, "dmap.timeoutinterval" }, { "msto", DMAP_INT, 0, "dmap.utcoffset" }, { "msts", DMAP_STR, 0, "dmap.statusstring" }, { "mstt", DMAP_UINT, 0, "dmap.status" }, { "msup", DMAP_UINT, 0, "dmap.supportsupdate" }, { "mtco", DMAP_UINT, 0, "dmap.specifiedtotalcount" }, { "mudl", DMAP_DICT, 0, "dmap.deletedidlisting" }, { "mupd", DMAP_DICT, 0, "dmap.updateresponse" }, { "musr", DMAP_UINT, 0, "dmap.serverrevision" }, { "muty", DMAP_UINT, 0, "dmap.updatetype" }, { "pasp", DMAP_STR, 0, "dpap.aspectratio" }, { "pcmt", DMAP_STR, 0, "dpap.imagecomments" }, { "peak", DMAP_UINT, 0, "com.apple.itunes.photos.album-kind" }, { "peed", DMAP_DATE, 0, "com.apple.itunes.photos.exposure-date" }, { "pefc", DMAP_DICT, 0, "com.apple.itunes.photos.faces" }, { "peki", DMAP_UINT, 0, "com.apple.itunes.photos.key-image-id" }, { "pekm", DMAP_DICT, 0, "com.apple.itunes.photos.key-image" }, { "pemd", DMAP_DATE, 0, "com.apple.itunes.photos.modification-date" }, { "pfai", DMAP_DICT, 0, "dpap.failureids" }, { "pfdt", DMAP_DICT, 0, "dpap.filedata" }, { "pfmt", DMAP_STR, 0, "dpap.imageformat" }, { "phgt", DMAP_UINT, 0, "dpap.imagepixelheight" }, { "picd", DMAP_DATE, 0, "dpap.creationdate" }, { "pifs", DMAP_UINT, 0, "dpap.imagefilesize" }, { "pimf", DMAP_STR, 0, "dpap.imagefilename" }, { "plsz", DMAP_UINT, 0, "dpap.imagelargefilesize" }, { "ppro", DMAP_VERS, 0, "dpap.protocolversion" }, { "prat", DMAP_UINT, 0, "dpap.imagerating" }, { "pret", DMAP_DICT, 0, "dpap.retryids" }, { "pwth", DMAP_UINT, 0, "dpap.imagepixelwidth" } }; static const size_t dmap_field_count = sizeof(dmap_fields) / sizeof(dmap_field); typedef int (*sort_func) (const void *, const void *); int dmap_version(void) { return DMAP_VERSION; } const char *dmap_version_string(void) { return DMAP_STRINGIFY(DMAP_VERSION_MAJOR) "." DMAP_STRINGIFY(DMAP_VERSION_MINOR) "." DMAP_STRINGIFY(DMAP_VERSION_PATCH); } static int dmap_field_sort(const dmap_field *a, const dmap_field *b) { return memcmp(a->code, b->code, 4); } static const dmap_field *dmap_field_from_code(const char *code) { dmap_field key; key.code = code; return bsearch(&key, dmap_fields, dmap_field_count, sizeof(dmap_field), (sort_func)dmap_field_sort); } const char *dmap_name_from_code(const char *code) { const dmap_field *field; if (!code) return NULL; field = dmap_field_from_code(code); return field ? field->name : NULL; } static uint16_t dmap_read_u16(const char *buf) { return (uint16_t)(((buf[0] & 0xff) << 8) | (buf[1] & 0xff)); } static int16_t dmap_read_i16(const char *buf) { return (int16_t)dmap_read_u16(buf); } static uint32_t dmap_read_u32(const char *buf) { return ((uint32_t)(buf[0] & 0xff) << 24) | ((uint32_t)(buf[1] & 0xff) << 16) | ((uint32_t)(buf[2] & 0xff) << 8) | ((uint32_t)(buf[3] & 0xff)); } static int32_t dmap_read_i32(const char *buf) { return (int32_t)dmap_read_u32(buf); } static uint64_t dmap_read_u64(const char *buf) { return ((uint64_t)(buf[0] & 0xff) << 56) | ((uint64_t)(buf[1] & 0xff) << 48) | ((uint64_t)(buf[2] & 0xff) << 40) | ((uint64_t)(buf[3] & 0xff) << 32) | ((uint64_t)(buf[4] & 0xff) << 24) | ((uint64_t)(buf[5] & 0xff) << 16) | ((uint64_t)(buf[6] & 0xff) << 8) | ((uint64_t)(buf[7] & 0xff)); } static int64_t dmap_read_i64(const char *buf) { return (int64_t)dmap_read_u64(buf); } static int dmap_parse_internal(const dmap_settings *settings, const char *buf, size_t len, const dmap_field *parent) { const dmap_field *field; DMAP_TYPE field_type; size_t field_len; const char *field_name; const char *p = buf; const char *end = buf + len; char code[5] = {0}; if (!settings || !buf) return -1; while (end - p >= 8) { memcpy(code, p, 4); field = dmap_field_from_code(code); p += 4; field_len = dmap_read_u32(p); p += 4; if (p + field_len > end) return -1; if (field) { field_type = field->type; field_name = field->name; if (field_type == DMAP_ITEM) { if (parent != NULL && parent->list_item_type) { field_type = parent->list_item_type; } else { field_type = DMAP_DICT; } } } else { /* Make a best guess of the type */ field_type = DMAP_UNKNOWN; field_name = code; if (field_len >= 8) { /* Look for a four char code followed by a length within the current field */ if (isalpha(p[0] & 0xff) && isalpha(p[1] & 0xff) && isalpha(p[2] & 0xff) && isalpha(p[3] & 0xff)) { if (dmap_read_u32(p + 4) < field_len) field_type = DMAP_DICT; } } if (field_type == DMAP_UNKNOWN) { size_t i; int is_string = 1; for (i=0; i < field_len; i++) { if (!isprint(p[i] & 0xff)) { is_string = 0; break; } } field_type = is_string ? DMAP_STR : DMAP_UINT; } } switch (field_type) { case DMAP_UINT: /* Determine the integer's type based on its size */ switch (field_len) { case 1: if (settings->on_uint32) settings->on_uint32(settings->ctx, code, field_name, (unsigned char)*p); break; case 2: if (settings->on_uint32) settings->on_uint32(settings->ctx, code, field_name, dmap_read_u16(p)); break; case 4: if (settings->on_uint32) settings->on_uint32(settings->ctx, code, field_name, dmap_read_u32(p)); break; case 8: if (settings->on_uint64) settings->on_uint64(settings->ctx, code, field_name, dmap_read_u64(p)); break; default: if (settings->on_data) settings->on_data(settings->ctx, code, field_name, p, field_len); break; } break; case DMAP_INT: switch (field_len) { case 1: if (settings->on_int32) settings->on_int32(settings->ctx, code, field_name, *p); break; case 2: if (settings->on_int32) settings->on_int32(settings->ctx, code, field_name, dmap_read_i16(p)); break; case 4: if (settings->on_int32) settings->on_int32(settings->ctx, code, field_name, dmap_read_i32(p)); break; case 8: if (settings->on_int64) settings->on_int64(settings->ctx, code, field_name, dmap_read_i64(p)); break; default: if (settings->on_data) settings->on_data(settings->ctx, code, field_name, p, field_len); break; } break; case DMAP_STR: if (settings->on_string) settings->on_string(settings->ctx, code, field_name, p, field_len); break; case DMAP_DATA: if (settings->on_data) settings->on_data(settings->ctx, code, field_name, p, field_len); break; case DMAP_DATE: /* Seconds since epoch */ if (settings->on_date) settings->on_date(settings->ctx, code, field_name, dmap_read_u32(p)); break; case DMAP_VERS: if (settings->on_string && field_len >= 4) { char version[20]; sprintf(version, "%u.%u", dmap_read_u16(p), dmap_read_u16(p+2)); settings->on_string(settings->ctx, code, field_name, version, strlen(version)); } break; case DMAP_DICT: if (settings->on_dict_start) settings->on_dict_start(settings->ctx, code, field_name); if (dmap_parse_internal(settings, p, field_len, field) != 0) return -1; if (settings->on_dict_end) settings->on_dict_end(settings->ctx, code, field_name); break; case DMAP_ITEM: /* Unreachable: listing item types are always mapped to another type */ abort(); case DMAP_UNKNOWN: break; } p += field_len; } if (p != end) return -1; return 0; } int dmap_parse(const dmap_settings *settings, const char *buf, size_t len) { return dmap_parse_internal(settings, buf, len, NULL); }