88#include < sys/types.h>
99#include < sys/un.h>
1010#include < unistd.h>
11+ #include < dirent.h>
12+
13+ #include < queue>
14+ #include < string>
15+ #include < utility>
16+ #include < functional>
1117
1218int GetProcessId ()
1319{
@@ -26,14 +32,8 @@ static int MsgFlags = MSG_NOSIGNAL;
2632static int MsgFlags = 0 ;
2733#endif
2834
29- // Directories where the discord-ipc socket might live
30- // https://github.com/flathub/com.discordapp.Discord/wiki/Rich-Precense-(discord-rpc)
31- static const char * DiscordIpcSocketDirs[] = {
32- " " , // Default
33- " snap.discord" , // https://snapcraft.io/discord
34- " .flatpak/com.discordapp.Discord/xdg-run" , // https://flathub.org/apps/com.discordapp.Discord
35- " .flatpak/dev.vencord.Vesktop/xdg-run" , // https://flathub.org/apps/dev.vencord.Vesktop
36- };
35+ static std::string IpcFilenamePrefix = " discord-ipc-" ;
36+ static std::string IpcExtraRootDirPrefixes[] = {" snap." , " .flatpak" };
3737
3838static const char * GetTempPath ()
3939{
@@ -45,6 +45,145 @@ static const char* GetTempPath()
4545 return temp;
4646}
4747
48+ class directory_iterator {
49+ public:
50+ directory_iterator (std::string const & path)
51+ : m_path{path}
52+ {
53+ }
54+ directory_iterator (std::string&& path)
55+ : m_path{std::move (path)}
56+ {
57+ }
58+ directory_iterator (const directory_iterator& o)
59+ : m_path{o.m_path }
60+ {
61+ }
62+ directory_iterator (directory_iterator&& o) noexcept
63+ : m_path(std::move(o.m_path))
64+ , m_stream(std::exchange(o.m_stream, nullptr ))
65+ {
66+ }
67+ directory_iterator& operator =(const directory_iterator& o)
68+ {
69+ if (this == &o)
70+ return *this ;
71+ close ();
72+ this ->m_path = o.m_path ;
73+ return *this ;
74+ }
75+ directory_iterator& operator =(directory_iterator&& o) noexcept
76+ {
77+ if (this == &o)
78+ return *this ;
79+ close ();
80+ m_path = std::move (o.m_path );
81+ m_stream = std::exchange (o.m_stream , nullptr );
82+ return *this ;
83+ }
84+ ~directory_iterator () { close (); }
85+ std::string const & path () const { return m_path; }
86+ DIR* stream () const { return m_stream; }
87+ explicit operator bool () const noexcept { return m_stream != nullptr ; }
88+ bool open ()
89+ {
90+ m_stream = opendir (m_path.c_str ());
91+ return m_stream != nullptr ;
92+ }
93+ struct dirent * next ()
94+ {
95+ if (!m_stream) {
96+ return nullptr ;
97+ }
98+ auto result = readdir (m_stream);
99+ if (!result) {
100+ close ();
101+ }
102+ return result;
103+ }
104+ void close ()
105+ {
106+ if (m_stream) {
107+ closedir (m_stream);
108+ m_stream = nullptr ;
109+ }
110+ }
111+
112+ private:
113+ std::string m_path;
114+ DIR* m_stream{nullptr };
115+ };
116+
117+ static bool discord_ipc_directory_predicate (std::string const & root_parent,
118+ std::string const & parent,
119+ const char * directory)
120+ {
121+ if (parent != root_parent)
122+ return true ;
123+ for (std::string const & prefix : IpcExtraRootDirPrefixes)
124+ if (prefix.compare (0 , std::string::npos, directory, prefix.size ()) == 0 )
125+ return true ;
126+ return false ;
127+ }
128+
129+ static bool discord_ipc_file_predicate (struct dirent * entry)
130+ {
131+ return entry->d_type == DT_SOCK &&
132+ strncmp (IpcFilenamePrefix.c_str (), entry->d_name , IpcFilenamePrefix.size ()) == 0 &&
133+ isdigit (entry->d_name [IpcFilenamePrefix.size ()]);
134+ }
135+
136+ static std::string resolve_path (std::string const & dir, struct dirent * entry)
137+ {
138+ std::string file;
139+ file.reserve (dir.size () + 1 + sizeof (entry->d_name ) + 1 );
140+ file.append (dir);
141+ file.append (" /" );
142+ file.append (entry->d_name );
143+ return file;
144+ }
145+
146+ // Reads the next iterator entry and sees if it satisfies the predicate.
147+ // If there is no next entry, this function returns an empty string. If all
148+ // remaining files read by this iterator are not a matching file, the next
149+ // directory is popped from the queue, the iterator is set to that directory,
150+ // it is opened and the function proceeds with the search using this iterator.
151+ // This is done until either a matching file is found or the queue is empty.
152+ // Once an entry satisfies the predicate, the full file path is returned.
153+ // Otherwise an empty string is returned.
154+ static std::string directory_find_next_recursive (
155+ directory_iterator& directory,
156+ std::function<bool (std::string const & parent, const char * directory)> directory_predicate,
157+ std::function<bool(struct dirent * entry)> filename_predicate,
158+ std::queue<std::string>& directory_queue)
159+ {
160+ while (directory) {
161+ while (auto entry = directory.next ()) {
162+ if (entry->d_type == DT_DIR) {
163+ if (strcmp (entry->d_name , " ." ) == 0 || strcmp (entry->d_name , " .." ) == 0 ) {
164+ continue ;
165+ }
166+ if (!directory_predicate (directory.path (), entry->d_name )) {
167+ continue ;
168+ }
169+ directory_queue.push (resolve_path (directory.path (), entry));
170+ continue ;
171+ }
172+ if (filename_predicate (entry)) {
173+ auto res = resolve_path (directory.path (), entry);
174+ return res;
175+ }
176+ }
177+ directory.close ();
178+ while (!directory && !directory_queue.empty ()) {
179+ directory = directory_iterator (std::move (directory_queue.front ()));
180+ directory.open ();
181+ directory_queue.pop ();
182+ }
183+ }
184+ return " " ;
185+ }
186+
48187/* static*/ BaseConnection* BaseConnection::Create ()
49188{
50189 PipeAddr.sun_family = AF_UNIX;
@@ -71,22 +210,22 @@ bool BaseConnection::Open()
71210 int optval = 1 ;
72211 setsockopt (self->sock , SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof (optval));
73212#endif
74-
75- for ( const char * dir : DiscordIpcSocketDirs) {
76- const char * dir_prefix = dir ? " / " : " " ;
77- for ( int pipeNum = 0 ; pipeNum < 10 ; ++pipeNum) {
78- snprintf (PipeAddr. sun_path ,
79- sizeof (PipeAddr. sun_path ),
80- " %s%s%s/discord-ipc-%d " ,
81- tempPath,
82- dir_prefix,
83- dir,
84- pipeNum);
85- int err = connect (self-> sock , ( const sockaddr*)& PipeAddr, sizeof (PipeAddr ));
86- if (err == 0 ) {
87- self-> isOpen = true ;
88- return true ;
89- }
213+ directory_iterator directory (tempPath);
214+ directory. open ();
215+ std::queue<std::string> queue ;
216+ auto directory_predicate = std::bind (
217+ discord_ipc_directory_predicate, tempPath, std::placeholders::_1, std::placeholders::_2);
218+ while ( true ) {
219+ std::string path = directory_find_next_recursive (
220+ directory, directory_predicate, discord_ipc_file_predicate, queue);
221+ if (path. empty ()) {
222+ break ;
223+ }
224+ snprintf (PipeAddr. sun_path , sizeof ( PipeAddr. sun_path ), " %s " , path. c_str ( ));
225+ int err = connect (self-> sock , ( const sockaddr*)&PipeAddr, sizeof (PipeAddr));
226+ if (err == 0 ) {
227+ self-> isOpen = true ;
228+ return true ;
90229 }
91230 }
92231 self->Close ();
0 commit comments