To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / protocol.cpp
History | View | Annotate | Download (9.43 KB)
| 1 | 1:150ad38c1871 | cannam | /* ------------------------------------------------------------------
|
|---|---|---|---|
| 2 | |||
| 3 | libofa -- the Open Fingerprint Architecture library
|
||
| 4 | |||
| 5 | Public Domain (PD) 2006 MusicIP Corporation
|
||
| 6 | No rights reserved.
|
||
| 7 | |||
| 8 | -------------------------------------------------------------------*/
|
||
| 9 | #include <stdio.h> |
||
| 10 | #include <stdlib.h> |
||
| 11 | #include <string> |
||
| 12 | #include <map> |
||
| 13 | #include <expat.h> |
||
| 14 | #include <curl/curl.h> |
||
| 15 | #include <curl/easy.h> |
||
| 16 | 7:9b8599d10aa2 | cannam | #include <cstring> |
| 17 | 1:150ad38c1871 | cannam | |
| 18 | using namespace std; |
||
| 19 | |||
| 20 | #include "protocol.h" |
||
| 21 | |||
| 22 | const char *url = "http://ofa.musicdns.org/ofa/1/track"; |
||
| 23 | const char *userAgent = "libofa_example"; |
||
| 24 | const char *unknown = "unknown"; |
||
| 25 | |||
| 26 | // Lookup by fingerprint
|
||
| 27 | const char *request_format = |
||
| 28 | "cid=%s&" // Client ID |
||
| 29 | "cvr=%s&" // Client Version |
||
| 30 | "fpt=%s&" // Fingerprint |
||
| 31 | "rmd=%d&" // m = 1: return metadata; m = 0: only return id |
||
| 32 | "brt=%d&" // bitrate (kbps) |
||
| 33 | "fmt=%s&" // File extension (e.g. mp3, ogg, flac) |
||
| 34 | "dur=%ld&" // Length of track (milliseconds) |
||
| 35 | "art=%s&" // Artist name. If there is none, send "unknown" |
||
| 36 | "ttl=%s&" // Track title. If there is none, send "unknown" |
||
| 37 | "alb=%s&" // Album name. If there is none, send "unknown" |
||
| 38 | "tnm=%d&" // Track number in album. If there is none, send "0" |
||
| 39 | "gnr=%s&" // Genre. If there is none, send "unknown" |
||
| 40 | "yrr=%s&" // Year. If there is none, send "0" |
||
| 41 | "enc=%s&" // Encoding. e = true: ISO-8859-15; e = false: UTF-8 (default). Optional. |
||
| 42 | "\r\n";
|
||
| 43 | |||
| 44 | // Lookup by PUID (Most fields drop out)
|
||
| 45 | const char *request_format2 = |
||
| 46 | "cid=%s&" // Client ID |
||
| 47 | "cvr=%s&" // Client Version |
||
| 48 | "pid=%s&" // PUID |
||
| 49 | "rmd=%d&" // m = 1: return metadata; m = 0: only return id |
||
| 50 | "brt=%d&" // bitrate (kbps) |
||
| 51 | "fmt=%s&" // File extension (e.g. mp3, ogg, flac) |
||
| 52 | "dur=%ld&" // Length of track (milliseconds) |
||
| 53 | "art=%s&" // Artist name. If there is none, send "unknown" |
||
| 54 | "ttl=%s&" // Track title. If there is none, send "unknown" |
||
| 55 | "alb=%s&" // Album name. If there is none, send "unknown" |
||
| 56 | "tnm=%d&" // Track number in album. If there is none, send "0" |
||
| 57 | "gnr=%s&" // Genre. If there is none, send "unknown" |
||
| 58 | "yrr=%s&" // Year. If there is none, send "0" |
||
| 59 | "enc=%s&" // Encoding. e = true: ISO-8859-15; e = false: UTF-8 (default). Optional. |
||
| 60 | "\r\n";
|
||
| 61 | |||
| 62 | |||
| 63 | // --------------------------------------------------------------------
|
||
| 64 | // HTTP POST support using standard curl calls
|
||
| 65 | // --------------------------------------------------------------------
|
||
| 66 | size_t data_callback(void *ptr, size_t size, size_t num, void *arg) |
||
| 67 | {
|
||
| 68 | string *str = (string *)arg; |
||
| 69 | (*str) += string((const char *)ptr, size * num); |
||
| 70 | return size * num;
|
||
| 71 | } |
||
| 72 | |||
| 73 | long http_post(const string &url, const string &userAgent, const string &postData, string &doc) |
||
| 74 | {
|
||
| 75 | CURL *curl; |
||
| 76 | long ret = 0; |
||
| 77 | struct curl_slist *headerlist=NULL; |
||
| 78 | |||
| 79 | headerlist = curl_slist_append(headerlist, "Expect:");
|
||
| 80 | |||
| 81 | curl_global_init(CURL_GLOBAL_ALL); |
||
| 82 | curl = curl_easy_init(); |
||
| 83 | curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); |
||
| 84 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&doc);
|
||
| 85 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, data_callback); |
||
| 86 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); |
||
| 87 | curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postData.length()); |
||
| 88 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str()); |
||
| 89 | curl_easy_setopt(curl, CURLOPT_POST, 1);
|
||
| 90 | curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str()); |
||
| 91 | curl_easy_perform(curl); |
||
| 92 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret); |
||
| 93 | curl_easy_cleanup(curl); |
||
| 94 | |||
| 95 | curl_slist_free_all (headerlist); |
||
| 96 | |||
| 97 | return ret;
|
||
| 98 | } |
||
| 99 | |||
| 100 | // --------------------------------------------------------------------
|
||
| 101 | // XML Parsing support
|
||
| 102 | // --------------------------------------------------------------------
|
||
| 103 | |||
| 104 | struct ParseInfo
|
||
| 105 | {
|
||
| 106 | string path;
|
||
| 107 | string pcdata;
|
||
| 108 | TrackInformation *info; |
||
| 109 | }; |
||
| 110 | |||
| 111 | void begin_element(void *data, const XML_Char *el, const XML_Char **attr) |
||
| 112 | {
|
||
| 113 | map<string, string> attrs; |
||
| 114 | |||
| 115 | for(; *attr;) {
|
||
| 116 | string key = string((char *)*(attr++)); |
||
| 117 | string value = string((char *)*(attr++)); |
||
| 118 | attrs[key] = value; |
||
| 119 | } |
||
| 120 | |||
| 121 | ((ParseInfo *)data)->path += string("/") + string(el); |
||
| 122 | if (((ParseInfo *)data)->path == "/metadata/track/puid-list/puid") |
||
| 123 | ((ParseInfo *)data)->info->setPUID(attrs["id"]);
|
||
| 124 | |||
| 125 | ((ParseInfo *)data)->pcdata = "";
|
||
| 126 | } |
||
| 127 | |||
| 128 | void end_element(void *data, const XML_Char *el) |
||
| 129 | {
|
||
| 130 | string::size_type pos;
|
||
| 131 | |||
| 132 | if (((ParseInfo *)data)->path == "/metadata/track/title") |
||
| 133 | ((ParseInfo *)data)->info->setTrack(((ParseInfo *)data)->pcdata); |
||
| 134 | if (((ParseInfo *)data)->path == "/metadata/track/artist/name") |
||
| 135 | ((ParseInfo *)data)->info->setArtist(((ParseInfo *)data)->pcdata); |
||
| 136 | |||
| 137 | pos = ((ParseInfo *)data)->path.rfind("/");
|
||
| 138 | if (pos != string::npos) |
||
| 139 | ((ParseInfo *)data)->path = ((ParseInfo *)data)->path.substr(0, pos);
|
||
| 140 | } |
||
| 141 | |||
| 142 | void pc_data(void *data, const XML_Char *charData, int len) |
||
| 143 | {
|
||
| 144 | char *temp;
|
||
| 145 | |||
| 146 | temp = new char[len + 1]; |
||
| 147 | strncpy(temp, (char *)charData, len);
|
||
| 148 | temp[len] = 0;
|
||
| 149 | ((ParseInfo *)data)->pcdata += string(temp);
|
||
| 150 | delete temp;
|
||
| 151 | } |
||
| 152 | |||
| 153 | bool parse_xml(const string &doc, TrackInformation *info, string &err) |
||
| 154 | {
|
||
| 155 | ParseInfo pinfo; |
||
| 156 | |||
| 157 | err = "";
|
||
| 158 | pinfo.info = info; |
||
| 159 | XML_Parser parser = XML_ParserCreate(NULL);
|
||
| 160 | XML_SetUserData(parser, (void *)&pinfo);
|
||
| 161 | XML_SetElementHandler(parser, ::begin_element, ::end_element); |
||
| 162 | XML_SetCharacterDataHandler(parser, ::pc_data); |
||
| 163 | int ret = XML_Parse(parser, doc.c_str(), doc.length(), 1); |
||
| 164 | |||
| 165 | if (ret)
|
||
| 166 | {
|
||
| 167 | XML_ParserFree(parser); |
||
| 168 | return true; |
||
| 169 | } |
||
| 170 | |||
| 171 | err = string(XML_ErrorString(XML_GetErrorCode(parser)));
|
||
| 172 | char num[10]; |
||
| 173 | 6:e537b3b8b2b9 | cannam | sprintf(num, "%d", (int)XML_GetCurrentLineNumber(parser)); |
| 174 | 1:150ad38c1871 | cannam | err += string(" on line ") + string(num); |
| 175 | XML_ParserFree(parser); |
||
| 176 | |||
| 177 | return false; |
||
| 178 | } |
||
| 179 | |||
| 180 | // --------------------------------------------------------------------
|
||
| 181 | // Retrieve metadata for fingerprint
|
||
| 182 | // --------------------------------------------------------------------
|
||
| 183 | |||
| 184 | // Returns true on success
|
||
| 185 | bool retrieve_metadata(string client_key, string client_version, |
||
| 186 | TrackInformation *info, bool getMetadata)
|
||
| 187 | {
|
||
| 188 | if (!info)
|
||
| 189 | return false; |
||
| 190 | |||
| 191 | // All metadata fields must be provided before this call if the
|
||
| 192 | // information is available, as part of the Terms of Service.
|
||
| 193 | // This helps create a better database for all users of the system.
|
||
| 194 | //
|
||
| 195 | // If the fields are not available, you can use default values.
|
||
| 196 | // Here we check for fields which have no default values.
|
||
| 197 | if (client_key.length() == 0) |
||
| 198 | return false; |
||
| 199 | if (client_version.length() == 0) |
||
| 200 | return false; |
||
| 201 | |||
| 202 | bool lookupByPrint = false; |
||
| 203 | if (info->getPUID().length() == 0) { |
||
| 204 | // Lookup by fingerprint
|
||
| 205 | if (info->getPrint().length() == 0) |
||
| 206 | return false; |
||
| 207 | if (info->getFormat().length() == 0) |
||
| 208 | return false; |
||
| 209 | if (info->getLengthInMS() == 0) |
||
| 210 | return false; |
||
| 211 | |||
| 212 | lookupByPrint = true;
|
||
| 213 | } |
||
| 214 | |||
| 215 | // Sloppily estimate the size of the resultant URL. Err on the side of making the string too big.
|
||
| 216 | int bufSize = strlen(lookupByPrint ? request_format : request_format2) +
|
||
| 217 | client_key.length() + client_version.length() + |
||
| 218 | (lookupByPrint ? info->getPrint().length() : info->getPUID().length()) + |
||
| 219 | 16 + // getMetadata ? 1 : 0, |
||
| 220 | 16 + // info->getBitrate(), |
||
| 221 | 16 + //info->getFormat().c_str(), |
||
| 222 | 16 + //info->getLengthInMS(), |
||
| 223 | ((info->getArtist().c_str() == 0) ? strlen(unknown) : info->getArtist().length()) +
|
||
| 224 | ((info->getTrack().c_str() == 0) ? strlen(unknown) : info->getTrack().length()) +
|
||
| 225 | ((info->getAlbum().c_str() == 0) ? strlen(unknown) : info->getAlbum().length()) +
|
||
| 226 | 16 + // info->getTrackNum() + |
||
| 227 | ((info->getGenre().c_str() == 0) ? strlen(unknown) : info->getGenre().length()) +
|
||
| 228 | ((info->getYear().c_str() == 0) ? 1 : info->getYear().length()) + |
||
| 229 | info->getEncoding().length(); |
||
| 230 | |||
| 231 | char *buf = new char[bufSize]; |
||
| 232 | sprintf(buf, lookupByPrint ? request_format : request_format2, |
||
| 233 | client_key.c_str(), |
||
| 234 | client_version.c_str(), |
||
| 235 | lookupByPrint ? info->getPrint().c_str() : info->getPUID().c_str(), |
||
| 236 | getMetadata ? 1 : 0, |
||
| 237 | info->getBitrate(), |
||
| 238 | info->getFormat().c_str(), |
||
| 239 | info->getLengthInMS(), |
||
| 240 | (info->getArtist().length() == 0) ? unknown : info->getArtist().c_str(),
|
||
| 241 | (info->getTrack().length() == 0) ? unknown : info->getTrack().c_str(),
|
||
| 242 | (info->getAlbum().length() == 0) ? unknown : info->getAlbum().c_str(),
|
||
| 243 | info->getTrackNum(), |
||
| 244 | (info->getGenre().length() == 0) ? unknown : info->getGenre().c_str(),
|
||
| 245 | (info->getYear().length() == 0) ? "0" : info->getYear().c_str(), |
||
| 246 | info->getEncoding().c_str()); |
||
| 247 | |||
| 248 | string response;
|
||
| 249 | // printf("request: '%s'\n", buf);
|
||
| 250 | long ret = http_post(url, userAgent, buf, response);
|
||
| 251 | delete [] buf;
|
||
| 252 | |||
| 253 | if (ret != 200) |
||
| 254 | {
|
||
| 255 | // printf("Error: %ld\n", ret);
|
||
| 256 | // printf("response: %s\n\n", response.c_str());
|
||
| 257 | return false; |
||
| 258 | } |
||
| 259 | // printf("response: %s\n\n", response.c_str());
|
||
| 260 | |||
| 261 | 6:e537b3b8b2b9 | cannam | int q = response.find("<?xml"); |
| 262 | 1:150ad38c1871 | cannam | if (q != string::npos) { |
| 263 | response = response.substr(q); |
||
| 264 | } |
||
| 265 | string err;
|
||
| 266 | if (!parse_xml(response, info, err)) {
|
||
| 267 | // Clears title if it wasn't returned
|
||
| 268 | info->setTrack("");
|
||
| 269 | |||
| 270 | // Clears artists if it wasn't returned
|
||
| 271 | info->setArtist("");
|
||
| 272 | } |
||
| 273 | return true; |
||
| 274 | } |