1 //this file is part of MimeTools (plugin for Notepad++)
2 //Copyright (C)2019 Don HO <don.h@free.fr>
3 //
4 //
5 // Enhance Base64 features, and rewrite Base64 encode/decode implementation
6 // Copyright 2019 by Paul Nankervis <paulnank@hotmail.com>
7 //
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
12 //version 2 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.  See the
17 //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
21 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 module npp_mimetools.mimeTools;
23 
24 
25 private static import core.stdc.string;
26 private static import core.sys.windows.basetsd;
27 private static import core.sys.windows.winbase;
28 private static import core.sys.windows.windef;
29 private static import core.sys.windows.winnt;
30 private static import core.sys.windows.winuser;
31 private static import npp_api.PowerEditor.MISC.PluginsManager.PluginInterface;
32 private static import npp_api.pluginfunc.basic_interface;
33 private static import npp_api.pluginfunc.npp_msgs;
34 private static import npp_api.pluginfunc.scintilla_msg;
35 private static import npp_api.pluginfunc.string;
36 private static import npp_mimetools.b64;
37 private static import npp_mimetools.qp;
38 private static import npp_mimetools.saml;
39 private static import npp_mimetools.url;
40 
41 enum IDD_ABOUTBOX = 250;
42 enum IDC_STATIC = -1;
43 
44 enum PLUGIN_NAME = npp_api.pluginfunc..string.c_wstring!(`MIME Tools Sample`);
45 
46 core.sys.windows.windef.HINSTANCE _hInst;
47 
48 enum npp_api.PowerEditor.MISC.PluginsManager.PluginInterface.FuncItem[] main_menu_items_def =
49 [
50 	{
51 		_itemName: `Base64 Encode`w,
52 		_pFunc: &.convertToBase64FromAscii,
53 		_init2Check: false,
54 		_pShKey: null,
55 	},
56 	{
57 		_itemName: `Base64 Encode with padding`w,
58 		_pFunc: &.convertToBase64FromAscii_pad,
59 		_init2Check: false,
60 		_pShKey: null,
61 	},
62 	{
63 		_itemName: `Base64 Encode with Unix EOL`w,
64 		_pFunc: &.convertToBase64FromAscii_B64Format,
65 		_init2Check: false,
66 		_pShKey: null,
67 	},
68 	{
69 		_itemName: `Base64 Encode by line`w,
70 		_pFunc: &.convertToBase64FromAscii_byline,
71 		_init2Check: false,
72 		_pShKey: null,
73 	},
74 	{
75 		_itemName: `Base64 Decode`w,
76 		_pFunc: &.convertToAsciiFromBase64,
77 		_init2Check: false,
78 		_pShKey: null,
79 	},
80 	{
81 		_itemName: `Base64 Decode strict`w,
82 		_pFunc: &.convertToAsciiFromBase64_strict,
83 		_init2Check: false,
84 		_pShKey: null,
85 	},
86 	{
87 		_itemName: `Base64 Decode by line`w,
88 		_pFunc: &.convertToAsciiFromBase64_whitespaceReset,
89 		_init2Check: false,
90 		_pShKey: null,
91 	},
92 	{
93 		_pFunc: null,
94 	},
95 	{
96 		_itemName: `Quoted-printable Encode`w,
97 		_pFunc: &.convertToQuotedPrintable,
98 		_init2Check: false,
99 		_pShKey: null,
100 	},
101 	{
102 		_itemName: `Quoted-printable Decode`w,
103 		_pFunc: &.convertToAsciiFromQuotedPrintable,
104 		_init2Check: false,
105 		_pShKey: null,
106 	},
107 	{
108 		_pFunc: null,
109 	},
110 	{
111 		_itemName: `URL Encode`w,
112 		_pFunc: &.convertURLMinEncode,
113 		_init2Check: false,
114 		_pShKey: null,
115 	},
116 	{
117 		_itemName: `Full URL Encode`w,
118 		_pFunc: &.convertURLFullEncode,
119 		_init2Check: false,
120 		_pShKey: null,
121 	},
122 	{
123 		_itemName: `URL Decode`w,
124 		_pFunc: &.convertURLDecode,
125 		_init2Check: false,
126 		_pShKey: null,
127 	},
128 	{
129 		_pFunc: null,
130 	},
131 	{
132 		_itemName: `SAML Decode`w,
133 		_pFunc: &.convertSamlDecode,
134 		_init2Check: false,
135 		_pShKey: null,
136 	},
137 	{
138 		_pFunc: null,
139 	},
140 	{
141 		_itemName: `About`w,
142 		_pFunc: &.about,
143 		_init2Check: false,
144 		_pShKey: null,
145 	},
146 ];
147 
148 nothrow @nogc
149 core.sys.windows.windef.HWND getCurrentScintillaHandle()
150 
151 	do
152 	{
153 		int currentEdit;
154 		npp_api.pluginfunc.npp_msgs.send_NPPM_GETCURRENTSCINTILLA(nppData._nppHandle, currentEdit);
155 
156 		return (currentEdit == 0) ? (nppData._scintillaMainHandle) : (nppData._scintillaSecondHandle);
157 	}
158 
159 nothrow
160 void convertAsciiToBase64(size_t wrapLength, bool padFlag, bool byLineFlag)
161 
162 	do
163 	{
164 		core.sys.windows.windef.HWND hCurrScintilla = .getCurrentScintillaHandle();
165 		size_t selectedLength = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla);
166 
167 		if (selectedLength == 0) {
168 			return;
169 		}
170 
171 		char[] selectedText = new char[selectedLength];
172 		npp_api.pluginfunc.scintilla_msg.send_SCI_TARGETFROMSELECTION(hCurrScintilla);
173 		npp_api.pluginfunc.scintilla_msg.send_SCI_GETTARGETTEXT(hCurrScintilla, &(selectedText[0]));
174 
175 		size_t bufferLength = (selectedLength + 2) / 3 * 4 + 1;
176 
177 		if (wrapLength > 0) {
178 			bufferLength += bufferLength / wrapLength;
179 		}
180 
181 		char[] encodedText = new char[bufferLength + 1];
182 
183 		int len = npp_mimetools.b64.base64Encode(encodedText, selectedText, selectedLength - 1, wrapLength, padFlag, byLineFlag);
184 		encodedText[len] = '\0';
185 
186 		npp_api.pluginfunc.scintilla_msg.send_SCI_TARGETFROMSELECTION(hCurrScintilla);
187 		npp_api.pluginfunc.scintilla_msg.send_SCI_REPLACETARGET(hCurrScintilla, len, &(encodedText[0]));
188 	}
189 
190 extern (C)
191 nothrow
192 void convertToBase64FromAscii()
193 
194 	do
195 	{
196 		.convertAsciiToBase64(0, false, false);
197 	}
198 
199 extern (C)
200 nothrow
201 void convertToBase64FromAscii_pad()
202 
203 	do
204 	{
205 		.convertAsciiToBase64(0, true, false);
206 	}
207 
208 extern (C)
209 nothrow
210 void convertToBase64FromAscii_B64Format()
211 
212 	do
213 	{
214 		.convertAsciiToBase64(64, true, false);
215 	}
216 
217 extern (C)
218 nothrow
219 void convertToBase64FromAscii_byline()
220 
221 	do
222 	{
223 		.convertAsciiToBase64(0, false, true);
224 	}
225 
226 nothrow
227 void convertBase64ToAscii(bool strictFlag, bool whitespaceReset)
228 
229 	do
230 	{
231 		core.sys.windows.windef.HWND hCurrScintilla = .getCurrentScintillaHandle();
232 		size_t selectedLength = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla);
233 
234 		if (selectedLength == 0) {
235 			return;
236 		}
237 
238 		char[] selectedText = new char[selectedLength];
239 		npp_api.pluginfunc.scintilla_msg.send_SCI_TARGETFROMSELECTION(hCurrScintilla);
240 		npp_api.pluginfunc.scintilla_msg.send_SCI_GETTARGETTEXT(hCurrScintilla, &(selectedText[0]));
241 
242 		char[] decodedText = new char[selectedLength];
243 
244 		int len = npp_mimetools.b64.base64Decode(decodedText, selectedText, selectedLength - 1, strictFlag, whitespaceReset);
245 
246 		if (len < 0) {
247 			core.sys.windows.winuser.MessageBoxW(nppData._nppHandle, &(npp_api.pluginfunc..string.c_wstring!(`Problem!`)[0]), &(npp_api.pluginfunc..string.c_wstring!(`Base64`)[0]), core.sys.windows.winuser.MB_OK);
248 		} else {
249 			decodedText[len] = '\0';
250 			npp_api.pluginfunc.scintilla_msg.send_SCI_TARGETFROMSELECTION(hCurrScintilla);
251 			npp_api.pluginfunc.scintilla_msg.send_SCI_REPLACETARGET(hCurrScintilla, len, &(decodedText[0]));
252 		}
253 	}
254 
255 extern (C)
256 nothrow
257 void convertToAsciiFromBase64()
258 
259 	do
260 	{
261 		.convertBase64ToAscii(false, false);
262 	}
263 
264 extern (C)
265 nothrow
266 void convertToAsciiFromBase64_strict()
267 
268 	do
269 	{
270 		.convertBase64ToAscii(true, false);
271 	}
272 
273 extern (C)
274 nothrow
275 void convertToAsciiFromBase64_whitespaceReset()
276 
277 	do
278 	{
279 		.convertBase64ToAscii(false, true);
280 	}
281 
282 extern (C)
283 nothrow
284 void convertURLMinEncode()
285 
286 	do
287 	{
288 		.convertURLEncode(false);
289 	}
290 
291 extern (C)
292 nothrow
293 void convertURLFullEncode()
294 
295 	do
296 	{
297 		.convertURLEncode(true);
298 	}
299 
300 nothrow
301 void convertURLEncode(bool encodeAll)
302 
303 	do
304 	{
305 		core.sys.windows.windef.HWND hCurrScintilla = .getCurrentScintillaHandle();
306 		size_t bufLength = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla);
307 
308 		if (bufLength == 0) {
309 			return;
310 		}
311 
312 		char[] selectedText = new char[bufLength];
313 		char[] pEncodedText = new char[bufLength * 3];
314 		npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla, &(selectedText[0]));
315 
316 		size_t len = npp_mimetools.url.AsciiToUrl(pEncodedText, selectedText, encodeAll);
317 		size_t start = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONSTART(hCurrScintilla);
318 		size_t end = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONEND(hCurrScintilla);
319 
320 		if (end < start) {
321 			size_t tmp = start;
322 			start = end;
323 			end = tmp;
324 		}
325 
326 		npp_api.pluginfunc.scintilla_msg.send_SCI_SETTARGETSTART(hCurrScintilla, start);
327 		npp_api.pluginfunc.scintilla_msg.send_SCI_SETTARGETEND(hCurrScintilla, end);
328 		npp_api.pluginfunc.scintilla_msg.send_SCI_REPLACETARGET(hCurrScintilla, len, &(pEncodedText[0]));
329 		npp_api.pluginfunc.scintilla_msg.send_SCI_SETSEL(hCurrScintilla, start, start + len);
330 	}
331 
332 extern (C)
333 nothrow
334 void convertURLDecode()
335 
336 	do
337 	{
338 		core.sys.windows.windef.HWND hCurrScintilla = .getCurrentScintillaHandle();
339 		size_t bufLength = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla);
340 
341 		if (bufLength == 0) {
342 			return;
343 		}
344 
345 		char[] selectedText = new char[bufLength];
346 		npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla, &(selectedText[0]));
347 		char[] pDecodedText = new char[bufLength];
348 
349 		size_t len = npp_mimetools.url.UrlToAscii(pDecodedText, selectedText);
350 
351 		if (len == 0) {
352 			core.sys.windows.winuser.MessageBoxW(nppData._nppHandle, &(npp_api.pluginfunc..string.c_wstring!(`Encoding Invalid!`)[0]), &(npp_api.pluginfunc..string.c_wstring!(`URL Decode`)[0]), core.sys.windows.winuser.MB_OK);
353 		} else {
354 			size_t start = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONSTART(hCurrScintilla);
355 			size_t end = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONEND(hCurrScintilla);
356 
357 			if (end < start) {
358 				size_t tmp = start;
359 				start = end;
360 				end = tmp;
361 			}
362 
363 			npp_api.pluginfunc.scintilla_msg.send_SCI_SETTARGETSTART(hCurrScintilla, start);
364 			npp_api.pluginfunc.scintilla_msg.send_SCI_SETTARGETEND(hCurrScintilla, end);
365 			npp_api.pluginfunc.scintilla_msg.send_SCI_REPLACETARGET(hCurrScintilla, len, &(pDecodedText[0]));
366 			npp_api.pluginfunc.scintilla_msg.send_SCI_SETSEL(hCurrScintilla, start, start + len);
367 		}
368 	}
369 
370 enum qpOp
371 {
372 	qp_encode,
373 	qp_decode,
374 }
375 
376 nothrow
377 void quotedPrintableConvert(.qpOp op)
378 
379 	do
380 	{
381 		core.sys.windows.windef.HWND hCurrScintilla = .getCurrentScintillaHandle();
382 		size_t bufLength = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla);
383 
384 		if (bufLength == 0) {
385 			return;
386 		}
387 
388 		char[] selectedText = new char[bufLength];
389 		npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla, &(selectedText[0]));
390 
391 		string qpText;
392 		npp_mimetools.qp.QuotedPrintable qp;
393 
394 		if (op == .qpOp.qp_decode) {
395 			qpText = qp.decode(selectedText);
396 
397 			if (qpText == null) {
398 				core.sys.windows.winuser.MessageBoxW(nppData._nppHandle, &(npp_api.pluginfunc..string.c_wstring!(`It's not a valid Quoted-printable text`)[0]), &(npp_api.pluginfunc..string.c_wstring!(`Quoted-printable decode error`)[0]), core.sys.windows.winuser.MB_OK);
399 
400 				return;
401 			}
402 		} else {
403 			qpText = qp.encode(selectedText);
404 		}
405 
406 		if (qpText ==  null) {
407 			core.sys.windows.winuser.MessageBoxW(nppData._nppHandle, &(npp_api.pluginfunc..string.c_wstring!(`Problem!`)[0]), &(npp_api.pluginfunc..string.c_wstring!(`Quoted-printable encoding`)[0]), core.sys.windows.winuser.MB_OK);
408 		} else {
409 			size_t start = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONSTART(hCurrScintilla);
410 			size_t end = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONEND(hCurrScintilla);
411 
412 			if (end < start) {
413 				size_t tmp = start;
414 				start = end;
415 				end = tmp;
416 			}
417 
418 			npp_api.pluginfunc.scintilla_msg.send_SCI_SETTARGETSTART(hCurrScintilla, start);
419 			npp_api.pluginfunc.scintilla_msg.send_SCI_SETTARGETEND(hCurrScintilla, end);
420 			npp_api.pluginfunc.scintilla_msg.send_SCI_REPLACETARGET(hCurrScintilla, core.stdc..string.strlen(&(qpText[0])), &(qpText[0]));
421 			npp_api.pluginfunc.scintilla_msg.send_SCI_SETSEL(hCurrScintilla, start, start + core.stdc..string.strlen(&(qpText[0])));
422 		}
423 	}
424 
425 extern (C)
426 nothrow
427 void convertToAsciiFromQuotedPrintable()
428 
429 	do
430 	{
431 		.quotedPrintableConvert(.qpOp.qp_decode);
432 	}
433 
434 extern (C)
435 nothrow
436 void convertToQuotedPrintable()
437 
438 	do
439 	{
440 		.quotedPrintableConvert(.qpOp.qp_encode);
441 	}
442 
443 extern (Windows)
444 nothrow @nogc
445 core.sys.windows.windef.BOOL dlgProc(core.sys.windows.windef.HWND hwnd, core.sys.windows.windef.UINT message, core.sys.windows.windef.WPARAM wParam, core.sys.windows.windef.LPARAM lParam)
446 
447 	do
448 	{
449 		switch (message) {
450 			case core.sys.windows.winuser.WM_COMMAND:
451 				switch (core.sys.windows.windef.LOWORD(wParam)) {
452 					case core.sys.windows.winuser.IDCLOSE:
453 						core.sys.windows.winuser.EndDialog(hwnd, 0);
454 
455 						return core.sys.windows.windef.TRUE;
456 
457 					default:
458 						break;
459 				}
460 
461 				return core.sys.windows.windef.FALSE;
462 
463 			default:
464 				break;
465 		}
466 
467 		return core.sys.windows.windef.FALSE;
468 	}
469 
470 extern (C)
471 nothrow @nogc
472 void about()
473 
474 	do
475 	{
476 		core.sys.windows.windef.HWND hSelf = core.sys.windows.winuser.CreateDialogParamW(_hInst, core.sys.windows.winuser.MAKEINTRESOURCEW(.IDD_ABOUTBOX), nppData._nppHandle, cast(core.sys.windows.winuser.DLGPROC)(&.dlgProc), 0);
477 
478 		// Go to center
479 		core.sys.windows.windef.RECT rc;
480 		core.sys.windows.winuser.GetClientRect(nppData._nppHandle, &rc);
481 		core.sys.windows.windef.POINT center;
482 		int w = rc.right - rc.left;
483 		int h = rc.bottom - rc.top;
484 		center.x = rc.left + w / 2;
485 		center.y = rc.top + h / 2;
486 		core.sys.windows.winuser.ClientToScreen(nppData._nppHandle, &center);
487 
488 		core.sys.windows.windef.RECT dlgRect;
489 		core.sys.windows.winuser.GetClientRect(hSelf, &dlgRect);
490 		int x = center.x - (dlgRect.right - dlgRect.left) / 2;
491 		int y = center.y - (dlgRect.bottom - dlgRect.top) / 2;
492 
493 		core.sys.windows.winuser.SetWindowPos(hSelf, core.sys.windows.winuser.HWND_TOP, x, y, (dlgRect.right - dlgRect.left), (dlgRect.bottom - dlgRect.top), core.sys.windows.winuser.SWP_SHOWWINDOW);
494 	}
495 
496 extern (C)
497 nothrow
498 void convertSamlDecode()
499 
500 	do
501 	{
502 		core.sys.windows.windef.HWND hCurrScintilla = .getCurrentScintillaHandle();
503 		size_t bufLength = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla);
504 
505 		if (bufLength == 0) {
506 			return;
507 		}
508 
509 		char[] selectedText = new char[bufLength];
510 		char[] samlDecodedText = new char[npp_mimetools.saml.SAML_MESSAGE_MAX_SIZE];
511 		npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla, &(selectedText[0]));
512 
513 		int len = npp_mimetools.saml.samlDecode(samlDecodedText, selectedText);
514 
515 		switch (len) {
516 			case 0:
517 				core.sys.windows.winuser.MessageBoxW(nppData._nppHandle, &(npp_api.pluginfunc..string.c_wstring!(`SAML Decode returned zero size.`)[0]), &(npp_api.pluginfunc..string.c_wstring!(`SAML Decode`)[0]), core.sys.windows.winuser.MB_OK);
518 
519 				break;
520 
521 			case npp_mimetools.saml.SAML_DECODE_ERROR_URLDECODE:
522 				core.sys.windows.winuser.MessageBoxW(nppData._nppHandle, &(npp_api.pluginfunc..string.c_wstring!(`Could not URL Decode text.`)[0]), &(npp_api.pluginfunc..string.c_wstring!(`SAML Decode`)[0]), core.sys.windows.winuser.MB_OK);
523 
524 				break;
525 
526 			case npp_mimetools.saml.SAML_DECODE_ERROR_BASE64DECODE:
527 				core.sys.windows.winuser.MessageBoxW(nppData._nppHandle, &(npp_api.pluginfunc..string.c_wstring!(`Could not BASE64 Decode text after URL Decoding.`)[0]), &(npp_api.pluginfunc..string.c_wstring!(`SAML Decode`)[0]), core.sys.windows.winuser.MB_OK);
528 
529 				break;
530 
531 			case npp_mimetools.saml.SAML_DECODE_ERROR_INFLATE:
532 				core.sys.windows.winuser.MessageBoxW(nppData._nppHandle, &(npp_api.pluginfunc..string.c_wstring!(`Could not inflate text after BASE64 Decoding.`)[0]), &(npp_api.pluginfunc..string.c_wstring!(`SAML Decode`)[0]), core.sys.windows.winuser.MB_OK);
533 
534 				break;
535 
536 			default:
537 				size_t start = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONSTART(hCurrScintilla);
538 				size_t end = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONEND(hCurrScintilla);
539 
540 				if (end < start) {
541 					size_t tmp = start;
542 					start = end;
543 					end = tmp;
544 				}
545 
546 				npp_api.pluginfunc.scintilla_msg.send_SCI_SETTARGETSTART(hCurrScintilla, start);
547 				npp_api.pluginfunc.scintilla_msg.send_SCI_SETTARGETEND(hCurrScintilla, end);
548 				npp_api.pluginfunc.scintilla_msg.send_SCI_REPLACETARGET(hCurrScintilla, len, &(samlDecodedText[0]));
549 				npp_api.pluginfunc.scintilla_msg.send_SCI_SETSEL(hCurrScintilla, start, start + len);
550 		}
551 	}
552 
553 mixin npp_api.pluginfunc.basic_interface.npp_plugin_interface!(PLUGIN_NAME, .main_menu_items_def);