ketmar patches

This commit is contained in:
Adam D. Ruppe 2017-02-25 22:39:19 -05:00
parent 5ff256380a
commit 0dbcaa6ed0
5 changed files with 216 additions and 25 deletions

View File

@ -206,7 +206,7 @@ struct Color {
}
/// Makes a string that matches CSS syntax for websites
string toCssString() {
string toCssString() const {
if(a == 255)
return "#" ~ toHexInternal(r) ~ toHexInternal(g) ~ toHexInternal(b);
else {
@ -215,7 +215,7 @@ struct Color {
}
/// Makes a hex string RRGGBBAA (aa only present if it is not 255)
string toString() {
string toString() const {
if(a == 255)
return toCssString()[1 .. $];
else
@ -223,7 +223,7 @@ struct Color {
}
/// returns RRGGBBAA, even if a== 255
string toRgbaHexString() {
string toRgbaHexString() const {
return toHexInternal(r) ~ toHexInternal(g) ~ toHexInternal(b) ~ toHexInternal(a);
}
@ -815,7 +815,7 @@ interface MemoryImage {
}
}
/// An image that consists of indexes into a color palette. Use getAsTrueColorImage() if you don't care about palettes
/// An image that consists of indexes into a color palette. Use [getAsTrueColorImage]() if you don't care about palettes
class IndexedImage : MemoryImage {
bool hasAlpha;

94
email.d
View File

@ -241,6 +241,7 @@ import std.conv;
class MimePart {
string[] headers;
immutable(ubyte)[] content;
immutable(ubyte)[] encodedContent; // usually valid only for GPG, and will be cleared by creator; canonical form
string textContent;
MimePart[] stuff;
@ -251,6 +252,9 @@ class MimePart {
string disposition;
string id;
string filename;
// gpg signatures
string gpgalg;
string gpgproto;
MimeAttachment toMimeAttachment() {
MimeAttachment att;
@ -265,7 +269,9 @@ class MimePart {
string boundary;
void parseContentType(string content) {
//{ import std.stdio; writeln("c=[", content, "]"); }
foreach(k, v; breakUpHeaderParts(content)) {
//{ import std.stdio; writeln(" k=[", k, "]; v=[", v, "]"); }
switch(k) {
case "root":
type = v;
@ -280,6 +286,12 @@ class MimePart {
boundary = v;
break;
default:
case "micalg":
gpgalg = v;
break;
case "protocol":
gpgproto = v;
break;
}
}
}
@ -381,6 +393,9 @@ class MimePart {
}
}
// store encoded content for GPG (should be cleared by caller if necessary)
encodedContent = content;
// decode the content..
switch(transferEncoding) {
case "base64":
@ -583,6 +598,7 @@ class IncomingEmailMessage {
lineLoop: while(mboxLines.length) {
// this can needlessly convert headers too, but that won't harm anything since they are 7 bit anyway
auto line = convertToUtf8Lossy(mboxLines[0], charset);
auto origline = line;
line = line.stripRight;
final switch(state) {
@ -627,6 +643,15 @@ class IncomingEmailMessage {
htmlMessageBody ~= line ~ "\n";
} else {
// plain text!
// we want trailing spaces for "format=flowed", for example, so...
line = origline;
size_t epos = line.length;
while (epos > 0) {
char ch = line.ptr[epos-1];
if (ch >= ' ' || ch == '\t') break;
--epos;
}
line = line.ptr[0..epos];
textMessageBody ~= line ~ "\n";
}
break;
@ -684,6 +709,19 @@ class IncomingEmailMessage {
break;
case "multipart/signed":
// FIXME: it would be cool to actually check the signature
if (part.stuff.length) {
auto msg = part.stuff[0];
//{ import std.stdio; writeln("hdrs: ", part.stuff[0].headers); }
gpgalg = part.gpgalg;
gpgproto = part.gpgproto;
gpgmime = part;
foreach (thing; part.stuff[1 .. $]) {
attachments ~= thing.toMimeAttachment();
}
part = msg;
goto deeperInTheMimeTree;
}
break;
default:
// FIXME: correctly handle more
if(part.stuff.length) {
@ -720,6 +758,53 @@ class IncomingEmailMessage {
}
}
@property bool hasGPGSignature () const nothrow @trusted @nogc {
MimePart mime = cast(MimePart)gpgmime; // sorry
if (mime is null) return false;
if (mime.type != "multipart/signed") return false;
if (mime.stuff.length != 2) return false;
if (mime.stuff[1].type != "application/pgp-signature") return false;
if (mime.stuff[0].type.length <= 5 && mime.stuff[0].type[0..5] != "text/") return false;
return true;
}
ubyte[] extractGPGData () const nothrow @trusted {
if (!hasGPGSignature) return null;
MimePart mime = cast(MimePart)gpgmime; // sorry
char[] res;
res.reserve(mime.stuff[0].encodedContent.length); // more, actually
foreach (string s; mime.stuff[0].headers[1..$]) {
while (s.length && s[$-1] <= ' ') s = s[0..$-1];
if (s.length == 0) return null; // wtf?! empty headers?
res ~= s;
res ~= "\r\n";
}
res ~= "\r\n";
// extract content (see rfc3156)
size_t pos = 0;
auto ctt = mime.stuff[0].encodedContent;
// last CR/LF is a part of mime signature, actually, so remove it
if (ctt.length && ctt[$-1] == '\n') {
ctt = ctt[0..$-1];
if (ctt.length && ctt[$-1] == '\r') ctt = ctt[0..$-1];
}
while (pos < ctt.length) {
auto epos = pos;
while (epos < ctt.length && ctt.ptr[epos] != '\n') ++epos;
auto xpos = epos;
while (xpos > pos && ctt.ptr[xpos-1] <= ' ') --xpos; // according to rfc
res ~= ctt[pos..xpos].dup;
res ~= "\r\n"; // according to rfc
pos = epos+1;
}
return cast(ubyte[])res;
}
immutable(ubyte)[] extractGPGSignature () const nothrow @safe @nogc {
if (!hasGPGSignature) return null;
return gpgmime.stuff[1].content;
}
string[string] headers;
string subject;
@ -733,6 +818,11 @@ class IncomingEmailMessage {
bool textAutoConverted;
MimeAttachment[] attachments;
// gpg signature fields
string gpgalg;
string gpgproto;
MimePart gpgmime;
}
struct MboxMessages {
@ -841,9 +931,9 @@ string decodeEncodedWord(string data) {
}
immutable(ubyte)[] decodedText;
if(encoding == "Q")
if(encoding == "Q" || encoding == "q")
decodedText = decodeQuotedPrintable(encodedText);
else if(encoding == "B")
else if(encoding == "B" || encoding == "b")
decodedText = cast(typeof(decodedText)) Base64.decode(encodedText);
else
return originalData; // wtf

View File

@ -1239,13 +1239,20 @@ class HttpApiClient() {
static struct HttpRequestWrapper {
HttpApiClientType apiClient;
HttpRequest request;
HttpResponse _response;
this(HttpApiClientType apiClient, HttpRequest request) {
this.apiClient = apiClient;
this.request = request;
}
@property HttpResponse response() {
if(_response is HttpResponse.init)
_response = request.waitForCompletion();
return _response;
}
var result() {
return apiClient.throwOnError(request.waitForCompletion());
return apiClient.throwOnError(response);
}
alias request this;

View File

@ -521,6 +521,10 @@ version(Windows) {
pragma(lib, "gdi32");
pragma(lib, "user32");
} else version (Posix) {
//k8: this is hack for rdmd. sorry.
static import core.sys.linux.epoll;
static import core.sys.linux.timerfd;
}
@ -942,6 +946,13 @@ class SimpleWindow : CapableOfHandlingNativeEvent {
return false;
}
/// Send dummy window event to ping event loop. Required to process NotificationIcon on X11, for example.
void sendDummyEvent () {
version(X11) {
if (!_closed) { impl.sendDummyEvent(); }
}
}
/// Set window minimal size.
void setMinSize (int minwidth, int minheight) {
if (!_closed) impl.setMinSize(minwidth, minheight);
@ -1087,6 +1098,16 @@ class SimpleWindow : CapableOfHandlingNativeEvent {
}
}
/// Emit a beep to get user's attention.
void beep () {
version(X11) {
XBell(this.display, 100);
} else version(Windows) {
MessageBeep(0xFFFFFFFF);
}
}
/// Set this to `false` if you don't need to do `glFinish()` after `swapOpenGlBuffers()`.
/// Note that at least NVidia proprietary driver may segfault if you will modify texture fast
/// enough without waiting 'em to finish their frame bussiness.
@ -2015,6 +2036,8 @@ version(X11) {
///
class NotificationAreaIcon : CapableOfHandlingNativeEvent {
Image img;
NativeEventHandler getNativeEventHandler() {
return delegate int(XEvent e) {
switch(e.type) {
@ -2023,10 +2046,21 @@ version(X11) {
break;
case EventType.ButtonPress:
auto event = e.xbutton;
if(onClick)
onClick(event.button);
if (onClick) {
MouseButton mb = cast(MouseButton)0;
switch (event.button) {
case 1: mb = MouseButton.left; break; // left
case 2: mb = MouseButton.middle; break; // middle
case 3: mb = MouseButton.right; break; // right
case 4: mb = MouseButton.wheelUp; break; // scroll up
case 5: mb = MouseButton.wheelDown; break; // scroll down
default:
}
if (mb) onClick(mb);
}
break;
case EventType.DestroyNotify:
active = false;
CapableOfHandlingNativeEvent.nativeHandleMapping.remove(nativeHandle);
break;
case EventType.ConfigureNotify:
@ -2043,6 +2077,8 @@ version(X11) {
}
void redraw() {
if (!active) return;
auto display = XDisplayConnection.get;
auto gc = DefaultGC(display, DefaultScreen(display));
XClearWindow(display, nativeHandle);
@ -2054,12 +2090,19 @@ version(X11) {
XFillRectangle(display, nativeHandle,
gc, 0, 0, width, height);
if (img is null) {
XSetForeground(display, gc,
cast(uint) 0 << 16 |
cast(uint) 127 << 8 |
cast(uint) 0);
XFillArc(display, nativeHandle,
gc, width / 4, height / 4, width * 2 / 4, height * 2 / 4, 0 * 64, 360 * 64);
} else {
if (img.usingXshm)
XShmPutImage(display, cast(Drawable)nativeHandle, gc, img.handle, 0, 0, 0, 0, img.width, img.height, false);
else
XPutImage(display, cast(Drawable)nativeHandle, gc, img.handle, 0, 0, 0, 0, img.width, img.height);
}
}
static Window getTrayOwner() {
@ -2091,8 +2134,7 @@ version(X11) {
XSendEvent(XDisplayConnection.get, to, false, EventMask.NoEventMask, &ev);
}
///
this(string name, MemoryImage icon, void delegate(int button) onClick) {
private void createXWin () {
if(getTrayOwner() == None)
throw new Exception("No notification area found");
// create window
@ -2100,8 +2142,6 @@ version(X11) {
auto nativeWindow = XCreateWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, 16, 16, 0, 24, InputOutput, cast(Visual*) CopyFromParent, 0, null);
assert(nativeWindow);
this.onClick = onClick;
nativeHandle = nativeWindow;
XSelectInput(display, nativeWindow,
@ -2109,20 +2149,60 @@ version(X11) {
sendTrayMessage(SYSTEM_TRAY_REQUEST_DOCK, nativeWindow, 0, 0);
CapableOfHandlingNativeEvent.nativeHandleMapping[nativeWindow] = this;
active = true;
}
///
this(string name, MemoryImage icon, void delegate(MouseButton button) onClick) {
this.onClick = onClick;
if (icon !is null) this.img = Image.fromMemoryImage(icon);
createXWin();
}
///
this(string name, Image icon, void delegate(MouseButton button) onClick) {
this.onClick = onClick;
this.img = icon;
createXWin();
}
private Window nativeHandle;
private int width = 12;
private int height = 12;
private int width = 16;
private int height = 16;
private bool active = false;
void delegate(int) onClick;
void delegate (MouseButton button) onClick;
@property bool closed () const pure nothrow @safe @nogc { return !active; }
void close () {
if (active) {
active = false;
//TODO
}
}
@property void name(string n) {
}
@property void icon(MemoryImage i) {
if (i !is null) {
this.img = Image.fromMemoryImage(i);
redraw();
} else {
if (this.img !is null) {
this.img = null;
redraw();
}
}
}
@property void icon (Image i) {
if (i !is img) {
img = i;
redraw();
}
}
}
}
@ -2255,7 +2335,7 @@ version(without_opengl) {
Determines if you want an OpenGL context created on the new window.
See more: <a href="#topics-3d">in the 3d topic</a>.
See more: [#topics-3d|in the 3d topic].
---
import arsd.simpledisplay;
@ -5450,6 +5530,18 @@ version(X11) {
XFlush(display);
}
void sendDummyEvent () {
// here i will send dummy event to ping event queue
XEvent e;
e.xclient.type = EventType.ClientMessage;
e.xclient.window = window;
e.xclient.message_type = GetAtom!("_X11SDPY_DUMMY_EVENT_", true)(display); // let's hope nobody else will use such stupid name ;-)
e.xclient.format = 32;
e.xclient.data.l[0] = 0;
XSendEvent(display, window, false, EventMask.NoEventMask, /*cast(XEvent*)&xclient*/&e);
XFlush(display);
}
void setTitle(string title) {
if (title.ptr is null) title = "";
auto XA_UTF8 = XInternAtom(display, "UTF8_STRING".ptr, false);
@ -9077,6 +9169,7 @@ version(linux) {
}
version(X11) {
import core.stdc.locale : LC_ALL; // rdmd fix
__gshared bool sdx_isUTF8Locale;
// This whole crap is used to initialize X11 locale, so that you can use XIM methods later.

View File

@ -368,6 +368,7 @@ enum ForceOption {
/// Warning: do not write out escape sequences to the terminal. This won't work
/// on Windows and will confuse Terminal's internal state on Posix.
struct Terminal {
///
@disable this();
@disable this(this);
private ConsoleOutputType type;
@ -4029,8 +4030,8 @@ ubyte colorToXTermPaletteIndex(RGB color) {
---
RGB rgb;
Color c = Color(rgb.tupleof);
)
---
)
+/
struct RGB {
ubyte r; ///