How to relaunch the moved application to a new location? - applescript-objc

I'm trying to simulate what Lets Move does, that is, when the user runs the application he checks to see if the application is running from within the Applications folder if he is not, he displays an alert asking the user to copy the application for applications folder, if he clicks on the button "Move to Applications Folder" he moves the application but I can't relaunch the application from the new location. I would like to know how to do this, thanks in advance.
on moveMyApp()
set checkpath to ((path to "apps" as string) & "Lets Move")
set myApp to ((path to current application as text))
tell application "Finder"
if exists file checkpath then
return
else
tell current application
display alert "Move to Applications Folder?" buttons {"Not Move", "Move to Applications Folder"} default button 2
set response to button returned of the result
if response is "Move to Applications Folder" then
do shell script "mv " & quoted form of POSIX path of myApp & space & "/Applications"
end if
end tell
tell application "Lets Move" to quit
delay 2
end if
end tell
tell application "Lets Move" to activate
end moveMyApp

The Objective-C class you are adapting this from does a bunch of other things, such as getting authorization and clearing the quarantine flag, but you can just tell the app to terminate after launching an NSTask that waits a bit before relaunching the app. For example, the quit and activate statements can be replaced with a single call to a handler that performs the relaunch task:
on moveMyApp()
set checkpath to ((path to "apps" as string) & "Lets Move.app")
set myApp to ((path to current application as text))
tell application "Finder" to set moved to exists file checkpath
if not moved then
display alert "Move to Applications Folder?" buttons {"Do Not Move", "Move to Applications Folder"} default button 2
set response to button returned of the result
if response is "Move to Applications Folder" then
do shell script "mv " & quoted form of POSIX path of myApp & space & "/Applications"
relaunch(checkpath)
end if
end if
end moveMyApp
on relaunch(appPath)
tell current application's NSTask's alloc()'s init()
its setLaunchPath:"/bin/sh"
its setArguments:{"-c", "sleep 1.0; open -a " & quoted form of "Lets Move"}
its |launch|()
end tell
tell current application's NSApp to terminate:me
end relaunch
Note that although I cleaned up your handler a little, no error handling is done (timing issues, failure to move the app, etc). Tested with Mojave and Xcode 10, moving the app to the user ~/Applications folder.

Related

How to run AppleScript from C++ in macOS sandbox environment without entitlement violations

