1 //=============================================================================
2 //
3 // File : libkviaddon.cpp
4 // Creation date : Tue 31 Mar 01:02:12 2005 GMT by Szymon Stefanek
5 //
6 // This file is part of the KVIrc IRC client distribution
7 // Copyright (C) 2005-2010 Szymon Stefanek (pragma at kvirc dot net)
8 //
9 // This program is FREE software. You can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the HOPE that it will be USEFUL,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 // See the GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, write to the Free Software Foundation,
21 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24
25 #include "AddonManagementDialog.h"
26 #include "AddonFunctions.h"
27
28 #include "KviModule.h"
29 #include "KviKvsScriptAddonManager.h"
30 #include "KviLocale.h"
31 #include "KviQString.h"
32 #include "KviCommandFormatter.h"
33 #include "KviError.h"
34 #include "kvi_out.h"
35 #include "KviIconManager.h"
36 #include "KviControlCodes.h"
37 #include "KviConfigurationFile.h"
38 #include "kvi_sourcesdate.h"
39 #include "KviMiscUtils.h"
40 #include "KviFileUtils.h"
41
42 #include <QFileInfo>
43 #include <QDir>
44
45 QRect g_rectManagementDialogGeometry(0, 0, 0, 0);
46
47 /*
48 @doc: addon.exists
49 @type:
50 function
51 @title:
52 $addon.exists
53 @short:
54 Checks if an addon is currently installed
55 @syntax:
56 <boolean> $addon.exists(<id:string>[,<version:string>])
57 @description:
58 Returns [b]1[/b] if the addon with the specified <id> is currently installed
59 and [b]0[/b] otherwise. If <version> is specified then any addon with
60 a version lower than <version> is ignored (so you can effectively
61 check if a greater or equal version is present).
62 */
63
addon_kvs_fnc_exists(KviKvsModuleFunctionCall * c)64 static bool addon_kvs_fnc_exists(KviKvsModuleFunctionCall * c)
65 {
66 QString szId;
67 QString szVersion;
68 KVSM_PARAMETERS_BEGIN(c)
69 KVSM_PARAMETER("id", KVS_PT_NONEMPTYSTRING, 0, szId)
70 KVSM_PARAMETER("version", KVS_PT_STRING, KVS_PF_OPTIONAL, szVersion)
71 KVSM_PARAMETERS_END(c)
72
73 KviKvsScriptAddon * a = KviKvsScriptAddonManager::instance()->findAddon(szId);
74 if(a)
75 {
76 if(szVersion.isEmpty())
77 {
78 c->returnValue()->setBoolean(true);
79 }
80 else
81 {
82 c->returnValue()->setBoolean(KviMiscUtils::compareVersions(a->version(), szVersion) < 0);
83 }
84 }
85 else
86 {
87 c->returnValue()->setBoolean(false);
88 }
89 return true;
90 }
91
92 /*
93 @doc: addon.version
94 @type:
95 function
96 @title:
97 $addon.version
98 @short:
99 Returns the version of an installed addon
100 @syntax:
101 <string> $addon.version(<id:string>)
102 @description:
103 Returns the version of the currently installed addon with the
104 specified <id>. If the addon with the given <id> does not exist
105 then an empty string is returned.
106 */
107
addon_kvs_fnc_version(KviKvsModuleFunctionCall * c)108 static bool addon_kvs_fnc_version(KviKvsModuleFunctionCall * c)
109 {
110 QString szId;
111 KVSM_PARAMETERS_BEGIN(c)
112 KVSM_PARAMETER("id", KVS_PT_NONEMPTYSTRING, 0, szId)
113 KVSM_PARAMETERS_END(c)
114
115 KviKvsScriptAddon * a = KviKvsScriptAddonManager::instance()->findAddon(szId);
116 if(a)
117 {
118 c->returnValue()->setString(a->version());
119 }
120 else
121 {
122 c->returnValue()->setNothing();
123 }
124 return true;
125 }
126
127 /*
128 @doc: addon.list
129 @type:
130 command
131 @title:
132 addon.list
133 @short:
134 Lists the installed addons
135 @syntax:
136 addon.list
137 @description:
138 Lists the currently installed addons
139 @seealso:
140 [cmd]addon.register[/cmd]
141 */
142
addon_kvs_cmd_list(KviKvsModuleCommandCall * c)143 static bool addon_kvs_cmd_list(KviKvsModuleCommandCall * c)
144 {
145 KviPointerHashTable<QString, KviKvsScriptAddon> * da = KviKvsScriptAddonManager::instance()->addonDict();
146
147 int cnt = 0;
148 KviPointerHashTableIterator<QString, KviKvsScriptAddon> it(*da);
149 while(KviKvsScriptAddon * a = it.current())
150 {
151 c->window()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs_ctx("%cAddon ID %Q, version %Q%c", "addon"), KviControlCodes::Bold, &(a->name()), &(a->version()), KviControlCodes::Bold);
152 c->window()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs_ctx("Name: %Q", "addon"), &(a->visibleName()));
153 c->window()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs_ctx("Description: %Q", "addon"), &(a->description()));
154
155 ++it;
156 cnt++;
157 }
158
159 c->window()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs_ctx("Total: %d addons installed", "addon"), cnt);
160 return true;
161 }
162
163 /*
164 @doc: addon.uninstall
165 @type:
166 command
167 @title:
168 addon.uninstall
169 @short:
170 Uninstalls an addon
171 @syntax:
172 addon.uninstall [-q] [-n] <id:string>
173 @switches:
174 !sw: -n | --no-callback
175 Doesn't call the uninstall callback but only removes the registration entry.
176 !sw: -q | --quiet
177 Makes the command run quietly
178 @description:
179 Uninstalls the specified addon by executing its uninstall callback function
180 and removing its installed files. It also removes the addon's registration entry.
181 If the [b]-n[/b] switch is specified the uninstall callback is not called,
182 only the registration entry is removed.
183 @seealso:
184 [cmd]addon.register[/cmd]
185 */
186
addon_kvs_cmd_uninstall(KviKvsModuleCommandCall * c)187 static bool addon_kvs_cmd_uninstall(KviKvsModuleCommandCall * c)
188 {
189 QString szName;
190 KVSM_PARAMETERS_BEGIN(c)
191 KVSM_PARAMETER("name", KVS_PT_NONEMPTYSTRING, 0, szName)
192 KVSM_PARAMETERS_END(c)
193
194 KviKvsScriptAddon * a = KviKvsScriptAddonManager::instance()->findAddon(szName);
195 if(a)
196 {
197 if(!c->switches()->find('q', "quiet"))
198 c->window()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs_ctx("Uninstalling existing addon version %Q", "addon"), &(a->version()));
199
200 // uninstall the existing version
201 KviKvsScriptAddonManager::instance()->unregisterAddon(szName, c->window(), !c->switches()->find('n', "no-callback"));
202 }
203 else
204 {
205 if(!c->switches()->find('q', "quiet"))
206 c->warning(__tr2qs_ctx("The addon \"%1\" doesn't exist", "addon").arg(szName));
207 }
208
209 return true;
210 }
211
212 /*
213 @doc: addon.configure
214 @type:
215 command
216 @title:
217 addon.configure
218 @short:
219 Executes an addon's configuration callback
220 @syntax:
221 addon.configure [-q] <id:string>
222 @switches:
223 !sw: -q | --quiet
224 Makes the command run quietly
225 @description:
226 Executes the configuration callback of the specified addon.
227 @seealso:
228 [cmd]addon.register[/cmd]
229 [cmd]addon.setconfigurecallback[/cmd]
230 */
231
addon_kvs_cmd_configure(KviKvsModuleCommandCall * c)232 static bool addon_kvs_cmd_configure(KviKvsModuleCommandCall * c)
233 {
234 QString szName;
235 KVSM_PARAMETERS_BEGIN(c)
236 KVSM_PARAMETER("name", KVS_PT_NONEMPTYSTRING, 0, szName)
237 KVSM_PARAMETERS_END(c)
238
239 KviKvsScriptAddon * a = KviKvsScriptAddonManager::instance()->findAddon(szName);
240 if(a)
241 {
242 QString ss = a->configureCallbackCode();
243 if(ss.isEmpty())
244 {
245 if(!c->switches()->find('q', "quiet"))
246 c->warning(__tr2qs_ctx("The addon \"%1\" has no configure callback set", "addon").arg(szName));
247 }
248 else
249 {
250 a->executeConfigureCallback(c->window());
251 }
252 }
253 else
254 {
255 if(!c->switches()->find('q', "quiet"))
256 c->warning(__tr2qs_ctx("The addon \"%1\" doesn't exist", "addon").arg(szName));
257 }
258
259 return true;
260 }
261
262 /*
263 @doc: addon.help
264 @type:
265 command
266 @title:
267 addon.help
268 @short:
269 Executes an addon's help callback
270 @syntax:
271 addon.help [-q] <id:string>
272 @switches:
273 !sw: -q | --quiet
274 Makes the command run quietly
275 @description:
276 Executes the help callback of the specified addon. It will usually
277 display the addon's documentation in the help viewer.
278 @seealso:
279 [cmd]addon.register[/cmd]
280 [cmd]addon.sethelpcallback[/cmd]
281 */
282
addon_kvs_cmd_help(KviKvsModuleCommandCall * c)283 static bool addon_kvs_cmd_help(KviKvsModuleCommandCall * c)
284 {
285 QString szName;
286 KVSM_PARAMETERS_BEGIN(c)
287 KVSM_PARAMETER("name", KVS_PT_NONEMPTYSTRING, 0, szName)
288 KVSM_PARAMETERS_END(c)
289
290 KviKvsScriptAddon * a = KviKvsScriptAddonManager::instance()->findAddon(szName);
291 if(a)
292 {
293 QString ss = a->helpCallbackCode();
294 if(ss.isEmpty())
295 {
296 if(!c->switches()->find('q', "quiet"))
297 c->warning(__tr2qs_ctx("The addon \"%1\" has no help callback set", "addon").arg(szName));
298 }
299 else
300 {
301 a->executeHelpCallback(c->window());
302 }
303 }
304 else
305 {
306 if(!c->switches()->find('q', "quiet"))
307 c->warning(__tr2qs_ctx("The addon \"%1\" doesn't exist", "addon").arg(szName));
308 }
309
310 return true;
311 }
312
313 /*
314 @doc: addon.setconfigurecallback
315 @type:
316 command
317 @title:
318 addon.setconfigurecallback
319 @short:
320 Sets an addon's configuration callback
321 @syntax:
322 addon.setconfigurecallback [-q] (<id:string>)
323 {
324 <configure_callback>
325 }
326 @switches:
327 !sw: -q
328 Makes the command run quietly
329 @description:
330 Sets the configure callback for the specified addon.
331 The configure callback will be called by the user either by the
332 means of [cmd]addon.configure[/cmd] or by accessing the
333 proper function via the GUI.
334 @seealso:
335 [cmd]addon.register[/cmd]
336 [cmd]addon.configure[/cmd]
337 */
338
addon_kvs_cmd_setconfigurecallback(KviKvsModuleCallbackCommandCall * c)339 static bool addon_kvs_cmd_setconfigurecallback(KviKvsModuleCallbackCommandCall * c)
340 {
341 QString szName;
342
343 KVSM_PARAMETERS_BEGIN(c)
344 KVSM_PARAMETER("name", KVS_PT_NONEMPTYSTRING, 0, szName)
345 KVSM_PARAMETERS_END(c)
346
347 KviKvsScriptAddon * a = KviKvsScriptAddonManager::instance()->findAddon(szName);
348 if(a)
349 {
350 a->setConfigureCallback(c->callback()->code());
351 }
352 else
353 {
354 if(!c->switches()->find('q', "quiet"))
355 c->warning(__tr2qs_ctx("The addon \"%1\" doesn't exist", "addon").arg(szName));
356 }
357
358 return true;
359 }
360
361 /*
362 @doc: addon.sethelpcallback
363 @type:
364 command
365 @title:
366 addon.sethelpcallback
367 @short:
368 Sets an addon's help callback
369 @syntax:
370 addon.sethelpcallback(<id:string>)
371 {
372 <help_callback>
373 }
374 @switches:
375 !sw: -q
376 Makes the command run quietly
377 @description:
378 Sets the help callback for the specified addon.
379 The help callback will be called by the user either by the
380 means of [cmd]addon.help[/cmd] or by accessing the
381 proper function via the GUI. It should display some sort
382 of addon documentation, usually in the help browser.
383 @seealso:
384 [cmd]addon.register[/cmd]
385 [cmd]addon.help[/cmd]
386 */
387
addon_kvs_cmd_sethelpcallback(KviKvsModuleCallbackCommandCall * c)388 static bool addon_kvs_cmd_sethelpcallback(KviKvsModuleCallbackCommandCall * c)
389 {
390 QString szName;
391
392 KVSM_PARAMETERS_BEGIN(c)
393 KVSM_PARAMETER("name", KVS_PT_NONEMPTYSTRING, 0, szName)
394 KVSM_PARAMETERS_END(c)
395
396 KviKvsScriptAddon * a = KviKvsScriptAddonManager::instance()->findAddon(szName);
397 if(a)
398 {
399 a->setHelpCallback(c->callback()->code());
400 }
401 else
402 {
403 if(!c->switches()->find('q', "quiet"))
404 c->warning(__tr2qs_ctx("The addon \"%1\" doesn't exist", "addon").arg(szName));
405 }
406
407 return true;
408 }
409
410 /*
411 @doc: addon.register
412 @type:
413 command
414 @title:
415 addon.register
416 @short:
417 Registers a script-based addon
418 @syntax:
419 addon.register [-f] [-n] [-q] (<id:string>,<version:string>,<visible_name:string>,<description:string>,<minkvircverion:string>[,<iconid:string>])
420 {
421 <uninstall callback>
422 }
423 @switches:
424 !sw: -f | --force
425 Registers the addon even if an addon with the same <id> and
426 a higher version already exists. The usage of this flag
427 is highly discouraged (i.e. [b]use it only for debugging purposes
428 on your own machine[/b]).
429 !sw: -n | --no-uninstall
430 Performs no uninstallation of existing versions of the addon:
431 it simply replaces the registration entry with the new data.
432 Again, [b]only use this switch for debugging purposes and on your own machine[/b].
433 !sw: -q | --quiet
434 Makes the command run quietly
435 @description:
436 Registers a script-based addon.[br][br]
437 The registration process allows to [i]show[/i] the addon in the script-addon manager
438 dialog and provides a standard way for the user to manage and uninstall the addons.
439 You simply register your addon BEFORE attempting to install it.[br][br]
440 A script-based addon is a set of scripts, icons, translations and possibly
441 other data files that add functionality to the KVIrc program.
442 The script-based addons are often simply called [i]scripts[/i] and
443 we will adhere to that naming in certain parts of the documentation too.[br][br]
444 Each script-based addon (a set of scripts) is identified by a UNIQUE
445 <id>. Two addons with the same <id> can't co-exist in the same
446 KVIrc installation (so be sure to choose a token unique enough
447 to avoid collisions with others). The <id> itself is used only for
448 identification purposes and the user will almost always see the <visible_name>
449 instead, which can contain the [fnc]$tr[/fnc] function that will handle
450 the translation for it.[br][br]
451 Each addon also has a <version> which is a string in the form x.y.z
452 where x, y and z are numbers (yes.. that's the standard major-minor-patch level
453 version numbering scheme). A <version> of 2.4.23 is greater than 2.4.3
454 even if 2.4.3 comes after when compared as a string.
455 When an updated addon is installed over the same or previous version, the current version is first uninstalled.
456 Installing a lower version over a greater one is not possible, unless
457 the lower version one is uninstalled first.[br][br]
458 <description> is another, possibly translated, string that will
459 be presented to the user in the addon management dialog.[br][br]
460 <minkvircversion> is the minimum KVIrc version required for the
461 addon to run. If the version of the running KVIrc executable
462 is lower than the requested one then the command will abort with an error.
463 If you want to completely ignore the KVIrc versioning (don't do it),
464 use [b][i]0.0.0[/i][/b] here. If you need fine tuning on git features you may also add
465 the sources date tag at the end of the required version string (e.g 3.2.1.20060303).[br][br]
466 <iconid> is the [doc:image_id]image identifier[/doc] of the icon
467 that will be displayed in the addon management dialog.
468 If not specified, a default icon will be used.[br][br]
469 The <uninstall_callback> is a snippet of code that should
470 wipe out the addon from the system. It is ALWAYS a good practice
471 to write a complete uninstallation procedure (think that YOU like
472 to be able to completely uninstall a program that you don't use anymore).
473 The <uninstall_callback> will be called by KVIrc when the addon
474 uninstallation is requested, either explicitly by using the GUI or the
475 command [cmd]addon.uninstall[/cmd], or implicitly by installing
476 a newer version of the addon (upgrading).[br][br]
477 If the user's security configuration doesn't allow your addon to be installed,
478 or a higher version of an addon with the same name already exists,
479 the command will fail with an error (aborting the installation process).
480 If you don't want to fail with an error but handle it gracefully instead,
481 you should use [fnc]$addon.exists()[/fnc] to check if an
482 addon with the same name and a greater version already exists.
483 You can't gracefully handle security error conditions: your installation
484 will be always aborted with an error in this case.[br][br]
485 The addon can also define a configuration callback via [cmd]addon.setconfigurecallback[/cmd]
486 and a help callback via [cmd]addon.sethelpcallback[/cmd]. The first
487 will usually display a configuration dialog, the second will display
488 some sort of addon documentation, usually in the help browser.[br][br]
489 The registration process uninstalls any previous addon version
490 by executing its uninstall callback routine. This is another reason that
491 you should call addon.register BEFORE you attempt to install your addon.
492 Failing to do this may result in the the old version uninstallation wiping
493 out your newly installed files or code.
494 @seealso:
495 [cmd]addon.uninstall[/cmd], [fnc]$addon.exists[/fnc],
496 [cmd]addon.setconfigurecallback[/cmd], [cmd]addon.configure[/cmd],
497 [cmd]addon.sethelpcallback[/cmd], [cmd]addon.help[/cmd], [cmd]addon.installfiles[/cmd]
498 @examples:
499 [example]
500 [/example]
501 */
502
addon_kvs_cmd_register(KviKvsModuleCallbackCommandCall * c)503 static bool addon_kvs_cmd_register(KviKvsModuleCallbackCommandCall * c)
504 {
505 KviKvsScriptAddonRegistrationData rd;
506 QString szMinKVIrcVersion;
507
508 KVSM_PARAMETERS_BEGIN(c)
509 KVSM_PARAMETER("name", KVS_PT_NONEMPTYSTRING, 0, (rd.szName))
510 KVSM_PARAMETER("version", KVS_PT_NONEMPTYSTRING, 0, (rd.szVersion))
511 KVSM_PARAMETER_IGNORED("visible_text")
512 KVSM_PARAMETER_IGNORED("description")
513 KVSM_PARAMETER("min_kvirc_version", KVS_PT_NONEMPTYSTRING, 0, szMinKVIrcVersion)
514 KVSM_PARAMETER("icon_id", KVS_PT_STRING, KVS_PF_OPTIONAL, (rd.szIconId))
515 KVSM_PARAMETERS_END(c)
516
517 if(!(c->getParameterCode(2, rd.szVisibleNameScript) && c->getParameterCode(3, rd.szDescriptionScript)))
518 {
519 c->error(__tr2qs_ctx("Internal error: call a head-shrinker", "addon"));
520 return false;
521 }
522
523 if(c->callback())
524 rd.szUninstallCallbackScript = c->callback()->code();
525
526 if(!KviMiscUtils::isValidVersionString(rd.szVersion))
527 {
528 c->error(__tr2qs_ctx("The specified version \"%Q\" is not a valid version string", "addon"), &(rd.szVersion));
529 return false;
530 }
531
532 if(!KviMiscUtils::isValidVersionString(szMinKVIrcVersion))
533 {
534 c->error(__tr2qs_ctx("The specified KVIrc version \"%Q\" is not a valid version string", "addon"), &szMinKVIrcVersion);
535 return false;
536 }
537
538 if(KviMiscUtils::compareVersions(szMinKVIrcVersion, KVI_VERSION "." KVI_SOURCES_DATE) < 0)
539 {
540 c->error(__tr2qs_ctx("This KVIrc executable is too old to run this addon (minimum version required is %Q)", "addon"), &szMinKVIrcVersion);
541 return false;
542 }
543
544 if(!c->switches()->find('q', "quiet"))
545 c->window()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs_ctx("Attempting to register addon \"%Q\" with version %Q", "addon"), &(rd.szName), &(rd.szVersion));
546
547 KviKvsScriptAddon * a = KviKvsScriptAddonManager::instance()->findAddon(rd.szName);
548 if(a)
549 {
550 // the same addon already exists
551 if(KviMiscUtils::compareVersions(a->version(), rd.szVersion) < 0)
552 {
553 // and it has a higher version...
554 // complain unless -f is used
555 if(!c->switches()->find('f', "force"))
556 {
557 c->error(__tr2qs_ctx("The addon \"%Q\" already exists with version %Q which is higher than %Q", "addon"), &(rd.szName), &(a->version()), &(rd.szVersion));
558 return false;
559 }
560 }
561
562 if(!c->switches()->find('q', "quiet"))
563 c->window()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs_ctx("Uninstalling existing addon version %Q", "addon"), &(a->version()));
564
565 bool bUninstall = !c->switches()->find('n', "no-uninstall");
566
567 // uninstall the existing version
568 KviKvsScriptAddonManager::instance()->unregisterAddon(rd.szName, c->window(), bUninstall, bUninstall);
569 }
570
571 if(!KviKvsScriptAddonManager::instance()->registerAddon(&rd))
572 {
573 c->error(__tr2qs_ctx("Addon registration failed", "addon"));
574 return false;
575 }
576
577 if(!c->switches()->find('q', "quiet"))
578 c->window()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs_ctx("Addon successfully registered", "addon"));
579
580 return true;
581 }
582
583 /*
584 @doc: addon.installfiles
585 @type:
586 command
587 @title:
588 addon.installfiles
589 @short:
590 Installs a set of files for an addon
591 @syntax:
592 addon.installfiles <id:string> <target:string> [files]
593 @switches:
594 !sw: -q | --quiet
595 Makes the command run quietly
596 !sw: -s | --skip-nonexistent
597 Skip nonexistent entries in the [files] list
598 @description:
599 Installs the [files] for the addon identified by the specified <id>.
600 These files will be automatically removed when the addon is uninstalled.[br][br]
601 <target> is the target path inside the local KVIrc directory. The following
602 standard paths should be used:
603 [ul]
604 [li]"pics" for image files.[/li]
605 [li]"locale" for translation *.mo files.[/li]
606 [li]"audio" for sound files.[/li]
607 [li]"config" for configuration files.[/li]
608 [li]"help/<language>" for help files.[/li]
609 [/ul]
610 Other target paths are allowed and subdirectories are supported (e.g. [i]pics/myaddon[/i]).[br][br]
611 [files] is a list of filenames or directory names.
612 Each file will be copied to the specified target path in the local KVIrc directory.
613 Filenames can contain wildcard characters in the last component.
614 @seealso:
615 [cmd]addon.register[/cmd]
616 [cmd]addon.uninstall[/cmd]
617 */
618
addon_kvs_cmd_installfiles(KviKvsModuleCommandCall * c)619 static bool addon_kvs_cmd_installfiles(KviKvsModuleCommandCall * c)
620 {
621 QString szName;
622 QString szTarget;
623 QStringList lEntries;
624
625 KVSM_PARAMETERS_BEGIN(c)
626 KVSM_PARAMETER("name", KVS_PT_NONEMPTYSTRING, 0, szName)
627 KVSM_PARAMETER("target", KVS_PT_NONEMPTYSTRING, 0, szTarget)
628 KVSM_PARAMETER("files", KVS_PT_STRINGLIST, 0, lEntries)
629 KVSM_PARAMETERS_END(c)
630
631 bool bQuiet = c->switches()->find('q', "quiet");
632 // we had a typo here, support the old switch name for backward scripts compatibility
633 bool bSkipNonExistent = c->switches()->find('i', "skip-nonexistent") || c->switches()->find('i', "skip-nonexisting");
634
635 KviKvsScriptAddon * a = KviKvsScriptAddonManager::instance()->findAddon(szName);
636 if(!a)
637 {
638 if(!bQuiet)
639 c->warning(__tr2qs_ctx("The addon \"%1\" doesn't exist", "addon").arg(szName));
640 return true;
641 }
642
643 if(szTarget.isEmpty())
644 return c->error(__tr2qs_ctx("The target path can't be empty", "addon"));
645
646 if(szTarget.indexOf("..") != -1)
647 return c->error(__tr2qs_ctx("The target path can't contain ..", "addon"));
648
649 if(lEntries.isEmpty())
650 {
651 if(!bQuiet)
652 c->warning(__tr2qs_ctx("Empty file list", "addon"));
653 return true; // nothing to do
654 }
655
656 QStringList lExpandedEntries;
657
658 foreach(QString szEntry, lEntries)
659 {
660 KviFileUtils::adjustFilePath(szEntry);
661
662 if(szEntry.contains("*"))
663 {
664 // filtered entry
665 int idx = szEntry.lastIndexOf(QChar(KVI_PATH_SEPARATOR_CHAR));
666 QString szPath;
667 QString szFilter;
668 if(idx < 0)
669 {
670 szPath = ".";
671 szFilter = szEntry;
672 }
673 else
674 {
675 szPath = szEntry.left(idx);
676 szFilter = szEntry.mid(idx + 1);
677 }
678
679 QDir dir(szPath);
680 if(!dir.exists())
681 {
682 if(!bSkipNonExistent)
683 return c->error(__tr2qs_ctx("The directory '%1' doesn't exist", "addon").arg(szPath));
684 if(!bQuiet)
685 c->warning(__tr2qs_ctx("Skipping non-existent entry '%1'", "addon").arg(szEntry));
686 continue;
687 }
688
689 QStringList sl = dir.entryList(
690 QStringList(szFilter),
691 QDir::Files | QDir::NoSymLinks | QDir::Readable | QDir::Hidden | QDir::System,
692 QDir::Unsorted);
693
694 foreach(QString sz, sl)
695 {
696 lExpandedEntries.append(QString("%1%2%3").arg(szPath).arg(QString(KVI_PATH_SEPARATOR_CHAR)).arg(sz));
697 }
698 continue;
699 }
700
701 QFileInfo inf(szEntry);
702 if(!inf.exists())
703 {
704 if(!bSkipNonExistent)
705 return c->error(__tr2qs_ctx("The file '%1' doesn't exist", "addon").arg(szEntry));
706 if(!bQuiet)
707 c->warning(__tr2qs_ctx("Skipping non-existent entry '%1'", "addon").arg(szEntry));
708 continue;
709 }
710
711 if(!inf.isFile())
712 {
713 if(!bSkipNonExistent)
714 return c->error(__tr2qs_ctx("The entry '%1' is not a file", "addon").arg(szEntry));
715 if(!bQuiet)
716 c->warning(__tr2qs_ctx("Skipping invalid entry '%1'", "addon").arg(szEntry));
717 continue;
718 }
719
720 lExpandedEntries.append(szEntry);
721 }
722
723 // create target path
724 QString szTargetPath;
725 g_pApp->getLocalKvircDirectory(szTargetPath, KviApplication::None, szTarget);
726
727 KviFileUtils::makeDir(szTargetPath);
728
729 for(auto & lExpandedEntrie : lExpandedEntries)
730 {
731 QFileInfo inf(lExpandedEntrie);
732 if(!inf.exists())
733 {
734 qDebug("ERROR: file %s doesn't exist, but it should...", inf.fileName().toUtf8().data());
735 continue; // bleah.. should never happen
736 }
737
738 QString szEntry = QString("%1%2%3").arg(szTarget).arg(QString(KVI_PATH_SEPARATOR_CHAR)).arg(inf.fileName());
739 g_pApp->getLocalKvircDirectory(szTargetPath, KviApplication::None, szEntry);
740
741 if(!bQuiet)
742 c->window()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs_ctx("Installing file '%1' into '%2'", "addon").arg(lExpandedEntrie).arg(szTargetPath));
743
744 KviFileUtils::copyFile(lExpandedEntrie, szTargetPath);
745
746 a->addInstalledFile(szEntry);
747 }
748
749 return true;
750 }
751
752 /*
753 @doc: addon.dialog
754 @type:
755 command
756 @title:
757 addon.dialog
758 @short:
759 Shows the addon management editor
760 @syntax:
761 addon.dialog [-t]
762 @description:
763 Shows the addon management editor.[br][br]
764 If the [b]-t[/b] switch is used, the dialog is opened as toplevel window,
765 otherwise it is opened as part of the current frame window.[br]
766 */
767
addon_kvs_cmd_dialog(KviKvsModuleCommandCall * c)768 static bool addon_kvs_cmd_dialog(KviKvsModuleCommandCall * c)
769 {
770
771 AddonManagementDialog::display(c->hasSwitch('t', "toplevel"));
772 return true;
773 }
774
775 /*
776 @doc: addon.install
777 @type:
778 command
779 @title:
780 addon.install
781 @short:
782 Installs the addon
783 @syntax:
784 addon.install <package_path:string>
785 @description:
786 Attempts to install the addon in the package specified by <package_path>.
787 */
788
addon_kvs_cmd_install(KviKvsModuleCommandCall * c)789 static bool addon_kvs_cmd_install(KviKvsModuleCommandCall * c)
790 {
791 QString szAddonPackFile;
792
793 KVSM_PARAMETERS_BEGIN(c)
794 KVSM_PARAMETER("package_path", KVS_PT_STRING, 0, szAddonPackFile)
795 KVSM_PARAMETERS_END(c)
796
797 QString szError;
798 if(!AddonFunctions::installAddonPackage(szAddonPackFile, szError))
799 {
800 c->error(__tr2qs_ctx("Error installing addon package: %Q", "addon"), &szError);
801 return false;
802 }
803
804 return true;
805 }
806
807 /*
808 @doc: addon.pack
809 @type:
810 command
811 @title:
812 addon.pack
813 @short:
814 Creates a kva package containing an addon
815 @syntax:
816 addon.pack <package_path> <addon_name> <addon_version> <description> <author> <min_kvirc_version> <image> <addon_path>
817 @description:
818 Creates a *.kva package containing a KVIrc addon.[br][br]
819 <package_path> is the absolute path and file name of the package that should be saved.[br]
820 <addon_name> is the visible name of the addon (something like [i][b]My Addon[/i][/b]).[br]
821 <addon_version> is the version of the addon in the form X.Y.Z.[br]
822 <description> is a textual description of the addon.[br]
823 <author> is the name of the person that is creating the addon.[br]
824 <min_kvirc_version> is the minimum KVIrc version that this addon supports. Pass an empty string if you want
825 this to become the current KVIrc version.[br]
826 <image> is the path of an image to be used in the installation dialog. Pass an empty string if you
827 don't want an image to be stored in the package.[br]
828 <addon_path> is a path to the directory containing the addon. It should contain an install.kvs file
829 that calls [cmd]addon.register[/cmd] and then installs all the addon aliases, events and files via [cmd]addon.installfiles[/cmd].
830 */
addon_kvs_cmd_pack(KviKvsModuleCommandCall * c)831 static bool addon_kvs_cmd_pack(KviKvsModuleCommandCall * c)
832 {
833 AddonInfo info;
834
835 KVSM_PARAMETERS_BEGIN(c)
836 KVSM_PARAMETER("package_path", KVS_PT_NONEMPTYSTRING, 0, info.szSavePath)
837 KVSM_PARAMETER("addon_name", KVS_PT_NONEMPTYSTRING, 0, info.szName)
838 KVSM_PARAMETER("addon_version", KVS_PT_NONEMPTYSTRING, 0, info.szVersion)
839 KVSM_PARAMETER("description", KVS_PT_STRING, 0, info.szDescription)
840 KVSM_PARAMETER("author", KVS_PT_NONEMPTYSTRING, 0, info.szAuthor)
841 KVSM_PARAMETER("min_kvirc_version", KVS_PT_STRING, 0, info.szMinVersion)
842 KVSM_PARAMETER("image", KVS_PT_STRING, 0, info.szImage)
843 KVSM_PARAMETER("addon_path", KVS_PT_NONEMPTYSTRING, 0, info.szDirPath)
844 KVSM_PARAMETERS_END(c)
845
846 QString szError;
847
848 if(AddonFunctions::pack(info, szError))
849 return true;
850
851 c->error(szError);
852 return false;
853 }
854
addon_module_init(KviModule * m)855 static bool addon_module_init(KviModule * m)
856 {
857 KVSM_REGISTER_FUNCTION(m, "exists", addon_kvs_fnc_exists);
858 KVSM_REGISTER_FUNCTION(m, "version", addon_kvs_fnc_version);
859
860 KVSM_REGISTER_SIMPLE_COMMAND(m, "dialog", addon_kvs_cmd_dialog);
861 KVSM_REGISTER_SIMPLE_COMMAND(m, "list", addon_kvs_cmd_list);
862 KVSM_REGISTER_SIMPLE_COMMAND(m, "install", addon_kvs_cmd_install);
863 KVSM_REGISTER_SIMPLE_COMMAND(m, "uninstall", addon_kvs_cmd_uninstall);
864 KVSM_REGISTER_SIMPLE_COMMAND(m, "configure", addon_kvs_cmd_configure);
865 KVSM_REGISTER_SIMPLE_COMMAND(m, "help", addon_kvs_cmd_help);
866 KVSM_REGISTER_SIMPLE_COMMAND(m, "installfiles", addon_kvs_cmd_installfiles);
867 KVSM_REGISTER_SIMPLE_COMMAND(m, "pack", addon_kvs_cmd_pack);
868
869 KVSM_REGISTER_CALLBACK_COMMAND(m, "setconfigurecallback", addon_kvs_cmd_setconfigurecallback);
870 KVSM_REGISTER_CALLBACK_COMMAND(m, "sethelpcallback", addon_kvs_cmd_sethelpcallback);
871 KVSM_REGISTER_CALLBACK_COMMAND(m, "register", addon_kvs_cmd_register);
872
873 QString szBuf;
874 m->getDefaultConfigFileName(szBuf);
875 KviConfigurationFile cfg(szBuf, KviConfigurationFile::Read);
876 g_rectManagementDialogGeometry = cfg.readRectEntry("EditorGeometry", QRect(10, 10, 390, 440));
877
878 return true;
879 }
880
addon_module_cleanup(KviModule * m)881 static bool addon_module_cleanup(KviModule * m)
882 {
883 AddonManagementDialog::cleanup();
884
885 QString szBuf;
886 m->getDefaultConfigFileName(szBuf);
887 KviConfigurationFile cfg(szBuf, KviConfigurationFile::Write);
888 cfg.writeEntry("EditorGeometry", g_rectManagementDialogGeometry);
889
890 return true;
891 }
892
addon_module_can_unload(KviModule *)893 static bool addon_module_can_unload(KviModule *)
894 {
895 return (!AddonManagementDialog::instance());
896 }
897
898 KVIRC_MODULE(
899 "Addon", // module name
900 "4.0.0", // module version
901 "Copyright (C) 2005 Szymon Stefanek (pragma at kvirc dot net)\n"
902 " 2008 Elvio Basello (hell at hellvis69 dot netsons dot org)", // author & (C)
903 "Addon management functions for the KVS engine",
904 addon_module_init,
905 addon_module_can_unload,
906 0,
907 addon_module_cleanup,
908 "addon")
909