1/*
2 * libjingle
3 * Copyright 2007 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sstream>
29
30#include "talk/base/common.h"
31#include "talk/base/logging.h"
32#include "talk/base/macutils.h"
33#include "talk/base/scoped_ptr.h"
34#include "talk/base/stringutils.h"
35
36namespace talk_base {
37
38///////////////////////////////////////////////////////////////////////////////
39
40bool ToUtf8(const CFStringRef str16, std::string* str8) {
41  if ((NULL == str16) || (NULL == str8))
42    return false;
43  size_t maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str16),
44                                                    kCFStringEncodingUTF8)
45                  + 1;
46  scoped_array<char> buffer(new char[maxlen]);
47  if (!buffer || !CFStringGetCString(str16, buffer.get(), maxlen,
48                                     kCFStringEncodingUTF8))
49    return false;
50  str8->assign(buffer.get());
51  return true;
52}
53
54bool ToUtf16(const std::string& str8, CFStringRef* str16) {
55  if (NULL == str16)
56    return false;
57  *str16 = CFStringCreateWithBytes(kCFAllocatorDefault,
58                                   reinterpret_cast<const UInt8*>(str8.data()),
59                                   str8.length(), kCFStringEncodingUTF8,
60                                   false);
61  return (NULL != *str16);
62}
63
64#ifdef OSX
65void DecodeFourChar(UInt32 fc, std::string* out) {
66  std::stringstream ss;
67  ss << '\'';
68  bool printable = true;
69  for (int i = 3; i >= 0; --i) {
70    char ch = (fc >> (8 * i)) & 0xFF;
71    if (isprint(static_cast<unsigned char>(ch))) {
72      ss << ch;
73    } else {
74      printable = false;
75      break;
76    }
77  }
78  if (printable) {
79    ss << '\'';
80  } else {
81    ss.str("");
82    ss << "0x" << std::hex << fc;
83  }
84  out->append(ss.str());
85}
86
87static bool GetGestalt(OSType ostype, int* value) {
88  ASSERT(NULL != value);
89  SInt32 native_value;
90  OSStatus result = Gestalt(ostype, &native_value);
91  if (noErr == result) {
92    *value = native_value;
93    return true;
94  }
95  std::string str;
96  DecodeFourChar(ostype, &str);
97  LOG_E(LS_ERROR, OS, result) << "Gestalt(" << str << ")";
98  return false;
99}
100
101bool GetOSVersion(int* major, int* minor, int* bugfix) {
102  ASSERT(major && minor && bugfix);
103  if (!GetGestalt(gestaltSystemVersion, major))
104    return false;
105  if (*major < 0x1040) {
106    *bugfix = *major & 0xF;
107    *minor = (*major >> 4) & 0xF;
108    *major = (*major >> 8);
109    return true;
110  }
111  return GetGestalt(gestaltSystemVersionMajor, major)
112      && GetGestalt(gestaltSystemVersionMinor, minor)
113      && GetGestalt(gestaltSystemVersionBugFix, bugfix);
114}
115
116MacOSVersionName GetOSVersionName() {
117  int major = 0, minor = 0, bugfix = 0;
118  if (!GetOSVersion(&major, &minor, &bugfix))
119    return kMacOSUnknown;
120  if (major > 10) {
121    return kMacOSNewer;
122  }
123  if ((major < 10) || (minor < 3)) {
124    return kMacOSOlder;
125  }
126  switch (minor) {
127    case 3:
128      return kMacOSPanther;
129    case 4:
130      return kMacOSTiger;
131    case 5:
132      return kMacOSLeopard;
133    case 6:
134      return kMacOSSnowLeopard;
135    case 7:
136      return kMacOSLion;
137    case 8:
138      return kMacOSMountainLion;
139  }
140  return kMacOSNewer;
141}
142
143bool GetQuickTimeVersion(std::string* out) {
144  int ver;
145  if (!GetGestalt(gestaltQuickTimeVersion, &ver))
146    return false;
147
148  std::stringstream ss;
149  ss << std::hex << ver;
150  *out = ss.str();
151  return true;
152}
153
154bool RunAppleScript(const std::string& script) {
155  // TODO(thaloun): Add a .mm file that contains something like this:
156  // NSString source from script
157  // NSAppleScript* appleScript = [[NSAppleScript alloc] initWithSource:&source]
158  // if (appleScript != nil) {
159  //   [appleScript executeAndReturnError:nil]
160  //   [appleScript release]
161#ifndef CARBON_DEPRECATED
162  ComponentInstance component = NULL;
163  AEDesc script_desc;
164  AEDesc result_data;
165  OSStatus err;
166  OSAID script_id, result_id;
167
168  AECreateDesc(typeNull, NULL, 0, &script_desc);
169  AECreateDesc(typeNull, NULL, 0, &result_data);
170  script_id = kOSANullScript;
171  result_id = kOSANullScript;
172
173  component = OpenDefaultComponent(kOSAComponentType, typeAppleScript);
174  if (component == NULL) {
175    LOG(LS_ERROR) << "Failed opening Apple Script component";
176    return false;
177  }
178  err = AECreateDesc(typeUTF8Text, script.data(), script.size(), &script_desc);
179  if (err != noErr) {
180    CloseComponent(component);
181    LOG(LS_ERROR) << "Failed creating Apple Script description";
182    return false;
183  }
184
185  err = OSACompile(component, &script_desc, kOSAModeCanInteract, &script_id);
186  if (err != noErr) {
187    AEDisposeDesc(&script_desc);
188    if (script_id != kOSANullScript) {
189      OSADispose(component, script_id);
190    }
191    CloseComponent(component);
192    LOG(LS_ERROR) << "Error compiling Apple Script";
193    return false;
194  }
195
196  err = OSAExecute(component, script_id, kOSANullScript, kOSAModeCanInteract,
197                   &result_id);
198
199  if (err == errOSAScriptError) {
200    LOG(LS_ERROR) << "Error when executing Apple Script: " << script;
201    AECreateDesc(typeNull, NULL, 0, &result_data);
202    OSAScriptError(component, kOSAErrorMessage, typeChar, &result_data);
203    int len = AEGetDescDataSize(&result_data);
204    char* data = (char*) malloc(len);
205    if (data != NULL) {
206      err = AEGetDescData(&result_data, data, len);
207      LOG(LS_ERROR) << "Script error: " << data;
208    }
209    AEDisposeDesc(&script_desc);
210    AEDisposeDesc(&result_data);
211    return false;
212  }
213  AEDisposeDesc(&script_desc);
214  if (script_id != kOSANullScript) {
215    OSADispose(component, script_id);
216  }
217  if (result_id != kOSANullScript) {
218    OSADispose(component, result_id);
219  }
220  CloseComponent(component);
221  return true;
222#else
223  // TODO(thaloun): Support applescripts with the NSAppleScript API.
224  return false;
225#endif  // CARBON_DEPRECATED
226}
227#endif  // OSX
228
229///////////////////////////////////////////////////////////////////////////////
230
231}  // namespace talk_base
232