I am trying to use AppleScript to control the Apple Photos app from within a QT C++ app that must run in the sandbox environment required for the macOS App Store.
I have tried to run the AppleScript via a QProcess launching osascript, like this :
const QString aScript = QString(
"tell application \"Photos\"\n"
" set selMedia to (get media items whose id contains \"%1\")\n"
" if not (album \"Trash from %2\" exists) then\n"
" make new album named \"Trash from %2\"\n"
" end if\n"
" add selMedia to album \"Trash from %2\"\n"
"end tell").arg(fileNameNoExt, APP_NAME);
QProcess script;
script.setProgram("osascript");
script.setArguments(QStringList() <<
"-e" << aScript);
script.start();
script.waitForFinished();
int exitCode = script.exitCode();
if(exitCode!=0){
QString outMsg = script.readAllStandardOutput();
QString errorOut = script.readAllStandardError();
// Warn user and output message...
}
This works fine outside the sandbox, adding the desired items from the library to the desired album. However, inside the sandbox, I get an errAEPrivilegeError. Looking in the Console for the error event, it states :
AppleEvents/sandbox: Returning errAEPrivilegeError/-10004 and denying dispatch of event core/getd from process '<private>'/0x0-0x4d84d8, pid=31654, because it is not entitled to send an AppleEvent to this process.
Here is my entitlements file when signing the app :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.scripting-targets</key>
<dict>
<key>com.apple.Photos</key>
<array>
<string>com.apple.Photos.library.read-write</string>
</array>
</dict>
</dict>
</plist>
When the app is first run (sandboxed) and the AppleScript is started, I get the system permission box, saying that the app is asking to control the Photos app. I click OK of course, but then I get the error. And upon next runs, as the permission is already granted, no system permission box comes up, but it fails nonetheless. When I reset the permissions with the terminal command tccutil reset All [my app bundle id], then upon the next execution of the app and the AppleScript, I get the permission box again.
If I delete the com.apple.security.scripting-targets entitlement, I get no system permission request and the error is different : from my app the error output states Photos isn't running, from the console it says it was denied due to sandboxing. If I keep the scripting-targets and com.apple.Photos but delete library.read-write I get the errAEPrivilegeError again.
I have tried to only test the make new album named "Trash [...]"but this still fails with errAEPrivilegeError. Even if only telling Photos to quit with AppleScript (which according to Photos.app/Contents/Resources/Photos.sdef doesn't require any specific permissions appart from scripting, I also get errAEPrivilegeError. The only command that seems to work is activate.
Also, if I try to run the qprocess not as a separate process, with QProcess::execute (if maybe the sandbox problem is because it is not the main app running the script ?), I still get the same errAEPrivilegeError.
What am I missing for it to work, or what other method should I use to run an applescript from C++/QT when in a sandbox ?
Did you include NSAppleEventsUsageDescription in your app’s Info.plist?
[ETA]
OK, after further digging I can confirm the behavior you’re seeing. With the correct app entitlements an in-process NSAppleScript can send AEs to Photos; however, the same process is unable to send AEs from a child osascript process.
According to Apple docs (which are lousy), a child process must have exactly 2 entitlements to inherit its parent’s: com.apple.security.app-sandbox and com.apple.security.inherit. However, running codesign --display --entitlements - /usr/bin/osascript shows osascript has neither of these but a bunch of unrelated entitlements. So this looks like Apple’s screwup in how they’ve built osascript.
When running AppleScripts that are part of a Swift/ObjC app, I generally recommend using the AppleScript-ObjC bridge to load and call your AppleScript handlers directly; no subprocess required. I have no idea how to do that in a Qt app though.
The simplest solution may be to write your own command-line executable/XPC service in ObjC which runs your AppleScript code via NSAppleScript, giving it the com.apple.security.app-sandbox and com.apple.security.inherit entitlements, and embed it in your Qt app’s main bundle.
(You may also need to add com.apple.security.automation.apple-events in the main app’s entitlements if the runtime is hardened.)
…
One more thing: avoid generating AppleScript code via string interpolation. If you want to parameterize a script, pack your values as parameters in an Apple event and call the corresponding AS handler via -executeAppleEvent:error:.

Open a c++ application installed on computer with a custom url in browser [duplicate]

How do i set up a custom protocol handler in chrome? Something like:
myprotocol://testfile
I would need this to send a request to http://example.com?query=testfile, then send the httpresponse to my extension.
The following method registers an application to a URI Scheme. So, you can use mycustproto: in your HTML code to trigger a local application. It works on a Google Chrome Version 51.0.2704.79 m (64-bit).
I mainly used this method for printing document silently without the print dialog popping up. The result is pretty good and is a seamless solution to integrate the external application with the browser.
HTML code (simple):
Click Me
HTML code (alternative):
<input id="DealerName" />
<button id="PrintBtn"></button>
$('#PrintBtn').on('click', function(event){
event.preventDefault();
window.location.href = 'mycustproto:dealer ' + $('#DealerName').val();
});
URI Scheme will look like this:
You can create the URI Scheme manually in registry, or run the "mycustproto.reg" file (see below).
HKEY_CURRENT_USER\Software\Classes
mycustproto
(Default) = "URL:MyCustProto Protocol"
URL Protocol = ""
DefaultIcon
(Default) = "myprogram.exe,1"
shell
open
command
(Default) = "C:\Program Files\MyProgram\myprogram.exe" "%1"
mycustproto.reg example:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\mycustproto]
"URL Protocol"="\"\""
#="\"URL:MyCustProto Protocol\""
[HKEY_CURRENT_USER\Software\Classes\mycustproto\DefaultIcon]
#="\"mycustproto.exe,1\""
[HKEY_CURRENT_USER\Software\Classes\mycustproto\shell]
[HKEY_CURRENT_USER\Software\Classes\mycustproto\shell\open]
[HKEY_CURRENT_USER\Software\Classes\mycustproto\shell\open\command]
#="\"C:\\Program Files\\MyProgram\\myprogram.exe\" \"%1\""
C# console application - myprogram.exe:
using System;
using System.Collections.Generic;
using System.Text;
namespace myprogram
{
class Program
{
static string ProcessInput(string s)
{
// TODO Verify and validate the input
// string as appropriate for your application.
return s;
}
static void Main(string[] args)
{
Console.WriteLine("Raw command-line: \n\t" + Environment.CommandLine);
Console.WriteLine("\n\nArguments:\n");
foreach (string s in args)
{
Console.WriteLine("\t" + ProcessInput(s));
}
Console.WriteLine("\nPress any key to continue...");
Console.ReadKey();
}
}
}
Try to run the program first to make sure the program has been placed in the correct path:
cmd> "C:\Program Files\MyProgram\myprogram.exe" "mycustproto:Hello World"
Click the link on your HTML page:
You will see a warning window popup for the first time.
To reset the external protocol handler setting in Chrome:
If you have ever accepted the custom protocol in Chrome and would like to reset the setting, do this (currently, there is no UI in Chrome to change the setting):
Edit "Local State" this file under this path:
C:\Users\Username\AppData\Local\Google\Chrome\User Data\
or Simply go to:
%USERPROFILE%\AppData\Local\Google\Chrome\User Data\
Then, search for this string: protocol_handler
You will see the custom protocol from there.
Note: Please close your Google Chrome before editing the file. Otherwise, the change you have made will be overwritten by Chrome.
Reference:
https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
Chrome 13 now supports the navigator.registerProtocolHandler API. For example,
navigator.registerProtocolHandler(
'web+custom', 'http://example.com/rph?q=%s', 'My App');
Note that your protocol name has to start with web+, with a few exceptions for common ones (like mailto, etc). For more details, see: http://updates.html5rocks.com/2011/06/Registering-a-custom-protocol-handler
This question is old now, but there's been a recent update to Chrome (at least where packaged apps are concerned)...
http://developer.chrome.com/apps/manifest/url_handlers
and
https://github.com/GoogleChrome/chrome-extensions-samples/blob/e716678b67fd30a5876a552b9665e9f847d6d84b/apps/samples/url-handler/README.md
It allows you to register a handler for a URL (as long as you own it). Sadly no myprotocol:// but at least you can do http://myprotocol.mysite.com and can create a webpage there that points people to the app in the app store.
This is how I did it. Your app would need to install a few reg keys on installation, then in any browser you can just link to foo:\anythingHere.txt and it will open your app and pass it that value.
This is not my code, just something I found on the web when searching the same question. Just change all "foo" in the text below to the protocol name you want and change the path to your exe as well.
(put this in to a text file as save as foo.reg on your desktop, then double click it to install the keys)
-----Below this line goes into the .reg file (NOT including this line)------
REGEDIT4
[HKEY_CLASSES_ROOT\foo]
#="URL:foo Protocol"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\foo\shell]
[HKEY_CLASSES_ROOT\foo\shell\open]
[HKEY_CLASSES_ROOT\foo\shell\open\command]
#="\"C:\\Program Files (x86)\\Notepad++\\notepad++.exe\" \"%1\""
Not sure whether this is the right place for my answer, but as I found very few helpful threads and this was one of them, I am posting my solution here.
Problem: I wanted Linux Mint 19.2 Cinnamon to open Evolution when clicking on mailto links in Chromium. Gmail was registered as default handler in chrome://settings/handlers and I could not choose any other handler.
Solution:
Use the xdg-settings in the console
xdg-settings set default-url-scheme-handler mailto org.gnome.Evolution.desktop
Solution was found here https://alt.os.linux.ubuntu.narkive.com/U3Gy7inF/kubuntu-mailto-links-in-chrome-doesn-t-open-evolution and adapted for my case.
I've found the solution by Jun Hsieh and MuffinMan generally works when it comes to clicking links on pages in Chrome or pasting into the URL bar, but it doesn't seem to work in a specific case of passing the string on the command line.
For example, both of the following commands open a blank Chrome window which then does nothing.
"c:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "foo://C:/test.txt"
"c:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --new-window "foo://C:/test.txt"
For comparison, feeding Chrome an http or https URL with either of these commands causes the web page to be opened.
This became apparent because one of our customers reported that clicking links for our product from a PDF being displayed within Adobe Reader fails to invoke our product when Chrome is the default browser. (It works fine with MSIE and Firefox as default, but not when either Chrome or Edge are default.)
I'm guessing that instead of just telling Windows to invoke the URL and letting Windows figure things out, the Adobe product is finding the default browser, which is Chrome in this case, and then passing the URL on the command line.
I'd be interested if anyone knows of Chrome security or other settings which might be relevant here so that Chrome will fully handle a protocol handler, even if it's provided via the command line. I've been looking but so far haven't found anything.
I've been testing this against Chrome 88.0.4324.182.
open
C:\Users\<Username>\AppData\Local\Google\Chrome\User Data\Default
open Preferences then search for excluded_schemes you will find it in 'protocol_handler' delete this excluded scheme(s) to reset chrome to open url with default application

