Skip to content

Commit 4e372c5

Browse files
committed
Plugin Manager V2 WIP
1 parent 1527677 commit 4e372c5

364 files changed

Lines changed: 126856 additions & 327 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

binaryninjaapi.cpp

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,6 @@ bool BinaryNinja::InitPlugins(bool allowUserPlugins)
4949
}
5050

5151

52-
void BinaryNinja::InitCorePlugins()
53-
{
54-
BNInitCorePlugins();
55-
}
56-
57-
58-
void BinaryNinja::InitUserPlugins()
59-
{
60-
BNInitUserPlugins();
61-
}
62-
63-
64-
void BinaryNinja::InitRepoPlugins()
65-
{
66-
BNInitRepoPlugins();
67-
}
68-
69-
7052
string BinaryNinja::GetBundledPluginDirectory()
7153
{
7254
char* path = BNGetBundledPluginDirectory();
@@ -298,7 +280,7 @@ uint32_t BinaryNinja::GetBuildId()
298280
}
299281

300282

301-
void BinaryNinja::SetCurrentPluginLoadOrder(BNPluginLoadOrder order)
283+
void BinaryNinja::SetCurrentPluginLoadOrder(BNPluginLoadPhase order)
302284
{
303285
BNSetCurrentPluginLoadOrder(order);
304286
}

