1 /*
2     Copyright (c) 2020, Lukas Holecek <hluk@email.cz>
3 
4     This file is part of CopyQ.
5 
6     CopyQ is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     CopyQ is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with CopyQ.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef SCRIPTABLE_H
21 #define SCRIPTABLE_H
22 
23 #include "common/clipboardmode.h"
24 #include "common/command.h"
25 #include "common/mimetypes.h"
26 
27 #include <QObject>
28 #include <QString>
29 #include <QJSValue>
30 #include <QVariantMap>
31 #include <QVector>
32 
33 #include "platform/platformnativeinterface.h"
34 
35 class Action;
36 class ClipboardBrowser;
37 class ItemFactory;
38 class NetworkReply;
39 class ScriptableProxy;
40 
41 class QFile;
42 class QMimeData;
43 class QNetworkReply;
44 class QNetworkAccessManager;
45 class QJSEngine;
46 class QTextCodec;
47 
48 enum class ClipboardOwnership;
49 
50 class Scriptable final : public QObject
51 {
52     Q_OBJECT
53     Q_PROPERTY(QJSValue inputSeparator READ getInputSeparator WRITE setInputSeparator)
54     Q_PROPERTY(QJSValue mimeText READ getMimeText CONSTANT)
55     Q_PROPERTY(QJSValue mimeHtml READ getMimeHtml CONSTANT)
56     Q_PROPERTY(QJSValue mimeUriList READ getMimeUriList CONSTANT)
57     Q_PROPERTY(QJSValue mimeWindowTitle READ getMimeWindowTitle CONSTANT)
58     Q_PROPERTY(QJSValue mimeItems READ getMimeItems CONSTANT)
59     Q_PROPERTY(QJSValue mimeItemNotes READ getMimeItemNotes CONSTANT)
60     Q_PROPERTY(QJSValue mimeIcon READ getMimeIcon CONSTANT)
61     Q_PROPERTY(QJSValue mimeOwner READ getMimeOwner CONSTANT)
62     Q_PROPERTY(QJSValue mimeClipboardMode READ getMimeClipboardMode CONSTANT)
63     Q_PROPERTY(QJSValue mimeCurrentTab READ getMimeCurrentTab CONSTANT)
64     Q_PROPERTY(QJSValue mimeSelectedItems READ getMimeSelectedItems CONSTANT)
65     Q_PROPERTY(QJSValue mimeCurrentItem READ getMimeCurrentItem CONSTANT)
66     Q_PROPERTY(QJSValue mimeHidden READ getMimeHidden CONSTANT)
67     Q_PROPERTY(QJSValue mimeShortcut READ getMimeShortcut CONSTANT)
68     Q_PROPERTY(QJSValue mimeColor READ getMimeColor CONSTANT)
69     Q_PROPERTY(QJSValue mimeOutputTab READ getMimeOutputTab CONSTANT)
70 
71     Q_PROPERTY(QJSValue plugins READ getPlugins CONSTANT)
72 
73     Q_PROPERTY(QJSValue _copyqUncaughtException READ uncaughtException WRITE setUncaughtException)
74     Q_PROPERTY(QJSValue _copyqHasUncaughtException READ hasUncaughtException)
75     Q_PROPERTY(QJSValue _initItemSelection WRITE initItemSelection)
76 
77 public:
78     explicit Scriptable(
79             QJSEngine *engine,
80             ScriptableProxy *proxy,
81             QObject *parent = nullptr);
82 
83     enum class Abort {
84         None,
85         CurrentEvaluation,
86         AllEvaluations,
87     };
88 
89     QJSValue argumentsArray() const;
90     int argumentCount() const;
91     QJSValue argument(int index) const;
92 
93     QJSValue newByteArray(const QByteArray &bytes) const;
94 
95     QByteArray fromString(const QString &value) const;
96     QVariant toVariant(const QJSValue &value);
97     bool toInt(const QJSValue &value, int *number) const;
98     QVariantMap toDataMap(const QJSValue &value) const;
99     QJSValue fromDataMap(const QVariantMap &dataMap) const;
100 
101     QByteArray makeByteArray(const QJSValue &value) const;
102 
103     /**
104      * Set data for item converted from @a value.
105      * Return true if data was successfully converted and set.
106      *
107      * If mime starts with "text/" or isn't byte array the value is re-encoded
108      * from local encoding to UTF8.
109      */
110     bool toItemData(const QJSValue &value, const QString &mime, QVariantMap *data) const;
111 
112     QJSValue getInputSeparator() const;
113     void setInputSeparator(const QJSValue &separator);
114 
115     QString getCurrentPath() const;
116     void setCurrentPath(const QString &path);
117 
118     QString getAbsoluteFilePath(const QString &fileName) const;
119 
120     QString arg(int i, const QString &defaultValue = QString());
121 
122     QJSValue throwError(const QString &errorMessage);
123     QJSValue throwSaveError(const QString &filePath);
124     QJSValue throwImportError(const QString &filePath);
125 
126     bool hasUncaughtException() const;
127     void clearExceptions();
uncaughtException()128     QJSValue uncaughtException() const { return m_uncaughtException; }
129     void setUncaughtException(const QJSValue &exc);
130 
engine()131     QJSEngine *engine() const { return m_engine; }
132 
canContinue()133     bool canContinue() const { return m_abort == Abort::None && !m_failed; }
134 
135     void installObject(QObject *fromObj, const QMetaObject *metaObject, QJSValue &toObject);
136 
getMimeText()137     QJSValue getMimeText() const { return mimeText; }
getMimeHtml()138     QJSValue getMimeHtml() const { return mimeHtml; }
getMimeUriList()139     QJSValue getMimeUriList() const { return mimeUriList; }
getMimeWindowTitle()140     QJSValue getMimeWindowTitle() const { return mimeWindowTitle; }
getMimeItems()141     QJSValue getMimeItems() const { return mimeItems; }
getMimeItemNotes()142     QJSValue getMimeItemNotes() const { return mimeItemNotes; }
getMimeIcon()143     QJSValue getMimeIcon() const { return mimeIcon; }
getMimeOwner()144     QJSValue getMimeOwner() const { return mimeOwner; }
getMimeClipboardMode()145     QJSValue getMimeClipboardMode() const { return mimeClipboardMode; }
getMimeCurrentTab()146     QJSValue getMimeCurrentTab() const { return mimeCurrentTab; }
getMimeSelectedItems()147     QJSValue getMimeSelectedItems() const { return mimeSelectedItems; }
getMimeCurrentItem()148     QJSValue getMimeCurrentItem() const { return mimeCurrentItem; }
getMimeHidden()149     QJSValue getMimeHidden() const { return mimeHidden; }
getMimeShortcut()150     QJSValue getMimeShortcut() const { return mimeShortcut; }
getMimeColor()151     QJSValue getMimeColor() const { return mimeColor; }
getMimeOutputTab()152     QJSValue getMimeOutputTab() const { return mimeOutputTab; }
153 
154     QJSValue getPlugins();
155 
156     QJSValue eval(const QString &script, const QString &label);
157 
158     QJSValue call(const QString &label, QJSValue *fn, const QVariantList &arguments);
159     QJSValue call(const QString &label, QJSValue *fn, const QJSValueList &arguments = QJSValueList());
160 
161     void setActionId(int actionId);
162     void setActionName(const QString &actionName);
163     int executeArguments(const QStringList &args);
164     int executeArgumentsSimple(const QStringList &args);
165 
166     void abortEvaluation(Abort abort = Abort::AllEvaluations);
167 
168 public slots:
169     QJSValue version();
170     QJSValue help();
171 
172     void show();
173     void showAt();
174     void hide();
175     QJSValue toggle();
176     QJSValue menu();
177     void exit();
178     void disable();
179     void enable();
180     QJSValue monitoring();
181     QJSValue visible();
182     QJSValue focused();
183 
184     QJSValue focusPrevious();
185 
186     QJSValue preview();
187 
188     QJSValue filter();
189 
190     void ignore();
191 
192     QJSValue clipboard();
193     QJSValue selection();
194     QJSValue hasClipboardFormat();
195     QJSValue hasSelectionFormat();
196     QJSValue isClipboard();
197     QJSValue copy();
198     QJSValue copySelection();
199     QJSValue paste();
200 
201     QJSValue tab();
202     QJSValue removeTab();
removetab()203     QJSValue removetab() { return removeTab(); }
204     QJSValue renameTab();
renametab()205     QJSValue renametab() { return renameTab(); }
206     QJSValue tabIcon();
tabicon()207     QJSValue tabicon() { return tabIcon(); }
208     QJSValue unload();
209     void forceUnload();
210 
211     QJSValue length();
size()212     QJSValue size() { return length(); }
count()213     QJSValue count() { return length(); }
214 
215     QJSValue select();
216     void next();
217     void previous();
218     void add();
219     void insert();
220     QJSValue remove();
221     void edit();
222     QJSValue move();
223 
224     QJSValue read();
225     QJSValue write();
226     QJSValue change();
227     void separator();
228 
229     void action();
230     void popup();
231     QJSValue notification();
232 
233     QJSValue exportTab();
exporttab()234     void exporttab() { exportTab(); }
235     QJSValue importTab();
importtab()236     void importtab() { importTab(); }
237 
238     QJSValue importData();
239     QJSValue exportData();
240 
241     QJSValue config();
242     QJSValue toggleConfig();
243 
244     QJSValue info();
245 
246     QJSValue eval();
247 
248     QJSValue source();
249 
250     QJSValue currentPath();
currentpath()251     QJSValue currentpath() { return currentPath(); }
252 
253     QJSValue str();
254     QJSValue input();
255     QJSValue toUnicode();
256     QJSValue fromUnicode();
257 
258     QJSValue dataFormats();
259     QJSValue data();
260     QJSValue setData();
261     QJSValue removeData();
262     void print();
263     void abort();
264     void fail();
265 
266     QJSValue keys();
267     QJSValue testSelected();
268     void serverLog();
269     QJSValue logs();
270 
271     void setCurrentTab();
272 
273     QJSValue selectItems();
selectitems()274     QJSValue selectitems() { return selectItems(); }
275 
276     QJSValue selectedTab();
selectedtab()277     QJSValue selectedtab() { return selectedTab(); }
278     QJSValue selectedItems();
selecteditems()279     QJSValue selecteditems() { return selectedItems(); }
280     QJSValue currentItem();
currentitem()281     QJSValue currentitem() { return currentItem(); }
index()282     QJSValue index() { return currentItem(); }
283 
284     QJSValue selectedItemData();
285     QJSValue setSelectedItemData();
286     QJSValue selectedItemsData();
287     void setSelectedItemsData();
288 
289     QJSValue escapeHtml();
escapeHTML()290     QJSValue escapeHTML() { return escapeHtml(); }
291 
292     QJSValue unpack();
293     QJSValue pack();
294 
295     QJSValue getItem();
getitem()296     QJSValue getitem() { return getItem(); }
297     void setItem();
setitem()298     void setitem() { setItem(); }
299 
300     QJSValue toBase64();
tobase64()301     QJSValue tobase64() { return toBase64(); }
302     QJSValue fromBase64();
frombase64()303     QJSValue frombase64() { return fromBase64(); }
304 
305     QJSValue md5sum();
306     QJSValue sha1sum();
307     QJSValue sha256sum();
308     QJSValue sha512sum();
309 
310     QJSValue open();
311     QJSValue execute();
312 
313     QJSValue currentWindowTitle();
314 
315     QJSValue dialog();
316 
317     QJSValue menuItems();
318 
319     QJSValue settings();
320 
321     QJSValue dateString();
322 
323     QJSValue commands();
324     void setCommands();
325     void addCommands();
326     QJSValue importCommands();
327     QJSValue exportCommands();
328 
329     QJSValue networkGet();
330     QJSValue networkPost();
331     QJSValue networkGetAsync();
332     QJSValue networkPostAsync();
333 
334     QJSValue env();
335     QJSValue setEnv();
336 
337     QJSValue sleep();
338     QJSValue afterMilliseconds();
339 
340     // Call scriptable method.
341     QVariant call(const QString &method, const QVariantList &arguments);
342     QVariantList currentArguments();
343     void throwException(const QString &errorMessage);
344 
345     QJSValue screenshot();
346     QJSValue screenshotSelect();
347     QJSValue screenNames();
348 
349     QJSValue queryKeyboardModifiers();
350     QJSValue pointerPosition();
351     QJSValue setPointerPosition();
352 
353     QJSValue iconColor();
354 
355     QJSValue iconTag();
356 
357     QJSValue iconTagColor();
358 
359     QJSValue loadTheme();
360 
361     void onClipboardChanged();
362     void onOwnClipboardChanged();
363     void onHiddenClipboardChanged();
364     void onClipboardUnchanged();
365 
onStart()366     void onStart() {}
onExit()367     void onExit() {}
368 
369     void synchronizeToSelection();
370     void synchronizeFromSelection();
371 
372     void setClipboardData();
373     void updateTitle();
374     void setTitle();
375     void saveData();
376     QJSValue hasData();
377     void showDataNotification();
378     void hideDataNotification();
379     void updateClipboardData();
380     void clearClipboardData();
381     QJSValue runAutomaticCommands();
382 
383     void runDisplayCommands();
384 
385     void runMenuCommandFilters();
386 
387     void monitorClipboard();
388     void provideClipboard();
389     void provideSelection();
390 
391     QJSValue clipboardFormatsToSave();
392 
393     QJSValue styles();
394 
395 signals:
396     void finished();
397     void dataReceived(const QByteArray &data);
398     void receiveData();
399 
400 private:
401     void onExecuteOutput(const QByteArray &output);
402     void onMonitorClipboardChanged(const QVariantMap &data, ClipboardOwnership ownership);
403     void onMonitorClipboardUnchanged(const QVariantMap &data);
404     void onSynchronizeSelection(ClipboardMode sourceMode, const QString &text, uint targetTextHash);
405 
406     bool sourceScriptCommands();
407     void callDisplayFunctions(QJSValueList displayFunctions);
408     void processUncaughtException(const QString &cmd);
409     void showExceptionMessage(const QString &message);
410     QVector<int> getRows() const;
411 
412     /**
413      * Parses arguments as one of these or raises an argument error:
414      * - item...
415      * - mimeType, data, [mimeType, data]...
416      * - list of items
417      * - text
418      */
419     QVector<QVariantMap> getItemArguments(int begin, int end, QString *error);
420     QVector<QVariantMap> getItemList(int begin, int end, const QJSValue &arguments);
421 
422     QJSValue copy(ClipboardMode mode);
423     QJSValue changeItem(bool create);
424     void nextToClipboard(int where);
425     QJSValue screenshot(bool select);
426     QByteArray serialize(const QJSValue &value);
427     QJSValue eval(const QString &script);
428     QTextCodec *codecFromNameOrThrow(const QJSValue &codecName);
429     bool runAction(Action *action);
430     bool runCommands(CommandType::CommandType type);
431     bool canExecuteCommand(const Command &command);
432     bool canExecuteCommandFilter(const QString &matchCommand);
433     bool canAccessClipboard() const;
434     bool verifyClipboardAccess();
435     void provideClipboard(ClipboardMode mode);
436 
437     void insert(int argumentsEnd);
438     void insert(int row, int argumentsBegin, int argumentsEnd);
439 
440     QStringList arguments();
441     QVariantList argumentsAsVariants();
442 
443     void print(const QByteArray &message);
444     void printError(const QByteArray &message);
445 
446     void getActionData();
447     void getActionData(int actionId);
448     void setActionData();
449 
450     QByteArray getClipboardData(const QString &mime, ClipboardMode mode = ClipboardMode::Clipboard);
451     bool hasClipboardFormat(const QString &mime, ClipboardMode mode = ClipboardMode::Clipboard);
452 
453     void synchronizeSelection(ClipboardMode targetMode);
454 
455     void saveData(const QString &tab);
456 
457     QJSValue readInput();
458 
459     PlatformClipboard *clipboardInstance();
460     const QMimeData *mimeData(ClipboardMode mode);
461 
462     void interruptibleSleep(int msec);
463 
464     NetworkReply *networkGetHelper();
465     NetworkReply *networkPostHelper();
466 
467     QJSValue initItemSelection(const QJSValue &obj);
468 
469     ScriptableProxy *m_proxy;
470     QJSEngine *m_engine;
471     QJSValue m_temporaryFileClass;
472     QString m_inputSeparator;
473     QJSValue m_input;
474     QVariantMap m_data;
475     QVariantMap m_oldData;
476     int m_actionId = -1;
477     QString m_actionName;
478     Abort m_abort = Abort::None;
479     int m_skipArguments = 0;
480 
481     // FIXME: Parameters for execute() shouldn't be global.
482     QByteArray m_executeStdoutData;
483     QString m_executeStdoutLastLine;
484     QJSValue m_executeStdoutCallback;
485 
486     bool m_displayFunctionsLock = false;
487 
488     QJSValue m_plugins;
489 
490     Action *m_action = nullptr;
491     bool m_failed = false;
492 
493     QString m_tabName;
494 
495     PlatformClipboardPtr m_clipboard;
496 
497     QJSValue m_uncaughtException;
498     bool m_hasUncaughtException = false;
499 
500     QStringList m_stack;
501     QStringList m_uncaughtExceptionStack;
502 
503     QJSValue m_safeCall;
504     QJSValue m_safeEval;
505     QJSValue m_createFn;
506     QJSValue m_createFnB;
507     QJSValue m_createProperty;
508 };
509 
510 class NetworkReply final : public QObject {
511     Q_OBJECT
512     Q_PROPERTY(QJSValue data READ data CONSTANT)
513     Q_PROPERTY(QJSValue error READ error CONSTANT)
514     Q_PROPERTY(QJSValue status READ status CONSTANT)
515     Q_PROPERTY(QJSValue redirect READ redirect CONSTANT)
516     Q_PROPERTY(QJSValue headers READ headers CONSTANT)
517     Q_PROPERTY(QJSValue finished READ finished CONSTANT)
518 
519 public:
520     static NetworkReply *get(const QString &url, Scriptable *scriptable);
521     static NetworkReply *post(const QString &url, const QByteArray &postData, Scriptable *scriptable);
522 
523     ~NetworkReply();
524 
525     QJSValue data();
526 
527     QJSValue error();
528 
529     QJSValue status();
530     QJSValue redirect();
531     QJSValue headers();
532 
533     QJSValue finished();
534 
535     QJSValue toScriptValue();
536 
537 private:
538     explicit NetworkReply(const QString &url, const QByteArray &postData, Scriptable *scriptable);
539 
540     Scriptable *m_scriptable;
541     QNetworkAccessManager *m_manager;
542     QNetworkReply *m_reply;
543     QJSValue m_data;
544     QJSValue m_self;
545     QByteArray m_rawData;
546 };
547 
548 class ScriptablePlugins final : public QObject {
549     Q_OBJECT
550 
551 public:
552     explicit ScriptablePlugins(Scriptable *scriptable);
553 
554 public slots:
555     QJSValue load(const QString &name);
556 
557 private:
558     ItemFactory *m_factory = nullptr;
559     Scriptable *m_scriptable;
560     QMap<QString, QJSValue> m_plugins;
561 };
562 
563 #endif // SCRIPTABLE_H
564