2003-11-16 Todd Berman <tberman@gentoo.org>
[mono.git] / mcs / tools / gacutil / gacutil.cs
1 // GacUtil
2 //
3 // Author: Todd Berman <tberman@gentoo.org>
4 //
5 // (C) 2003 Todd Berman
6
7 using System;
8 using System.IO;
9 using System.Text;
10 using System.Reflection;
11 using System.Collections;
12
13 namespace Mono.Tools
14 {
15
16         public class Driver
17         {
18
19                 private string gac_path = "/usr/lib/mono/gac/";
20                 
21                 public static void Main (string[] args)
22                 {
23                         Driver d = new Driver ();
24                         d.Run (args);
25                 }
26
27                 public void Run (string[] args)
28                 {
29                         if (args.Length == 0) {
30                                 ShowHelp (false);
31                                 return;
32                         }
33         
34                         if (args[0] == "/user" || args[0] == "--user") {
35                                 //FIXME: Need to check machine.config to make sure this is legal (potential security hole)
36                                 gac_path = Path.Combine (Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".mono"), "gac");
37                                 gac_path += Path.DirectorySeparatorChar;
38
39                                 string[] stripped = new string[args.Length - 1];
40                                 Array.Copy (args, 1, stripped, 0, args.Length - 1);
41                                 args = stripped;
42                         }
43         
44                         string[] remainder_args = new string[args.Length - 1];
45                         
46                         if (args.Length >= 2) {
47                                 Array.Copy (args, 1, remainder_args, 0, args.Length - 1);
48                         }
49                         
50                         switch (args[0]) {
51                                 case "/?":
52                                 case "--help":
53                                         ShowHelp (true);
54                                         break;
55                                 case "/i":
56                                 case "--install":
57                                         InstallAssembly (remainder_args);
58                                         break;
59                                 case "/l":
60                                 case "--ls":
61                                         ListAssemblies (remainder_args);
62                                         break;
63                                 case "/u":
64                                 case "--uninstall":
65                                         UninstallAssemblies (remainder_args);
66                                         break;
67                                 case "/il":
68                                 case "--install-from-list":
69                                         InstallAssembliesFromList (remainder_args);
70                                         break;
71                                 case "/ul":
72                                 case "--uninstall-from-list":
73                                         UninstallAssembliesFromList (remainder_args);
74                                         break;
75                                 default:
76                                         ShowHelp (false);
77                                         break;
78                         }
79                 }
80
81                 public void InstallAssembliesFromList (string[] args)
82                 {
83                         if (args.Length == 0) {
84                                 Console.WriteLine ("ERROR: need a file passed");
85                                 return;
86                         }
87
88                         if (!File.Exists (args[0])) {
89                                 Console.WriteLine ("ERROR: file '" + args[0] + "' does not exist");
90                                 return;
91                         }
92
93                         string[] perFile = args;
94
95                         using (StreamReader s = File.OpenText (args[0])) {
96                                 string line;
97
98                                 while((line = s.ReadLine()) != null) {
99                                         perFile[0] = line;
100                                         InstallAssembly (perFile);
101                                 }
102                         }
103                 }
104
105                 public void UninstallAssembliesFromList (string[] args)
106                 {
107                         if (args.Length == 0) {
108                                 Console.WriteLine ("ERROR: file must be passed.");
109                                 return;
110                         }
111
112                         if (!File.Exists (args[0])) {
113                                 Console.WriteLine ("ERROR: file '" + args[0] + "' does not exist");
114                                 return;
115                         }
116
117                         using (StreamReader s = File.OpenText (args[0])) {
118                                 string line;
119
120                                 while ((line = s.ReadLine ()) != null) {
121                                         UninstallAssemblies (new string[] { line } );
122                                 }
123                         }
124                 }
125
126                 public void UninstallAssemblies (string[] args)
127                 {
128                         if(args.Length == 0) {
129                                 Console.WriteLine ("ERROR: need an argument to uninstall");
130                                 return;
131                         }
132
133                         string joinedArgs = String.Join ("", args);
134
135                         string[] assemblyPieces = joinedArgs.Split(new char[] { ',' });
136
137                         Hashtable paramInfo = new Hashtable ();
138
139                         foreach (string item in assemblyPieces) {
140                                 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
141                                 if(pieces.Length == 1)
142                                         paramInfo["assembly"] = pieces[0];
143                                 else
144                                         paramInfo[pieces[0].ToLower ()] = pieces[1];
145                         }
146
147                         if (!Directory.Exists (Path.Combine (gac_path, (string) paramInfo["assembly"]))) {
148                                 Console.WriteLine ("ERROR: Assembly not in gac.");
149                                 return;
150                         }
151
152                         string searchString = (string) paramInfo["assembly"] + Path.DirectorySeparatorChar;
153
154                         if (paramInfo.Keys.Count != 1) {
155                                 if (paramInfo["version"] != null) {
156                                         searchString += (string) paramInfo["version"] + "*";
157                                 }
158                         } else {
159                                 searchString += "*";
160                         }
161
162                         string[] directories = Directory.GetDirectories (gac_path, searchString);
163
164                         foreach (string dir in directories) {
165                                 Hashtable info = GetAssemblyInfo (Path.Combine (dir, "__AssemblyInfo__"));
166                                 if(Convert.ToInt32 (info["RefCount"]) == 1) {
167                                         Directory.Delete (dir, true);
168                                         Console.WriteLine ("Assembly removed from the gac.");
169                                 } else {
170                                         info["RefCount"] = ((int) Convert.ToInt32 (info["RefCount"]) - 1).ToString ();
171                                         WriteAssemblyInfo (Path.Combine (dir, "__AssemblyInfo__"), info);
172                                         Console.WriteLine ("Assembly was not deleted because its still needed by other applications");
173                                 }
174                         }
175                         if(Directory.GetDirectories (Path.Combine (gac_path, (string) paramInfo["assembly"])).Length == 0) {
176                                 Console.WriteLine ("Cleaning assembly dir, its empty");
177                                 Directory.Delete (Path.Combine (gac_path, (string) paramInfo["assembly"]));
178                         }
179                         
180                 }
181
182                 public void ListAssemblies (string[] args)
183                 {
184                         Console.WriteLine ("The following assemblies are installed into the GAC:");
185                         DirectoryInfo d = new DirectoryInfo (gac_path);
186                         foreach (DirectoryInfo namedDir in d.GetDirectories ()) {
187                                 foreach (DirectoryInfo assemblyDir in namedDir.GetDirectories ()) {
188                                         Hashtable assemblyInfo = GetAssemblyInfo (Path.Combine (assemblyDir.FullName, "__AssemblyInfo__"));
189                                         Console.WriteLine ("\t" + assemblyInfo["DisplayName"]);
190                                 }
191                         }
192                 }
193
194                 private Hashtable GetAssemblyInfo (string filename)
195                 {
196                         Hashtable infoHash = new Hashtable ();
197                         using (StreamReader s = new StreamReader (filename)) {
198                                 string line;
199
200                                 while ((line = s.ReadLine ()) != null) {
201                                         string[] splitStr = line.Split (new char[] { '=' }, 2);
202                                         infoHash[splitStr[0]] = splitStr[1];
203                                 }
204                         }
205                         return infoHash;
206                 }
207
208                 private void WriteAssemblyInfo (string filename, Hashtable info)
209                 {
210                         using (StreamWriter s = File.CreateText (filename)) {
211                                 foreach (string key in info.Keys) {
212                                         s.WriteLine (key + "=" + (string) info[key]);
213                                 }
214                         }
215                 }
216
217                 public void InstallAssembly (string[] args)
218                 {
219                         if(args.Length == 0) {
220                                 Console.WriteLine ("ERROR: You must specify a valid assembly name after the install switch");
221                                 return;
222                         }
223
224                         if(!File.Exists (args[0])) {
225                                 Console.WriteLine ("ERROR: The assembly: '" + args[0] + "' does not exist");
226                                 return;
227                         }
228
229                         AssemblyName an = AssemblyName.GetAssemblyName (args[0]);
230
231                         byte[] pub_tok = an.GetPublicKeyToken ();
232
233                         if (pub_tok == null || pub_tok.Length == 0) {
234                                 Console.WriteLine ("ERROR: assembly has no valid public key token");
235                                 return;
236                         }
237
238                         //FIXME: need to ensure strongly named here.
239
240                         bool force = false;
241
242                         if(args.Length == 2 && (args[1] == "/f" || args[1] == "--force"))
243                                 force = true;
244
245                 
246                         string version_token = an.Version + "__" + GetStringToken (an.GetPublicKeyToken ());
247
248                         string fullPath = String.Format ("{0}{3}{1}{3}{2}{3}", gac_path, an.Name, version_token, Path.DirectorySeparatorChar);
249
250                         if (File.Exists (fullPath + an.Name + ".dll") && force == false) {
251                                 Hashtable assemInfo = GetAssemblyInfo (fullPath + "__AssemblyInfo__");
252                                 assemInfo["RefCount"] = ((int) Convert.ToInt32 (assemInfo["RefCount"]) + 1).ToString ();
253                                 WriteAssemblyInfo (fullPath + "__AssemblyInfo__", assemInfo);
254                                 Console.WriteLine ("RefCount of assembly '" + an.Name + "' increased by one.");
255                                 return;
256                         }
257
258                         if(!EnsureDirectories (an.Name, version_token)) {
259                                 Console.WriteLine ("ERROR: gac directories could not be created, possibly permission issues");
260                                 return;
261                         }
262
263                         File.Copy (args[0], fullPath + an.Name + ".dll", force);
264
265                         Hashtable info = new Hashtable ();
266
267                         info["DisplayName"] = an.FullName;
268                         info["RefCount"] = 1.ToString ();
269
270                         WriteAssemblyInfo (fullPath + "__AssemblyInfo__", info);
271
272                         Console.WriteLine ("Assembly installed into the gac");
273                 }
274
275                 private bool EnsureDirectories (string name, string tok)
276                 {
277                         //FIXME: Workaround for broken DirectoryInfo.CreateSubdirectory
278                         try {
279                                 DirectoryInfo d = new DirectoryInfo (gac_path);
280
281                                 d.CreateSubdirectory (name);
282                                 d = new DirectoryInfo (Path.Combine (gac_path, name));
283                                 d.CreateSubdirectory (tok);
284                         } catch {
285                                 return false;
286                         }
287                         return true;
288                 }
289
290                 private string GetStringToken (byte[] tok)
291                 {
292                         StringBuilder sb = new StringBuilder ();
293                         for (int i = 0; i < tok.Length ; i++) {
294                                 sb.Append (tok[i].ToString ("x2"));
295                         }
296                         return sb.ToString ();
297                 }
298
299                 public void ShowHelp (bool detailed)
300                 {
301                         Console.WriteLine ("help placeholder");
302                 }
303         }
304 }