sg-cookie-optin extension throws No class named SGalinski\SgCookieOptin\Hook\LicenceCheckHook

Downloaded this https://packagist.org/packages/sgalinski/sg-cookie-optin
Copied the file to the server, activated the extension in ext manager.
Suddenly front & backend not working anymore.
Deleted the extension manually from PackageStates.php according to
https://docs.typo3.org/m/typo3/guide-installation/master/en-us/ExtensionInstallation/Index.html
Frontend now working, backend still throws the error
No class named SGalinski\SgCookieOptin\Hook\LicenceCheckHook
in /home/sc/wwn/typo3_src-10.4.10/typo3/sysext/core/Classes/Utility/GeneralUtility.php line 3340
throw new \InvalidArgumentException($errorMsg, 1294585865);
}
} else {
$errorMsg = 'No class named ' . $parts[0];
throw new \InvalidArgumentException($errorMsg, 1294585866);
}
} elseif (function_exists($funcName) && is_callable($funcName)) {
// It's a function
$content = call_user_func_array($funcName, [&$params, &$ref]);
I did set up the key and the output folder in the config file
what I didn't do since backend not working anymore :
3. Add the static TypoScript named "Cookie Optin" to your instance with the "Template" backend module.
Open up the "Template" module in the backend of TYPO3.
Go to your root site page within the page tree.
Choose "Info/Modify" at the select on the top.
Click on the button "Edit the whole template record".
Select the tab "Includes".
Choose the template "Cookie Optin (sg_cookie_optin)" on the multi select box with the name "Include static (from extensions)"
Save
4. Go into the "Cookie Opt In" backend module, configure it and save it once.
Any idea how i can fix that? Pretty stuck atm.
The install tool of your installation should still work. Try to open yourdomain.tld/typo3/install.php.
To enable the Install Tool, the file ENABLE_INSTALL_TOOL must be created in the directory typo3conf/. Make sure that the file has no file extension like .txt.
Then log in to the install tool and go to Maintenance => Flush TYPO3 and PHP Cache and flush all caches.
Then the backend should work.
If the install tool is not accessible, try to find a folder var/Cache. Most probably it will be located in typo3temp. Delete all files and subfolders from the Cache folder.

Execute /bin/login from QProcess

I'm working on embedded system running linux (without X11 server). I want my Qt application to be started with root privileges then from within spawn new QProcess which should be "/bin/login user_name" then provide password.
The problem is the "Password:" that usually prints on console (I assume it's stdout) doesn't show up, and QProcess.write() doesn't seem to work at all. If I run this QProcess as normal user I get an error message at redirected stderr (via setStandardErrorFile). I think I have general problem with understanding /bin/login.
I tried to
echo "userPassword" > /proc/LOGIN_PID/fd/0
from different console, but it also doesn't work.
The function I wrote in c++ goes like this:
void BashWrapper::bashEcho(QStringList t_echo){
QString bash = "/bin/login";
QStringList args(t_echo[0]);
static QProcess *newProc = new QProcess();
newProc->setStandardOutputFile("/dev/pts/2");
newProc->setStandardErrorFile("/dev/pts/2");
newProc->start(bash,args);
if(newProc->waitForStarted()){
qDebug() << newProc->state();
}
newProc->write(t_echo[1].toUtf8());
if(!newProc->waitForFinished(10000)){
qDebug() << "timeout";
}
else{
qDebug() << "finished";
}
}
t_echo contains ("user_name", "userPassword")
What I want to do is spawn new program (let's say /bin/myApp) as user_name. I thought that login as that user and setting .bash_profile to run /bin/myApp would be easy, painless and... secure?
If using /bin/login from application is impossible is there any simple way to this?
Best Regards,
Michał
Ok, I found the answer here:
https://www.thegeekstuff.com/2010/10/expect-examples/ There is an example script that executes su command, simply changing it to /bin/login allowed me to pass the password via some addtional scripts.

Pywinauto 0.6.0 access browser URL in edit control for Firefox

I am currently trying to access URL of the active window for Firebox browser using Pywinauto 0.6.0, as part of a python app that can track website usage. I am a newbie python programmer, so if I have made some obvious mistakes then this is why.
I have read all the material I can find on Google and the Pywinauto docs, but there are no clear indications of how to do this, atleast not without using "Typekeys" after connecting to a window.
In fact, I have been able to access to the URL the "hack" way by connecting to the browser window with pywinauto, then using "TypeKeys()" to grab the URL and copy it to clipboard. But this approach will not work for me, as it interrupts the user and my app must run in the background whilst the user is accessing their system as usual. Using the typekeys method introduces some odd mouse and window behaviour when changing or refreshing windows and then trying to grab the URL - so the approach has proven unworkable for me.
Currently the code I have is as follows (I have used the window text title='' of a specific window I am using for testing, in practice it is whichever the active tab on the browser is):
from pywinauto import *
app = application.Application()
app.connect(title=u'Facebook - Log In or Sign Up - Mozilla Firefox', found_index=0)
window = app.top_window_()
window.PrintControlIdentifiers()
titlebar = window.child_window(title=u'Facebook - Log In or Sign Up - Mozilla Firefox')
toolbar = titlebar.child_window(title=u'Navigation Toolbar')
combobox = toolbar.child_window(title=u'Search or enter address')
edit = combobox.child_window(title=u'Search or enter address')
I use PrintControlIdentifiers() in order to see which elements I can interact with, but this returns only
MozillaWindowClass - 'Facebook - Log In or Sign Up - Mozilla Firefox' (L-32000, T-32000, R-31840, B-31944)
[u'Facebook - Log In or Sign Up - Mozilla Firefox', u'MozillaWindowClass', u'Facebook - Log In or Sign Up - Mozilla FirefoxMozillaWindowClass']
I can access the Firefox Window fine with the current active tab, but next when trying to access the ChildWindow and subsequent ChildWindow there is no error, until I try and do something with the childwindow e.g., Click() function on what I think in the URL bar UI element. However, I am not sure even if the code is even accessing the child window element correctly in the first place. Or if this the right way to try and filter through the child elements to get to the URL edit control element.
As can be seen in the below image of the tree view of the UI elements (using UISPY.exe), the following tree to access the firefox browser edit control element that contains the URL is:
Tree view of Firefox UI elements via UISPY
"window" -> "title bar" -> "tool bar" "Navigation Toolbar" -> "combo box" -> "Search or enter address" -> "edit" "Search or enter address
where the "edit" control contains the attribute Value, Value:"url", which I need to extract to a variable.
Any help with this would greatly appreciated.
This is probably too late to post but incase any future developers has similiar question. Here is working solution.
Just pass backend='uia' as argument like application.Application(backend='uia'). Now, accessing and performing actions on child_window is possible.
Here is complete code snippet for any url.
url='https://google.com'
app = application.Application(backend='uia').connect(title_re=".*Mozilla Firefox")
main_firefox_winspec = app.window(title_re=".*Mozilla Firefox")
address_bar = main_firefox_winspec.child_window(title_re=".*or enter address", control_type="Edit")
address_bar.set_edit_text(url)
time.sleep(3)
main_firefox_winspec.child_window(title="Go to the address in the Location Bar").wrapper_object().click_input()