To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Revision:

root / protocol.cpp @ 6:e537b3b8b2b9

History | View | Annotate | Download (9.43 KB)

1
/* ------------------------------------------------------------------
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/types.h>
16
#include <curl/easy.h>
17

    
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
    sprintf(num, "%d", (int)XML_GetCurrentLineNumber(parser));
174
    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
    int q = response.find("<?xml");
262
    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
}