Main.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. #include "main.h"
  2. #include "./api__ml_online.h"
  3. #include "./config.h"
  4. #include "./navigation.h"
  5. #include "./resource.h"
  6. #include "./preferences.h"
  7. #include "./serviceHelper.h"
  8. #include "../../General/gen_ml/ml.h"
  9. #include "../../General/gen_ml/ml_ipc_0313.h"
  10. #include "../nu/MediaLibraryInterface.h"
  11. #include "../nu/AutoChar.h"
  12. #include "../nu/ns_wc.h"
  13. #include "../nu/AutoWide.h"
  14. #include <vector>
  15. #include "../nu/nonewthrow.c"
  16. #include "../nu/ConfigCOM.h"
  17. #include "BufferCache.h"
  18. #include "OMCOM.h"
  19. #include "JNetCom.h" // for buffer_map
  20. #include <shlwapi.h>
  21. #include <strsafe.h>
  22. static int Plugin_Init();
  23. static void Plugin_Quit();
  24. static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3);
  25. static Navigation *navigation = NULL;
  26. static std::vector<PLUGINUNLOADCALLBACK> *unloadCallbacks = NULL;
  27. C_Config *g_config=NULL;
  28. extern "C" winampMediaLibraryPlugin plugin =
  29. {
  30. MLHDR_VER,
  31. "nullsoft(ml_online.dll)",
  32. Plugin_Init,
  33. Plugin_Quit,
  34. Plugin_MessageProc,
  35. 0,
  36. 0,
  37. 0,
  38. };
  39. HINSTANCE Plugin_GetInstance(void)
  40. {
  41. return plugin.hDllInstance;
  42. }
  43. HWND Plugin_GetWinamp(void)
  44. {
  45. return plugin.hwndWinampParent;
  46. }
  47. HWND Plugin_GetLibrary(void)
  48. {
  49. return plugin.hwndLibraryParent;
  50. }
  51. HRESULT Plugin_GetNavigation(Navigation **instance)
  52. {
  53. if(NULL == instance) return E_POINTER;
  54. if (NULL == navigation)
  55. {
  56. *instance = NULL;
  57. return E_UNEXPECTED;
  58. }
  59. *instance = navigation;
  60. navigation->AddRef();
  61. return S_OK;
  62. }
  63. typedef struct __PLUGINTIMERREC
  64. {
  65. UINT_PTR id;
  66. PLUGINTIMERPROC callback;
  67. ULONG_PTR data;
  68. } PLUGINTIMERREC;
  69. typedef std::vector<PLUGINTIMERREC> PluginTimerList;
  70. static void CALLBACK Plugin_TimerProcDispath(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elapsedMs)
  71. {
  72. HWND hLibrary = Plugin_GetLibrary();
  73. if (NULL != hLibrary && FALSE != IsWindow(hLibrary))
  74. {
  75. PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
  76. if (NULL != list)
  77. {
  78. size_t index = list->size();
  79. while(index--)
  80. {
  81. PLUGINTIMERREC *rec = &list->at(index);
  82. if (rec->id == eventId)
  83. {
  84. rec->callback(eventId, elapsedMs, rec->data);
  85. return;
  86. }
  87. }
  88. }
  89. }
  90. KillTimer(hwnd, eventId);
  91. }
  92. UINT_PTR Plugin_SetTimer(UINT elapseMs, PLUGINTIMERPROC callback, ULONG_PTR data)
  93. {
  94. HWND hLibrary = Plugin_GetLibrary();
  95. if (NULL == hLibrary || FALSE == IsWindow(hLibrary))
  96. return 0;
  97. if (GetCurrentThreadId() != GetWindowThreadProcessId(hLibrary, NULL))
  98. return 0;
  99. if (NULL == callback)
  100. return 0;
  101. PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
  102. if (NULL == list)
  103. {
  104. list = new PluginTimerList();
  105. if (NULL == list) return 0;
  106. if (0 == SetProp(hLibrary, L"OnlineMediaTimerData", list))
  107. {
  108. delete(list);
  109. return 0;
  110. }
  111. }
  112. PLUGINTIMERREC rec;
  113. rec.data = data;
  114. rec.callback = callback;
  115. rec.id = SetTimer(NULL, NULL, elapseMs, Plugin_TimerProcDispath);
  116. if (0 == rec.id)
  117. {
  118. if (0 == list->size())
  119. {
  120. RemoveProp(hLibrary, L"OnlineMediaTimerData");
  121. delete(list);
  122. }
  123. return 0;
  124. }
  125. list->push_back(rec);
  126. return rec.id;
  127. }
  128. void Plugin_KillTimer(UINT_PTR eventId)
  129. {
  130. KillTimer(NULL, eventId);
  131. HWND hLibrary = Plugin_GetLibrary();
  132. if (NULL == hLibrary || FALSE == IsWindow(hLibrary))
  133. return;
  134. PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
  135. if (NULL == list) return;
  136. size_t index = list->size();
  137. while(index--)
  138. {
  139. if (list->at(index).id == eventId)
  140. {
  141. list->erase(list->begin() + index);
  142. break;
  143. }
  144. }
  145. if (0 == list->size())
  146. {
  147. RemoveProp(hLibrary, L"OnlineMediaTimerData");
  148. delete(list);
  149. }
  150. }
  151. static void Plugin_UninitializeTimer()
  152. {
  153. HWND hLibrary = Plugin_GetLibrary();
  154. if (NULL == hLibrary || FALSE == IsWindow(hLibrary))
  155. return;
  156. PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
  157. RemoveProp(hLibrary, L"OnlineMediaTimerData");
  158. if (NULL == list) return;
  159. size_t index = list->size();
  160. while(index--)
  161. {
  162. KillTimer(NULL, list->at(index).id);
  163. }
  164. delete(list);
  165. }
  166. wchar_t g_w_cachedir[2048] = {0};
  167. int winampVersion=0;
  168. OMCOM omCOM;
  169. URLMap urlMap;
  170. MetadataMap metadataMap;
  171. Nullsoft::Utility::LockGuard urlMapGuard;
  172. void LoadCacheItem( wchar_t *path )
  173. {
  174. FILECACHETYPE cachefile = {0};
  175. unsigned long size = 0;
  176. HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  177. if (hFile == INVALID_HANDLE_VALUE) return;
  178. ReadFile(hFile, &cachefile, sizeof(FILECACHETYPE),&size, NULL);
  179. if ( size == sizeof(FILECACHETYPE ))
  180. {
  181. size = 0;
  182. time_t now = time(NULL);
  183. //read the header, validate
  184. if ( cachefile.version == FILECACHEVERSION )
  185. {
  186. if ( now < cachefile.expires )
  187. {
  188. char *url = (char *)calloc((size_t)cachefile.urllen, sizeof(char));
  189. if (url)
  190. {
  191. size = 0;
  192. ReadFile(hFile, url, (DWORD)cachefile.urllen, &size, NULL);
  193. if ( cachefile.urllen == size ) // we read it ok!
  194. {
  195. char tempbuf[16384] = {0};
  196. Buffer_GrowBuf *newbuffer = new Buffer_GrowBuf;
  197. INT64 readin=0;
  198. newbuffer->expire_time = (time_t)cachefile.expires;
  199. while ( readin != cachefile.datalen )
  200. {
  201. DWORD toread=(DWORD)cachefile.datalen - (DWORD)readin;
  202. if ( toread > 16384 ) toread=16384;
  203. size = 0;
  204. int success = ReadFile(hFile, &tempbuf, toread , &size, NULL);
  205. if ( success )
  206. {
  207. if ( size )
  208. {
  209. newbuffer->add(tempbuf,(int)size);
  210. readin += size;
  211. }
  212. }
  213. else
  214. {
  215. break;
  216. }
  217. }
  218. if ( readin != cachefile.datalen )
  219. {
  220. delete newbuffer;
  221. }
  222. else
  223. {
  224. buffer_map[(wchar_t *)AutoWide(url)]=newbuffer;
  225. }
  226. }
  227. else
  228. {
  229. free(url);
  230. }
  231. }
  232. }
  233. }
  234. }
  235. CloseHandle(hFile);
  236. DeleteFile(path);
  237. }
  238. void LoadCache()
  239. {
  240. WIN32_FIND_DATA FindFileData = {0};
  241. HANDLE hFind;
  242. wchar_t searchs[2048] = {0};
  243. buffer_map.clear();
  244. StringCchPrintf(searchs, 2048, L"%s\\*.w5x",g_w_cachedir);
  245. hFind = FindFirstFile(searchs, &FindFileData);
  246. if ( hFind != INVALID_HANDLE_VALUE )
  247. {
  248. do
  249. {
  250. wchar_t activefile[2048] = {0};
  251. StringCchPrintf(activefile, 2048, L"%s\\%s",g_w_cachedir,FindFileData.cFileName);
  252. LoadCacheItem(activefile);
  253. } while ( FindNextFile(hFind, &FindFileData) );
  254. FindClose(hFind);
  255. }
  256. }
  257. void SaveCache()
  258. {
  259. BufferMap::iterator buffer_it;
  260. DWORD start=0xABBACAFE;
  261. for(buffer_it = buffer_map.begin();buffer_it != buffer_map.end(); buffer_it++)
  262. {
  263. time_t now = time(NULL);
  264. if ( buffer_it->second->expire_time > now )
  265. {
  266. wchar_t filename[2048] = {0};
  267. FILECACHETYPE cachefile;
  268. HANDLE hFile;
  269. INT64 size=0;
  270. memset((void *)&cachefile,0,sizeof(FILECACHETYPE));
  271. cachefile.version = FILECACHEVERSION;
  272. cachefile.expires = buffer_it->second->expire_time;
  273. AutoChar charUrl(buffer_it->first.c_str());
  274. cachefile.urllen = strlen(charUrl)+1;
  275. cachefile.datalen = buffer_it->second->getlen()+1;
  276. StringCchPrintf(filename, 2048, L"%s\\%08X.w5x",g_w_cachedir,start++);
  277. hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL, NULL);
  278. if (hFile != INVALID_HANDLE_VALUE)
  279. {
  280. WriteFile(hFile, &cachefile, sizeof(FILECACHETYPE),(LPDWORD)&size,NULL);
  281. if ( size == sizeof(FILECACHETYPE) )
  282. {
  283. char blank[2]="\0";
  284. size = 0; WriteFile(hFile, (char *)charUrl ,(DWORD)cachefile.urllen, (LPDWORD)&size, NULL);
  285. size = 0; WriteFile(hFile, buffer_it->second->get() , (DWORD)buffer_it->second->getlen(), (LPDWORD)&size, NULL);
  286. size = 0; WriteFile(hFile, blank , 1, (LPDWORD)&size, NULL);
  287. }
  288. else
  289. {
  290. CloseHandle(hFile);
  291. hFile=NULL;
  292. DeleteFile(filename);
  293. }
  294. }
  295. if (hFile)
  296. {
  297. CloseHandle(hFile);
  298. hFile=NULL;
  299. }
  300. }
  301. }
  302. }
  303. void initConfigCache()
  304. {
  305. wchar_t iniFileName[2048] = {0};
  306. mediaLibrary.BuildPath(L"Plugins\\ml", iniFileName, 2048);
  307. CreateDirectory(iniFileName, NULL);
  308. mediaLibrary.BuildPath(L"Plugins\\ml\\cache", g_w_cachedir, 2048);
  309. CreateDirectory(g_w_cachedir, NULL);
  310. mediaLibrary.BuildPath(L"Plugins\\ml\\ml_online.ini", iniFileName, 2048);
  311. AutoChar charFn(iniFileName);
  312. g_config = new C_Config(AutoChar(iniFileName));
  313. int x = g_config->ReadInt("maxbandwidth", MAXBANDWIDTH );
  314. g_config->WriteInt("maxbandwidth",x);
  315. x = g_config->ReadInt("minbandwidth",1);
  316. g_config->WriteInt("minbandwidth",x);
  317. LoadCache();
  318. }
  319. static void Plugin_ExecuteOpenOnce()
  320. {
  321. CHAR szBuffer[128] = {0};
  322. INT cchLen = Config_ReadStr("Navigation", "openOnce", NULL, szBuffer, ARRAYSIZE(szBuffer));
  323. if (0 != cchLen)
  324. {
  325. UINT serviceId;
  326. if (FALSE != StrToIntExA(szBuffer, STIF_SUPPORT_HEX, (INT*)&serviceId))
  327. {
  328. cchLen = Config_ReadStr("Navigation", "openOnceMode", NULL, szBuffer, ARRAYSIZE(szBuffer));
  329. UINT showMode;
  330. if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "popup", -1, szBuffer, cchLen))
  331. showMode = SHOWMODE_POPUP;
  332. else if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "ensureVisible", -1, szBuffer, cchLen))
  333. showMode = SHOWMODE_ENSUREVISIBLE;
  334. else
  335. showMode = SHOWMODE_NORMAL;
  336. ServiceHelper_ShowService(serviceId, showMode);
  337. }
  338. Config_WriteStr("Navigation", "openOnce", NULL);
  339. Config_WriteStr("Navigation", "openOnceMode", NULL);
  340. }
  341. }
  342. static int Plugin_Init()
  343. {
  344. if (FAILED(WasabiApi_Initialize(plugin.hDllInstance, plugin.service)))
  345. return 1;
  346. if (FAILED(WasabiApi_LoadDefaults()) ||
  347. NULL == OMBROWSERMNGR ||
  348. NULL == OMSERVICEMNGR ||
  349. NULL == OMUTILITY)
  350. {
  351. WasabiApi_Release();
  352. return 2;
  353. }
  354. ServiceHelper_Initialize();
  355. if (NULL != WASABI_API_LNG)
  356. {
  357. static wchar_t szDescription[256];
  358. StringCchPrintf(szDescription, ARRAYSIZE(szDescription),
  359. WASABI_API_LNGSTRINGW(IDS_PLUGIN_NAME),
  360. PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR);
  361. plugin.description = (char*)szDescription;
  362. }
  363. mediaLibrary.library = plugin.hwndLibraryParent;
  364. mediaLibrary.winamp = plugin.hwndWinampParent;
  365. mediaLibrary.instance = plugin.hDllInstance;
  366. winampVersion = mediaLibrary.GetWinampVersion();
  367. omCOM.Publish();
  368. Preferences_Register();
  369. if (NULL == navigation)
  370. {
  371. if (FAILED(Navigation::CreateInstance(&navigation)))
  372. {
  373. navigation = NULL;
  374. if (NULL != unloadCallbacks)
  375. {
  376. size_t index = unloadCallbacks->size();
  377. while(index--)
  378. unloadCallbacks->at(index)();
  379. delete(unloadCallbacks);
  380. }
  381. Preferences_Unregister();
  382. WasabiApi_Release();
  383. return 3;
  384. }
  385. }
  386. initConfigCache();
  387. Plugin_ExecuteOpenOnce();
  388. return ML_INIT_SUCCESS;
  389. }
  390. static void Plugin_Quit()
  391. {
  392. SaveCache();
  393. buffer_map.clear();
  394. Plugin_UninitializeTimer();
  395. if (NULL != navigation)
  396. {
  397. navigation->Finish();
  398. navigation->Release();
  399. navigation = NULL;
  400. }
  401. if (NULL != unloadCallbacks)
  402. {
  403. size_t index = unloadCallbacks->size();
  404. while(index--)
  405. unloadCallbacks->at(index)();
  406. delete(unloadCallbacks);
  407. unloadCallbacks = NULL;
  408. }
  409. Preferences_Unregister();
  410. WasabiApi_Release();
  411. }
  412. static INT_PTR TitleHook(waHookTitleStructW *hookTitle)
  413. {
  414. if (NULL == hookTitle ||
  415. NULL == hookTitle->filename)
  416. {
  417. return 0;
  418. }
  419. Nullsoft::Utility::AutoLock lock(urlMapGuard);
  420. // this is kinda slow but AOL Videos is so underused anyway that this map won't fill up much
  421. URLMap::iterator itr;
  422. for (itr=urlMap.begin();itr!=urlMap.end();itr++)
  423. {
  424. if (!_wcsnicmp(hookTitle->filename, itr->url.c_str(), itr->url_wcslen))
  425. {
  426. if (NULL != hookTitle->title)
  427. StringCchCopy(hookTitle->title, 2048, itr->title.c_str());
  428. hookTitle->length = itr->length;
  429. return 1;
  430. }
  431. }
  432. return 0;
  433. }
  434. static INT_PTR MetadataHook(extendedFileInfoStructW *hookMetadata)
  435. {
  436. if (NULL == hookMetadata ||
  437. NULL == hookMetadata->filename ||
  438. NULL == hookMetadata->metadata)
  439. {
  440. return 0;
  441. }
  442. Nullsoft::Utility::AutoLock lock(urlMapGuard);
  443. // this is kinda slow but AOL Videos is so underused anyway that this map won't fill up much
  444. MetadataMap::iterator itr;
  445. for (itr=metadataMap.begin();itr!=metadataMap.end();itr++)
  446. {
  447. if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, hookMetadata->filename, -1, itr->url.c_str(), - 1) &&
  448. CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, hookMetadata->metadata, -1, itr->tag.c_str(), - 1))
  449. {
  450. StringCchCopy(hookMetadata->ret, hookMetadata->retlen, itr->metadata.c_str());
  451. return 1;
  452. }
  453. }
  454. return 0;
  455. }
  456. static INT_PTR Plugin_MessageProc(int msg, INT_PTR param1, INT_PTR param2, INT_PTR param3)
  457. {
  458. INT_PTR result = 0;
  459. if (NULL != navigation &&
  460. FALSE != navigation->ProcessMessage(msg, param1, param2, param3, &result))
  461. {
  462. return result;
  463. }
  464. switch (msg)
  465. {
  466. case ML_IPC_HOOKTITLEW: return TitleHook((waHookTitleStructW *)param1);
  467. case ML_IPC_HOOKEXTINFOW: return MetadataHook((extendedFileInfoStructW *)param1);
  468. case ML_MSG_CONFIG: Preferences_Show(); return TRUE;
  469. }
  470. return FALSE;
  471. }
  472. void Plugin_RegisterUnloadCallback(PLUGINUNLOADCALLBACK callback)
  473. {
  474. if (NULL == unloadCallbacks)
  475. {
  476. unloadCallbacks = new std::vector<PLUGINUNLOADCALLBACK>();
  477. if (NULL == unloadCallbacks)
  478. return;
  479. }
  480. unloadCallbacks->push_back(callback);
  481. }
  482. extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
  483. {
  484. return &plugin;
  485. }
  486. #if 0
  487. extern "C" __declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) {
  488. // prompt to remove our settings with default as no (just incase)
  489. /*if(MessageBoxA(hwndDlg,"Do you also want to remove the saved settings for this plugin?",
  490. plugin.description,MB_YESNO|MB_DEFBUTTON2) == IDYES)
  491. {
  492. WritePrivateProfileString("ml_rg", 0, 0, iniFile);
  493. }*/
  494. // also attempt to remove the ReplayGainAnalysis.dll so everything is kept cleaner
  495. /*char path[MAX_PATH] = {0};
  496. GetModuleFileName(hDllInst, path, MAX_PATH);
  497. PathRemoveFileSpec(path);
  498. PathAppend(path, "ReplayGainAnalysis.dll");
  499. // if we get a handle then try to lower the handle count so we can delete
  500. HINSTANCE rgLib = GetModuleHandle(path);
  501. if(rgLib)
  502. FreeLibrary(rgLib);
  503. DeleteFile(path);*/
  504. // allow an on-the-fly removal (since we've got to be with a compatible client build)
  505. return ML_PLUGIN_UNINSTALL_NOW;
  506. }
  507. #endif