binaryninjaapi.h

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,17 +1980,6 @@ namespace BinaryNinja {
19801980
void DisablePlugins();
19811981
bool IsPluginsEnabled();
19821982
bool InitPlugins(bool allowUserPlugins = true);
1983-
/*!
1984-
\deprecated Use `InitPlugins()`
1985-
*/
1986-
BN_DEPRECATED("Use InitPlugins", "InitPlugins")
1987-
void InitCorePlugins();
1988-
/*!
1989-
\deprecated Use `InitPlugins()`
1990-
*/
1991-
BN_DEPRECATED("Use InitPlugins", "InitPlugins")
1992-
void InitUserPlugins();
1993-
void InitRepoPlugins();
19941983

19951984
std::string GetBundledPluginDirectory();
19961985
void SetBundledPluginDirectory(const std::string& path);
@@ -2033,7 +2022,7 @@ namespace BinaryNinja {
20332022
std::string GetActiveUpdateChannel();
20342023
void SetActiveUpdateChannel(const std::string& channel);
20352024

2036-
void SetCurrentPluginLoadOrder(BNPluginLoadOrder order);
2025+
void SetCurrentPluginLoadOrder(BNPluginLoadPhase order);
20372026
void AddRequiredPluginDependency(const std::string& name);
20382027
void AddOptionalPluginDependency(const std::string& name);
20392028

@@ -19145,13 +19134,25 @@ namespace BinaryNinja {
1914519134
typedef BNPluginStatus PluginStatus;
1914619135
typedef BNPluginType PluginType;
1914719136

19137+
struct ExtensionVersion
19138+
{
19139+
std::string id;
19140+
std::string version;
19141+
19142+
std::string longDescription;
19143+
std::string changelog;
19144+
19145+
uint64_t minimumClientVersion;
19146+
std::string created;
19147+
};
19148+
1914819149
/*!
1914919150
\ingroup pluginmanager
1915019151
*/
19151-
class RepoPlugin : public CoreRefCountObject<BNRepoPlugin, BNNewPluginReference, BNFreePlugin>
19152+
class Extension : public CoreRefCountObject<BNPlugin, BNNewPluginReference, BNFreePlugin>
1915219153
{
1915319154
public:
19154-
RepoPlugin(BNRepoPlugin* plugin);
19155+
Extension(BNPlugin* plugin);
1915519156
PluginStatus GetPluginStatus() const;
1915619157
std::vector<std::string> GetApis() const;
1915719158
std::vector<std::string> GetInstallPlatforms() const;
@@ -19161,20 +19162,22 @@ namespace BinaryNinja {
1916119162
std::string GetPluginDirectory() const;
1916219163
std::string GetAuthor() const;
1916319164
std::string GetDescription() const;
19164-
std::string GetLicenseText() const;
19165-
std::string GetLongdescription() const;
1916619165
std::string GetName() const;
1916719166
std::vector<PluginType> GetPluginTypes() const;
1916819167
std::string GetPackageUrl() const;
1916919168
std::string GetProjectUrl() const;
1917019169
std::string GetAuthorUrl() const;
19171-
std::string GetVersion() const;
19170+
std::vector<ExtensionVersion> GetVersions() const;
19171+
ExtensionVersion GetCurrentVersion() const;
19172+
std::string GetCurrentVersionID() const;
19173+
std::string GetLatestVersionID() const;
19174+
bool IsVersionIDLessThan(const std::string& smaller, const std::string& larger) const;
1917219175
std::string GetCommit() const;
1917319176
std::string GetRepository() const;
1917419177
std::string GetProjectData();
1917519178
VersionInfo GetMinimumVersionInfo() const;
1917619179
VersionInfo GetMaximumVersionInfo() const;
19177-
uint64_t GetLastUpdate();
19180+
std::string GetCreationDate();
1917819181
bool IsViewOnly() const;
1917919182
bool IsBeingDeleted() const;
1918019183
bool IsBeingUpdated() const;
@@ -19188,12 +19191,12 @@ namespace BinaryNinja {
1918819191
bool AreDependenciesBeingInstalled() const;
1918919192

1919019193
bool Uninstall();
19191-
bool Install();
19194+
bool Install(std::string versionID);
1919219195
bool InstallDependencies();
1919319196
// `force` ignores optional checks for platform/api compliance
1919419197
bool Enable(bool force);
1919519198
bool Disable();
19196-
bool Update();
19199+
bool Update(std::string versionID);
1919719200
};
1919819201

1919919202
/*!
@@ -19207,26 +19210,22 @@ namespace BinaryNinja {
1920719210
std::string GetRepoPath() const;
1920819211
std::string GetLocalReference() const;
1920919212
std::string GetRemoteReference() const;
19210-
std::vector<Ref<RepoPlugin>> GetPlugins() const;
19213+
std::vector<Ref<Extension>> GetPlugins() const;
1921119214
std::string GetPluginDirectory() const;
19212-
Ref<RepoPlugin> GetPluginByPath(const std::string& pluginPath);
19215+
Ref<Extension> GetPluginByPath(const std::string& pluginPath);
1921319216
std::string GetFullPath() const;
1921419217
};
1921519218

1921619219
/*!
1921719220
\ingroup pluginmanager
1921819221
*/
19219-
class RepositoryManager :
19220-
public CoreRefCountObject<BNRepositoryManager, BNNewRepositoryManagerReference, BNFreeRepositoryManager>
19222+
class RepositoryManager
1922119223
{
1922219224
public:
19223-
RepositoryManager(const std::string& enabledPluginsPath);
19224-
RepositoryManager(BNRepositoryManager* repoManager);
19225-
RepositoryManager();
19226-
bool CheckForUpdates();
19227-
std::vector<Ref<Repository>> GetRepositories();
19228-
Ref<Repository> GetRepositoryByPath(const std::string& repoName);
19229-
bool AddRepository(const std::string& url, // URL to raw plugins.json file
19225+
static bool CheckForUpdates();
19226+
static std::vector<Ref<Repository>> GetRepositories();
19227+
static Ref<Repository> GetRepositoryByPath(const std::string& repoName);
19228+
static bool AddRepository(const std::string& url, // URL to raw plugins.json file
1923019229
const std::string& repoPath); // Relative path within the repositories directory
1923119230
Ref<Repository> GetDefaultRepository();
1923219231
};

binaryninjacore.h

Lines changed: 72 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,11 @@
216216
extern "C"
217217
{
218218
#endif
219-
BN_ENUM(uint8_t, BNPluginLoadOrder)
219+
BN_ENUM(uint8_t, BNPluginLoadPhase)
220220
{
221-
EarlyPluginLoadOrder,
222-
NormalPluginLoadOrder,
223-
LatePluginLoadOrder
221+
NativePluginLoadPhase,
222+
ScriptingProviderLoadPhase,
223+
ScriptPluginLoadPhase
224224
};
225225

226226
BN_ENUM(uint8_t, PluginLoadStatus)
@@ -231,6 +231,7 @@ extern "C"
231231
};
232232

233233
typedef bool (*BNCorePluginInitFunction)(void);
234+
typedef bool (*BNScriptPluginInitFunction)(const char*, const char*);
234235
typedef void (*BNCorePluginDependencyFunction)(void);
235236
typedef uint32_t (*BNCorePluginABIVersionFunction)(void);
236237

@@ -295,8 +296,7 @@ extern "C"
295296
typedef struct BNMainThreadAction BNMainThreadAction;
296297
typedef struct BNBackgroundTask BNBackgroundTask;
297298
typedef struct BNRepository BNRepository;
298-
typedef struct BNRepoPlugin BNRepoPlugin;
299-
typedef struct BNRepositoryManager BNRepositoryManager;
299+
typedef struct BNPlugin BNPlugin;
300300
typedef struct BNComponent BNComponent;
301301
typedef struct BNSettings BNSettings;
302302
typedef struct BNMetadata BNMetadata;
@@ -354,6 +354,18 @@ extern "C"
354354
typedef struct BNStringRecognizer BNStringRecognizer;
355355
typedef struct BNCustomStringType BNCustomStringType;
356356

357+
typedef struct BNPluginVersion
358+
{
359+
char* id;
360+
char* versionString;
361+
char* longDescription;
362+
char* changelog;
363+
364+
uint64_t minimumClientVersion;
365+
char* created;
366+
367+
} BNPluginVersion;
368+
357369
typedef struct BNRemoteFileSearchMatch
358370
{
359371
char* projectId;
@@ -4088,11 +4100,8 @@ extern "C"
40884100

40894101
// Plugin initialization
40904102
BINARYNINJACOREAPI bool BNInitPlugins(bool allowUserPlugins);
4091-
BINARYNINJACOREAPI bool BNInitCorePlugins(void); // Deprecated, use BNInitPlugins
40924103
BINARYNINJACOREAPI void BNDisablePlugins(void);
40934104
BINARYNINJACOREAPI bool BNIsPluginsEnabled(void);
4094-
BINARYNINJACOREAPI void BNInitUserPlugins(void); // Deprecated, use BNInitPlugins
4095-
BINARYNINJACOREAPI void BNInitRepoPlugins(void);
40964105

40974106
BINARYNINJACOREAPI char* BNGetInstallDirectory(void);
40984107
BINARYNINJACOREAPI char* BNGetBundledPluginDirectory(void);
@@ -4110,7 +4119,7 @@ extern "C"
41104119
BINARYNINJACOREAPI bool BNExecuteWorkerProcess(const char* path, const char** args, BNDataBuffer* input,
41114120
char** output, char** error, bool stdoutIsText, bool stderrIsText);
41124121

4113-
BINARYNINJACOREAPI void BNSetCurrentPluginLoadOrder(BNPluginLoadOrder order);
4122+
BINARYNINJACOREAPI void BNSetCurrentPluginLoadOrder(BNPluginLoadPhase order);
41144123
BINARYNINJACOREAPI void BNAddRequiredPluginDependency(const char* name);
41154124
BINARYNINJACOREAPI void BNAddOptionalPluginDependency(const char* name);
41164125

@@ -7940,75 +7949,73 @@ extern "C"
79407949
BNType** outType, BNQualifiedName* outVarName, BNBinaryView* view, bool simplify);
79417950

79427951
// Plugin repository APIs
7943-
BINARYNINJACOREAPI char** BNPluginGetApis(BNRepoPlugin* p, size_t* count);
7944-
BINARYNINJACOREAPI const char* BNPluginGetAuthor(BNRepoPlugin* p);
7945-
BINARYNINJACOREAPI const char* BNPluginGetDescription(BNRepoPlugin* p);
7946-
BINARYNINJACOREAPI const char* BNPluginGetLicenseText(BNRepoPlugin* p);
7947-
BINARYNINJACOREAPI const char* BNPluginGetLongdescription(BNRepoPlugin* p);
7948-
BINARYNINJACOREAPI BNVersionInfo BNPluginGetMinimumVersionInfo(BNRepoPlugin* p);
7949-
BINARYNINJACOREAPI BNVersionInfo BNPluginGetMaximumVersionInfo(BNRepoPlugin* p);
7952+
BINARYNINJACOREAPI char** BNPluginGetApis(BNPlugin* p, size_t* count);
7953+
BINARYNINJACOREAPI const char* BNPluginGetAuthor(BNPlugin* p);
7954+
BINARYNINJACOREAPI const char* BNPluginGetDescription(BNPlugin* p);
7955+
BINARYNINJACOREAPI BNVersionInfo BNPluginGetMinimumVersionInfo(BNPlugin* p);
7956+
BINARYNINJACOREAPI BNVersionInfo BNPluginGetMaximumVersionInfo(BNPlugin* p);
79507957
BINARYNINJACOREAPI BNVersionInfo BNParseVersionString(const char* v);
79517958
BINARYNINJACOREAPI bool BNVersionLessThan(const BNVersionInfo smaller, const BNVersionInfo larger);
7952-
BINARYNINJACOREAPI const char* BNPluginGetName(BNRepoPlugin* p);
7953-
BINARYNINJACOREAPI const char* BNPluginGetProjectUrl(BNRepoPlugin* p);
7954-
BINARYNINJACOREAPI const char* BNPluginGetPackageUrl(BNRepoPlugin* p);
7955-
BINARYNINJACOREAPI const char* BNPluginGetAuthorUrl(BNRepoPlugin* p);
7956-
BINARYNINJACOREAPI const char* BNPluginGetVersion(BNRepoPlugin* p);
7957-
BINARYNINJACOREAPI const char* BNPluginGetCommit(BNRepoPlugin* p);
7958-
BINARYNINJACOREAPI const bool BNPluginGetViewOnly(BNRepoPlugin* p);
7959+
BINARYNINJACOREAPI bool BNPluginVersionIDLessThan(BNPlugin* p, const char* smaller, const char* larger);
7960+
BINARYNINJACOREAPI const char* BNPluginGetName(BNPlugin* p);
7961+
BINARYNINJACOREAPI const char* BNPluginGetProjectUrl(BNPlugin* p);
7962+
BINARYNINJACOREAPI const char* BNPluginGetPackageUrl(BNPlugin* p);
7963+
BINARYNINJACOREAPI const char* BNPluginGetAuthorUrl(BNPlugin* p);
7964+
BINARYNINJACOREAPI BNPluginVersion* BNPluginGetVersions(BNPlugin* p, size_t* count);
7965+
BINARYNINJACOREAPI void BNFreePluginVersions(BNPluginVersion* r, size_t count);
7966+
BINARYNINJACOREAPI const char* BNPluginGetCurrentVersionID(BNPlugin* p);
7967+
BINARYNINJACOREAPI BNPluginVersion BNPluginGetCurrentVersion(BNPlugin* p);
7968+
BINARYNINJACOREAPI void BNPluginFreeVersion(BNPluginVersion v);
7969+
BINARYNINJACOREAPI const char* BNPluginGetCommit(BNPlugin* p);
7970+
BINARYNINJACOREAPI const bool BNPluginGetViewOnly(BNPlugin* p);
79597971
BINARYNINJACOREAPI void BNFreePluginTypes(BNPluginType* r);
7960-
BINARYNINJACOREAPI BNRepoPlugin* BNNewPluginReference(BNRepoPlugin* r);
7961-
BINARYNINJACOREAPI void BNFreePlugin(BNRepoPlugin* plugin);
7962-
BINARYNINJACOREAPI const char* BNPluginGetPath(BNRepoPlugin* p);
7963-
BINARYNINJACOREAPI const char* BNPluginGetSubdir(BNRepoPlugin* p);
7964-
BINARYNINJACOREAPI const char* BNPluginGetDependencies(BNRepoPlugin* p);
7965-
BINARYNINJACOREAPI bool BNPluginIsInstalled(BNRepoPlugin* p);
7966-
BINARYNINJACOREAPI bool BNPluginIsEnabled(BNRepoPlugin* p);
7967-
BINARYNINJACOREAPI BNPluginStatus BNPluginGetPluginStatus(BNRepoPlugin* p);
7968-
BINARYNINJACOREAPI BNPluginType* BNPluginGetPluginTypes(BNRepoPlugin* p, size_t* count);
7969-
BINARYNINJACOREAPI bool BNPluginEnable(BNRepoPlugin* p, bool force);
7970-
BINARYNINJACOREAPI bool BNPluginDisable(BNRepoPlugin* p);
7971-
BINARYNINJACOREAPI bool BNPluginInstall(BNRepoPlugin* p);
7972-
BINARYNINJACOREAPI bool BNPluginInstallDependencies(BNRepoPlugin* p);
7973-
BINARYNINJACOREAPI bool BNPluginUninstall(BNRepoPlugin* p);
7974-
BINARYNINJACOREAPI bool BNPluginUpdate(BNRepoPlugin* p);
7975-
BINARYNINJACOREAPI char** BNPluginGetPlatforms(BNRepoPlugin* p, size_t* count);
7972+
BINARYNINJACOREAPI BNPlugin* BNNewPluginReference(BNPlugin* r);
7973+
BINARYNINJACOREAPI void BNFreePlugin(BNPlugin* plugin);
7974+
BINARYNINJACOREAPI const char* BNPluginGetPath(BNPlugin* p);
7975+
BINARYNINJACOREAPI const char* BNPluginGetSubdir(BNPlugin* p);
7976+
BINARYNINJACOREAPI const char* BNPluginGetDependencies(BNPlugin* p);
7977+
BINARYNINJACOREAPI bool BNPluginIsInstalled(BNPlugin* p);
7978+
BINARYNINJACOREAPI bool BNPluginIsEnabled(BNPlugin* p);
7979+
BINARYNINJACOREAPI BNPluginStatus BNPluginGetPluginStatus(BNPlugin* p);
7980+
BINARYNINJACOREAPI BNPluginType* BNPluginGetPluginTypes(BNPlugin* p, size_t* count);
7981+
BINARYNINJACOREAPI bool BNPluginEnable(BNPlugin* p, bool force);
7982+
BINARYNINJACOREAPI bool BNPluginDisable(BNPlugin* p);
7983+
BINARYNINJACOREAPI bool BNPluginInstall(BNPlugin* p, const char* versionID);
7984+
BINARYNINJACOREAPI bool BNPluginInstallDependencies(BNPlugin* p);
7985+
BINARYNINJACOREAPI bool BNPluginUninstall(BNPlugin* p);
7986+
BINARYNINJACOREAPI bool BNPluginUpdate(BNPlugin* p, const char* versionID);
7987+
BINARYNINJACOREAPI char** BNPluginGetPlatforms(BNPlugin* p, size_t* count);
79767988
BINARYNINJACOREAPI void BNFreePluginPlatforms(char** platforms, size_t count);
7977-
BINARYNINJACOREAPI const char* BNPluginGetRepository(BNRepoPlugin* p);
7978-
BINARYNINJACOREAPI bool BNPluginIsBeingDeleted(BNRepoPlugin* p);
7979-
BINARYNINJACOREAPI bool BNPluginIsBeingUpdated(BNRepoPlugin* p);
7980-
BINARYNINJACOREAPI bool BNPluginIsRunning(BNRepoPlugin* p);
7981-
BINARYNINJACOREAPI bool BNPluginIsUpdatePending(BNRepoPlugin* p);
7982-
BINARYNINJACOREAPI bool BNPluginIsDisablePending(BNRepoPlugin* p);
7983-
BINARYNINJACOREAPI bool BNPluginIsDeletePending(BNRepoPlugin* p);
7984-
BINARYNINJACOREAPI bool BNPluginIsUpdateAvailable(BNRepoPlugin* p);
7985-
BINARYNINJACOREAPI bool BNPluginAreDependenciesBeingInstalled(BNRepoPlugin* p);
7986-
7987-
BINARYNINJACOREAPI char* BNPluginGetProjectData(BNRepoPlugin* p);
7988-
BINARYNINJACOREAPI uint64_t BNPluginGetLastUpdate(BNRepoPlugin* p);
7989+
BINARYNINJACOREAPI const char* BNPluginGetRepository(BNPlugin* p);
7990+
BINARYNINJACOREAPI bool BNPluginIsBeingDeleted(BNPlugin* p);
7991+
BINARYNINJACOREAPI bool BNPluginIsBeingUpdated(BNPlugin* p);
7992+
BINARYNINJACOREAPI bool BNPluginIsRunning(BNPlugin* p);
7993+
BINARYNINJACOREAPI bool BNPluginIsUpdatePending(BNPlugin* p);
7994+
BINARYNINJACOREAPI bool BNPluginIsDisablePending(BNPlugin* p);
7995+
BINARYNINJACOREAPI bool BNPluginIsDeletePending(BNPlugin* p);
7996+
BINARYNINJACOREAPI bool BNPluginIsUpdateAvailable(BNPlugin* p);
7997+
BINARYNINJACOREAPI bool BNPluginAreDependenciesBeingInstalled(BNPlugin* p);
7998+
7999+
BINARYNINJACOREAPI char* BNPluginGetProjectData(BNPlugin* p);
8000+
BINARYNINJACOREAPI char* BNPluginGetCurrentVersionCreationDate(BNPlugin* p);
79898001

79908002
BINARYNINJACOREAPI BNRepository* BNNewRepositoryReference(BNRepository* r);
79918003
BINARYNINJACOREAPI void BNFreeRepository(BNRepository* r);
79928004
BINARYNINJACOREAPI char* BNRepositoryGetUrl(BNRepository* r);
79938005
BINARYNINJACOREAPI char* BNRepositoryGetRepoPath(BNRepository* r);
7994-
BINARYNINJACOREAPI BNRepoPlugin** BNRepositoryGetPlugins(BNRepository* r, size_t* count);
7995-
BINARYNINJACOREAPI void BNFreeRepositoryPluginList(BNRepoPlugin** r);
8006+
BINARYNINJACOREAPI BNPlugin** BNRepositoryGetPlugins(BNRepository* r, size_t* count);
8007+
BINARYNINJACOREAPI void BNFreeRepositoryPluginList(BNPlugin** r);
79968008
BINARYNINJACOREAPI void BNRepositoryFreePluginDirectoryList(char** list, size_t count);
7997-
BINARYNINJACOREAPI BNRepoPlugin* BNRepositoryGetPluginByPath(BNRepository* r, const char* pluginPath);
8009+
BINARYNINJACOREAPI BNPlugin* BNRepositoryGetPluginByPath(BNRepository* r, const char* pluginPath);
79988010
BINARYNINJACOREAPI const char* BNRepositoryGetPluginsPath(BNRepository* r);
79998011

8000-
BINARYNINJACOREAPI BNRepositoryManager* BNCreateRepositoryManager(const char* enabledPluginsPath);
8001-
BINARYNINJACOREAPI BNRepositoryManager* BNNewRepositoryManagerReference(BNRepositoryManager* r);
8002-
BINARYNINJACOREAPI void BNFreeRepositoryManager(BNRepositoryManager* r);
8003-
BINARYNINJACOREAPI bool BNRepositoryManagerCheckForUpdates(BNRepositoryManager* r);
8004-
BINARYNINJACOREAPI BNRepository** BNRepositoryManagerGetRepositories(BNRepositoryManager* r, size_t* count);
8012+
BINARYNINJACOREAPI bool BNRepositoryManagerCheckForUpdates();
8013+
BINARYNINJACOREAPI BNRepository** BNRepositoryManagerGetRepositories(size_t* count);
80058014
BINARYNINJACOREAPI void BNFreeRepositoryManagerRepositoriesList(BNRepository** r);
8006-
BINARYNINJACOREAPI bool BNRepositoryManagerAddRepository(
8007-
BNRepositoryManager* r, const char* url, const char* repoPath);
8008-
BINARYNINJACOREAPI BNRepository* BNRepositoryGetRepositoryByPath(BNRepositoryManager* r, const char* repoPath);
8009-
BINARYNINJACOREAPI BNRepositoryManager* BNGetRepositoryManager(void);
8015+
BINARYNINJACOREAPI bool BNRepositoryManagerAddRepository(const char* url, const char* repoPath);
8016+
BINARYNINJACOREAPI BNRepository* BNRepositoryGetRepositoryByPath(const char* repoPath);
80108017

8011-
BINARYNINJACOREAPI BNRepository* BNRepositoryManagerGetDefaultRepository(BNRepositoryManager* r);
8018+
BINARYNINJACOREAPI BNRepository* BNRepositoryManagerGetDefaultRepository();
80128019

80138020
// Components
80148021

0 commit comments

Comments
 (0)