1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "GMPUtils.h"
8 #include "nsDirectoryServiceDefs.h"
9 #include "nsIFile.h"
10 #include "nsCOMPtr.h"
11 #include "nsLiteralString.h"
12 #include "nsCRTGlue.h"
13 #include "mozilla/Base64.h"
14 #include "nsISimpleEnumerator.h"
15 
16 namespace mozilla {
17 
18 bool
GetEMEVoucherPath(nsIFile ** aPath)19 GetEMEVoucherPath(nsIFile** aPath)
20 {
21   nsCOMPtr<nsIFile> path;
22   NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(path));
23   if (!path) {
24     NS_WARNING("GetEMEVoucherPath can't get NS_GRE_DIR!");
25     return false;
26   }
27   path->AppendNative(NS_LITERAL_CSTRING("voucher.bin"));
28   path.forget(aPath);
29   return true;
30 }
31 
32 bool
EMEVoucherFileExists()33 EMEVoucherFileExists()
34 {
35   nsCOMPtr<nsIFile> path;
36   bool exists;
37   return GetEMEVoucherPath(getter_AddRefs(path)) &&
38          NS_SUCCEEDED(path->Exists(&exists)) &&
39          exists;
40 }
41 
42 void
SplitAt(const char * aDelims,const nsACString & aInput,nsTArray<nsCString> & aOutTokens)43 SplitAt(const char* aDelims,
44         const nsACString& aInput,
45         nsTArray<nsCString>& aOutTokens)
46 {
47   nsAutoCString str(aInput);
48   char* end = str.BeginWriting();
49   const char* start = nullptr;
50   while (!!(start = NS_strtok(aDelims, &end))) {
51     aOutTokens.AppendElement(nsCString(start));
52   }
53 }
54 
55 nsCString
ToBase64(const nsTArray<uint8_t> & aBytes)56 ToBase64(const nsTArray<uint8_t>& aBytes)
57 {
58   nsAutoCString base64;
59   nsDependentCSubstring raw(reinterpret_cast<const char*>(aBytes.Elements()),
60                             aBytes.Length());
61   nsresult rv = Base64Encode(raw, base64);
62   if (NS_WARN_IF(NS_FAILED(rv))) {
63     return NS_LITERAL_CSTRING("[Base64EncodeFailed]");
64   }
65   return base64;
66 }
67 
68 bool
FileExists(nsIFile * aFile)69 FileExists(nsIFile* aFile)
70 {
71   bool exists = false;
72   return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists;
73 }
74 
DirectoryEnumerator(nsIFile * aPath,Mode aMode)75 DirectoryEnumerator::DirectoryEnumerator(nsIFile* aPath, Mode aMode)
76   : mMode(aMode)
77 {
78   aPath->GetDirectoryEntries(getter_AddRefs(mIter));
79 }
80 
81 already_AddRefed<nsIFile>
Next()82 DirectoryEnumerator::Next()
83 {
84   if (!mIter) {
85     return nullptr;
86   }
87   bool hasMore = false;
88   while (NS_SUCCEEDED(mIter->HasMoreElements(&hasMore)) && hasMore) {
89     nsCOMPtr<nsISupports> supports;
90     nsresult rv = mIter->GetNext(getter_AddRefs(supports));
91     if (NS_FAILED(rv)) {
92       continue;
93     }
94 
95     nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
96     if (NS_FAILED(rv)) {
97       continue;
98     }
99 
100     if (mMode == DirsOnly) {
101       bool isDirectory = false;
102       rv = path->IsDirectory(&isDirectory);
103       if (NS_FAILED(rv) || !isDirectory) {
104         continue;
105       }
106     }
107     return path.forget();
108   }
109   return nullptr;
110 }
111 
112 bool
ReadIntoArray(nsIFile * aFile,nsTArray<uint8_t> & aOutDst,size_t aMaxLength)113 ReadIntoArray(nsIFile* aFile,
114               nsTArray<uint8_t>& aOutDst,
115               size_t aMaxLength)
116 {
117   if (!FileExists(aFile)) {
118     return false;
119   }
120 
121   PRFileDesc* fd = nullptr;
122   nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
123   if (NS_FAILED(rv)) {
124     return false;
125   }
126 
127   int32_t length = PR_Seek(fd, 0, PR_SEEK_END);
128   PR_Seek(fd, 0, PR_SEEK_SET);
129 
130   if (length < 0 || (size_t)length > aMaxLength) {
131     NS_WARNING("EME file is longer than maximum allowed length");
132     PR_Close(fd);
133     return false;
134   }
135   aOutDst.SetLength(length);
136   int32_t bytesRead = PR_Read(fd, aOutDst.Elements(), length);
137   PR_Close(fd);
138   return (bytesRead == length);
139 }
140 
141 bool
ReadIntoString(nsIFile * aFile,nsCString & aOutDst,size_t aMaxLength)142 ReadIntoString(nsIFile* aFile,
143                nsCString& aOutDst,
144                size_t aMaxLength)
145 {
146   nsTArray<uint8_t> buf;
147   bool rv = ReadIntoArray(aFile, buf, aMaxLength);
148   if (rv) {
149     buf.AppendElement(0); // Append null terminator, required by nsC*String.
150     aOutDst = nsDependentCString((const char*)buf.Elements(), buf.Length() - 1);
151   }
152   return rv;
153 }
154 
155 bool
Init(nsIFile * aInfoFile)156 GMPInfoFileParser::Init(nsIFile* aInfoFile)
157 {
158   nsTArray<nsCString> lines;
159   static const size_t MAX_GMP_INFO_FILE_LENGTH = 5 * 1024;
160 
161   nsAutoCString info;
162   if (!ReadIntoString(aInfoFile, info, MAX_GMP_INFO_FILE_LENGTH)) {
163     NS_WARNING("Failed to read info file in GMP process.");
164     return false;
165   }
166 
167   // Note: we pass "\r\n" to SplitAt so that we'll split lines delimited
168   // by \n (Unix), \r\n (Windows) and \r (old MacOSX).
169   SplitAt("\r\n", info, lines);
170 
171   for (nsCString line : lines) {
172     // Field name is the string up to but not including the first ':'
173     // character on the line.
174     int32_t colon = line.FindChar(':');
175     if (colon <= 0) {
176       // Not allowed to be the first character.
177       // Info field name must be at least one character.
178       continue;
179     }
180     nsAutoCString key(Substring(line, 0, colon));
181     ToLowerCase(key);
182     key.Trim(" ");
183 
184     nsCString* value = new nsCString(Substring(line, colon + 1));
185     value->Trim(" ");
186     mValues.Put(key, value); // Hashtable assumes ownership of value.
187   }
188 
189   return true;
190 }
191 
192 bool
Contains(const nsCString & aKey) const193 GMPInfoFileParser::Contains(const nsCString& aKey) const {
194   nsCString key(aKey);
195   ToLowerCase(key);
196   return mValues.Contains(key);
197 }
198 
199 nsCString
Get(const nsCString & aKey) const200 GMPInfoFileParser::Get(const nsCString& aKey) const {
201   MOZ_ASSERT(Contains(aKey));
202   nsCString key(aKey);
203   ToLowerCase(key);
204   nsCString* p = nullptr;
205   if (mValues.Get(key, &p)) {
206     return nsCString(*p);
207   }
208   return EmptyCString();
209 }
210 
211 bool
HaveGMPFor(const nsCString & aAPI,nsTArray<nsCString> && aTags)212 HaveGMPFor(const nsCString& aAPI,
213            nsTArray<nsCString>&& aTags)
214 {
215   nsCOMPtr<mozIGeckoMediaPluginService> mps =
216     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
217   if (NS_WARN_IF(!mps)) {
218     return false;
219   }
220 
221   bool hasPlugin = false;
222   if (NS_FAILED(mps->HasPluginForAPI(aAPI, &aTags, &hasPlugin))) {
223     return false;
224   }
225   return hasPlugin;
226 }
227 
228 
229 } // namespace mozilla
230