1 /* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
2 /*
3  * Copyright (C) 2009-2011  Tiger Soldier <tigersoldi@gmail.com>
4  *
5  * This file is part of OSD Lyrics.
6  * OSD Lyrics is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * OSD Lyrics is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with OSD Lyrics.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <string.h>
20 #include <glib.h>
21 #include "ol_intl.h"
22 #include "ol_lrc_fetch_ttplayer.h"
23 #include "ol_lrc_fetch_utils.h"
24 #include "ol_utils.h"
25 #include "ol_debug.h"
26 
27 #define MAX_CANDIDATE 5
28 
29 const char *PREFIX_SEARCH_URL = "http://ttlrcct.qianqian.com/dll/lyricsvr.dll?sh?";
30 const char *DOWNLOAD_URL = "http://ttlrcct.qianqian.com/dll/lyricsvr.dll?dl?Id=%d&Code=%d&uid=01&mac=%012x";
31 const char *FIELD_DELIMIT = "\'";
32 
33 struct CandidateParserData
34 {
35   const OlMusicInfo *info;
36   OlLrcCandidate *candidate_list;
37   int count;
38 };
39 
40 static OlLrcCandidate* _search(const OlMusicInfo *info,
41                                int *size,
42                                const char* charset);
43 static int _download (OlLrcCandidate *candidate,
44                       const char *pathname,
45                       const char *charset);
46 static char* _encode_request_field (const char *value);
47 
48 static void _parse_candidate (GMarkupParseContext *context,
49                               const gchar *element_name,
50                               const gchar **attribute_names,
51                               const gchar **attribute_values,
52                               gpointer user_data,
53                               GError **error);
54 static size_t _remove_whitespace (char *str);
55 static gint32 _calc_download_code (int id, const char *data);
56 
57 static gint32
_calc_download_code(int id,const char * data)58 _calc_download_code (int id, const char *data)
59 {
60   guint32 id1 = id & 0xff;
61   guint32 id2 = (id >> 8) & 0xff;
62   guint32 id3 = (id >> 16) & 0xff;
63   guint32 id4 = (id >> 24) & 0xff;
64 
65   if (id3 == 0) id3 = ~id2 & 0xff;
66   if (id4 == 0) id4 = ~id1 & 0xff;
67   gint32 encoded_id = (id1 << 24) | (id3 << 16) | (id2 << 8) | id4;
68   gint32 encoded_data = 0;
69   size_t len = strlen(data);
70   int i;
71   for (i = len - 1; i >= 0; i--)
72     encoded_data = encoded_data + data[i] + (encoded_data << (i % 2 + 4));
73   gint32 encoded_data2 = 0;
74   for (i = 0; i < len; i++)
75     encoded_data2 = encoded_data2 + data[i] + (encoded_data2 << (i % 2 + 3));
76   gint32 ret = ((encoded_data ^ encoded_id) + (encoded_data2 | id)) * (encoded_data2 | encoded_id);
77   ret = ret * (encoded_data ^ id);
78   return ret;
79 }
80 
81 static void
_parse_candidate(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)82 _parse_candidate (GMarkupParseContext *context,
83                   const gchar *element_name,
84                   const gchar **attribute_names,
85                   const gchar **attribute_values,
86                   gpointer user_data,
87                   GError **error)
88 {
89   ol_debugf ("Got element: %s\n", element_name);
90   if (strcmp (element_name, "lrc") == 0)
91   {
92     const char **attr;
93     const char **value;
94     const char *url = NULL;
95     const char *title = NULL;
96     const char *artist = NULL;
97     struct CandidateParserData *data = (struct CandidateParserData*) user_data;
98     for (attr = attribute_names, value = attribute_values;
99          *attr != NULL && *value != NULL;
100          attr++, value++)
101     {
102       if (strcmp (*attr, "id") == 0)
103         url = *value;
104       else if (strcmp (*attr, "title") == 0)
105         title = *value;
106       else if (strcmp (*attr, "artist") == 0)
107         artist = *value;
108       else
109         ol_debugf ("Unknown attribute: %s=%s\n", *attr, *value);
110     }
111     if (url != NULL && (title != NULL || artist != NULL))
112     {
113       OlLrcCandidate *candidate = ol_lrc_candidate_new ();
114       ol_lrc_candidate_set_url (candidate, url);
115       if (title != NULL)
116         ol_lrc_candidate_set_title (candidate, title);
117       if (artist != NULL)
118         ol_lrc_candidate_set_artist (candidate, artist);
119       data->count = ol_lrc_fetch_add_candidate (data->info,
120                                                 data->candidate_list,
121                                                 data->count,
122                                                 MAX_CANDIDATE,
123                                                 candidate);
124       ol_lrc_candidate_free (candidate);
125     }
126   }
127 }
128 
129 static size_t
_remove_whitespace(char * str)130 _remove_whitespace (char *str)
131 {
132   size_t len = 0;
133   size_t tail = 0;
134   for (; str[len] != '\0'; len++)
135   {
136     if (str[len] != ' ')
137     {
138       if (tail != len)
139         str[tail] = str[len];
140       tail++;
141     }
142   }
143   str[tail] = '\0';
144   return tail;
145 }
146 
147 static char*
_encode_request_field(const char * value)148 _encode_request_field (const char *value)
149 {
150   size_t len = strlen (value);
151   size_t utf16_len = len * 2 + 1;
152   char *utf16 = g_new0 (char, utf16_len);
153   char *hex = NULL;
154   char *lower_str = g_utf8_strdown (value, -1);
155   g_strdelimit (lower_str, FIELD_DELIMIT, ' ');
156   len = _remove_whitespace (lower_str);
157   utf16_len = convert ("UTF-8", "UTF16LE",
158                        (char*)lower_str, len,
159                        utf16, utf16_len);
160   if ((int)utf16_len >= 0)
161     hex = ol_encode_hex (utf16, utf16_len);
162   g_free (lower_str);
163   g_free (utf16);
164   return hex;
165 }
166 
167 static OlLrcCandidate *
_search(const OlMusicInfo * info,int * size,const char * charset)168 _search(const OlMusicInfo *info,
169         int *size,
170         const char* charset)
171 {
172   ol_assert_ret (info != NULL, NULL);
173   ol_assert_ret (size != NULL, NULL);
174   if (ol_music_info_get_title (info) == NULL ||
175       ol_music_info_get_artist (info) == NULL)
176     return NULL;
177   static OlLrcCandidate candidate_list[MAX_CANDIDATE];
178   OlLrcCandidate *retval = NULL;
179   GString *surl = g_string_new (PREFIX_SEARCH_URL);
180   char *encoded_title = _encode_request_field (ol_music_info_get_title (info));
181   char *encoded_artist = _encode_request_field (ol_music_info_get_artist (info));
182   g_string_append_printf (surl,
183                           "Artist=%s&Title=%s&Flags=0",
184                           encoded_artist, encoded_title);
185   g_free (encoded_title);
186   g_free (encoded_artist);
187   char *url = g_string_free (surl, FALSE);
188   ol_debugf ("url: %s\n", url);
189   struct memo content = {.mem_base = NULL, .mem_len = 0};
190   if (fetch_into_memory (url, NULL, NULL, NULL, 0, &content) != 0)
191   {
192     ol_debugf ("Search lyrics failed\n");
193     if (content.mem_base != NULL)
194       free (content.mem_base);
195   }
196   else
197   {
198     GMarkupParser parser = {.start_element = _parse_candidate};
199     GError *error = NULL;
200     struct CandidateParserData data = {
201       .info = info,
202       .candidate_list = candidate_list,
203       .count = 0,
204     };
205     GMarkupParseContext *context = g_markup_parse_context_new (&parser,
206                                                                0,
207                                                                &data,
208                                                                NULL);
209     ol_debugf ("Search result: %s\n", content.mem_base);
210     if (!g_markup_parse_context_parse (context,
211                                        (const char*)content.mem_base,
212                                        content.mem_len,
213                                        &error))
214     {
215       ol_errorf ("failed to parse: %s\n", error->message);
216       g_error_free (error);
217     }
218     else if (!g_markup_parse_context_end_parse (context, &error))
219     {
220       ol_errorf ("failed to parse: %s\n", error->message);
221       g_error_free (error);
222     }
223     g_markup_parse_context_free (context);
224     retval = candidate_list;
225     *size = data.count;
226     free (content.mem_base);
227   }
228   g_free (url);
229   return retval;
230 }
231 
232 static int
_download(OlLrcCandidate * candidate,const char * pathname,const char * charset)233 _download(OlLrcCandidate *candidate,
234           const char *pathname,
235           const char *charset)
236 {
237   ol_assert_ret (candidate != NULL, -1);
238   ol_assert_ret (pathname != NULL, -1);
239   int ret = 0;
240   int id = atoi (candidate->url);
241   char *data = g_strdup_printf ("%s%s", candidate->artist, candidate->title);
242   int code = _calc_download_code (id, data);
243   int mac = random ();
244   char *url = g_strdup_printf (DOWNLOAD_URL, id, code, mac);
245   ol_debugf ("Download url: %s\n", url);
246   struct memo content = {.mem_base = NULL, .mem_len = 0};
247   if (fetch_into_memory (url, NULL, NULL, NULL, 0, &content) < 0 ||
248       content.mem_len == 0)
249   {
250     ol_errorf ("Download lyrics (%s, %s, %s) failed\n",
251                candidate->url, candidate->title, candidate->artist);
252     ret = -1;
253   }
254   else
255   {
256     GError *error = NULL;
257     if (!g_file_set_contents (pathname,
258                               content.mem_base,
259                               content.mem_len,
260                               &error))
261     {
262       ol_errorf ("Cannot save lyric file %s: %s\n",
263                  error->message);
264       g_error_free (error);
265       ret = -1;
266     }
267   }
268   if (content.mem_base != NULL)
269     free (content.mem_base);
270   g_free (data);
271   g_free (url);
272   return ret;
273 }
274 
275 static OlLrcFetchEngine ttplayer = {
276   N_("ttPlayer"),
277   _search,
278   _download,
279 };
280 
281 OlLrcFetchEngine *
ol_lrc_fetch_ttplayer_engine()282 ol_lrc_fetch_ttplayer_engine ()
283 {
284   return &ttplayer;
285 }
286