source: trunk/libtransmission/platform.c @ 14488

Last change on this file since 14488 was 14488, checked in by mikedld, 6 years ago

#5920: Fix memory leak and access to freed memory in tr_getDefaultConfigDir (patch by jyelloz)

  • Property svn:keywords set to Date Rev Author Id
File size: 13.7 KB
Line 
1/*
2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: platform.c 14488 2015-04-05 06:39:06Z mikedld $
8 */
9
10#define _XOPEN_SOURCE 600  /* needed for recursive locks. */
11#ifndef __USE_UNIX98
12 #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
13#endif
14
15#include <assert.h>
16#include <stdlib.h>
17#include <string.h>
18
19#ifdef __HAIKU__
20 #include <limits.h> /* PATH_MAX */
21#endif
22
23#ifdef _WIN32
24 #include <process.h> /* _beginthreadex (), _endthreadex () */
25 #include <windows.h>
26 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
27#else
28#include <unistd.h> /* getuid() */
29 #ifdef BUILD_MAC_CLIENT
30  #include <CoreFoundation/CoreFoundation.h>
31 #endif
32 #ifdef __HAIKU__
33  #include <FindDirectory.h>
34 #endif
35 #include <pthread.h>
36#endif
37
38#include "transmission.h"
39#include "file.h"
40#include "list.h"
41#include "log.h"
42#include "platform.h"
43#include "session.h"
44#include "utils.h"
45
46/***
47****  THREADS
48***/
49
50#ifdef _WIN32
51typedef DWORD tr_thread_id;
52#else
53typedef pthread_t tr_thread_id;
54#endif
55
56static tr_thread_id
57tr_getCurrentThread (void)
58{
59#ifdef _WIN32
60  return GetCurrentThreadId ();
61#else
62  return pthread_self ();
63#endif
64}
65
66static bool
67tr_areThreadsEqual (tr_thread_id a, tr_thread_id b)
68{
69#ifdef _WIN32
70  return a == b;
71#else
72  return pthread_equal (a, b) != 0;
73#endif
74}
75
76/** @brief portability wrapper around OS-dependent threads */
77struct tr_thread
78{
79  void          (* func)(void *);
80  void           * arg;
81  tr_thread_id     thread;
82#ifdef _WIN32
83  HANDLE           thread_handle;
84#endif
85};
86
87bool
88tr_amInThread (const tr_thread * t)
89{
90  return tr_areThreadsEqual (tr_getCurrentThread (), t->thread);
91}
92
93#ifdef _WIN32
94 #define ThreadFuncReturnType unsigned WINAPI
95#else
96 #define ThreadFuncReturnType void
97#endif
98
99static ThreadFuncReturnType
100ThreadFunc (void * _t)
101{
102  tr_thread * t = _t;
103
104  t->func (t->arg);
105
106  tr_free (t);
107#ifdef _WIN32
108  _endthreadex (0);
109  return 0;
110#endif
111}
112
113tr_thread *
114tr_threadNew (void (*func)(void *), void * arg)
115{
116  tr_thread * t = tr_new0 (tr_thread, 1);
117
118  t->func = func;
119  t->arg  = arg;
120
121#ifdef _WIN32
122  {
123    unsigned int id;
124    t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id);
125    t->thread = (DWORD) id;
126  }
127#else
128  pthread_create (&t->thread, NULL, (void* (*)(void*))ThreadFunc, t);
129  pthread_detach (t->thread);
130#endif
131
132  return t;
133}
134
135/***
136****  LOCKS
137***/
138
139/** @brief portability wrapper around OS-dependent thread mutexes */
140struct tr_lock
141{
142  int                 depth;
143#ifdef _WIN32
144  CRITICAL_SECTION    lock;
145  DWORD               lockThread;
146#else
147  pthread_mutex_t     lock;
148  pthread_t           lockThread;
149#endif
150};
151
152tr_lock*
153tr_lockNew (void)
154{
155  tr_lock * l = tr_new0 (tr_lock, 1);
156
157#ifdef _WIN32
158  InitializeCriticalSection (&l->lock); /* supports recursion */
159#else
160  pthread_mutexattr_t attr;
161  pthread_mutexattr_init (&attr);
162  pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
163  pthread_mutex_init (&l->lock, &attr);
164#endif
165
166  return l;
167}
168
169void
170tr_lockFree (tr_lock * l)
171{
172#ifdef _WIN32
173    DeleteCriticalSection (&l->lock);
174#else
175    pthread_mutex_destroy (&l->lock);
176#endif
177    tr_free (l);
178}
179
180void
181tr_lockLock (tr_lock * l)
182{
183#ifdef _WIN32
184  EnterCriticalSection (&l->lock);
185#else
186  pthread_mutex_lock (&l->lock);
187#endif
188
189  assert (l->depth >= 0);
190  assert (!l->depth || tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
191  l->lockThread = tr_getCurrentThread ();
192  ++l->depth;
193}
194
195int
196tr_lockHave (const tr_lock * l)
197{
198  return (l->depth > 0) &&
199         (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
200}
201
202void
203tr_lockUnlock (tr_lock * l)
204{
205  assert (l->depth > 0);
206  assert (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
207
208  --l->depth;
209  assert (l->depth >= 0);
210#ifdef _WIN32
211  LeaveCriticalSection (&l->lock);
212#else
213  pthread_mutex_unlock (&l->lock);
214#endif
215}
216
217/***
218****  PATHS
219***/
220
221#ifndef _WIN32
222 #include <pwd.h>
223#endif
224
225#ifdef _WIN32
226
227static char *
228win32_get_special_folder (int folder_id)
229{
230  wchar_t path[MAX_PATH]; /* SHGetFolderPath () requires MAX_PATH */
231  *path = L'\0';
232  SHGetFolderPathW (NULL, folder_id, NULL, 0, path);
233  return tr_win32_native_to_utf8 (path, -1);
234}
235
236#endif
237
238static const char *
239getHomeDir (void)
240{
241  static char * home = NULL;
242
243  if (!home)
244    {
245      home = tr_env_get_string ("HOME", NULL);
246
247      if (!home)
248        {
249#ifdef _WIN32
250          home = win32_get_special_folder (CSIDL_PERSONAL);
251#else
252          struct passwd * pw = getpwuid (getuid ());
253          if (pw)
254            home = tr_strdup (pw->pw_dir);
255          endpwent ();
256#endif
257        }
258
259      if (!home)
260        home = tr_strdup ("");
261    }
262
263  return home;
264}
265
266#if defined (__APPLE__) || defined (_WIN32)
267 #define RESUME_SUBDIR  "Resume"
268 #define TORRENT_SUBDIR "Torrents"
269#else
270 #define RESUME_SUBDIR  "resume"
271 #define TORRENT_SUBDIR "torrents"
272#endif
273
274void
275tr_setConfigDir (tr_session * session, const char * configDir)
276{
277  char * path;
278
279  session->configDir = tr_strdup (configDir);
280
281  path = tr_buildPath (configDir, RESUME_SUBDIR, NULL);
282  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
283  session->resumeDir = path;
284
285  path = tr_buildPath (configDir, TORRENT_SUBDIR, NULL);
286  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
287  session->torrentDir = path;
288}
289
290const char *
291tr_sessionGetConfigDir (const tr_session * session)
292{
293  return session->configDir;
294}
295
296const char *
297tr_getTorrentDir (const tr_session * session)
298{
299  return session->torrentDir;
300}
301
302const char *
303tr_getResumeDir (const tr_session * session)
304{
305  return session->resumeDir;
306}
307
308const char*
309tr_getDefaultConfigDir (const char * appname)
310{
311  static char * s = NULL;
312
313  if (!appname || !*appname)
314    appname = "Transmission";
315
316  if (!s)
317    {
318      s = tr_env_get_string ("TRANSMISSION_HOME", NULL);
319
320      if (s == NULL)
321        {
322#ifdef __APPLE__
323          s = tr_buildPath (getHomeDir (), "Library", "Application Support", appname, NULL);
324#elif defined (_WIN32)
325          char * appdata = win32_get_special_folder (CSIDL_APPDATA);
326          s = tr_buildPath (appdata, appname, NULL);
327          tr_free (appdata);
328#elif defined (__HAIKU__)
329          char buf[PATH_MAX];
330          find_directory (B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof (buf));
331          s = tr_buildPath (buf, appname, NULL);
332#else
333          char * const xdg_config_home = tr_env_get_string ("XDG_CONFIG_HOME", NULL);
334          if (xdg_config_home != NULL)
335            {
336              s = tr_buildPath (xdg_config_home, appname, NULL);
337              tr_free (xdg_config_home);
338            }
339          else
340            {
341              s = tr_buildPath (getHomeDir (), ".config", appname, NULL);
342            }
343#endif
344        }
345    }
346
347  return s;
348}
349
350const char*
351tr_getDefaultDownloadDir (void)
352{
353  static char * user_dir = NULL;
354
355  if (user_dir == NULL)
356    {
357      char * config_home;
358      char * config_file;
359      char * content;
360      size_t content_len;
361
362      /* figure out where to look for user-dirs.dirs */
363      config_home = tr_env_get_string ("XDG_CONFIG_HOME", NULL);
364      if (config_home && *config_home)
365        config_file = tr_buildPath (config_home, "user-dirs.dirs", NULL);
366      else
367        config_file = tr_buildPath (getHomeDir (), ".config", "user-dirs.dirs", NULL);
368      tr_free (config_home);
369
370      /* read in user-dirs.dirs and look for the download dir entry */
371      content = (char *) tr_loadFile (config_file, &content_len);
372      if (content && content_len>0)
373        {
374          const char * key = "XDG_DOWNLOAD_DIR=\"";
375          char * line = strstr (content, key);
376          if (line != NULL)
377            {
378              char * value = line + strlen (key);
379              char * end = strchr (value, '"');
380
381              if (end)
382                {
383                  *end = '\0';
384
385                  if (!memcmp (value, "$HOME/", 6))
386                    user_dir = tr_buildPath (getHomeDir (), value+6, NULL);
387                  else if (!strcmp (value, "$HOME"))
388                    user_dir = tr_strdup (getHomeDir ());
389                  else
390                    user_dir = tr_strdup (value);
391                }
392            }
393        }
394
395      if (user_dir == NULL)
396#ifdef __HAIKU__
397        user_dir = tr_buildPath (getHomeDir (), "Desktop", NULL);
398#else
399        user_dir = tr_buildPath (getHomeDir (), "Downloads", NULL);
400#endif
401
402      tr_free (content);
403      tr_free (config_file);
404    }
405
406  return user_dir;
407}
408
409/***
410****
411***/
412
413static bool
414isWebClientDir (const char * path)
415{
416  char * tmp = tr_buildPath (path, "index.html", NULL);
417  const bool ret = tr_sys_path_exists (tmp, NULL);
418  tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp);
419  tr_free (tmp);
420
421  return ret;
422}
423
424const char *
425tr_getWebClientDir (const tr_session * session UNUSED)
426{
427  static char * s = NULL;
428
429  if (!s)
430    {
431      s = tr_env_get_string ("CLUTCH_HOME", NULL);
432      if (s == NULL)
433        s = tr_env_get_string ("TRANSMISSION_WEB_HOME", NULL);
434      if (s == NULL)
435        {
436
437#ifdef BUILD_MAC_CLIENT /* on Mac, look in the Application Support folder first, then in the app bundle. */
438
439          /* Look in the Application Support folder */
440          s = tr_buildPath (tr_sessionGetConfigDir (session), "web", NULL);
441
442          if (!isWebClientDir (s))
443            {
444              tr_free (s);
445
446              CFURLRef appURL = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
447              CFStringRef appRef = CFURLCopyFileSystemPath (appURL,
448                                                            kCFURLPOSIXPathStyle);
449              const CFIndex appStringLength = CFStringGetMaximumSizeOfFileSystemRepresentation (appRef);
450
451              char * appString = tr_malloc (appStringLength);
452              const bool success = CFStringGetFileSystemRepresentation (appRef, appString, appStringLength);
453              assert (success);
454
455              CFRelease (appURL);
456              CFRelease (appRef);
457
458              /* Fallback to the app bundle */
459              s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
460              if (!isWebClientDir (s))
461                {
462                  tr_free (s);
463                  s = NULL;
464                }
465
466              tr_free (appString);
467            }
468
469#elif defined (_WIN32)
470
471          /* Generally, Web interface should be stored in a Web subdir of
472           * calling executable dir. */
473
474          if (s == NULL) /* check personal AppData/Transmission/Web */
475            {
476              char * dir = win32_get_special_folder (CSIDL_COMMON_APPDATA);
477              s = tr_buildPath (dir, "Transmission", "Web", NULL);
478              tr_free (dir);
479              if (!isWebClientDir (s))
480                {
481                  tr_free (s);
482                  s = NULL;
483                }
484            }
485
486          if (s == NULL) /* check personal AppData */
487            {
488              char * dir = win32_get_special_folder (CSIDL_APPDATA);
489              s = tr_buildPath (dir, "Transmission", "Web", NULL);
490              tr_free (dir);
491              if (!isWebClientDir (s))
492                {
493                  tr_free (s);
494                  s = NULL;
495                }
496            }
497
498            if (s == NULL) /* check calling module place */
499              {
500                wchar_t wide_module_path[MAX_PATH];
501                char * module_path;
502                char * dir;
503                GetModuleFileNameW (NULL, wide_module_path,
504                                    sizeof (wide_module_path) / sizeof (*wide_module_path));
505                module_path = tr_win32_native_to_utf8 (wide_module_path, -1);
506                dir = tr_sys_path_dirname (module_path, NULL);
507                tr_free (module_path);
508                s = tr_buildPath (dir, "Web", NULL);
509                tr_free (dir);
510                if (!isWebClientDir (s))
511                  {
512                    tr_free (s);
513                    s = NULL;
514                  }
515            }
516
517#else /* everyone else, follow the XDG spec */
518
519          tr_list *candidates = NULL, *l;
520          char * tmp;
521
522          /* XDG_DATA_HOME should be the first in the list of candidates */
523          tmp = tr_env_get_string ("XDG_DATA_HOME", NULL);
524          if (tmp && *tmp)
525            {
526              tr_list_append (&candidates, tmp);
527            }
528          else
529            {
530              char * dhome = tr_buildPath (getHomeDir (), ".local", "share", NULL);
531              tr_list_append (&candidates, dhome);
532              tr_free (tmp);
533            }
534
535          /* XDG_DATA_DIRS are the backup directories */
536          {
537            const char * pkg = PACKAGE_DATA_DIR;
538            char * xdg = tr_env_get_string ("XDG_DATA_DIRS", NULL);
539            const char * fallback = "/usr/local/share:/usr/share";
540            char * buf = tr_strdup_printf ("%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback);
541            tr_free (xdg);
542            tmp = buf;
543            while (tmp && *tmp)
544              {
545                const char * end = strchr (tmp, ':');
546                if (end)
547                  {
548                    if ((end - tmp) > 1)
549                      tr_list_append (&candidates, tr_strndup (tmp, end - tmp));
550                    tmp = (char *) end + 1;
551                  }
552                else if (tmp && *tmp)
553                  {
554                    tr_list_append (&candidates, tr_strdup (tmp));
555                    break;
556                  }
557              }
558            tr_free (buf);
559          }
560
561          /* walk through the candidates & look for a match */
562          for (l=candidates; l; l=l->next)
563            {
564              char * path = tr_buildPath (l->data, "transmission", "web", NULL);
565              const int found = isWebClientDir (path);
566              if (found)
567                {
568                  s = path;
569                  break;
570                }
571              tr_free (path);
572            }
573
574          tr_list_free (&candidates, tr_free);
575
576#endif
577
578        }
579    }
580
581  return s;
582}
Note: See TracBrowser for help on using the repository browser.